@itwin/core-i18n 3.4.0-dev.9 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -1
- package/lib/cjs/ITwinLocalization.d.ts.map +1 -1
- package/lib/cjs/ITwinLocalization.js +26 -10
- package/lib/cjs/ITwinLocalization.js.map +1 -1
- package/lib/cjs/test/ITwinLocalization.test.d.ts +2 -0
- package/lib/cjs/test/ITwinLocalization.test.d.ts.map +1 -0
- package/lib/cjs/test/ITwinLocalization.test.js +125 -0
- package/lib/cjs/test/ITwinLocalization.test.js.map +1 -0
- package/lib/cjs/test/webpack/bundled-tests.js +23328 -0
- package/lib/cjs/test/webpack/bundled-tests.js.map +1 -0
- package/lib/esm/ITwinLocalization.d.ts.map +1 -1
- package/lib/esm/ITwinLocalization.js +26 -10
- package/lib/esm/ITwinLocalization.js.map +1 -1
- package/lib/esm/test/ITwinLocalization.test.d.ts +2 -0
- package/lib/esm/test/ITwinLocalization.test.d.ts.map +1 -0
- package/lib/esm/test/ITwinLocalization.test.js +123 -0
- package/lib/esm/test/ITwinLocalization.test.js.map +1 -0
- package/package.json +22 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
# Change Log - @itwin/core-i18n
|
|
2
2
|
|
|
3
|
-
This log was last generated on
|
|
3
|
+
This log was last generated on Thu, 13 Oct 2022 20:24:47 GMT and should not be manually modified.
|
|
4
|
+
|
|
5
|
+
## 3.4.0
|
|
6
|
+
Thu, 13 Oct 2022 20:24:47 GMT
|
|
7
|
+
|
|
8
|
+
### Updates
|
|
9
|
+
|
|
10
|
+
- Add webpack:test to build:ci
|
|
11
|
+
- Replace deprecated i18next-xhr-backend with i18next-http-backend, and add some ITwinLocalization tests
|
|
12
|
+
- Updated Node types declaration to support latest v16
|
|
13
|
+
|
|
14
|
+
## 3.3.5
|
|
15
|
+
Tue, 27 Sep 2022 11:50:59 GMT
|
|
16
|
+
|
|
17
|
+
_Version update only_
|
|
18
|
+
|
|
19
|
+
## 3.3.4
|
|
20
|
+
Thu, 08 Sep 2022 19:00:05 GMT
|
|
21
|
+
|
|
22
|
+
_Version update only_
|
|
23
|
+
|
|
24
|
+
## 3.3.3
|
|
25
|
+
Tue, 06 Sep 2022 20:54:19 GMT
|
|
26
|
+
|
|
27
|
+
_Version update only_
|
|
28
|
+
|
|
29
|
+
## 3.3.2
|
|
30
|
+
Thu, 01 Sep 2022 14:37:22 GMT
|
|
31
|
+
|
|
32
|
+
_Version update only_
|
|
33
|
+
|
|
34
|
+
## 3.3.1
|
|
35
|
+
Fri, 26 Aug 2022 15:40:02 GMT
|
|
36
|
+
|
|
37
|
+
_Version update only_
|
|
38
|
+
|
|
39
|
+
## 3.3.0
|
|
40
|
+
Thu, 18 Aug 2022 19:08:02 GMT
|
|
41
|
+
|
|
42
|
+
_Version update only_
|
|
43
|
+
|
|
44
|
+
## 3.2.9
|
|
45
|
+
Fri, 26 Aug 2022 14:21:40 GMT
|
|
46
|
+
|
|
47
|
+
_Version update only_
|
|
4
48
|
|
|
5
49
|
## 3.2.8
|
|
6
50
|
Tue, 09 Aug 2022 15:52:41 GMT
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ITwinLocalization.d.ts","sourceRoot":"","sources":["../../src/ITwinLocalization.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAgB,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAuC,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnG,
|
|
1
|
+
{"version":3,"file":"ITwinLocalization.d.ts","sourceRoot":"","sources":["../../src/ITwinLocalization.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAgB,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAuC,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnG,OAAgB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,kBAAkB,CAAC,EAAE,cAAc,CAAC;IACpC,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,YAAY;IAC7C,OAAO,EAAE,IAAI,CAAC;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkB;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoC;gBAE7C,OAAO,CAAC,EAAE,mBAAmB;IA+BnC,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B5D;;;;;;;;;;;;;;;OAeG;IACI,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI7C;;;;;;;;;;;;OAYG;IACI,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM;IAcjF;;;;;;OAMG;IACI,+BAA+B,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM;IAcjH;;;;;;OAMG;IACI,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM;IAalG;;;OAGG;IACI,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS;IAInE,gBAAgB;IACT,eAAe,IAAI,SAAS,MAAM,EAAE;IAI3C,qDAAqD;IACxC,cAAc,CAAC,QAAQ,EAAE,MAAM;IAI5C;;;;;;;OAOG;IACU,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC3D,gBAAgB;IACT,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAG/C"}
|
|
@@ -13,8 +13,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.ITwinLocalization = void 0;
|
|
14
14
|
const i18next_1 = __importDefault(require("i18next"));
|
|
15
15
|
const i18next_browser_languagedetector_1 = __importDefault(require("i18next-browser-languagedetector"));
|
|
16
|
-
const
|
|
16
|
+
const i18next_http_backend_1 = __importDefault(require("i18next-http-backend"));
|
|
17
17
|
const core_bentley_1 = require("@itwin/core-bentley");
|
|
18
|
+
const DEFAULT_MAX_RETRIES = 1; // a low number prevents wasted time and potential timeouts when requesting localization files throws an error
|
|
18
19
|
/** Supplies localizations for iTwin.js
|
|
19
20
|
* @note this class uses the [i18next](https://www.i18next.com/) package.
|
|
20
21
|
* @public
|
|
@@ -38,20 +39,28 @@ class ITwinLocalization {
|
|
|
38
39
|
this._initOptions = {
|
|
39
40
|
interpolation: { escapeValue: true },
|
|
40
41
|
fallbackLng: "en",
|
|
42
|
+
maxRetries: DEFAULT_MAX_RETRIES,
|
|
41
43
|
backend: this._backendOptions,
|
|
42
44
|
detection: this._detectionOptions,
|
|
43
45
|
...options === null || options === void 0 ? void 0 : options.initOptions,
|
|
44
46
|
};
|
|
45
47
|
this.i18next
|
|
46
48
|
.use((_b = options === null || options === void 0 ? void 0 : options.detectorPlugin) !== null && _b !== void 0 ? _b : i18next_browser_languagedetector_1.default)
|
|
47
|
-
.use((_c = options === null || options === void 0 ? void 0 : options.backendPlugin) !== null && _c !== void 0 ? _c :
|
|
49
|
+
.use((_c = options === null || options === void 0 ? void 0 : options.backendPlugin) !== null && _c !== void 0 ? _c : i18next_http_backend_1.default)
|
|
48
50
|
.use(TranslationLogger);
|
|
49
51
|
}
|
|
50
52
|
async initialize(namespaces) {
|
|
53
|
+
var _a;
|
|
54
|
+
// Also consider namespaces passed into constructor
|
|
55
|
+
const initNamespaces = [this._initOptions.ns || []].flat();
|
|
56
|
+
const combinedNamespaces = new Set([...namespaces, ...initNamespaces]); // without duplicates
|
|
57
|
+
const defaultNamespace = (_a = this._initOptions.defaultNS) !== null && _a !== void 0 ? _a : namespaces[0];
|
|
58
|
+
if (defaultNamespace)
|
|
59
|
+
combinedNamespaces.add(defaultNamespace); // Make sure default namespace is in namespaces list
|
|
51
60
|
const initOptions = {
|
|
52
61
|
...this._initOptions,
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
defaultNS: defaultNamespace,
|
|
63
|
+
ns: [...combinedNamespaces],
|
|
55
64
|
};
|
|
56
65
|
// if in a development environment, set debugging
|
|
57
66
|
if (process.env.NODE_ENV === "development")
|
|
@@ -94,9 +103,13 @@ class ITwinLocalization {
|
|
|
94
103
|
* @public
|
|
95
104
|
*/
|
|
96
105
|
getLocalizedString(key, options) {
|
|
106
|
+
if ((options === null || options === void 0 ? void 0 : options.returnDetails) || (options === null || options === void 0 ? void 0 : options.returnObjects)) {
|
|
107
|
+
throw new Error("Translation key must map to a string, but the given options will result in an object");
|
|
108
|
+
}
|
|
97
109
|
const value = this.i18next.t(key, options);
|
|
98
|
-
if (typeof value !== "string")
|
|
99
|
-
throw new Error("Translation key(s) not found");
|
|
110
|
+
if (typeof value !== "string") {
|
|
111
|
+
throw new Error("Translation key(s) string not found");
|
|
112
|
+
}
|
|
100
113
|
return value;
|
|
101
114
|
}
|
|
102
115
|
/** Similar to `getLocalizedString` but the namespace is a separate param and the key does not include the namespace.
|
|
@@ -126,6 +139,9 @@ class ITwinLocalization {
|
|
|
126
139
|
* @internal
|
|
127
140
|
*/
|
|
128
141
|
getEnglishString(namespace, key, options) {
|
|
142
|
+
if ((options === null || options === void 0 ? void 0 : options.returnDetails) || (options === null || options === void 0 ? void 0 : options.returnObjects)) {
|
|
143
|
+
throw new Error("Translation key must map to a string, but the given options will result in an object");
|
|
144
|
+
}
|
|
129
145
|
const en = this.i18next.getFixedT("en", namespace);
|
|
130
146
|
const str = en(key, options);
|
|
131
147
|
if (typeof str !== "string")
|
|
@@ -165,10 +181,10 @@ class ITwinLocalization {
|
|
|
165
181
|
if (!err)
|
|
166
182
|
return resolve();
|
|
167
183
|
// Here we got a non-null err object.
|
|
168
|
-
// This method is called when the system has attempted to load the resources for the
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
// might be errs for some other namespaces as well as this one. We resolve the promise unless there's an error for each possible
|
|
184
|
+
// This method is called when the system has attempted to load the resources for the namespaces for each possible locale.
|
|
185
|
+
// For example 'fr-ca' might be the most specific locale, in which case 'fr' and 'en' are fallback locales.
|
|
186
|
+
// Using Backend from i18next-http-backend, err will be an array of strings of each namespace it tried to read and its locale.
|
|
187
|
+
// There might be errs for some other namespaces as well as this one. We resolve the promise unless there's an error for each possible locale.
|
|
172
188
|
let locales = this.getLanguageList().map((thisLocale) => `/${thisLocale}/`);
|
|
173
189
|
try {
|
|
174
190
|
for (const thisError of err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ITwinLocalization.js","sourceRoot":"","sources":["../../src/ITwinLocalization.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module Localization\r\n */\r\n\r\nimport i18next, { i18n, InitOptions, Module, TOptionsBase } from \"i18next\";\r\nimport i18nextBrowserLanguageDetector, { DetectorOptions } from \"i18next-browser-languagedetector\";\r\nimport { BackendOptions } from \"i18next-http-backend\";\r\nimport XHR from \"i18next-xhr-backend\";\r\nimport { Logger } from \"@itwin/core-bentley\";\r\nimport type { Localization } from \"@itwin/core-common\";\r\n\r\n/** Options for ITwinLocalization\r\n * @public\r\n */\r\nexport interface LocalizationOptions {\r\n urlTemplate?: string;\r\n backendPlugin?: Module;\r\n detectorPlugin?: Module;\r\n initOptions?: InitOptions;\r\n backendHttpOptions?: BackendOptions;\r\n detectorOptions?: DetectorOptions;\r\n}\r\n\r\n/** Supplies localizations for iTwin.js\r\n * @note this class uses the [i18next](https://www.i18next.com/) package.\r\n * @public\r\n */\r\nexport class ITwinLocalization implements Localization {\r\n public i18next: i18n;\r\n private readonly _initOptions: InitOptions;\r\n private readonly _backendOptions: BackendOptions;\r\n private readonly _detectionOptions: DetectorOptions;\r\n private readonly _namespaces = new Map<string, Promise<void>>();\r\n\r\n public constructor(options?: LocalizationOptions) {\r\n this.i18next = i18next.createInstance();\r\n\r\n this._backendOptions = {\r\n loadPath: options?.urlTemplate ?? \"locales/{{lng}}/{{ns}}.json\",\r\n crossDomain: true,\r\n ...options?.backendHttpOptions,\r\n };\r\n\r\n this._detectionOptions = {\r\n order: [\"querystring\", \"navigator\", \"htmlTag\"],\r\n lookupQuerystring: \"lng\",\r\n caches: [],\r\n ...options?.detectorOptions,\r\n };\r\n this._initOptions = {\r\n interpolation: { escapeValue: true },\r\n fallbackLng: \"en\",\r\n backend: this._backendOptions,\r\n detection: this._detectionOptions,\r\n ...options?.initOptions,\r\n };\r\n\r\n this.i18next\r\n .use(options?.detectorPlugin ?? i18nextBrowserLanguageDetector)\r\n .use(options?.backendPlugin ?? XHR)\r\n .use(TranslationLogger);\r\n }\r\n\r\n public async initialize(namespaces: string[]): Promise<void> {\r\n const initOptions: InitOptions = {\r\n ... this._initOptions,\r\n ns: namespaces,\r\n defaultNS: namespaces[0],\r\n };\r\n\r\n // if in a development environment, set debugging\r\n if (process.env.NODE_ENV === \"development\")\r\n initOptions.debug = true;\r\n\r\n const initPromise = this.i18next.init(initOptions) as unknown as Promise<void>;\r\n\r\n for (const ns of namespaces)\r\n this._namespaces.set(ns, initPromise);\r\n\r\n return initPromise;\r\n }\r\n\r\n /** Replace all instances of `%{key}` within a string with the translations of those keys.\r\n * For example:\r\n * ``` ts\r\n * \"MyKeys\": {\r\n * \"Key1\": \"First value\",\r\n * \"Key2\": \"Second value\"\r\n * }\r\n * ```\r\n *\r\n * ``` ts\r\n * i18.translateKeys(\"string with %{MyKeys.Key1} followed by %{MyKeys.Key2}!\"\") // returns \"string with First Value followed by Second Value!\"\r\n * ```\r\n * @param line The input line, potentially containing %{keys}.\r\n * @returns The line with all %{keys} translated\r\n * @public\r\n */\r\n public getLocalizedKeys(line: string): string {\r\n return line.replace(/\\%\\{(.+?)\\}/g, (_match, tag) => this.getLocalizedString(tag));\r\n }\r\n\r\n /** Return the translated value of a key.\r\n * @param key - the key that matches a property in the JSON localization file.\r\n * @note The key includes the namespace, which identifies the particular localization file that contains the property,\r\n * followed by a colon, followed by the property in the JSON file.\r\n * For example:\r\n * ``` ts\r\n * const dataString: string = IModelApp.localization.getLocalizedString(\"iModelJs:BackgroundMap.BingDataAttribution\");\r\n * ```\r\n * assigns to dataString the string with property BackgroundMap.BingDataAttribution from the iModelJs.json localization file.\r\n * @returns The string corresponding to the first key that resolves.\r\n * @throws Error if no keys resolve to a string.\r\n * @public\r\n */\r\n public getLocalizedString(key: string | string[], options?: TOptionsBase): string {\r\n const value = this.i18next.t(key, options);\r\n if (typeof value !== \"string\")\r\n throw new Error(\"Translation key(s) not found\");\r\n\r\n return value;\r\n }\r\n\r\n /** Similar to `getLocalizedString` but the namespace is a separate param and the key does not include the namespace.\r\n * @param namespace - the namespace that identifies the particular localization file that contains the property.\r\n * @param key - the key that matches a property in the JSON localization file.\r\n * @returns The string corresponding to the first key that resolves.\r\n * @throws Error if no keys resolve to a string.\r\n * @internal\r\n */\r\n public getLocalizedStringWithNamespace(namespace: string, key: string | string[], options?: TOptionsBase): string {\r\n let fullKey: string | string[] = \"\";\r\n\r\n if (typeof key === \"string\") {\r\n fullKey = `${namespace}:${key}`;\r\n } else {\r\n fullKey = key.map((subKey: string) => {\r\n return `${namespace}:${subKey}`;\r\n });\r\n }\r\n\r\n return this.getLocalizedString(fullKey, options);\r\n }\r\n\r\n /** Gets the English translation.\r\n * @param namespace - the namespace that identifies the particular localization file that contains the property.\r\n * @param key - the key that matches a property in the JSON localization file.\r\n * @returns The string corresponding to the first key that resolves.\r\n * @throws Error if no keys resolve to a string.\r\n * @internal\r\n */\r\n public getEnglishString(namespace: string, key: string | string[], options?: TOptionsBase): string {\r\n const en = this.i18next.getFixedT(\"en\", namespace);\r\n const str = en(key, options);\r\n if (typeof str !== \"string\")\r\n throw new Error(\"Translation key(s) not found\");\r\n\r\n return str;\r\n }\r\n\r\n /** Get the promise for an already registered Namespace.\r\n * @param name - the name of the namespace\r\n * @public\r\n */\r\n public getNamespacePromise(name: string): Promise<void> | undefined {\r\n return this._namespaces.get(name);\r\n }\r\n\r\n /** @internal */\r\n public getLanguageList(): readonly string[] {\r\n return this.i18next.languages;\r\n }\r\n\r\n /** override the language detected in the browser */\r\n public async changeLanguage(language: string) {\r\n return this.i18next.changeLanguage(language) as unknown as Promise<void>;\r\n }\r\n\r\n /** Register a new Namespace and return it. If the namespace is already registered, it will be returned.\r\n * @param name - the name of the namespace, which is the base name of the JSON file that contains the localization properties.\r\n * @note - The registerNamespace method starts fetching the appropriate version of the JSON localization file from the server,\r\n * based on the current locale. To make sure that fetch is complete before performing translations from this namespace, await\r\n * fulfillment of the readPromise Promise property of the returned LocalizationNamespace.\r\n * @see [Localization in iTwin.js]($docs/learning/frontend/Localization.md)\r\n * @public\r\n */\r\n public async registerNamespace(name: string): Promise<void> {\r\n const existing = this._namespaces.get(name);\r\n if (existing !== undefined)\r\n return existing;\r\n\r\n const theReadPromise = new Promise<void>((resolve) => {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this.i18next.loadNamespaces(name, (err) => {\r\n if (!err)\r\n return resolve();\r\n\r\n // Here we got a non-null err object.\r\n // This method is called when the system has attempted to load the resources for the namespace for each\r\n // possible locale. For example 'fr-ca' might be the most specific local, in which case 'fr' ) and 'en are fallback locales.\r\n // using i18next-xhr-backend, err will be an array of strings that includes the namespace it tried to read and the locale. There\r\n // might be errs for some other namespaces as well as this one. We resolve the promise unless there's an error for each possible language.\r\n let locales = this.getLanguageList().map((thisLocale: any) => `/${thisLocale}/`);\r\n\r\n try {\r\n for (const thisError of err) {\r\n if (typeof thisError === \"string\")\r\n locales = locales.filter((thisLocale) => !thisError.includes(thisLocale));\r\n }\r\n } catch (e) {\r\n locales = [];\r\n }\r\n // if we removed every locale from the array, it wasn't loaded.\r\n if (locales.length === 0)\r\n Logger.logError(\"i18n\", `No resources for namespace ${name} could be loaded`);\r\n\r\n resolve();\r\n });\r\n });\r\n this._namespaces.set(name, theReadPromise);\r\n return theReadPromise;\r\n }\r\n\r\n /** @internal */\r\n public unregisterNamespace(name: string): void {\r\n this._namespaces.delete(name);\r\n }\r\n}\r\n\r\nclass TranslationLogger {\r\n public static readonly type = \"logger\";\r\n public log(args: string[]) { Logger.logInfo(\"i18n\", this.createLogMessage(args)); }\r\n public warn(args: string[]) { Logger.logWarning(\"i18n\", this.createLogMessage(args)); }\r\n public error(args: string[]) { Logger.logError(\"i18n\", this.createLogMessage(args)); }\r\n private createLogMessage(args: string[]) {\r\n let message = args[0];\r\n for (let i = 1; i < args.length; ++i) {\r\n if (typeof args[i] === \"string\")\r\n message += `\\n ${args[i]}`;\r\n }\r\n return message;\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"ITwinLocalization.js","sourceRoot":"","sources":["../../src/ITwinLocalization.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module Localization\r\n */\r\n\r\nimport i18next, { i18n, InitOptions, Module, TOptionsBase } from \"i18next\";\r\nimport i18nextBrowserLanguageDetector, { DetectorOptions } from \"i18next-browser-languagedetector\";\r\nimport Backend, { BackendOptions } from \"i18next-http-backend\";\r\nimport { Logger } from \"@itwin/core-bentley\";\r\nimport type { Localization } from \"@itwin/core-common\";\r\n\r\nconst DEFAULT_MAX_RETRIES: number = 1; // a low number prevents wasted time and potential timeouts when requesting localization files throws an error\r\n\r\n/** Options for ITwinLocalization\r\n * @public\r\n */\r\nexport interface LocalizationOptions {\r\n urlTemplate?: string;\r\n backendPlugin?: Module;\r\n detectorPlugin?: Module;\r\n initOptions?: InitOptions;\r\n backendHttpOptions?: BackendOptions;\r\n detectorOptions?: DetectorOptions;\r\n}\r\n\r\n/** Supplies localizations for iTwin.js\r\n * @note this class uses the [i18next](https://www.i18next.com/) package.\r\n * @public\r\n */\r\nexport class ITwinLocalization implements Localization {\r\n public i18next: i18n;\r\n private readonly _initOptions: InitOptions;\r\n private readonly _backendOptions: BackendOptions;\r\n private readonly _detectionOptions: DetectorOptions;\r\n private readonly _namespaces = new Map<string, Promise<void>>();\r\n\r\n public constructor(options?: LocalizationOptions) {\r\n this.i18next = i18next.createInstance();\r\n\r\n this._backendOptions = {\r\n loadPath: options?.urlTemplate ?? \"locales/{{lng}}/{{ns}}.json\",\r\n crossDomain: true,\r\n ...options?.backendHttpOptions,\r\n };\r\n\r\n this._detectionOptions = {\r\n order: [\"querystring\", \"navigator\", \"htmlTag\"],\r\n lookupQuerystring: \"lng\",\r\n caches: [],\r\n ...options?.detectorOptions,\r\n };\r\n\r\n this._initOptions = {\r\n interpolation: { escapeValue: true },\r\n fallbackLng: \"en\",\r\n maxRetries: DEFAULT_MAX_RETRIES,\r\n backend: this._backendOptions,\r\n detection: this._detectionOptions,\r\n ...options?.initOptions,\r\n };\r\n\r\n this.i18next\r\n .use(options?.detectorPlugin ?? i18nextBrowserLanguageDetector)\r\n .use(options?.backendPlugin ?? Backend)\r\n .use(TranslationLogger);\r\n }\r\n\r\n public async initialize(namespaces: string[]): Promise<void> {\r\n\r\n // Also consider namespaces passed into constructor\r\n const initNamespaces: string[] = [this._initOptions.ns || []].flat();\r\n const combinedNamespaces: Set<string> = new Set([...namespaces, ...initNamespaces]); // without duplicates\r\n\r\n const defaultNamespace: string | false | readonly string[] = this._initOptions.defaultNS ?? namespaces[0];\r\n if (defaultNamespace)\r\n combinedNamespaces.add(defaultNamespace as string); // Make sure default namespace is in namespaces list\r\n\r\n const initOptions: InitOptions = {\r\n ...this._initOptions,\r\n defaultNS: defaultNamespace,\r\n ns: [...combinedNamespaces],\r\n };\r\n\r\n // if in a development environment, set debugging\r\n if (process.env.NODE_ENV === \"development\")\r\n initOptions.debug = true;\r\n\r\n const initPromise = this.i18next.init(initOptions) as unknown as Promise<void>;\r\n\r\n for (const ns of namespaces)\r\n this._namespaces.set(ns, initPromise);\r\n\r\n return initPromise;\r\n }\r\n\r\n /** Replace all instances of `%{key}` within a string with the translations of those keys.\r\n * For example:\r\n * ``` ts\r\n * \"MyKeys\": {\r\n * \"Key1\": \"First value\",\r\n * \"Key2\": \"Second value\"\r\n * }\r\n * ```\r\n *\r\n * ``` ts\r\n * i18.translateKeys(\"string with %{MyKeys.Key1} followed by %{MyKeys.Key2}!\"\") // returns \"string with First Value followed by Second Value!\"\r\n * ```\r\n * @param line The input line, potentially containing %{keys}.\r\n * @returns The line with all %{keys} translated\r\n * @public\r\n */\r\n public getLocalizedKeys(line: string): string {\r\n return line.replace(/\\%\\{(.+?)\\}/g, (_match, tag) => this.getLocalizedString(tag));\r\n }\r\n\r\n /** Return the translated value of a key.\r\n * @param key - the key that matches a property in the JSON localization file.\r\n * @note The key includes the namespace, which identifies the particular localization file that contains the property,\r\n * followed by a colon, followed by the property in the JSON file.\r\n * For example:\r\n * ``` ts\r\n * const dataString: string = IModelApp.localization.getLocalizedString(\"iModelJs:BackgroundMap.BingDataAttribution\");\r\n * ```\r\n * assigns to dataString the string with property BackgroundMap.BingDataAttribution from the iModelJs.json localization file.\r\n * @returns The string corresponding to the first key that resolves.\r\n * @throws Error if no keys resolve to a string.\r\n * @public\r\n */\r\n public getLocalizedString(key: string | string[], options?: TOptionsBase): string {\r\n if (options?.returnDetails || options?.returnObjects) {\r\n throw new Error(\"Translation key must map to a string, but the given options will result in an object\");\r\n }\r\n\r\n const value = this.i18next.t(key, options);\r\n\r\n if (typeof value !== \"string\") {\r\n throw new Error(\"Translation key(s) string not found\");\r\n }\r\n\r\n return value;\r\n }\r\n\r\n /** Similar to `getLocalizedString` but the namespace is a separate param and the key does not include the namespace.\r\n * @param namespace - the namespace that identifies the particular localization file that contains the property.\r\n * @param key - the key that matches a property in the JSON localization file.\r\n * @returns The string corresponding to the first key that resolves.\r\n * @throws Error if no keys resolve to a string.\r\n * @internal\r\n */\r\n public getLocalizedStringWithNamespace(namespace: string, key: string | string[], options?: TOptionsBase): string {\r\n let fullKey: string | string[] = \"\";\r\n\r\n if (typeof key === \"string\") {\r\n fullKey = `${namespace}:${key}`;\r\n } else {\r\n fullKey = key.map((subKey: string) => {\r\n return `${namespace}:${subKey}`;\r\n });\r\n }\r\n\r\n return this.getLocalizedString(fullKey, options);\r\n }\r\n\r\n /** Gets the English translation.\r\n * @param namespace - the namespace that identifies the particular localization file that contains the property.\r\n * @param key - the key that matches a property in the JSON localization file.\r\n * @returns The string corresponding to the first key that resolves.\r\n * @throws Error if no keys resolve to a string.\r\n * @internal\r\n */\r\n public getEnglishString(namespace: string, key: string | string[], options?: TOptionsBase): string {\r\n if (options?.returnDetails || options?.returnObjects) {\r\n throw new Error(\"Translation key must map to a string, but the given options will result in an object\");\r\n }\r\n\r\n const en = this.i18next.getFixedT(\"en\", namespace);\r\n const str = en(key, options);\r\n if (typeof str !== \"string\")\r\n throw new Error(\"Translation key(s) not found\");\r\n\r\n return str;\r\n }\r\n\r\n /** Get the promise for an already registered Namespace.\r\n * @param name - the name of the namespace\r\n * @public\r\n */\r\n public getNamespacePromise(name: string): Promise<void> | undefined {\r\n return this._namespaces.get(name);\r\n }\r\n\r\n /** @internal */\r\n public getLanguageList(): readonly string[] {\r\n return this.i18next.languages;\r\n }\r\n\r\n /** override the language detected in the browser */\r\n public async changeLanguage(language: string) {\r\n return this.i18next.changeLanguage(language) as unknown as Promise<void>;\r\n }\r\n\r\n /** Register a new Namespace and return it. If the namespace is already registered, it will be returned.\r\n * @param name - the name of the namespace, which is the base name of the JSON file that contains the localization properties.\r\n * @note - The registerNamespace method starts fetching the appropriate version of the JSON localization file from the server,\r\n * based on the current locale. To make sure that fetch is complete before performing translations from this namespace, await\r\n * fulfillment of the readPromise Promise property of the returned LocalizationNamespace.\r\n * @see [Localization in iTwin.js]($docs/learning/frontend/Localization.md)\r\n * @public\r\n */\r\n public async registerNamespace(name: string): Promise<void> {\r\n const existing = this._namespaces.get(name);\r\n if (existing !== undefined)\r\n return existing;\r\n\r\n const theReadPromise = new Promise<void>((resolve) => {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this.i18next.loadNamespaces(name, (err) => {\r\n if (!err)\r\n return resolve();\r\n\r\n // Here we got a non-null err object.\r\n // This method is called when the system has attempted to load the resources for the namespaces for each possible locale.\r\n // For example 'fr-ca' might be the most specific locale, in which case 'fr' and 'en' are fallback locales.\r\n // Using Backend from i18next-http-backend, err will be an array of strings of each namespace it tried to read and its locale.\r\n // There might be errs for some other namespaces as well as this one. We resolve the promise unless there's an error for each possible locale.\r\n let locales = this.getLanguageList().map((thisLocale: any) => `/${thisLocale}/`);\r\n\r\n try {\r\n for (const thisError of err) {\r\n if (typeof thisError === \"string\")\r\n locales = locales.filter((thisLocale) => !thisError.includes(thisLocale));\r\n }\r\n } catch (e) {\r\n locales = [];\r\n }\r\n // if we removed every locale from the array, it wasn't loaded.\r\n if (locales.length === 0)\r\n Logger.logError(\"i18n\", `No resources for namespace ${name} could be loaded`);\r\n\r\n resolve();\r\n });\r\n });\r\n this._namespaces.set(name, theReadPromise);\r\n return theReadPromise;\r\n }\r\n\r\n /** @internal */\r\n public unregisterNamespace(name: string): void {\r\n this._namespaces.delete(name);\r\n }\r\n}\r\n\r\nclass TranslationLogger {\r\n public static readonly type = \"logger\";\r\n public log(args: string[]) { Logger.logInfo(\"i18n\", this.createLogMessage(args)); }\r\n public warn(args: string[]) { Logger.logWarning(\"i18n\", this.createLogMessage(args)); }\r\n public error(args: string[]) { Logger.logError(\"i18n\", this.createLogMessage(args)); }\r\n private createLogMessage(args: string[]) {\r\n let message = args[0];\r\n for (let i = 1; i < args.length; ++i) {\r\n if (typeof args[i] === \"string\")\r\n message += `\\n ${args[i]}`;\r\n }\r\n return message;\r\n }\r\n}\r\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITwinLocalization.test.d.ts","sourceRoot":"","sources":["../../../src/test/ITwinLocalization.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const chai_1 = require("chai");
|
|
8
|
+
const ITwinLocalization_1 = require("../ITwinLocalization");
|
|
9
|
+
describe("ITwinLocalization", () => {
|
|
10
|
+
let localization;
|
|
11
|
+
describe("#initialize", () => {
|
|
12
|
+
describe("with default namespace provided in constructor", () => {
|
|
13
|
+
let itwinLocalization;
|
|
14
|
+
before(() => {
|
|
15
|
+
itwinLocalization = new ITwinLocalization_1.ITwinLocalization({ initOptions: { defaultNS: "Default" } });
|
|
16
|
+
});
|
|
17
|
+
it("default namespace set when initialized with empty array", async () => {
|
|
18
|
+
await itwinLocalization.initialize([]);
|
|
19
|
+
chai_1.assert.equal(itwinLocalization.i18next.options.defaultNS, "Default");
|
|
20
|
+
});
|
|
21
|
+
it("default namespace not overridden by one namespace", async () => {
|
|
22
|
+
await itwinLocalization.initialize(["Test"]);
|
|
23
|
+
chai_1.assert.equal(itwinLocalization.i18next.options.defaultNS, "Default");
|
|
24
|
+
});
|
|
25
|
+
it("default namespace not overridden by two namespaces", async () => {
|
|
26
|
+
await itwinLocalization.initialize(["NotExist", "Test"]);
|
|
27
|
+
chai_1.assert.equal(itwinLocalization.i18next.options.defaultNS, "Default");
|
|
28
|
+
});
|
|
29
|
+
it("duplicate namespace value does not break default namespace", async () => {
|
|
30
|
+
await itwinLocalization.initialize(["Default"]);
|
|
31
|
+
chai_1.assert.equal(itwinLocalization.i18next.options.defaultNS, "Default");
|
|
32
|
+
});
|
|
33
|
+
it("default namespace is in list of all namespaces initalized with empty array", async () => {
|
|
34
|
+
var _a;
|
|
35
|
+
await itwinLocalization.initialize([]);
|
|
36
|
+
chai_1.assert.isTrue((_a = itwinLocalization.i18next.options.ns) === null || _a === void 0 ? void 0 : _a.includes("Default"));
|
|
37
|
+
});
|
|
38
|
+
it("default namespace is in list of all namespaces", async () => {
|
|
39
|
+
var _a;
|
|
40
|
+
await itwinLocalization.initialize(["Test"]);
|
|
41
|
+
chai_1.assert.isTrue((_a = itwinLocalization.i18next.options.ns) === null || _a === void 0 ? void 0 : _a.includes("Default"));
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
// The goal is not to test i18next's interpolation,
|
|
46
|
+
// but just to have some simple tests to make sure the
|
|
47
|
+
// basics work through the ITwinLocalization class.
|
|
48
|
+
// For interpolation options, see: https://www.i18next.com/translation-function/interpolation
|
|
49
|
+
describe("#getLocalizedString", () => {
|
|
50
|
+
before(async () => {
|
|
51
|
+
localization = new ITwinLocalization_1.ITwinLocalization();
|
|
52
|
+
await localization.initialize(["Default", "Test"]);
|
|
53
|
+
});
|
|
54
|
+
describe("Default Namespace", () => {
|
|
55
|
+
it("first level with no substitution", () => {
|
|
56
|
+
chai_1.assert.equal(localization.getLocalizedString("FirstTrivial"), "First level string (default)");
|
|
57
|
+
});
|
|
58
|
+
it("second level with no substitution", () => {
|
|
59
|
+
chai_1.assert.equal(localization.getLocalizedString("SecondTrivial.Test1"), "Second level string 1 (default)");
|
|
60
|
+
chai_1.assert.equal(localization.getLocalizedString("SecondTrivial.Test2"), "Second level string 2 (default)");
|
|
61
|
+
});
|
|
62
|
+
it("first level with substitution", () => {
|
|
63
|
+
chai_1.assert.equal(localization.getLocalizedString("FirstSubstitution1", { str: "CUSTOM1" }), "First level CUSTOM1 (default)");
|
|
64
|
+
chai_1.assert.equal(localization.getLocalizedString("FirstSubstitution1", { str: "CUSTOM2" }), "First level CUSTOM2 (default)");
|
|
65
|
+
chai_1.assert.equal(localization.getLocalizedString("FirstSubstitution2", { str1: "CUSTOM1", str2: "CUSTOM2" }), "First level CUSTOM1 and CUSTOM2 (default)");
|
|
66
|
+
});
|
|
67
|
+
it("second level with substitution", () => {
|
|
68
|
+
chai_1.assert.equal(localization.getLocalizedString("SecondSubstitution.Test1", { varA: "Variable1", varB: "Variable2" }), "Substitute Variable1 and Variable2 (default)");
|
|
69
|
+
chai_1.assert.equal(localization.getLocalizedString("SecondSubstitution.Test2", { varA: "Variable1", varB: "Variable2" }), "Reverse substitute Variable2 and Variable1 (default)");
|
|
70
|
+
});
|
|
71
|
+
it("first level missing key doesn't find a value", () => {
|
|
72
|
+
chai_1.assert.equal(localization.getLocalizedString("MissingKeyString"), "MissingKeyString");
|
|
73
|
+
});
|
|
74
|
+
it("second level missing key doesn't find a value", () => {
|
|
75
|
+
chai_1.assert.equal(localization.getLocalizedString("SecondTrivial.MissingString"), "SecondTrivial.MissingString");
|
|
76
|
+
chai_1.assert.equal(localization.getLocalizedString("MissingKeyObject.MissingString"), "MissingKeyObject.MissingString");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe("Given Namespace", () => {
|
|
80
|
+
it("first level with no substitution", () => {
|
|
81
|
+
chai_1.assert.equal(localization.getLocalizedString("Default:FirstTrivial"), "First level string (default)");
|
|
82
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:FirstTrivial"), "First level string (test)");
|
|
83
|
+
});
|
|
84
|
+
it("second level with no substitution", () => {
|
|
85
|
+
chai_1.assert.equal(localization.getLocalizedString("Default:SecondTrivial.Test1"), "Second level string 1 (default)");
|
|
86
|
+
chai_1.assert.equal(localization.getLocalizedString("Default:SecondTrivial.Test2"), "Second level string 2 (default)");
|
|
87
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:SecondTrivial.Test1"), "Second level string 1 (test)");
|
|
88
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:SecondTrivial.Test2"), "Second level string 2 (test)");
|
|
89
|
+
});
|
|
90
|
+
it("first level with substitution", () => {
|
|
91
|
+
chai_1.assert.equal(localization.getLocalizedString("Default:FirstSubstitution1", { str: "CUSTOM1" }), "First level CUSTOM1 (default)");
|
|
92
|
+
chai_1.assert.equal(localization.getLocalizedString("Default:FirstSubstitution1", { str: "CUSTOM2" }), "First level CUSTOM2 (default)");
|
|
93
|
+
chai_1.assert.equal(localization.getLocalizedString("Default:FirstSubstitution2", { str1: "CUSTOM1", str2: "CUSTOM2" }), "First level CUSTOM1 and CUSTOM2 (default)");
|
|
94
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:FirstSubstitution1", { str: "CUSTOM1" }), "First level CUSTOM1 (test)");
|
|
95
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:FirstSubstitution1", { str: "CUSTOM2" }), "First level CUSTOM2 (test)");
|
|
96
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:FirstSubstitution2", { str1: "CUSTOM1", str2: "CUSTOM2" }), "First level CUSTOM1 and CUSTOM2 (test)");
|
|
97
|
+
});
|
|
98
|
+
it("second level with substitution", () => {
|
|
99
|
+
chai_1.assert.equal(localization.getLocalizedString("Default:SecondSubstitution.Test1", { varA: "Variable1", varB: "Variable2" }), "Substitute Variable1 and Variable2 (default)");
|
|
100
|
+
chai_1.assert.equal(localization.getLocalizedString("Default:SecondSubstitution.Test2", { varA: "Variable1", varB: "Variable2" }), "Reverse substitute Variable2 and Variable1 (default)");
|
|
101
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:SecondSubstitution.Test1", { varA: "Variable1", varB: "Variable2" }), "Substitute Variable1 and Variable2 (test)");
|
|
102
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:SecondSubstitution.Test2", { varA: "Variable1", varB: "Variable2" }), "Reverse substitute Variable2 and Variable1 (test)");
|
|
103
|
+
});
|
|
104
|
+
it("first level missing key doesn't find a value", () => {
|
|
105
|
+
chai_1.assert.equal(localization.getLocalizedString("Default:MissingKeyString"), "MissingKeyString");
|
|
106
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:MissingKeyString"), "MissingKeyString");
|
|
107
|
+
});
|
|
108
|
+
it("second level missing key doesn't find a value", () => {
|
|
109
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:SecondTrivial.MissingString"), "SecondTrivial.MissingString");
|
|
110
|
+
chai_1.assert.equal(localization.getLocalizedString("Test:MissingKeyObject.MissingString"), "MissingKeyObject.MissingString");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe("Nonexisting Namespace", () => {
|
|
114
|
+
it("first level fails", () => {
|
|
115
|
+
chai_1.assert.equal(localization.getLocalizedString("Nonexisting:FirstTrivial"), "FirstTrivial");
|
|
116
|
+
chai_1.assert.equal(localization.getLocalizedString("Nonexisting:MissingKeyString"), "MissingKeyString");
|
|
117
|
+
});
|
|
118
|
+
it("second level fails", () => {
|
|
119
|
+
chai_1.assert.equal(localization.getLocalizedString("Nonexisting:SecondTrivial.Test1"), "SecondTrivial.Test1");
|
|
120
|
+
chai_1.assert.equal(localization.getLocalizedString("Nonexisting:Missing.String"), "Missing.String");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=ITwinLocalization.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITwinLocalization.test.js","sourceRoot":"","sources":["../../../src/test/ITwinLocalization.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { assert } from \"chai\";\r\nimport { Localization } from \"@itwin/core-common\";\r\nimport { ITwinLocalization } from \"../ITwinLocalization\";\r\n\r\ndescribe(\"ITwinLocalization\", () => {\r\n\r\n let localization: Localization;\r\n\r\n describe(\"#initialize\", () => {\r\n\r\n describe(\"with default namespace provided in constructor\", () => {\r\n let itwinLocalization: ITwinLocalization;\r\n before(() => {\r\n itwinLocalization = new ITwinLocalization({ initOptions: { defaultNS: \"Default\" } });\r\n });\r\n\r\n it(\"default namespace set when initialized with empty array\", async () => {\r\n await itwinLocalization.initialize([]);\r\n assert.equal(itwinLocalization.i18next.options.defaultNS, \"Default\");\r\n });\r\n\r\n it(\"default namespace not overridden by one namespace\", async () => {\r\n await itwinLocalization.initialize([\"Test\"]);\r\n assert.equal(itwinLocalization.i18next.options.defaultNS, \"Default\");\r\n });\r\n\r\n it(\"default namespace not overridden by two namespaces\", async () => {\r\n await itwinLocalization.initialize([\"NotExist\", \"Test\"]);\r\n assert.equal(itwinLocalization.i18next.options.defaultNS, \"Default\");\r\n });\r\n\r\n it(\"duplicate namespace value does not break default namespace\", async () => {\r\n await itwinLocalization.initialize([\"Default\"]);\r\n assert.equal(itwinLocalization.i18next.options.defaultNS, \"Default\");\r\n });\r\n\r\n it(\"default namespace is in list of all namespaces initalized with empty array\", async () => {\r\n await itwinLocalization.initialize([]);\r\n assert.isTrue(itwinLocalization.i18next.options.ns?.includes(\"Default\"));\r\n });\r\n\r\n it(\"default namespace is in list of all namespaces\", async () => {\r\n await itwinLocalization.initialize([\"Test\"]);\r\n assert.isTrue(itwinLocalization.i18next.options.ns?.includes(\"Default\"));\r\n });\r\n });\r\n });\r\n\r\n // The goal is not to test i18next's interpolation,\r\n // but just to have some simple tests to make sure the\r\n // basics work through the ITwinLocalization class.\r\n // For interpolation options, see: https://www.i18next.com/translation-function/interpolation\r\n describe(\"#getLocalizedString\", () => {\r\n\r\n before(async () => {\r\n localization = new ITwinLocalization();\r\n await localization.initialize([\"Default\", \"Test\"]);\r\n });\r\n\r\n describe(\"Default Namespace\", () => {\r\n\r\n it(\"first level with no substitution\", () => {\r\n assert.equal(localization.getLocalizedString(\"FirstTrivial\"), \"First level string (default)\");\r\n });\r\n\r\n it(\"second level with no substitution\", () => {\r\n assert.equal(localization.getLocalizedString(\"SecondTrivial.Test1\"), \"Second level string 1 (default)\");\r\n assert.equal(localization.getLocalizedString(\"SecondTrivial.Test2\"), \"Second level string 2 (default)\");\r\n });\r\n\r\n it(\"first level with substitution\", () => {\r\n assert.equal(localization.getLocalizedString(\"FirstSubstitution1\", { str: \"CUSTOM1\" }), \"First level CUSTOM1 (default)\");\r\n assert.equal(localization.getLocalizedString(\"FirstSubstitution1\", { str: \"CUSTOM2\" }), \"First level CUSTOM2 (default)\");\r\n assert.equal(localization.getLocalizedString(\"FirstSubstitution2\", { str1: \"CUSTOM1\", str2: \"CUSTOM2\" }), \"First level CUSTOM1 and CUSTOM2 (default)\");\r\n });\r\n\r\n it(\"second level with substitution\", () => {\r\n assert.equal(localization.getLocalizedString(\"SecondSubstitution.Test1\", { varA: \"Variable1\", varB: \"Variable2\" }), \"Substitute Variable1 and Variable2 (default)\");\r\n assert.equal(localization.getLocalizedString(\"SecondSubstitution.Test2\", { varA: \"Variable1\", varB: \"Variable2\" }), \"Reverse substitute Variable2 and Variable1 (default)\");\r\n });\r\n\r\n it(\"first level missing key doesn't find a value\", () => {\r\n assert.equal(localization.getLocalizedString(\"MissingKeyString\"), \"MissingKeyString\");\r\n });\r\n\r\n it(\"second level missing key doesn't find a value\", () => {\r\n assert.equal(localization.getLocalizedString(\"SecondTrivial.MissingString\"), \"SecondTrivial.MissingString\");\r\n assert.equal(localization.getLocalizedString(\"MissingKeyObject.MissingString\"), \"MissingKeyObject.MissingString\");\r\n });\r\n });\r\n\r\n describe(\"Given Namespace\", () => {\r\n\r\n it(\"first level with no substitution\", () => {\r\n assert.equal(localization.getLocalizedString(\"Default:FirstTrivial\"), \"First level string (default)\");\r\n assert.equal(localization.getLocalizedString(\"Test:FirstTrivial\"), \"First level string (test)\");\r\n });\r\n\r\n it(\"second level with no substitution\", () => {\r\n assert.equal(localization.getLocalizedString(\"Default:SecondTrivial.Test1\"), \"Second level string 1 (default)\");\r\n assert.equal(localization.getLocalizedString(\"Default:SecondTrivial.Test2\"), \"Second level string 2 (default)\");\r\n assert.equal(localization.getLocalizedString(\"Test:SecondTrivial.Test1\"), \"Second level string 1 (test)\");\r\n assert.equal(localization.getLocalizedString(\"Test:SecondTrivial.Test2\"), \"Second level string 2 (test)\");\r\n });\r\n\r\n it(\"first level with substitution\", () => {\r\n assert.equal(localization.getLocalizedString(\"Default:FirstSubstitution1\", { str: \"CUSTOM1\" }), \"First level CUSTOM1 (default)\");\r\n assert.equal(localization.getLocalizedString(\"Default:FirstSubstitution1\", { str: \"CUSTOM2\" }), \"First level CUSTOM2 (default)\");\r\n assert.equal(localization.getLocalizedString(\"Default:FirstSubstitution2\", { str1: \"CUSTOM1\", str2: \"CUSTOM2\" }), \"First level CUSTOM1 and CUSTOM2 (default)\");\r\n assert.equal(localization.getLocalizedString(\"Test:FirstSubstitution1\", { str: \"CUSTOM1\" }), \"First level CUSTOM1 (test)\");\r\n assert.equal(localization.getLocalizedString(\"Test:FirstSubstitution1\", { str: \"CUSTOM2\" }), \"First level CUSTOM2 (test)\");\r\n assert.equal(localization.getLocalizedString(\"Test:FirstSubstitution2\", { str1: \"CUSTOM1\", str2: \"CUSTOM2\" }), \"First level CUSTOM1 and CUSTOM2 (test)\");\r\n });\r\n\r\n it(\"second level with substitution\", () => {\r\n assert.equal(localization.getLocalizedString(\"Default:SecondSubstitution.Test1\", { varA: \"Variable1\", varB: \"Variable2\" }), \"Substitute Variable1 and Variable2 (default)\");\r\n assert.equal(localization.getLocalizedString(\"Default:SecondSubstitution.Test2\", { varA: \"Variable1\", varB: \"Variable2\" }), \"Reverse substitute Variable2 and Variable1 (default)\");\r\n assert.equal(localization.getLocalizedString(\"Test:SecondSubstitution.Test1\", { varA: \"Variable1\", varB: \"Variable2\" }), \"Substitute Variable1 and Variable2 (test)\");\r\n assert.equal(localization.getLocalizedString(\"Test:SecondSubstitution.Test2\", { varA: \"Variable1\", varB: \"Variable2\" }), \"Reverse substitute Variable2 and Variable1 (test)\");\r\n });\r\n\r\n it(\"first level missing key doesn't find a value\", () => {\r\n assert.equal(localization.getLocalizedString(\"Default:MissingKeyString\"), \"MissingKeyString\");\r\n assert.equal(localization.getLocalizedString(\"Test:MissingKeyString\"), \"MissingKeyString\");\r\n });\r\n\r\n it(\"second level missing key doesn't find a value\", () => {\r\n assert.equal(localization.getLocalizedString(\"Test:SecondTrivial.MissingString\"), \"SecondTrivial.MissingString\");\r\n assert.equal(localization.getLocalizedString(\"Test:MissingKeyObject.MissingString\"), \"MissingKeyObject.MissingString\");\r\n });\r\n });\r\n\r\n describe(\"Nonexisting Namespace\", () => {\r\n\r\n it(\"first level fails\", () => {\r\n assert.equal(localization.getLocalizedString(\"Nonexisting:FirstTrivial\"), \"FirstTrivial\");\r\n assert.equal(localization.getLocalizedString(\"Nonexisting:MissingKeyString\"), \"MissingKeyString\");\r\n });\r\n\r\n it(\"second level fails\", () => {\r\n assert.equal(localization.getLocalizedString(\"Nonexisting:SecondTrivial.Test1\"), \"SecondTrivial.Test1\");\r\n assert.equal(localization.getLocalizedString(\"Nonexisting:Missing.String\"), \"Missing.String\");\r\n });\r\n });\r\n });\r\n\r\n});\r\n"]}
|