From 0b500f7f4134439bd806375f87f9aed75c709a93 Mon Sep 17 00:00:00 2001 From: Daniel Naber Date: Sun, 13 Nov 2022 15:03:48 +0100 Subject: [PATCH] improve LibreOffice 7.4 integration by extending the output of the /languages endpoint and by also accepting codes like 'fr-FR' (#7421) --- .../main/java/org/languagetool/Languages.java | 44 ++++++++++++++++--- .../java/org/languagetool/server/ApiV2.java | 14 +++++- .../org/languagetool/server/ApiV2Test.java | 4 +- languagetool-standalone/CHANGES.md | 6 +++ 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/languagetool-core/src/main/java/org/languagetool/Languages.java b/languagetool-core/src/main/java/org/languagetool/Languages.java index 68fae90b263b..7c76e5a6b868 100644 --- a/languagetool-core/src/main/java/org/languagetool/Languages.java +++ b/languagetool-core/src/main/java/org/languagetool/Languages.java @@ -18,6 +18,7 @@ */ package org.languagetool; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.languagetool.noop.NoopLanguage; import org.languagetool.tools.MultiKeyProperties; @@ -217,23 +218,38 @@ public static Language getLanguageForShortCode(String langCode) { */ public static Language getLanguageForShortCode(String langCode, List noopLanguageCodes) { Language language = getLanguageForShortCodeOrNull(langCode); + if (language == null) { + // e.g. 'fr-FR' requested (happens with LibreOffice 7.4): + language = Languages.getLongCodeToLangMapping().get(langCode); + } if (language == null) { if (noopLanguageCodes.contains(langCode)) { return NOOP_LANGUAGE; } else { - List codes = new ArrayList<>(); - for (Language realLanguage : getStaticAndDynamicLanguages()) { - codes.add(realLanguage.getShortCodeWithCountryAndVariant()); - } - Collections.sort(codes); throw new IllegalArgumentException("'" + langCode + "' is not a language code known to LanguageTool." + - " Supported language codes are: " + String.join(", ", codes) + ". The list of languages is read from " + PROPERTIES_PATH + + " Supported language codes are: " + String.join(", ", getLangCodes()) + ". The list of languages is read from " + PROPERTIES_PATH + " in the Java classpath. See https://dev.languagetool.org/java-api for details."); } } return language; } + @NotNull + private static List getLangCodes() { + List codes = new ArrayList<>(); + for (Language realLanguage : getStaticAndDynamicLanguages()) { + codes.add(realLanguage.getShortCodeWithCountryAndVariant()); + } + Map longCodeToLang = getLongCodeToLangMapping(); + for (Map.Entry entry : longCodeToLang.entrySet()) { + if (!codes.contains(entry.getKey())) { + codes.add(entry.getKey()); + } + } + Collections.sort(codes); + return codes; + } + /** * Return whether a language with the given language code is supported. Which languages * are supported depends on the classpath when the {@code Language} object is initialized. @@ -270,6 +286,22 @@ public static Language getLanguageForLocale(Locale locale) { throw new RuntimeException("No appropriate language found, not even en-US. Supported languages: " + get()); } + /** + * For internal use only. + * Returns a mapping from {@code fr-FR} to its language etc. Used to support requests + * from LibreOffice 7.4, which sends these language codes. + */ + public static Map getLongCodeToLangMapping() { + Map map = new HashMap<>(); + List languages = Languages.get(); + for (Language language : languages) { + if (language.getCountries().length > 0 && !language.getCountries()[0].isEmpty()) { + map.put(language.getShortCode() + "-" + language.getCountries()[0], language); + } + } + return map; + } + @Nullable private static Language getLanguageForShortCodeOrNull(String langCode) { StringTools.assureSet(langCode, "langCode"); diff --git a/languagetool-server/src/main/java/org/languagetool/server/ApiV2.java b/languagetool-server/src/main/java/org/languagetool/server/ApiV2.java index bff7dc2dc5a0..f535f3d6891a 100644 --- a/languagetool-server/src/main/java/org/languagetool/server/ApiV2.java +++ b/languagetool-server/src/main/java/org/languagetool/server/ApiV2.java @@ -458,14 +458,26 @@ String getLanguages() throws IOException { try (JsonGenerator g = factory.createGenerator(sw)) { g.writeStartArray(); List languages = new ArrayList<>(Languages.get()); - languages.sort(Comparator.comparing(Language::getName)); + Set longCodes = new HashSet<>(); for (Language lang : languages) { g.writeStartObject(); g.writeStringField("name", lang.getName()); g.writeStringField("code", lang.getShortCode()); g.writeStringField("longCode", lang.getShortCodeWithCountryAndVariant()); + longCodes.add(lang.getShortCodeWithCountryAndVariant()); g.writeEndObject(); } + // add mappings like "fr-FR -> fr" because LibreOffice 7.4 needs them: + Map codeMap = Languages.getLongCodeToLangMapping(); + for (Map.Entry entry : codeMap.entrySet()) { + if (!longCodes.contains(entry.getKey())) { + g.writeStartObject(); + g.writeStringField("name", entry.getValue().getName()); + g.writeStringField("code", entry.getValue().getShortCode()); + g.writeStringField("longCode", entry.getKey()); + g.writeEndObject(); + } + } g.writeEndArray(); } return sw.toString(); diff --git a/languagetool-server/src/test/java/org/languagetool/server/ApiV2Test.java b/languagetool-server/src/test/java/org/languagetool/server/ApiV2Test.java index 1e3f3a8eabbf..aa035e72174d 100644 --- a/languagetool-server/src/test/java/org/languagetool/server/ApiV2Test.java +++ b/languagetool-server/src/test/java/org/languagetool/server/ApiV2Test.java @@ -41,7 +41,9 @@ public void testLanguages() throws IOException { assertTrue(json.contains("\"German (Germany)\"")); assertTrue(json.contains("\"de\"")); assertTrue(json.contains("\"de-DE\"")); - assertTrue(StringUtils.countMatches(json, "\"name\"") >= 43); + assertTrue(json.contains("\"fr\"")); + assertTrue(json.contains("\"fr-FR\"")); + assertTrue(StringUtils.countMatches(json, "\"name\"") >= 55); } @Test diff --git a/languagetool-standalone/CHANGES.md b/languagetool-standalone/CHANGES.md index e758a7246ede..ede1186fe852 100644 --- a/languagetool-standalone/CHANGES.md +++ b/languagetool-standalone/CHANGES.md @@ -4,6 +4,12 @@ ... +#### HTTP API / LT server + * The `/languages` endpoint now lists language codes like `fr-FR` and `es-ES` for languages + that actually don't have a variant (e.g. there is no `fr-CA`). These codes can also be used + for the `language` parameter when sending a request. `fr-FR` will internally be mapped + to `fr` etc. (https://github.com/languagetool-org/languagetool/issues/7421) + ### General * The `--api` parameter for the command-line version has been removed. It had long been deprecated and replaced by `--json`.