@jsverse/transloco 7.0.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.
Files changed (167) hide show
  1. package/CHANGELOG.md +1005 -0
  2. package/LICENSE +22 -0
  3. package/README.md +44 -0
  4. package/esm2022/index.mjs +20 -0
  5. package/esm2022/jsverse-transloco.mjs +5 -0
  6. package/esm2022/lib/browser-lang.mjs +28 -0
  7. package/esm2022/lib/get-fallbacks-loaders.mjs +13 -0
  8. package/esm2022/lib/helpers.mjs +104 -0
  9. package/esm2022/lib/lang-resolver.mjs +54 -0
  10. package/esm2022/lib/loader-component.component.mjs +21 -0
  11. package/esm2022/lib/resolve-loader.mjs +13 -0
  12. package/esm2022/lib/scope-resolver.mjs +24 -0
  13. package/esm2022/lib/shared.mjs +73 -0
  14. package/esm2022/lib/template-handler.mjs +28 -0
  15. package/esm2022/lib/transloco-fallback-strategy.mjs +26 -0
  16. package/esm2022/lib/transloco-lang.mjs +3 -0
  17. package/esm2022/lib/transloco-loading-template.mjs +3 -0
  18. package/esm2022/lib/transloco-missing-handler.mjs +18 -0
  19. package/esm2022/lib/transloco-scope.mjs +3 -0
  20. package/esm2022/lib/transloco-testing.module.mjs +76 -0
  21. package/esm2022/lib/transloco.config.mjs +37 -0
  22. package/esm2022/lib/transloco.directive.mjs +169 -0
  23. package/esm2022/lib/transloco.interceptor.mjs +17 -0
  24. package/esm2022/lib/transloco.loader.mjs +13 -0
  25. package/esm2022/lib/transloco.module.mjs +18 -0
  26. package/esm2022/lib/transloco.pipe.mjs +97 -0
  27. package/esm2022/lib/transloco.providers.mjs +92 -0
  28. package/esm2022/lib/transloco.service.mjs +579 -0
  29. package/esm2022/lib/transloco.transpiler.mjs +145 -0
  30. package/esm2022/lib/types.mjs +2 -0
  31. package/fesm2022/jsverse-transloco.mjs +1580 -0
  32. package/fesm2022/jsverse-transloco.mjs.map +1 -0
  33. package/index.d.ts +19 -0
  34. package/lib/browser-lang.d.ts +8 -0
  35. package/lib/get-fallbacks-loaders.d.ts +14 -0
  36. package/lib/helpers.d.ts +21 -0
  37. package/lib/lang-resolver.d.ts +32 -0
  38. package/lib/loader-component.component.d.ts +6 -0
  39. package/lib/resolve-loader.d.ts +10 -0
  40. package/lib/scope-resolver.d.ts +12 -0
  41. package/lib/shared.d.ts +17 -0
  42. package/lib/template-handler.d.ts +9 -0
  43. package/lib/transloco-fallback-strategy.d.ts +14 -0
  44. package/lib/transloco-lang.d.ts +2 -0
  45. package/lib/transloco-loading-template.d.ts +3 -0
  46. package/lib/transloco-missing-handler.d.ts +16 -0
  47. package/lib/transloco-scope.d.ts +3 -0
  48. package/lib/transloco-testing.module.d.ts +27 -0
  49. package/lib/transloco.config.d.ts +27 -0
  50. package/lib/transloco.directive.d.ts +50 -0
  51. package/lib/transloco.interceptor.d.ts +14 -0
  52. package/lib/transloco.loader.d.ts +15 -0
  53. package/lib/transloco.module.d.ts +8 -0
  54. package/lib/transloco.pipe.d.ts +23 -0
  55. package/lib/transloco.providers.d.ts +30 -0
  56. package/lib/transloco.service.d.ts +199 -0
  57. package/lib/transloco.transpiler.d.ts +58 -0
  58. package/lib/types.d.ts +45 -0
  59. package/package.json +54 -0
  60. package/schematics/src/assets/i18n/en.json +1 -0
  61. package/schematics/src/collection.json +59 -0
  62. package/schematics/src/component/index.d.ts +2 -0
  63. package/schematics/src/component/index.js +21 -0
  64. package/schematics/src/component/index.js.map +1 -0
  65. package/schematics/src/join/index.d.ts +3 -0
  66. package/schematics/src/join/index.js +79 -0
  67. package/schematics/src/join/index.js.map +1 -0
  68. package/schematics/src/join/schema.d.ts +28 -0
  69. package/schematics/src/join/schema.js +3 -0
  70. package/schematics/src/join/schema.js.map +1 -0
  71. package/schematics/src/join/schema.json +43 -0
  72. package/schematics/src/keys-manager/index.d.ts +5 -0
  73. package/schematics/src/keys-manager/index.js +97 -0
  74. package/schematics/src/keys-manager/index.js.map +1 -0
  75. package/schematics/src/keys-manager/schema.d.ts +19 -0
  76. package/schematics/src/keys-manager/schema.js +3 -0
  77. package/schematics/src/keys-manager/schema.js.map +1 -0
  78. package/schematics/src/keys-manager/schema.json +33 -0
  79. package/schematics/src/migrate/index.d.ts +3 -0
  80. package/schematics/src/migrate/index.js +21 -0
  81. package/schematics/src/migrate/index.js.map +1 -0
  82. package/schematics/src/migrate/ngx-translate-migration.d.ts +3 -0
  83. package/schematics/src/migrate/ngx-translate-migration.js +169 -0
  84. package/schematics/src/migrate/ngx-translate-migration.js.map +1 -0
  85. package/schematics/src/migrate/schema.d.ts +10 -0
  86. package/schematics/src/migrate/schema.js +3 -0
  87. package/schematics/src/migrate/schema.js.map +1 -0
  88. package/schematics/src/migrate/schema.json +23 -0
  89. package/schematics/src/ng-add/files/transloco-loader/transloco-loader.__ts__ +12 -0
  90. package/schematics/src/ng-add/files/transloco-module/transloco-root.module.__ts__ +24 -0
  91. package/schematics/src/ng-add/generators/http-loader.gen.d.ts +4 -0
  92. package/schematics/src/ng-add/generators/http-loader.gen.js +15 -0
  93. package/schematics/src/ng-add/generators/http-loader.gen.js.map +1 -0
  94. package/schematics/src/ng-add/generators/root-module.gen.d.ts +9 -0
  95. package/schematics/src/ng-add/generators/root-module.gen.js +31 -0
  96. package/schematics/src/ng-add/generators/root-module.gen.js.map +1 -0
  97. package/schematics/src/ng-add/generators/translation-files.gen.d.ts +4 -0
  98. package/schematics/src/ng-add/generators/translation-files.gen.js +23 -0
  99. package/schematics/src/ng-add/generators/translation-files.gen.js.map +1 -0
  100. package/schematics/src/ng-add/index.d.ts +3 -0
  101. package/schematics/src/ng-add/index.js +103 -0
  102. package/schematics/src/ng-add/index.js.map +1 -0
  103. package/schematics/src/ng-add/schema.d.ts +42 -0
  104. package/schematics/src/ng-add/schema.js +14 -0
  105. package/schematics/src/ng-add/schema.js.map +1 -0
  106. package/schematics/src/ng-add/schema.json +53 -0
  107. package/schematics/src/ng-migrate/index.d.ts +3 -0
  108. package/schematics/src/ng-migrate/index.js +14 -0
  109. package/schematics/src/ng-migrate/index.js.map +1 -0
  110. package/schematics/src/ng-migrate/ng-migrate.d.ts +5 -0
  111. package/schematics/src/ng-migrate/ng-migrate.js +106 -0
  112. package/schematics/src/ng-migrate/ng-migrate.js.map +1 -0
  113. package/schematics/src/ng-migrate/schema.d.ts +14 -0
  114. package/schematics/src/ng-migrate/schema.js +3 -0
  115. package/schematics/src/ng-migrate/schema.js.map +1 -0
  116. package/schematics/src/ng-migrate/schema.json +27 -0
  117. package/schematics/src/schematics.consts.d.ts +4 -0
  118. package/schematics/src/schematics.consts.js +8 -0
  119. package/schematics/src/schematics.consts.js.map +1 -0
  120. package/schematics/src/scope/index.d.ts +3 -0
  121. package/schematics/src/scope/index.js +101 -0
  122. package/schematics/src/scope/index.js.map +1 -0
  123. package/schematics/src/scope/schema.d.ts +28 -0
  124. package/schematics/src/scope/schema.js +3 -0
  125. package/schematics/src/scope/schema.js.map +1 -0
  126. package/schematics/src/scope/schema.json +84 -0
  127. package/schematics/src/split/index.d.ts +3 -0
  128. package/schematics/src/split/index.js +66 -0
  129. package/schematics/src/split/index.js.map +1 -0
  130. package/schematics/src/split/schema.d.ts +20 -0
  131. package/schematics/src/split/schema.js +3 -0
  132. package/schematics/src/split/schema.js.map +1 -0
  133. package/schematics/src/split/schema.json +35 -0
  134. package/schematics/src/types.d.ts +5 -0
  135. package/schematics/src/types.js +10 -0
  136. package/schematics/src/types.js.map +1 -0
  137. package/schematics/src/upgrade/index.d.ts +3 -0
  138. package/schematics/src/upgrade/index.js +19 -0
  139. package/schematics/src/upgrade/index.js.map +1 -0
  140. package/schematics/src/upgrade/schema.d.ts +6 -0
  141. package/schematics/src/upgrade/schema.js +3 -0
  142. package/schematics/src/upgrade/schema.js.map +1 -0
  143. package/schematics/src/upgrade/schema.json +16 -0
  144. package/schematics/src/upgrade/v2.d.ts +1 -0
  145. package/schematics/src/upgrade/v2.js +89 -0
  146. package/schematics/src/upgrade/v2.js.map +1 -0
  147. package/schematics/src/utils/array.d.ts +3 -0
  148. package/schematics/src/utils/array.js +12 -0
  149. package/schematics/src/utils/array.js.map +1 -0
  150. package/schematics/src/utils/config.d.ts +2 -0
  151. package/schematics/src/utils/config.js +13 -0
  152. package/schematics/src/utils/config.js.map +1 -0
  153. package/schematics/src/utils/find-module.d.ts +23 -0
  154. package/schematics/src/utils/find-module.js +110 -0
  155. package/schematics/src/utils/find-module.js.map +1 -0
  156. package/schematics/src/utils/package.d.ts +5 -0
  157. package/schematics/src/utils/package.js +19 -0
  158. package/schematics/src/utils/package.js.map +1 -0
  159. package/schematics/src/utils/projects.d.ts +8 -0
  160. package/schematics/src/utils/projects.js +56 -0
  161. package/schematics/src/utils/projects.js.map +1 -0
  162. package/schematics/src/utils/translations.d.ts +7 -0
  163. package/schematics/src/utils/translations.js +31 -0
  164. package/schematics/src/utils/translations.js.map +1 -0
  165. package/schematics/src/utils/transloco.d.ts +24 -0
  166. package/schematics/src/utils/transloco.js +93 -0
  167. package/schematics/src/utils/transloco.js.map +1 -0
