Skip to content
Snippets Groups Projects
Unverified Commit 64b5c40c authored by dpsutton's avatar dpsutton Committed by GitHub
Browse files

Handle locationlization errors from java.util.Locale (#22226)


* Handle locationlization errors from java.util.Locale

Indonesian has code "id". But
```
;; java 11
(str (java.util.Locale/forLanguageTag "id")) => "in"

;; java 17
(str (java.util.Locale/forLanguageTag "id")) => "id"
```

And this was bad. Because the frontend sends us the language to use:
"id" for indonesian. We write that down. And then when we need to
construct the index.html page with the correct translations, instead of
using "id"-- the value we were given-- we construct a Locale from "id"
and then get the str value of that which is "in". We don't have a in.edn
file, there's no frontend in.json file, and moment.js has no in
localization. And this all happened because we kept this value we all
agreed upon in a java util class and then asked it for that value back.

* Localization names have hyphens, files underscores

* Added docstring

* docstrings

* Assert language id and POE header value in tests

* localized json files don't exist until a build step

Co-authored-by: default avatarAdam James <adam.vermeer2@gmail.com>
parent edca9029
No related branches found
No related tags found
No related merge requests found
......@@ -35,26 +35,26 @@
{"" {"Metabase" {"msgid" "Metabase"
"msgstr" ["Metabase"]}}}}))
(defn- localization-json-file-name [locale-or-name]
(format "frontend_client/app/locales/%s.json" (str (i18n/locale locale-or-name))))
(defn- localization-json-file-name [locale-string]
(format "frontend_client/app/locales/%s.json" (str/replace locale-string \- \_)))
(defn- load-localization* [locale-or-name]
(defn- load-localization* [locale-string]
(or
(when-let [locale-name (some-> locale-or-name str)]
(when-not (= locale-name "en")
(when locale-string
(when-not (= locale-string "en")
(try
(slurp (or (io/resource (localization-json-file-name locale-name))
(when-let [fallback-locale (i18n/fallback-locale locale-name)]
(slurp (or (io/resource (localization-json-file-name locale-string))
(when-let [fallback-locale (i18n/fallback-locale locale-string)]
(io/resource (localization-json-file-name (str fallback-locale))))
;; don't try to i18n the Exception message below, we have no locale to translate it to!
(throw (FileNotFoundException. (format "Locale '%s' not found." locale-name)))))
(throw (FileNotFoundException. (format "Locale '%s' not found." locale-string)))))
(catch Throwable e
(log/warn (.getMessage e))))))
(fallback-localization locale-or-name)))
(fallback-localization locale-string)))
(def ^:private ^{:arglists '([])} load-localization
"Load a JSON-encoded map of localized strings for the current user's Locale."
(comp (memoize load-localization*) #(some-> (i18n/user-locale) str)))
(comp (memoize load-localization*) #(i18n/user-locale-string)))
(defn- load-inline-js* [resource-name]
(slurp (io/resource (format "frontend_client/inline_js/%s.js" resource-name))))
......
......@@ -25,25 +25,35 @@
nil)
(def ^:dynamic *site-locale-override*
"Bind this to a string, keyword, or `Locale` to override the value returned by `site-locale`. For testing purposes,
"Bind this to a string, keyword to override the value returned by `site-locale`. For testing purposes,
such as when swapping out an application database temporarily, when the setting table may not even exist."
nil)
(defn site-locale-string
"The default locale string for this Metabase installation. Normally this is the value of the `site-locale` Setting,
which is also a string."
[]
(or *site-locale-override*
(i18n.impl/site-locale-from-setting)
"en"))
(defn user-locale-string
"Locale string we should *use* for the current User (e.g. `tru` messages) -- `*user-locale*` if bound, otherwise the
system locale as a string."
[]
(or *user-locale*
(site-locale-string)))
(defn site-locale
"The default locale for this Metabase installation. Normally this is the value of the `site-locale` Setting."
^Locale []
(locale (or *site-locale-override*
(i18n.impl/site-locale-from-setting)
;; if DB is not initialized yet fall back to English
"en")))
(locale (site-locale-string)))
(defn user-locale
"Locale we should *use* for the current User (e.g. `tru` messages) -- `*user-locale*` if bound, otherwise the system
locale."
^Locale []
(locale
(or *user-locale*
(site-locale))))
(locale (user-locale-string)))
(defn available-locales-with-names
"Returns all locale abbreviations and their full names"
......@@ -57,7 +67,8 @@
"Translate a string with the System locale."
[format-string & args]
(let [translated (apply translate (site-locale) format-string args)]
(log/tracef "Translated %s for site locale %s -> %s" (pr-str format-string) (pr-str (site-locale)) (pr-str translated))
(log/tracef "Translated %s for site locale %s -> %s"
(pr-str format-string) (pr-str (site-locale-string)) (pr-str translated))
translated))
(defn translate-user-locale
......@@ -65,7 +76,8 @@
[format-string & args]
(let [translated (apply translate (user-locale) format-string args)]
(log/tracef "Translating %s for user locale %s (site locale %s) -> %s"
(pr-str format-string) (pr-str (user-locale)) (pr-str (site-locale)) (pr-str translated))
(pr-str format-string) (pr-str (user-locale-string))
(pr-str (site-locale-string)) (pr-str translated))
translated))
(p.types/defrecord+ UserLocalizedString [format-string args]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment