UNPKG

terriajs

Version:

Geospatial data visualization platform.

190 lines (172 loc) 6.94 kB
import i18next, { ReactOptions, TFunction } from "i18next"; import LanguageDetector from "i18next-browser-languagedetector"; import HttpApi, { RequestCallback } from "i18next-http-backend"; import { initReactI18next } from "react-i18next"; import isDefined from "../Core/isDefined"; export interface I18nBackendOptions { /** * A few overrides that would be useful from a TerriaMap. The * i18next-http-backend library is still in its early stages, so the * documentation is sparse - the types are a quick glean from upstream * `i18next-http-backend` source * */ crossDomain?: boolean; loadPath?: string; parse?: ( data: any, languages: string | [string], namespaces: string ) => { [key: string]: any }; request?: ( options: any, url: string, payload: any, callback: RequestCallback ) => void; } export interface I18nStartOptions { backend?: I18nBackendOptions; skipInit?: boolean; // skip initialising i18next. Used in CI } export interface LanguageConfiguration { enabled: boolean; debug: boolean; react: ReactOptions; languages: object; fallbackLanguage: string; changeLanguageOnStartWhen: string[]; lookupCookie?: string; /** Base URL for override namespace translation files. If set, this makes up the base URL for translation override files. Should end in / * * For example, if `overridesBaseUrl = "test/path/"`, then the full path for translation override files will be `"test/path/{{lng}}.json"` **/ overridesBaseUrl?: string; } const defaultLanguageConfiguration = { enabled: false, debug: false, react: { useSuspense: false }, languages: { en: "english" }, fallbackLanguage: "en", changeLanguageOnStartWhen: [ "querystring", "localStorage", "navigator", "htmlTag" ], lookupCookie: "i18next" }; class Internationalization { static initLanguage( languageConfiguration: LanguageConfiguration | undefined, /** * i18nOptions is explicitly a separate option from `languageConfiguration`, * as `languageConfiguration` can be serialised, but `i18nOptions` may have * some functions that are passed in from a TerriaMap */ i18StartOptions: I18nStartOptions | undefined, terriajsResourcesBaseUrl: string ): Promise<TFunction> { const languageConfig = Object.assign( defaultLanguageConfiguration, languageConfiguration ); /** * initialization of the language with i18next * * @param {Object} languageConfiguration configuration read from config.json * @param {Boolean} languageConfiguration.enabled is GUI language switching enabled * @param {Object} languageConfiguration.languages the languages to be used, example `{en: "english"} * @param {String} languageConfiguration.fallbackLanguage the language to be used on startup * @param {Array} languageConfiguration.changeLanguageOnStartWhen * @param {String} languageConfiguration.lookupCookie name of the cookie that handles i18n */ return i18next .use(HttpApi) .use(LanguageDetector) .use(initReactI18next) .init({ // use i18next-json-v3 as weblate still doesn't support v4 compatibilityJSON: "v3", debug: languageConfig.debug, react: languageConfig.react, fallbackLng: languageConfig.fallbackLanguage, // whitelist: Object.keys(languageConfig.languages), // deprecated supportedLngs: Object.keys(languageConfig.languages), // to allow en-US when only en is on the whitelist - nonExplicitWhitelist must be set to true /** * for anyone else confused as I was, `nonExplicitSupportedLngs` && * `load: "languageOnly"` seems functionally equivalent but perhaps this * will change in the future * * > also in my mind I conflated the config surrounding this a little * > bit with `load: "languageOnly"` - but upon looking at the source in * > i18next this confirms that they both intend to do similar things: * * https://github.com/i18next/i18next/blob/80a38100d21a7e7c1f9cb2acff5f709063027b9f/src/LanguageUtils.js#L78-L80 * * */ nonExplicitSupportedLngs: true, // to not look into a folder like /locals/en-US/... when en-US is detected, use load: "languageOnly" to avoid using Country-Code in path load: "languageOnly", // send not translated keys to endpoint saveMissing: false, // allow loading of internal trnaslation files and backend files partialBundledLanguages: true, /* This setting adds a posibility for users to override translations using their own translation json file stored in `TerriaMap/wwwroot/languages/{{lng}}/languageOverrides.json` It will first look in defaultNS for translation and then check the fallbackNS */ ns: ["translation", "languageOverrides"], defaultNS: "languageOverrides", fallbackNS: "translation", backend: Object.assign( { // Loads translation files from either a TerriaMap's languages assets or from TerriaJS' assets // Always load "translation" namespace from TerriaJS assets, and load "languageOverrides" namespace from the TerriaMap loadPath: function loadPath( [_lng]: string[], [namespace]: string[] ) { if (namespace === "translation") return `${terriajsResourcesBaseUrl}languages/{{lng}}/{{ns}}.json`; // Apply languageConfig.overridesBaseUrl to path for "languageOverrides" namespace if defined if ( namespace === "languageOverrides" && isDefined(languageConfig.overridesBaseUrl) ) { return `${languageConfig.overridesBaseUrl}{{lng}}.json`; } return "languages/{{lng}}/{{ns}}.json"; }, crossDomain: false }, { ...i18StartOptions?.backend } ), detection: { // order and from where user language should be detected order: languageConfig.changeLanguageOnStartWhen, // keys or params to lookup language from lookupQuerystring: "lng", lookupCookie: languageConfig.lookupCookie, lookupLocalStorage: "i18nextLng", // cache user language on caches: ["localStorage"], excludeCacheFor: ["cimode"] // languages to not persist (cookie, localStorage) // optional expire and domain for set cookie // cookieMinutes: 10, // cookieDomain: "myDomain", }, interpolation: { escapeValue: false // not needed for react as it escapes by default and not needed in node } }); } } export default Internationalization;