@@ -0,0 +1,1580 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, Injectable, Injector, Inject, Optional, Component, Input, TemplateRef, ChangeDetectorRef, ElementRef, ViewContainerRef, Renderer2, Directive, Pipe, NgModule, makeEnvironmentProviders, APP_INITIALIZER } from '@angular/core';
3
+ import { of, take, from, map, Subject, BehaviorSubject, forkJoin, retry, tap, catchError, shareReplay, switchMap, combineLatest, EMPTY } from 'rxjs';
4
+ import { unflatten as unflatten$1, flatten as flatten$1 } from 'flat';
5
+
6
+ class DefaultLoader {
7
+ translations;
8
+ constructor(translations) {
9
+ this.translations = translations;
10
+ }
11
+ getTranslation(lang) {
12
+ return of(this.translations.get(lang) || {});
13
+ }
14
+ }
15
+ const TRANSLOCO_LOADER = new InjectionToken('TRANSLOCO_LOADER');
16
+
17
+ function getValue(obj, path) {
18
+ if (!obj) {
19
+ return obj;
20
+ }
21
+ /* For cases where the key is like: 'general.something.thing' */
22
+ if (Object.prototype.hasOwnProperty.call(obj, path)) {
23
+ return obj[path];
24
+ }
25
+ return path.split('.').reduce((p, c) => p?.[c], obj);
26
+ }
27
+ function setValue(obj, prop, val) {
28
+ obj = { ...obj };
29
+ const split = prop.split('.');
30
+ const lastIndex = split.length - 1;
31
+ split.reduce((acc, part, index) => {
32
+ if (index === lastIndex) {
33
+ acc[part] = val;
34
+ }
35
+ else {
36
+ acc[part] = Array.isArray(acc[part])
37
+ ? acc[part].slice()
38
+ : { ...acc[part] };
39
+ }
40
+ return acc && acc[part];
41
+ }, obj);
42
+ return obj;
43
+ }
44
+ function size(collection) {
45
+ if (!collection) {
46
+ return 0;
47
+ }
48
+ if (Array.isArray(collection)) {
49
+ return collection.length;
50
+ }
51
+ if (isObject(collection)) {
52
+ return Object.keys(collection).length;
53
+ }
54
+ return collection ? collection.length : 0;
55
+ }
56
+ function isEmpty(collection) {
57
+ return size(collection) === 0;
58
+ }
59
+ function isFunction(val) {
60
+ return typeof val === 'function';
61
+ }
62
+ function isString(val) {
63
+ return typeof val === 'string';
64
+ }
65
+ function isNumber(val) {
66
+ return typeof val === 'number';
67
+ }
68
+ function isObject(item) {
69
+ return !!item && typeof item === 'object' && !Array.isArray(item);
70
+ }
71
+ function coerceArray(value) {
72
+ return Array.isArray(value) ? value : [value];
73
+ }
74
+ /*
75
+ * @example
76
+ *
77
+ * given: path-to-happiness => pathToHappiness
78
+ * given: path_to_happiness => pathToHappiness
79
+ * given: path-to_happiness => pathToHappiness
80
+ *
81
+ */
82
+ function toCamelCase(str) {
83
+ return str
84
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => index == 0 ? word.toLowerCase() : word.toUpperCase())
85
+ .replace(/\s+|_|-|\//g, '');
86
+ }
87
+ function isBrowser() {
88
+ return typeof window !== 'undefined';
89
+ }
90
+ function isNil(value) {
91
+ return value === null || value === undefined;
92
+ }
93
+ function isDefined(value) {
94
+ return isNil(value) === false;
95
+ }
96
+ function toNumber(value) {
97
+ if (isNumber(value))
98
+ return value;
99
+ if (isString(value) && !isNaN(Number(value) - parseFloat(value))) {
100
+ return Number(value);
101
+ }
102
+ return null;
103
+ }
104
+ function isScopeObject(item) {
105
+ return item && typeof item.scope === 'string';
106
+ }
107
+ function isScopeArray(item) {
108
+ return Array.isArray(item) && item.every(isScopeObject);
109
+ }
110
+ function hasInlineLoader(item) {
111
+ return item && isObject(item.loader);
112
+ }
113
+ function unflatten(obj) {
114
+ return unflatten$1(obj);
115
+ }
116
+ function flatten(obj) {
117
+ return flatten$1(obj, { safe: true });
118
+ }
119
+
120
+ const TRANSLOCO_CONFIG = new InjectionToken('TRANSLOCO_CONFIG', {
121
+ providedIn: 'root',
122
+ factory: () => defaultConfig,
123
+ });
124
+ const defaultConfig = {
125
+ defaultLang: 'en',
126
+ reRenderOnLangChange: false,
127
+ prodMode: false,
128
+ failedRetries: 2,
129
+ fallbackLang: [],
130
+ availableLangs: [],
131
+ missingHandler: {
132
+ logMissingKey: true,
133
+ useFallbackTranslation: false,
134
+ allowEmpty: false,
135
+ },
136
+ flatten: {
137
+ aot: false,
138
+ },
139
+ interpolation: ['{{', '}}'],
140
+ };
141
+ function translocoConfig(config = {}) {
142
+ return {
143
+ ...defaultConfig,
144
+ ...config,
145
+ missingHandler: {
146
+ ...defaultConfig.missingHandler,
147
+ ...config.missingHandler,
148
+ },
149
+ flatten: {
150
+ ...defaultConfig.flatten,
151
+ ...config.flatten,
152
+ },
153
+ };
154
+ }
155
+
156
+ const TRANSLOCO_TRANSPILER = new InjectionToken('TRANSLOCO_TRANSPILER');
157
+ class DefaultTranspiler {
158
+ config = inject(TRANSLOCO_CONFIG, { optional: true }) ?? defaultConfig;
159
+ get interpolationMatcher() {
160
+ return resolveMatcher(this.config);
161
+ }
162
+ transpile({ value, params = {}, translation, key }) {
163
+ if (isString(value)) {
164
+ let paramMatch;
165
+ let parsedValue = value;
166
+ while ((paramMatch = this.interpolationMatcher.exec(parsedValue)) !== null) {
167
+ const [match, paramValue] = paramMatch;
168
+ parsedValue = parsedValue.replace(match, () => {
169
+ const match = paramValue.trim();
170
+ if (isDefined(params[match])) {
171
+ return params[match];
172
+ }
173
+ return isDefined(translation[match])
174
+ ? this.transpile({
175
+ params, translation, key,
176
+ value: translation[match]
177
+ })
178
+ : '';
179
+ });
180
+ }
181
+ return parsedValue;
182
+ }
183
+ else if (params) {
184
+ if (isObject(value)) {
185
+ value = this.handleObject({
186
+ value: value,
187
+ params,
188
+ translation,
189
+ key
190
+ });
191
+ }
192
+ else if (Array.isArray(value)) {
193
+ value = this.handleArray({ value, params, translation, key });
194
+ }
195
+ }
196
+ return value;
197
+ }
198
+ /**
199
+ *
200
+ * @example
201
+ *
202
+ * const en = {
203
+ * a: {
204
+ * b: {
205
+ * c: "Hello {{ value }}"
206
+ * }
207
+ * }
208
+ * }
209
+ *
210
+ * const params = {
211
+ * "b.c": { value: "Transloco "}
212
+ * }
213
+ *
214
+ * service.selectTranslate('a', params);
215
+ *
216
+ * // the first param will be the result of `en.a`.
217
+ * // the second param will be `params`.
218
+ * parser.transpile(value, params, {});
219
+ *
220
+ *
221
+ */
222
+ handleObject({ value, params = {}, translation, key }) {
223
+ let result = value;
224
+ Object.keys(params).forEach((p) => {
225
+ // transpile the value => "Hello Transloco"
226
+ const transpiled = this.transpile({
227
+ // get the value of "b.c" inside "a" => "Hello {{ value }}"
228
+ value: getValue(result, p),
229
+ // get the params of "b.c" => { value: "Transloco" }
230
+ params: getValue(params, p),
231
+ translation,
232
+ key
233
+ });
234
+ // set "b.c" to `transpiled`
235
+ result = setValue(result, p, transpiled);
236
+ });
237
+ return result;
238
+ }
239
+ handleArray({ value, ...rest }) {
240
+ return value.map((v) => this.transpile({
241
+ value: v,
242
+ ...rest
243
+ }));
244
+ }
245
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultTranspiler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
246
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultTranspiler });
247
+ }
248
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultTranspiler, decorators: [{
249
+ type: Injectable
250
+ }] });
251
+ function resolveMatcher(config) {
252
+ const [start, end] = config.interpolation;
253
+ return new RegExp(`${start}([^${start}${end}]*?)${end}`, 'g');
254
+ }
255
+ function getFunctionArgs(argsString) {
256
+ const splitted = argsString ? argsString.split(',') : [];
257
+ const args = [];
258
+ for (let i = 0; i < splitted.length; i++) {
259
+ let value = splitted[i].trim();
260
+ while (value[value.length - 1] === '\\') {
261
+ i++;
262
+ value = value.replace('\\', ',') + splitted[i];
263
+ }
264
+ args.push(value);
265
+ }
266
+ return args;
267
+ }
268
+ class FunctionalTranspiler extends DefaultTranspiler {
269
+ injector = inject(Injector);
270
+ transpile({ value, ...rest }) {
271
+ let transpiled = value;
272
+ if (isString(value)) {
273
+ transpiled = value.replace(/\[\[\s*(\w+)\((.*?)\)\s*]]/g, (match, functionName, args) => {
274
+ try {
275
+ const func = this.injector.get(functionName);
276
+ return func.transpile(...getFunctionArgs(args));
277
+ }
278
+ catch (e) {
279
+ let message = `There is an error in: '${value}'.
280
+ Check that the you used the right syntax in your translation and that the implementation of ${functionName} is correct.`;
281
+ if (e.message.includes('NullInjectorError')) {
282
+ message = `You are using the '${functionName}' function in your translation but no provider was found!`;
283
+ }
284
+ throw new Error(message);
285
+ }
286
+ });
287
+ }
288
+ return super.transpile({ value: transpiled, ...rest });
289
+ }
290
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: FunctionalTranspiler, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
291
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: FunctionalTranspiler });
292
+ }
293
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: FunctionalTranspiler, decorators: [{
294
+ type: Injectable
295
+ }] });
296
+
297
+ const TRANSLOCO_MISSING_HANDLER = new InjectionToken('TRANSLOCO_MISSING_HANDLER');
298
+ class DefaultHandler {
299
+ handle(key, config) {
300
+ if (config.missingHandler.logMissingKey && !config.prodMode) {
301
+ const msg = `Missing translation for '${key}'`;
302
+ console.warn(`%c ${msg}`, 'font-size: 12px; color: red');
303
+ }
304
+ return key;
305
+ }
306
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
307
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultHandler });
308
+ }
309
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultHandler, decorators: [{
310
+ type: Injectable
311
+ }] });
312
+
313
+ const TRANSLOCO_INTERCEPTOR = new InjectionToken('TRANSLOCO_INTERCEPTOR');
314
+ class DefaultInterceptor {
315
+ preSaveTranslation(translation) {
316
+ return translation;
317
+ }
318
+ preSaveTranslationKey(_, value) {
319
+ return value;
320
+ }
321
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
322
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultInterceptor });
323
+ }
324
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultInterceptor, decorators: [{
325
+ type: Injectable
326
+ }] });
327
+
328
+ const TRANSLOCO_FALLBACK_STRATEGY = new InjectionToken('TRANSLOCO_FALLBACK_STRATEGY');
329
+ class DefaultFallbackStrategy {
330
+ userConfig;
331
+ constructor(userConfig) {
332
+ this.userConfig = userConfig;
333
+ }
334
+ getNextLangs() {
335
+ const fallbackLang = this.userConfig.fallbackLang;
336
+ if (!fallbackLang) {
337
+ throw new Error('When using the default fallback, a fallback language must be provided in the config!');
338
+ }
339
+ return Array.isArray(fallbackLang) ? fallbackLang : [fallbackLang];
340
+ }
341
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultFallbackStrategy, deps: [{ token: TRANSLOCO_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable });
342
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultFallbackStrategy });
343
+ }
344
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: DefaultFallbackStrategy, decorators: [{
345
+ type: Injectable
346
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
347
+ type: Inject,
348
+ args: [TRANSLOCO_CONFIG]
349
+ }] }] });
350
+
351
+ /*
352
+ * @example
353
+ *
354
+ * given: lazy-page/en => lazy-page
355
+ *
356
+ */
357
+ function getScopeFromLang(lang) {
358
+ if (!lang) {
359
+ return '';
360
+ }
361
+ const split = lang.split('/');
362
+ split.pop();
363
+ return split.join('/');
364
+ }
365
+ /*
366
+ * @example
367
+ *
368
+ * given: lazy-page/en => en
369
+ *
370
+ */
371
+ function getLangFromScope(lang) {
372
+ if (!lang) {
373
+ return '';
374
+ }
375
+ return lang.split('/').pop();
376
+ }
377
+ /**
378
+ * @example
379
+ *
380
+ * getPipeValue('todos|scoped', 'scoped') [true, 'todos']
381
+ * getPipeValue('en|static', 'static') [true, 'en']
382
+ * getPipeValue('en', 'static') [false, 'en']
383
+ */
384
+ function getPipeValue(str, value, char = '|') {
385
+ if (isString(str)) {
386
+ const splitted = str.split(char);
387
+ const lastItem = splitted.pop();
388
+ return lastItem === value ? [true, splitted.toString()] : [false, lastItem];
389
+ }
390
+ return [false, ''];
391
+ }
392
+ function shouldListenToLangChanges(service, lang) {
393
+ const [hasStatic] = getPipeValue(lang, 'static');
394
+ if (!hasStatic) {
395
+ // If we didn't get 'lang|static' check if it's set in the global level
396
+ return !!service.config.reRenderOnLangChange;
397
+ }
398
+ // We have 'lang|static' so don't listen to lang changes
399
+ return false;
400
+ }
401
+ function listenOrNotOperator(listenToLangChange) {
402
+ return listenToLangChange ? (source) => source : take(1);
403
+ }
404
+ function prependScope(inlineLoader, scope) {
405
+ return Object.keys(inlineLoader).reduce((acc, lang) => {
406
+ acc[`${scope}/${lang}`] = inlineLoader[lang];
407
+ return acc;
408
+ }, {});
409
+ }
410
+ function resolveInlineLoader(providerScope, scope) {
411
+ return hasInlineLoader(providerScope)
412
+ ? prependScope(providerScope.loader, scope)
413
+ : undefined;
414
+ }
415
+ function getEventPayload(lang) {
416
+ return {
417
+ scope: getScopeFromLang(lang) || null,
418
+ langName: getLangFromScope(lang),
419
+ };
420
+ }
421
+
422
+ function resolveLoader(options) {
423
+ const { path, inlineLoader, mainLoader, data } = options;
424
+ if (inlineLoader) {
425
+ const pathLoader = inlineLoader[path];
426
+ if (isFunction(pathLoader) === false) {
427
+ throw `You're using an inline loader but didn't provide a loader for ${path}`;
428
+ }
429
+ return inlineLoader[path]().then((res) => res.default ? res.default : res);
430
+ }
431
+ return mainLoader.getTranslation(path, data);
432
+ }
433
+
434
+ function getFallbacksLoaders({ mainLoader, path, data, fallbackPath, inlineLoader, }) {
435
+ const paths = fallbackPath ? [path, fallbackPath] : [path];
436
+ return paths.map((path) => {
437
+ const loader = resolveLoader({ path, mainLoader, inlineLoader, data });
438
+ return from(loader).pipe(map((translation) => ({
439
+ translation,
440
+ lang: path,
441
+ })));
442
+ });
443
+ }
444
+
445
+ let service;
446
+ function translate(key, params = {}, lang) {
447
+ return service.translate(key, params, lang);
448
+ }
449
+ function translateObject(key, params = {}, lang) {
450
+ return service.translateObject(key, params, lang);
451
+ }
452
+ class TranslocoService {
453
+ loader;
454
+ parser;
455
+ missingHandler;
456
+ interceptor;
457
+ fallbackStrategy;
458
+ langChanges$;
459
+ subscription = null;
460
+ translations = new Map();
461
+ cache = new Map();
462
+ firstFallbackLang;
463
+ defaultLang = '';
464
+ availableLangs = [];
465
+ isResolvedMissingOnce = false;
466
+ lang;
467
+ failedLangs = new Set();
468
+ events = new Subject();
469
+ events$ = this.events.asObservable();
470
+ config;
471
+ constructor(loader, parser, missingHandler, interceptor, userConfig, fallbackStrategy) {
472
+ this.loader = loader;
473
+ this.parser = parser;
474
+ this.missingHandler = missingHandler;
475
+ this.interceptor = interceptor;
476
+ this.fallbackStrategy = fallbackStrategy;
477
+ if (!this.loader) {
478
+ this.loader = new DefaultLoader(this.translations);
479
+ }
480
+ service = this;
481
+ this.config = JSON.parse(JSON.stringify(userConfig));
482
+ this.setAvailableLangs(this.config.availableLangs || []);
483
+ this.setFallbackLangForMissingTranslation(this.config);
484
+ this.setDefaultLang(this.config.defaultLang);
485
+ this.lang = new BehaviorSubject(this.getDefaultLang());
486
+ // Don't use distinctUntilChanged as we need the ability to update
487
+ // the value when using setTranslation or setTranslationKeys
488
+ this.langChanges$ = this.lang.asObservable();
489
+ /**
490
+ * When we have a failure, we want to define the next language that succeeded as the active
491
+ */
492
+ this.subscription = this.events$.subscribe((e) => {
493
+ if (e.type === 'translationLoadSuccess' && e.wasFailure) {
494
+ this.setActiveLang(e.payload.langName);
495
+ }
496
+ });
497
+ }
498
+ getDefaultLang() {
499
+ return this.defaultLang;
500
+ }
501
+ setDefaultLang(lang) {
502
+ this.defaultLang = lang;
503
+ }
504
+ getActiveLang() {
505
+ return this.lang.getValue();
506
+ }
507
+ setActiveLang(lang) {
508
+ this.parser.onLangChanged?.(lang);
509
+ this.lang.next(lang);
510
+ this.events.next({
511
+ type: 'langChanged',
512
+ payload: getEventPayload(lang),
513
+ });
514
+ return this;
515
+ }
516
+ setAvailableLangs(langs) {
517
+ this.availableLangs = langs;
518
+ }
519
+ /**
520
+ * Gets the available languages.
521
+ *
522
+ * @returns
523
+ * An array of the available languages. Can be either a `string[]` or a `{ id: string; label: string }[]`
524
+ * depending on how the available languages are set in your module.
525
+ */
526
+ getAvailableLangs() {
527
+ return this.availableLangs;
528
+ }
529
+ load(path, options = {}) {
530
+ const cached = this.cache.get(path);
531
+ if (cached) {
532
+ return cached;
533
+ }
534
+ let loadTranslation;
535
+ const isScope = this._isLangScoped(path);
536
+ let scope;
537
+ if (isScope) {
538
+ scope = getScopeFromLang(path);
539
+ }
540
+ const loadersOptions = {
541
+ path,
542
+ mainLoader: this.loader,
543
+ inlineLoader: options.inlineLoader,
544
+ data: isScope ? { scope: scope } : undefined,
545
+ };
546
+ if (this.useFallbackTranslation(path)) {
547
+ // if the path is scope the fallback should be `scope/fallbackLang`;
548
+ const fallback = isScope
549
+ ? `${scope}/${this.firstFallbackLang}`
550
+ : this.firstFallbackLang;
551
+ const loaders = getFallbacksLoaders({
552
+ ...loadersOptions,
553
+ fallbackPath: fallback,
554
+ });
555
+ loadTranslation = forkJoin(loaders);
556
+ }
557
+ else {
558
+ const loader = resolveLoader(loadersOptions);
559
+ loadTranslation = from(loader);
560
+ }
561
+ const load$ = loadTranslation.pipe(retry(this.config.failedRetries), tap((translation) => {
562
+ if (Array.isArray(translation)) {
563
+ translation.forEach((t) => {
564
+ this.handleSuccess(t.lang, t.translation);
565
+ // Save the fallback in cache so we'll not create a redundant request
566
+ if (t.lang !== path) {
567
+ this.cache.set(t.lang, of({}));
568
+ }
569
+ });
570
+ return;
571
+ }
572
+ this.handleSuccess(path, translation);
573
+ }), catchError((error) => {
574
+ if (!this.config.prodMode) {
575
+ console.error(`Error while trying to load "${path}"`, error);
576
+ }
577
+ return this.handleFailure(path, options);
578
+ }), shareReplay(1));
579
+ this.cache.set(path, load$);
580
+ return load$;
581
+ }
582
+ /**
583
+ * Gets the instant translated value of a key
584
+ *
585
+ * @example
586
+ *
587
+ * translate<string>('hello')
588
+ * translate('hello', { value: 'value' })
589
+ * translate<string[]>(['hello', 'key'])
590
+ * translate('hello', { }, 'en')
591
+ * translate('scope.someKey', { }, 'en')
592
+ */
593
+ translate(key, params = {}, lang = this.getActiveLang()) {
594
+ if (!key)
595
+ return key;
596
+ const { scope, resolveLang } = this.resolveLangAndScope(lang);
597
+ if (Array.isArray(key)) {
598
+ return key.map((k) => this.translate(scope ? `${scope}.${k}` : k, params, resolveLang));
599
+ }
600
+ key = scope ? `${scope}.${key}` : key;
601
+ const translation = this.getTranslation(resolveLang);
602
+ const value = translation[key];
603
+ if (!value) {
604
+ return this._handleMissingKey(key, value, params);
605
+ }
606
+ return this.parser.transpile({
607
+ value, params, translation, key
608
+ });
609
+ }
610
+ /**
611
+ * Gets the translated value of a key as observable
612
+ *
613
+ * @example
614
+ *
615
+ * selectTranslate<string>('hello').subscribe(value => ...)
616
+ * selectTranslate<string>('hello', {}, 'es').subscribe(value => ...)
617
+ * selectTranslate<string>('hello', {}, 'todos').subscribe(value => ...)
618
+ * selectTranslate<string>('hello', {}, { scope: 'todos' }).subscribe(value => ...)
619
+ *
620
+ */
621
+ selectTranslate(key, params, lang, _isObject = false) {
622
+ let inlineLoader;
623
+ const load = (lang, options) => this.load(lang, options).pipe(map(() => _isObject
624
+ ? this.translateObject(key, params, lang)
625
+ : this.translate(key, params, lang)));
626
+ if (isNil(lang)) {
627
+ return this.langChanges$.pipe(switchMap((lang) => load(lang)));
628
+ }
629
+ if (isScopeArray(lang) || isScopeObject(lang)) {
630
+ // it's a scope object.
631
+ const providerScope = Array.isArray(lang) ? lang[0] : lang;
632
+ lang = providerScope.scope;
633
+ inlineLoader = resolveInlineLoader(providerScope, providerScope.scope);
634
+ }
635
+ lang = lang;
636
+ if (this.isLang(lang) || this.isScopeWithLang(lang)) {
637
+ return load(lang);
638
+ }
639
+ // it's a scope
640
+ const scope = lang;
641
+ return this.langChanges$.pipe(switchMap((lang) => load(`${scope}/${lang}`, { inlineLoader })));
642
+ }
643
+ /**
644
+ * Whether the scope with lang
645
+ *
646
+ * @example
647
+ *
648
+ * todos/en => true
649
+ * todos => false
650
+ */
651
+ isScopeWithLang(lang) {
652
+ return this.isLang(getLangFromScope(lang));
653
+ }
654
+ translateObject(key, params = {}, lang = this.getActiveLang()) {
655
+ if (isString(key) || Array.isArray(key)) {
656
+ const { resolveLang, scope } = this.resolveLangAndScope(lang);
657
+ if (Array.isArray(key)) {
658
+ return key.map((k) => this.translateObject(scope ? `${scope}.${k}` : k, params, resolveLang));
659
+ }
660
+ const translation = this.getTranslation(resolveLang);
661
+ key = scope ? `${scope}.${key}` : key;
662
+ const value = unflatten(this.getObjectByKey(translation, key));
663
+ /* If an empty object was returned we want to try and translate the key as a string and not an object */
664
+ return isEmpty(value)
665
+ ? this.translate(key, params, lang)
666
+ : this.parser.transpile({ value, params: params, translation, key });
667
+ }
668
+ const translations = [];
669
+ for (const [_key, _params] of this.getEntries(key)) {
670
+ translations.push(this.translateObject(_key, _params, lang));
671
+ }
672
+ return translations;
673
+ }
674
+ selectTranslateObject(key, params, lang) {
675
+ if (isString(key) || Array.isArray(key)) {
676
+ return this.selectTranslate(key, params, lang, true);
677
+ }
678
+ const [[firstKey, firstParams], ...rest] = this.getEntries(key);
679
+ /* In order to avoid subscribing multiple times to the load language event by calling selectTranslateObject for each pair,
680
+ * we listen to when the first key has been translated (the language is loaded) and translate the rest synchronously */
681
+ return this.selectTranslateObject(firstKey, firstParams, lang).pipe(map((value) => {
682
+ const translations = [value];
683
+ for (const [_key, _params] of rest) {
684
+ translations.push(this.translateObject(_key, _params, lang));
685
+ }
686
+ return translations;
687
+ }));
688
+ }
689
+ getTranslation(langOrScope) {
690
+ if (langOrScope) {
691
+ if (this.isLang(langOrScope)) {
692
+ return this.translations.get(langOrScope) || {};
693
+ }
694
+ else {
695
+ // This is a scope, build the scope value from the translation object
696
+ const { scope, resolveLang } = this.resolveLangAndScope(langOrScope);
697
+ const translation = this.translations.get(resolveLang) || {};
698
+ return this.getObjectByKey(translation, scope);
699
+ }
700
+ }
701
+ return this.translations;
702
+ }
703
+ /**
704
+ * Gets an object of translations for a given language
705
+ *
706
+ * @example
707
+ *
708
+ * selectTranslation().subscribe() - will return the current lang translation
709
+ * selectTranslation('es').subscribe()
710
+ * selectTranslation('admin-page').subscribe() - will return the current lang scope translation
711
+ * selectTranslation('admin-page/es').subscribe()
712
+ */
713
+ selectTranslation(lang) {
714
+ let language$ = this.langChanges$;
715
+ if (lang) {
716
+ const scopeLangSpecified = getLangFromScope(lang) !== lang;
717
+ if (this.isLang(lang) || scopeLangSpecified) {
718
+ language$ = of(lang);
719
+ }
720
+ else {
721
+ language$ = this.langChanges$.pipe(map((currentLang) => `${lang}/${currentLang}`));
722
+ }
723
+ }
724
+ return language$.pipe(switchMap((language) => this.load(language).pipe(map(() => this.getTranslation(language)))));
725
+ }
726
+ /**
727
+ * Sets or merge a given translation object to current lang
728
+ *
729
+ * @example
730
+ *
731
+ * setTranslation({ ... })
732
+ * setTranslation({ ... }, 'en')
733
+ * setTranslation({ ... }, 'es', { merge: false } )
734
+ * setTranslation({ ... }, 'todos/en', { merge: false } )
735
+ */
736
+ setTranslation(translation, lang = this.getActiveLang(), options = {}) {
737
+ const defaults = { merge: true, emitChange: true };
738
+ const mergedOptions = { ...defaults, ...options };
739
+ const scope = getScopeFromLang(lang);
740
+ /**
741
+ * If this isn't a scope we use the whole translation as is
742
+ * otherwise we need to flat the scope and use it
743
+ */
744
+ let flattenScopeOrTranslation = translation;
745
+ // Merged the scoped language into the active language
746
+ if (scope) {
747
+ const key = this.getMappedScope(scope);
748
+ flattenScopeOrTranslation = flatten({ [key]: translation });
749
+ }
750
+ const currentLang = scope ? getLangFromScope(lang) : lang;
751
+ const mergedTranslation = {
752
+ ...(mergedOptions.merge && this.getTranslation(currentLang)),
753
+ ...flattenScopeOrTranslation,
754
+ };
755
+ const flattenTranslation = this.config.flatten.aot
756
+ ? mergedTranslation
757
+ : flatten(mergedTranslation);
758
+ const withHook = this.interceptor.preSaveTranslation(flattenTranslation, currentLang);
759
+ this.translations.set(currentLang, withHook);
760
+ mergedOptions.emitChange && this.setActiveLang(this.getActiveLang());
761
+ }
762
+ /**
763
+ * Sets translation key with given value
764
+ *
765
+ * @example
766
+ *
767
+ * setTranslationKey('key', 'value')
768
+ * setTranslationKey('key.nested', 'value')
769
+ * setTranslationKey('key.nested', 'value', 'en')
770
+ * setTranslationKey('key.nested', 'value', 'en', { emitChange: false } )
771
+ */
772
+ setTranslationKey(key, value, options = {}) {
773
+ const lang = options.lang || this.getActiveLang();
774
+ const withHook = this.interceptor.preSaveTranslationKey(key, value, lang);
775
+ const newValue = {
776
+ [key]: withHook,
777
+ };
778
+ this.setTranslation(newValue, lang, { ...options, merge: true });
779
+ }
780
+ /**
781
+ * Sets the fallback lang for the currently active language
782
+ * @param fallbackLang
783
+ */
784
+ setFallbackLangForMissingTranslation({ fallbackLang, }) {
785
+ const lang = Array.isArray(fallbackLang) ? fallbackLang[0] : fallbackLang;
786
+ if (fallbackLang && this.useFallbackTranslation(lang)) {
787
+ this.firstFallbackLang = lang;
788
+ }
789
+ }
790
+ /**
791
+ * @internal
792
+ */
793
+ _handleMissingKey(key, value, params) {
794
+ if (this.config.missingHandler.allowEmpty && value === '') {
795
+ return '';
796
+ }
797
+ if (!this.isResolvedMissingOnce && this.useFallbackTranslation()) {
798
+ // We need to set it to true to prevent a loop
799
+ this.isResolvedMissingOnce = true;
800
+ const fallbackValue = this.translate(key, params, this.firstFallbackLang);
801
+ this.isResolvedMissingOnce = false;
802
+ return fallbackValue;
803
+ }
804
+ return this.missingHandler.handle(key, this.getMissingHandlerData(), params);
805
+ }
806
+ /**
807
+ * @internal
808
+ */
809
+ _isLangScoped(lang) {
810
+ return this.getAvailableLangsIds().indexOf(lang) === -1;
811
+ }
812
+ /**
813
+ * Checks if a given string is one of the specified available languages.
814
+ * @returns
815
+ * True if the given string is an available language.
816
+ * False if the given string is not an available language.
817
+ */
818
+ isLang(lang) {
819
+ return this.getAvailableLangsIds().indexOf(lang) !== -1;
820
+ }
821
+ /**
822
+ * @internal
823
+ *
824
+ * We always want to make sure the global lang is loaded
825
+ * before loading the scope since you can access both via the pipe/directive.
826
+ */
827
+ _loadDependencies(path, inlineLoader) {
828
+ const mainLang = getLangFromScope(path);
829
+ if (this._isLangScoped(path) && !this.isLoadedTranslation(mainLang)) {
830
+ return combineLatest([
831
+ this.load(mainLang),
832
+ this.load(path, { inlineLoader }),
833
+ ]);
834
+ }
835
+ return this.load(path, { inlineLoader });
836
+ }
837
+ /**
838
+ * @internal
839
+ */
840
+ _completeScopeWithLang(langOrScope) {
841
+ if (this._isLangScoped(langOrScope) &&
842
+ !this.isLang(getLangFromScope(langOrScope))) {
843
+ return `${langOrScope}/${this.getActiveLang()}`;
844
+ }
845
+ return langOrScope;
846
+ }
847
+ /**
848
+ * @internal
849
+ */
850
+ _setScopeAlias(scope, alias) {
851
+ if (!this.config.scopeMapping) {
852
+ this.config.scopeMapping = {};
853
+ }
854
+ this.config.scopeMapping[scope] = alias;
855
+ }
856
+ ngOnDestroy() {
857
+ if (this.subscription) {
858
+ this.subscription.unsubscribe();
859
+ // Caretaker note: it's important to clean up references to subscriptions since they save the `next`
860
+ // callback within its `destination` property, preventing classes from being GC'd.
861
+ this.subscription = null;
862
+ }
863
+ // Caretaker note: since this is the root provider, it'll be destroyed when the `NgModuleRef.destroy()` is run.
864
+ // Cached values capture `this`, thus leading to a circular reference and preventing the `TranslocoService` from
865
+ // being GC'd. This would lead to a memory leak when server-side rendering is used since the service is created
866
+ // and destroyed per each HTTP request, but any service is not getting GC'd.
867
+ this.cache.clear();
868
+ }
869
+ isLoadedTranslation(lang) {
870
+ return size(this.getTranslation(lang));
871
+ }
872
+ getAvailableLangsIds() {
873
+ const first = this.getAvailableLangs()[0];
874
+ if (isString(first)) {
875
+ return this.getAvailableLangs();
876
+ }
877
+ return this.getAvailableLangs().map((l) => l.id);
878
+ }
879
+ getMissingHandlerData() {
880
+ return {
881
+ ...this.config,
882
+ activeLang: this.getActiveLang(),
883
+ availableLangs: this.availableLangs,
884
+ defaultLang: this.defaultLang,
885
+ };
886
+ }
887
+ /**
888
+ * Use a fallback translation set for missing keys of the primary language
889
+ * This is unrelated to the fallback language (which changes the active language)
890
+ */
891
+ useFallbackTranslation(lang) {
892
+ return (this.config.missingHandler.useFallbackTranslation &&
893
+ lang !== this.firstFallbackLang);
894
+ }
895
+ handleSuccess(lang, translation) {
896
+ this.setTranslation(translation, lang, { emitChange: false });
897
+ this.events.next({
898
+ wasFailure: !!this.failedLangs.size,
899
+ type: 'translationLoadSuccess',
900
+ payload: getEventPayload(lang),
901
+ });
902
+ this.failedLangs.forEach((l) => this.cache.delete(l));
903
+ this.failedLangs.clear();
904
+ }
905
+ handleFailure(lang, loadOptions) {
906
+ // When starting to load a first choice language, initialize
907
+ // the failed counter and resolve the fallback langs.
908
+ if (isNil(loadOptions.failedCounter)) {
909
+ loadOptions.failedCounter = 0;
910
+ if (!loadOptions.fallbackLangs) {
911
+ loadOptions.fallbackLangs = this.fallbackStrategy.getNextLangs(lang);
912
+ }
913
+ }
914
+ const splitted = lang.split('/');
915
+ const fallbacks = loadOptions.fallbackLangs;
916
+ const nextLang = fallbacks[loadOptions.failedCounter];
917
+ this.failedLangs.add(lang);
918
+ // This handles the case where a loaded fallback language is requested again
919
+ if (this.cache.has(nextLang)) {
920
+ this.handleSuccess(nextLang, this.getTranslation(nextLang));
921
+ return EMPTY;
922
+ }
923
+ const isFallbackLang = nextLang === splitted[splitted.length - 1];
924
+ if (!nextLang || isFallbackLang) {
925
+ let msg = `Unable to load translation and all the fallback languages`;
926
+ if (splitted.length > 1) {
927
+ msg += `, did you misspelled the scope name?`;
928
+ }
929
+ throw new Error(msg);
930
+ }
931
+ let resolveLang = nextLang;
932
+ // if it's scoped lang
933
+ if (splitted.length > 1) {
934
+ // We need to resolve it to:
935
+ // todos/langNotExists => todos/nextLang
936
+ splitted[splitted.length - 1] = nextLang;
937
+ resolveLang = splitted.join('/');
938
+ }
939
+ loadOptions.failedCounter++;
940
+ this.events.next({
941
+ type: 'translationLoadFailure',
942
+ payload: getEventPayload(lang),
943
+ });
944
+ return this.load(resolveLang, loadOptions);
945
+ }
946
+ getMappedScope(scope) {
947
+ const { scopeMapping = {} } = this.config;
948
+ return scopeMapping[scope] || toCamelCase(scope);
949
+ }
950
+ /**
951
+ * If lang is scope we need to check the following cases:
952
+ * todos/es => in this case we should take `es` as lang
953
+ * todos => in this case we should set the active lang as lang
954
+ */
955
+ resolveLangAndScope(lang) {
956
+ let resolveLang = lang;
957
+ let scope;
958
+ if (this._isLangScoped(lang)) {
959
+ // en for example
960
+ const langFromScope = getLangFromScope(lang);
961
+ // en is lang
962
+ const hasLang = this.isLang(langFromScope);
963
+ // take en
964
+ resolveLang = hasLang ? langFromScope : this.getActiveLang();
965
+ // find the scope
966
+ scope = this.getMappedScope(hasLang ? getScopeFromLang(lang) : lang);
967
+ }
968
+ return { scope, resolveLang };
969
+ }
970
+ getObjectByKey(translation, key) {
971
+ const result = {};
972
+ const prefix = `${key}.`;
973
+ for (const currentKey in translation) {
974
+ if (currentKey.startsWith(prefix)) {
975
+ result[currentKey.replace(prefix, '')] = translation[currentKey];
976
+ }
977
+ }
978
+ return result;
979
+ }
980
+ getEntries(key) {
981
+ return key instanceof Map ? key.entries() : Object.entries(key);
982
+ }
983
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoService, deps: [{ token: TRANSLOCO_LOADER, optional: true }, { token: TRANSLOCO_TRANSPILER }, { token: TRANSLOCO_MISSING_HANDLER }, { token: TRANSLOCO_INTERCEPTOR }, { token: TRANSLOCO_CONFIG }, { token: TRANSLOCO_FALLBACK_STRATEGY }], target: i0.ɵɵFactoryTarget.Injectable });
984
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoService, providedIn: 'root' });
985
+ }
986
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoService, decorators: [{
987
+ type: Injectable,
988
+ args: [{ providedIn: 'root' }]
989
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
990
+ type: Optional
991
+ }, {
992
+ type: Inject,
993
+ args: [TRANSLOCO_LOADER]
994
+ }] }, { type: undefined, decorators: [{
995
+ type: Inject,
996
+ args: [TRANSLOCO_TRANSPILER]
997
+ }] }, { type: undefined, decorators: [{
998
+ type: Inject,
999
+ args: [TRANSLOCO_MISSING_HANDLER]
1000
+ }] }, { type: undefined, decorators: [{
1001
+ type: Inject,
1002
+ args: [TRANSLOCO_INTERCEPTOR]
1003
+ }] }, { type: undefined, decorators: [{
1004
+ type: Inject,
1005
+ args: [TRANSLOCO_CONFIG]
1006
+ }] }, { type: undefined, decorators: [{
1007
+ type: Inject,
1008
+ args: [TRANSLOCO_FALLBACK_STRATEGY]
1009
+ }] }] });
1010
+
1011
+ class TranslocoLoaderComponent {
1012
+ html;
1013
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1014
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.0.9", type: TranslocoLoaderComponent, isStandalone: true, selector: "ng-component", inputs: { html: "html" }, ngImport: i0, template: `
1015
+ <div class="transloco-loader-template" [innerHTML]="html"></div>
1016
+ `, isInline: true });
1017
+ }
1018
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoLoaderComponent, decorators: [{
1019
+ type: Component,
1020
+ args: [{
1021
+ template: `
1022
+ <div class="transloco-loader-template" [innerHTML]="html"></div>
1023
+ `,
1024
+ standalone: true,
1025
+ }]
1026
+ }], propDecorators: { html: [{
1027
+ type: Input
1028
+ }] } });
1029
+
1030
+ class TemplateHandler {
1031
+ view;
1032
+ vcr;
1033
+ constructor(view, vcr) {
1034
+ this.view = view;
1035
+ this.vcr = vcr;
1036
+ }
1037
+ attachView() {
1038
+ if (this.view instanceof TemplateRef) {
1039
+ this.vcr.createEmbeddedView(this.view);
1040
+ }
1041
+ else if (isString(this.view)) {
1042
+ const componentRef = this.vcr.createComponent(TranslocoLoaderComponent);
1043
+ componentRef.instance.html = this.view;
1044
+ componentRef.hostView.detectChanges();
1045
+ }
1046
+ else {
1047
+ this.vcr.createComponent(this.view);
1048
+ }
1049
+ }
1050
+ detachView() {
1051
+ this.vcr.clear();
1052
+ }
1053
+ }
1054
+
1055
+ const TRANSLOCO_LANG = new InjectionToken('TRANSLOCO_LANG');
1056
+
1057
+ const TRANSLOCO_LOADING_TEMPLATE = new InjectionToken('TRANSLOCO_LOADING_TEMPLATE');
1058
+
1059
+ const TRANSLOCO_SCOPE = new InjectionToken('TRANSLOCO_SCOPE');
1060
+
1061
+ class LangResolver {
1062
+ initialized = false;
1063
+ // inline => provider => active
1064
+ resolve({ inline, provider, active }) {
1065
+ let lang = active;
1066
+ /**
1067
+ * When the user changes the lang we need to update
1068
+ * the view. Otherwise, the lang will remain the inline/provided lang
1069
+ */
1070
+ if (this.initialized) {
1071
+ lang = active;
1072
+ return lang;
1073
+ }
1074
+ if (provider) {
1075
+ const [, extracted] = getPipeValue(provider, 'static');
1076
+ lang = extracted;
1077
+ }
1078
+ if (inline) {
1079
+ const [, extracted] = getPipeValue(inline, 'static');
1080
+ lang = extracted;
1081
+ }
1082
+ this.initialized = true;
1083
+ return lang;
1084
+ }
1085
+ /**
1086
+ *
1087
+ * Resolve the lang
1088
+ *
1089
+ * @example
1090
+ *
1091
+ * resolveLangBasedOnScope('todos/en') => en
1092
+ * resolveLangBasedOnScope('en') => en
1093
+ *
1094
+ */
1095
+ resolveLangBasedOnScope(lang) {
1096
+ const scope = getScopeFromLang(lang);
1097
+ return scope ? getLangFromScope(lang) : lang;
1098
+ }
1099
+ /**
1100
+ *
1101
+ * Resolve the lang path for loading
1102
+ *
1103
+ * @example
1104
+ *
1105
+ * resolveLangPath('todos', 'en') => todos/en
1106
+ * resolveLangPath('en') => en
1107
+ *
1108
+ */
1109
+ resolveLangPath(lang, scope) {
1110
+ return scope ? `${scope}/${lang}` : lang;
1111
+ }
1112
+ }
1113
+
1114
+ class ScopeResolver {
1115
+ service;
1116
+ constructor(service) {
1117
+ this.service = service;
1118
+ }
1119
+ // inline => provider
1120
+ resolve(params) {
1121
+ const { inline, provider } = params;
1122
+ if (inline) {
1123
+ return inline;
1124
+ }
1125
+ if (provider) {
1126
+ if (isScopeObject(provider)) {
1127
+ const { scope, alias = toCamelCase(scope) } = provider;
1128
+ this.service._setScopeAlias(scope, alias);
1129
+ return scope;
1130
+ }
1131
+ return provider;
1132
+ }
1133
+ return undefined;
1134
+ }
1135
+ }
1136
+
1137
+ class TranslocoDirective {
1138
+ service = inject(TranslocoService);
1139
+ tpl = inject(TemplateRef, {
1140
+ optional: true,
1141
+ });
1142
+ providerLang = inject(TRANSLOCO_LANG, { optional: true });
1143
+ providerScope = inject(TRANSLOCO_SCOPE, { optional: true });
1144
+ providedLoadingTpl = inject(TRANSLOCO_LOADING_TEMPLATE, {
1145
+ optional: true,
1146
+ });
1147
+ cdr = inject(ChangeDetectorRef);
1148
+ host = inject(ElementRef);
1149
+ vcr = inject(ViewContainerRef);
1150
+ renderer = inject(Renderer2);
1151
+ subscription = null;
1152
+ view;
1153
+ translationMemo = {};
1154
+ key;
1155
+ params = {};
1156
+ inlineScope;
1157
+ inlineRead;
1158
+ inlineLang;
1159
+ inlineTpl;
1160
+ currentLang;
1161
+ loaderTplHandler;
1162
+ // Whether we already rendered the view once
1163
+ initialized = false;
1164
+ path;
1165
+ langResolver = new LangResolver();
1166
+ scopeResolver = new ScopeResolver(this.service);
1167
+ strategy = this.tpl === null ? 'attribute' : 'structural';
1168
+ static ngTemplateContextGuard(dir, ctx) {
1169
+ return true;
1170
+ }
1171
+ ngOnInit() {
1172
+ const listenToLangChange = shouldListenToLangChanges(this.service, this.providerLang || this.inlineLang);
1173
+ this.subscription = this.service.langChanges$
1174
+ .pipe(switchMap((activeLang) => {
1175
+ const lang = this.langResolver.resolve({
1176
+ inline: this.inlineLang,
1177
+ provider: this.providerLang,
1178
+ active: activeLang,
1179
+ });
1180
+ return Array.isArray(this.providerScope)
1181
+ ? forkJoin(this.providerScope.map((providerScope) => this.resolveScope(lang, providerScope)))
1182
+ : this.resolveScope(lang, this.providerScope);
1183
+ }), listenOrNotOperator(listenToLangChange))
1184
+ .subscribe(() => {
1185
+ this.currentLang = this.langResolver.resolveLangBasedOnScope(this.path);
1186
+ this.strategy === 'attribute'
1187
+ ? this.attributeStrategy()
1188
+ : this.structuralStrategy(this.currentLang, this.inlineRead);
1189
+ this.cdr.markForCheck();
1190
+ this.initialized = true;
1191
+ });
1192
+ if (!this.initialized) {
1193
+ const loadingContent = this.resolveLoadingContent();
1194
+ if (loadingContent) {
1195
+ this.loaderTplHandler = new TemplateHandler(loadingContent, this.vcr);
1196
+ this.loaderTplHandler.attachView();
1197
+ }
1198
+ }
1199
+ }
1200
+ ngOnChanges(changes) {
1201
+ // We need to support dynamic keys/params, so if this is not the first change CD cycle
1202
+ // we need to run the function again in order to update the value
1203
+ if (this.strategy === 'attribute') {
1204
+ const notInit = Object.keys(changes).some((v) => !changes[v].firstChange);
1205
+ notInit && this.attributeStrategy();
1206
+ }
1207
+ }
1208
+ attributeStrategy() {
1209
+ this.detachLoader();
1210
+ this.renderer.setProperty(this.host.nativeElement, 'innerText', this.service.translate(this.key, this.params, this.currentLang));
1211
+ }
1212
+ structuralStrategy(lang, read) {
1213
+ this.translationMemo = {};
1214
+ if (this.view) {
1215
+ // when the lang changes we need to change the reference so Angular will update the view
1216
+ this.view.context['$implicit'] = this.getTranslateFn(lang, read);
1217
+ this.view.context['currentLang'] = this.currentLang;
1218
+ }
1219
+ else {
1220
+ this.detachLoader();
1221
+ this.view = this.vcr.createEmbeddedView(this.tpl, {
1222
+ $implicit: this.getTranslateFn(lang, read),
1223
+ currentLang: this.currentLang,
1224
+ });
1225
+ }
1226
+ }
1227
+ getTranslateFn(lang, read) {
1228
+ return (key, params) => {
1229
+ const withRead = read ? `${read}.${key}` : key;
1230
+ const withParams = params
1231
+ ? `${withRead}${JSON.stringify(params)}`
1232
+ : withRead;
1233
+ if (Object.prototype.hasOwnProperty.call(this.translationMemo, withParams)) {
1234
+ return this.translationMemo[withParams].value;
1235
+ }
1236
+ this.translationMemo[withParams] = {
1237
+ params,
1238
+ value: this.service.translate(withRead, params, lang),
1239
+ };
1240
+ return this.translationMemo[withParams].value;
1241
+ };
1242
+ }
1243
+ resolveLoadingContent() {
1244
+ return this.inlineTpl || this.providedLoadingTpl;
1245
+ }
1246
+ ngOnDestroy() {
1247
+ if (this.subscription) {
1248
+ this.subscription.unsubscribe();
1249
+ // Caretaker note: it's important to clean up references to subscriptions since they save the `next`
1250
+ // callback within its `destination` property, preventing classes from being GC'd.
1251
+ this.subscription = null;
1252
+ }
1253
+ }
1254
+ detachLoader() {
1255
+ this.loaderTplHandler?.detachView();
1256
+ }
1257
+ resolveScope(lang, providerScope) {
1258
+ const resolvedScope = this.scopeResolver.resolve({
1259
+ inline: this.inlineScope,
1260
+ provider: providerScope,
1261
+ });
1262
+ this.path = this.langResolver.resolveLangPath(lang, resolvedScope);
1263
+ const inlineLoader = resolveInlineLoader(providerScope, resolvedScope);
1264
+ return this.service._loadDependencies(this.path, inlineLoader);
1265
+ }
1266
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1267
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.9", type: TranslocoDirective, isStandalone: true, selector: "[transloco]", inputs: { key: ["transloco", "key"], params: ["translocoParams", "params"], inlineScope: ["translocoScope", "inlineScope"], inlineRead: ["translocoRead", "inlineRead"], inlineLang: ["translocoLang", "inlineLang"], inlineTpl: ["translocoLoadingTpl", "inlineTpl"] }, usesOnChanges: true, ngImport: i0 });
1268
+ }
1269
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoDirective, decorators: [{
1270
+ type: Directive,
1271
+ args: [{
1272
+ selector: '[transloco]',
1273
+ standalone: true,
1274
+ }]
1275
+ }], propDecorators: { key: [{
1276
+ type: Input,
1277
+ args: ['transloco']
1278
+ }], params: [{
1279
+ type: Input,
1280
+ args: ['translocoParams']
1281
+ }], inlineScope: [{
1282
+ type: Input,
1283
+ args: ['translocoScope']
1284
+ }], inlineRead: [{
1285
+ type: Input,
1286
+ args: ['translocoRead']
1287
+ }], inlineLang: [{
1288
+ type: Input,
1289
+ args: ['translocoLang']
1290
+ }], inlineTpl: [{
1291
+ type: Input,
1292
+ args: ['translocoLoadingTpl']
1293
+ }] } });
1294
+
1295
+ class TranslocoPipe {
1296
+ service;
1297
+ providerScope;
1298
+ providerLang;
1299
+ cdr;
1300
+ subscription = null;
1301
+ lastValue = '';
1302
+ lastKey;
1303
+ path;
1304
+ langResolver = new LangResolver();
1305
+ scopeResolver;
1306
+ constructor(service, providerScope, providerLang, cdr) {
1307
+ this.service = service;
1308
+ this.providerScope = providerScope;
1309
+ this.providerLang = providerLang;
1310
+ this.cdr = cdr;
1311
+ this.scopeResolver = new ScopeResolver(this.service);
1312
+ }
1313
+ // null is for handling strict mode + async pipe types https://github.com/jsverse/transloco/issues/311
1314
+ // null is for handling strict mode + optional chaining types https://github.com/jsverse/transloco/issues/488
1315
+ transform(key, params, inlineLang) {
1316
+ if (!key) {
1317
+ return key;
1318
+ }
1319
+ const keyName = params ? `${key}${JSON.stringify(params)}` : key;
1320
+ if (keyName === this.lastKey) {
1321
+ return this.lastValue;
1322
+ }
1323
+ this.lastKey = keyName;
1324
+ this.subscription?.unsubscribe();
1325
+ const listenToLangChange = shouldListenToLangChanges(this.service, this.providerLang || inlineLang);
1326
+ this.subscription = this.service.langChanges$
1327
+ .pipe(switchMap((activeLang) => {
1328
+ const lang = this.langResolver.resolve({
1329
+ inline: inlineLang,
1330
+ provider: this.providerLang,
1331
+ active: activeLang,
1332
+ });
1333
+ return Array.isArray(this.providerScope)
1334
+ ? forkJoin(this.providerScope.map((providerScope) => this.resolveScope(lang, providerScope)))
1335
+ : this.resolveScope(lang, this.providerScope);
1336
+ }), listenOrNotOperator(listenToLangChange))
1337
+ .subscribe(() => this.updateValue(key, params));
1338
+ return this.lastValue;
1339
+ }
1340
+ ngOnDestroy() {
1341
+ this.subscription?.unsubscribe();
1342
+ // Caretaker note: it's important to clean up references to subscriptions since they save the `next`
1343
+ // callback within its `destination` property, preventing classes from being GC'd.
1344
+ this.subscription = null;
1345
+ }
1346
+ updateValue(key, params) {
1347
+ const lang = this.langResolver.resolveLangBasedOnScope(this.path);
1348
+ this.lastValue = this.service.translate(key, params, lang);
1349
+ this.cdr.markForCheck();
1350
+ }
1351
+ resolveScope(lang, providerScope) {
1352
+ const resolvedScope = this.scopeResolver.resolve({
1353
+ inline: undefined,
1354
+ provider: providerScope,
1355
+ });
1356
+ this.path = this.langResolver.resolveLangPath(lang, resolvedScope);
1357
+ const inlineLoader = resolveInlineLoader(providerScope, resolvedScope);
1358
+ return this.service._loadDependencies(this.path, inlineLoader);
1359
+ }
1360
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoPipe, deps: [{ token: TranslocoService }, { token: TRANSLOCO_SCOPE, optional: true }, { token: TRANSLOCO_LANG, optional: true }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Pipe });
1361
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "17.0.9", ngImport: i0, type: TranslocoPipe, isStandalone: true, name: "transloco", pure: false });
1362
+ }
1363
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoPipe, decorators: [{
1364
+ type: Pipe,
1365
+ args: [{
1366
+ name: 'transloco',
1367
+ pure: false,
1368
+ standalone: true,
1369
+ }]
1370
+ }], ctorParameters: () => [{ type: TranslocoService }, { type: undefined, decorators: [{
1371
+ type: Optional
1372
+ }, {
1373
+ type: Inject,
1374
+ args: [TRANSLOCO_SCOPE]
1375
+ }] }, { type: undefined, decorators: [{
1376
+ type: Optional
1377
+ }, {
1378
+ type: Inject,
1379
+ args: [TRANSLOCO_LANG]
1380
+ }] }, { type: i0.ChangeDetectorRef }] });
1381
+
1382
+ const decl = [TranslocoDirective, TranslocoPipe];
1383
+ class TranslocoModule {
1384
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1385
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.0.9", ngImport: i0, type: TranslocoModule, imports: [TranslocoDirective, TranslocoPipe], exports: [TranslocoDirective, TranslocoPipe] });
1386
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoModule });
1387
+ }
1388
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoModule, decorators: [{
1389
+ type: NgModule,
1390
+ args: [{
1391
+ imports: decl,
1392
+ exports: decl,
1393
+ }]
1394
+ }] });
1395
+
1396
+ function provideTransloco(options) {
1397
+ const providers = [
1398
+ provideTranslocoTranspiler(DefaultTranspiler),
1399
+ provideTranslocoMissingHandler(DefaultHandler),
1400
+ provideTranslocoInterceptor(DefaultInterceptor),
1401
+ provideTranslocoFallbackStrategy(DefaultFallbackStrategy),
1402
+ ];
1403
+ if (options.config) {
1404
+ providers.push(provideTranslocoConfig(options.config));
1405
+ }
1406
+ if (options.loader) {
1407
+ providers.push(provideTranslocoLoader(options.loader));
1408
+ }
1409
+ return providers;
1410
+ }
1411
+ function provideTranslocoConfig(config) {
1412
+ return makeEnvironmentProviders([
1413
+ {
1414
+ provide: TRANSLOCO_CONFIG,
1415
+ useValue: translocoConfig(config),
1416
+ },
1417
+ ]);
1418
+ }
1419
+ function provideTranslocoLoader(loader) {
1420
+ return makeEnvironmentProviders([
1421
+ { provide: TRANSLOCO_LOADER, useClass: loader },
1422
+ ]);
1423
+ }
1424
+ function provideTranslocoScope(scope) {
1425
+ return {
1426
+ provide: TRANSLOCO_SCOPE,
1427
+ useValue: scope,
1428
+ multi: true,
1429
+ };
1430
+ }
1431
+ function provideTranslocoLoadingTpl(content) {
1432
+ return {
1433
+ provide: TRANSLOCO_LOADING_TEMPLATE,
1434
+ useValue: content,
1435
+ };
1436
+ }
1437
+ function provideTranslocoTranspiler(transpiler) {
1438
+ return makeEnvironmentProviders([
1439
+ {
1440
+ provide: TRANSLOCO_TRANSPILER,
1441
+ useClass: transpiler,
1442
+ deps: [TRANSLOCO_CONFIG],
1443
+ },
1444
+ ]);
1445
+ }
1446
+ function provideTranslocoFallbackStrategy(strategy) {
1447
+ return makeEnvironmentProviders([
1448
+ {
1449
+ provide: TRANSLOCO_FALLBACK_STRATEGY,
1450
+ useClass: strategy,
1451
+ deps: [TRANSLOCO_CONFIG],
1452
+ },
1453
+ ]);
1454
+ }
1455
+ function provideTranslocoMissingHandler(handler) {
1456
+ return makeEnvironmentProviders([
1457
+ {
1458
+ provide: TRANSLOCO_MISSING_HANDLER,
1459
+ useClass: handler,
1460
+ },
1461
+ ]);
1462
+ }
1463
+ function provideTranslocoInterceptor(interceptor) {
1464
+ return makeEnvironmentProviders([
1465
+ {
1466
+ provide: TRANSLOCO_INTERCEPTOR,
1467
+ useClass: interceptor,
1468
+ },
1469
+ ]);
1470
+ }
1471
+ function provideTranslocoLang(lang) {
1472
+ return {
1473
+ provide: TRANSLOCO_LANG,
1474
+ useValue: lang,
1475
+ };
1476
+ }
1477
+
1478
+ const TRANSLOCO_TEST_LANGS = new InjectionToken('TRANSLOCO_TEST_LANGS - Available testing languages');
1479
+ const TRANSLOCO_TEST_OPTIONS = new InjectionToken('TRANSLOCO_TEST_OPTIONS - Testing options');
1480
+ class TestingLoader {
1481
+ langs;
1482
+ constructor(langs) {
1483
+ this.langs = langs;
1484
+ }
1485
+ getTranslation(lang) {
1486
+ return of(this.langs[lang]);
1487
+ }
1488
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TestingLoader, deps: [{ token: TRANSLOCO_TEST_LANGS }], target: i0.ɵɵFactoryTarget.Injectable });
1489
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TestingLoader });
1490
+ }
1491
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TestingLoader, decorators: [{
1492
+ type: Injectable
1493
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1494
+ type: Inject,
1495
+ args: [TRANSLOCO_TEST_LANGS]
1496
+ }] }] });
1497
+ function initTranslocoService(service, langs = {}, options) {
1498
+ const preloadAllLangs = () => options.preloadLangs
1499
+ ? Promise.all(Object.keys(langs).map((lang) => service.load(lang).toPromise()))
1500
+ : Promise.resolve();
1501
+ return preloadAllLangs;
1502
+ }
1503
+ class TranslocoTestingModule {
1504
+ static forRoot(options) {
1505
+ return {
1506
+ ngModule: TranslocoTestingModule,
1507
+ providers: [
1508
+ provideTransloco({
1509
+ loader: TestingLoader,
1510
+ config: {
1511
+ prodMode: true,
1512
+ missingHandler: { logMissingKey: false },
1513
+ ...options.translocoConfig,
1514
+ },
1515
+ }),
1516
+ {
1517
+ provide: TRANSLOCO_TEST_LANGS,
1518
+ useValue: options.langs,
1519
+ },
1520
+ {
1521
+ provide: TRANSLOCO_TEST_OPTIONS,
1522
+ useValue: options,
1523
+ },
1524
+ {
1525
+ provide: APP_INITIALIZER,
1526
+ useFactory: initTranslocoService,
1527
+ deps: [
1528
+ TranslocoService,
1529
+ TRANSLOCO_TEST_LANGS,
1530
+ TRANSLOCO_TEST_OPTIONS,
1531
+ ],
1532
+ multi: true,
1533
+ },
1534
+ ],
1535
+ };
1536
+ }
1537
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoTestingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1538
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.0.9", ngImport: i0, type: TranslocoTestingModule, exports: [TranslocoModule] });
1539
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoTestingModule, imports: [TranslocoModule] });
1540
+ }
1541
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: TranslocoTestingModule, decorators: [{
1542
+ type: NgModule,
1543
+ args: [{
1544
+ exports: [TranslocoModule],
1545
+ }]
1546
+ }] });
1547
+
1548
+ /**
1549
+ * Returns the language code name from the browser, e.g. "en"
1550
+ */
1551
+ function getBrowserLang() {
1552
+ let browserLang = getBrowserCultureLang();
1553
+ if (!browserLang || !isBrowser()) {
1554
+ return undefined;
1555
+ }
1556
+ if (browserLang.indexOf('-') !== -1) {
1557
+ browserLang = browserLang.split('-')[0];
1558
+ }
1559
+ if (browserLang.indexOf('_') !== -1) {
1560
+ browserLang = browserLang.split('_')[0];
1561
+ }
1562
+ return browserLang;
1563
+ }
1564
+ /**
1565
+ * Returns the culture language code name from the browser, e.g. "en-US"
1566
+ */
1567
+ function getBrowserCultureLang() {
1568
+ if (!isBrowser()) {
1569
+ return '';
1570
+ }
1571
+ const navigator = window.navigator;
1572
+ return navigator.languages?.[0] ?? navigator.language;
1573
+ }
1574
+
1575
+ /**
1576
+ * Generated bundle index. Do not edit.
1577
+ */
1578
+
1579
+ export { DefaultFallbackStrategy, DefaultTranspiler, FunctionalTranspiler, TRANSLOCO_CONFIG, TRANSLOCO_FALLBACK_STRATEGY, TRANSLOCO_INTERCEPTOR, TRANSLOCO_LANG, TRANSLOCO_LOADER, TRANSLOCO_LOADING_TEMPLATE, TRANSLOCO_MISSING_HANDLER, TRANSLOCO_SCOPE, TRANSLOCO_TRANSPILER, TestingLoader, TranslocoDirective, TranslocoModule, TranslocoPipe, TranslocoService, TranslocoTestingModule, coerceArray, defaultConfig, flatten, getBrowserCultureLang, getBrowserLang, getFunctionArgs, getLangFromScope, getPipeValue, getScopeFromLang, getValue, hasInlineLoader, isBrowser, isDefined, isEmpty, isFunction, isNil, isNumber, isObject, isScopeArray, isScopeObject, isString, provideTransloco, provideTranslocoConfig, provideTranslocoFallbackStrategy, provideTranslocoInterceptor, provideTranslocoLang, provideTranslocoLoader, provideTranslocoLoadingTpl, provideTranslocoMissingHandler, provideTranslocoScope, provideTranslocoTranspiler, setValue, size, toCamelCase, toNumber, translate, translateObject, translocoConfig, unflatten };
1580
+ //# sourceMappingURL=jsverse-transloco.mjs.map