UNPKG

42.6 kBJavaScriptView Raw
1import * as i0 from '@angular/core';
2import { Injectable, EventEmitter, InjectionToken, Inject, Directive, Input, Pipe, NgModule } from '@angular/core';
3import { of, isObservable, forkJoin, concat, defer } from 'rxjs';
4import { take, shareReplay, map, concatMap, switchMap } from 'rxjs/operators';
5
6class TranslateLoader {
7}
8/**
9 * This loader is just a placeholder that does nothing, in case you don't need a loader at all
10 */
11class TranslateFakeLoader extends TranslateLoader {
12 getTranslation(lang) {
13 return of({});
14 }
15}
16TranslateFakeLoader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateFakeLoader, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
17TranslateFakeLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateFakeLoader });
18i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateFakeLoader, decorators: [{
19 type: Injectable
20 }] });
21
22class MissingTranslationHandler {
23}
24/**
25 * This handler is just a placeholder that does nothing, in case you don't need a missing translation handler at all
26 */
27class FakeMissingTranslationHandler {
28 handle(params) {
29 return params.key;
30 }
31}
32FakeMissingTranslationHandler.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: FakeMissingTranslationHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
33FakeMissingTranslationHandler.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: FakeMissingTranslationHandler });
34i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: FakeMissingTranslationHandler, decorators: [{
35 type: Injectable
36 }] });
37
38/* tslint:disable */
39/**
40 * Determines if two objects or two values are equivalent.
41 *
42 * Two objects or values are considered equivalent if at least one of the following is true:
43 *
44 * * Both objects or values pass `===` comparison.
45 * * Both objects or values are of the same type and all of their properties are equal by
46 * comparing them with `equals`.
47 *
48 * @param o1 Object or value to compare.
49 * @param o2 Object or value to compare.
50 * @returns true if arguments are equal.
51 */
52function equals(o1, o2) {
53 if (o1 === o2)
54 return true;
55 if (o1 === null || o2 === null)
56 return false;
57 if (o1 !== o1 && o2 !== o2)
58 return true; // NaN === NaN
59 let t1 = typeof o1, t2 = typeof o2, length, key, keySet;
60 if (t1 == t2 && t1 == 'object') {
61 if (Array.isArray(o1)) {
62 if (!Array.isArray(o2))
63 return false;
64 if ((length = o1.length) == o2.length) {
65 for (key = 0; key < length; key++) {
66 if (!equals(o1[key], o2[key]))
67 return false;
68 }
69 return true;
70 }
71 }
72 else {
73 if (Array.isArray(o2)) {
74 return false;
75 }
76 keySet = Object.create(null);
77 for (key in o1) {
78 if (!equals(o1[key], o2[key])) {
79 return false;
80 }
81 keySet[key] = true;
82 }
83 for (key in o2) {
84 if (!(key in keySet) && typeof o2[key] !== 'undefined') {
85 return false;
86 }
87 }
88 return true;
89 }
90 }
91 return false;
92}
93/* tslint:enable */
94function isDefined(value) {
95 return typeof value !== 'undefined' && value !== null;
96}
97function isObject(item) {
98 return (item && typeof item === 'object' && !Array.isArray(item));
99}
100function mergeDeep(target, source) {
101 let output = Object.assign({}, target);
102 if (isObject(target) && isObject(source)) {
103 Object.keys(source).forEach((key) => {
104 if (isObject(source[key])) {
105 if (!(key in target)) {
106 Object.assign(output, { [key]: source[key] });
107 }
108 else {
109 output[key] = mergeDeep(target[key], source[key]);
110 }
111 }
112 else {
113 Object.assign(output, { [key]: source[key] });
114 }
115 });
116 }
117 return output;
118}
119
120class TranslateParser {
121}
122class TranslateDefaultParser extends TranslateParser {
123 constructor() {
124 super(...arguments);
125 this.templateMatcher = /{{\s?([^{}\s]*)\s?}}/g;
126 }
127 interpolate(expr, params) {
128 let result;
129 if (typeof expr === 'string') {
130 result = this.interpolateString(expr, params);
131 }
132 else if (typeof expr === 'function') {
133 result = this.interpolateFunction(expr, params);
134 }
135 else {
136 // this should not happen, but an unrelated TranslateService test depends on it
137 result = expr;
138 }
139 return result;
140 }
141 getValue(target, key) {
142 let keys = typeof key === 'string' ? key.split('.') : [key];
143 key = '';
144 do {
145 key += keys.shift();
146 if (isDefined(target) && isDefined(target[key]) && (typeof target[key] === 'object' || !keys.length)) {
147 target = target[key];
148 key = '';
149 }
150 else if (!keys.length) {
151 target = undefined;
152 }
153 else {
154 key += '.';
155 }
156 } while (keys.length);
157 return target;
158 }
159 interpolateFunction(fn, params) {
160 return fn(params);
161 }
162 interpolateString(expr, params) {
163 if (!params) {
164 return expr;
165 }
166 return expr.replace(this.templateMatcher, (substring, b) => {
167 let r = this.getValue(params, b);
168 return isDefined(r) ? r : substring;
169 });
170 }
171}
172TranslateDefaultParser.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateDefaultParser, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
173TranslateDefaultParser.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateDefaultParser });
174i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateDefaultParser, decorators: [{
175 type: Injectable
176 }] });
177
178class TranslateCompiler {
179}
180/**
181 * This compiler is just a placeholder that does nothing, in case you don't need a compiler at all
182 */
183class TranslateFakeCompiler extends TranslateCompiler {
184 compile(value, lang) {
185 return value;
186 }
187 compileTranslations(translations, lang) {
188 return translations;
189 }
190}
191TranslateFakeCompiler.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateFakeCompiler, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
192TranslateFakeCompiler.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateFakeCompiler });
193i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateFakeCompiler, decorators: [{
194 type: Injectable
195 }] });
196
197class TranslateStore {
198 constructor() {
199 /**
200 * The lang currently used
201 */
202 this.currentLang = this.defaultLang;
203 /**
204 * a list of translations per lang
205 */
206 this.translations = {};
207 /**
208 * an array of langs
209 */
210 this.langs = [];
211 /**
212 * An EventEmitter to listen to translation change events
213 * onTranslationChange.subscribe((params: TranslationChangeEvent) => {
214 * // do something
215 * });
216 */
217 this.onTranslationChange = new EventEmitter();
218 /**
219 * An EventEmitter to listen to lang change events
220 * onLangChange.subscribe((params: LangChangeEvent) => {
221 * // do something
222 * });
223 */
224 this.onLangChange = new EventEmitter();
225 /**
226 * An EventEmitter to listen to default lang change events
227 * onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => {
228 * // do something
229 * });
230 */
231 this.onDefaultLangChange = new EventEmitter();
232 }
233}
234
235const USE_STORE = new InjectionToken('USE_STORE');
236const USE_DEFAULT_LANG = new InjectionToken('USE_DEFAULT_LANG');
237const DEFAULT_LANGUAGE = new InjectionToken('DEFAULT_LANGUAGE');
238const USE_EXTEND = new InjectionToken('USE_EXTEND');
239class TranslateService {
240 /**
241 *
242 * @param store an instance of the store (that is supposed to be unique)
243 * @param currentLoader An instance of the loader currently used
244 * @param compiler An instance of the compiler currently used
245 * @param parser An instance of the parser currently used
246 * @param missingTranslationHandler A handler for missing translations.
247 * @param useDefaultLang whether we should use default language translation when current language translation is missing.
248 * @param isolate whether this service should use the store or not
249 * @param extend To make a child module extend (and use) translations from parent modules.
250 * @param defaultLanguage Set the default language using configuration
251 */
252 constructor(store, currentLoader, compiler, parser, missingTranslationHandler, useDefaultLang = true, isolate = false, extend = false, defaultLanguage) {
253 this.store = store;
254 this.currentLoader = currentLoader;
255 this.compiler = compiler;
256 this.parser = parser;
257 this.missingTranslationHandler = missingTranslationHandler;
258 this.useDefaultLang = useDefaultLang;
259 this.isolate = isolate;
260 this.extend = extend;
261 this.pending = false;
262 this._onTranslationChange = new EventEmitter();
263 this._onLangChange = new EventEmitter();
264 this._onDefaultLangChange = new EventEmitter();
265 this._langs = [];
266 this._translations = {};
267 this._translationRequests = {};
268 /** set the default language from configuration */
269 if (defaultLanguage) {
270 this.setDefaultLang(defaultLanguage);
271 }
272 }
273 /**
274 * An EventEmitter to listen to translation change events
275 * onTranslationChange.subscribe((params: TranslationChangeEvent) => {
276 * // do something
277 * });
278 */
279 get onTranslationChange() {
280 return this.isolate ? this._onTranslationChange : this.store.onTranslationChange;
281 }
282 /**
283 * An EventEmitter to listen to lang change events
284 * onLangChange.subscribe((params: LangChangeEvent) => {
285 * // do something
286 * });
287 */
288 get onLangChange() {
289 return this.isolate ? this._onLangChange : this.store.onLangChange;
290 }
291 /**
292 * An EventEmitter to listen to default lang change events
293 * onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => {
294 * // do something
295 * });
296 */
297 get onDefaultLangChange() {
298 return this.isolate ? this._onDefaultLangChange : this.store.onDefaultLangChange;
299 }
300 /**
301 * The default lang to fallback when translations are missing on the current lang
302 */
303 get defaultLang() {
304 return this.isolate ? this._defaultLang : this.store.defaultLang;
305 }
306 set defaultLang(defaultLang) {
307 if (this.isolate) {
308 this._defaultLang = defaultLang;
309 }
310 else {
311 this.store.defaultLang = defaultLang;
312 }
313 }
314 /**
315 * The lang currently used
316 */
317 get currentLang() {
318 return this.isolate ? this._currentLang : this.store.currentLang;
319 }
320 set currentLang(currentLang) {
321 if (this.isolate) {
322 this._currentLang = currentLang;
323 }
324 else {
325 this.store.currentLang = currentLang;
326 }
327 }
328 /**
329 * an array of langs
330 */
331 get langs() {
332 return this.isolate ? this._langs : this.store.langs;
333 }
334 set langs(langs) {
335 if (this.isolate) {
336 this._langs = langs;
337 }
338 else {
339 this.store.langs = langs;
340 }
341 }
342 /**
343 * a list of translations per lang
344 */
345 get translations() {
346 return this.isolate ? this._translations : this.store.translations;
347 }
348 set translations(translations) {
349 if (this.isolate) {
350 this._translations = translations;
351 }
352 else {
353 this.store.translations = translations;
354 }
355 }
356 /**
357 * Sets the default language to use as a fallback
358 */
359 setDefaultLang(lang) {
360 if (lang === this.defaultLang) {
361 return;
362 }
363 let pending = this.retrieveTranslations(lang);
364 if (typeof pending !== "undefined") {
365 // on init set the defaultLang immediately
366 if (this.defaultLang == null) {
367 this.defaultLang = lang;
368 }
369 pending.pipe(take(1))
370 .subscribe((res) => {
371 this.changeDefaultLang(lang);
372 });
373 }
374 else { // we already have this language
375 this.changeDefaultLang(lang);
376 }
377 }
378 /**
379 * Gets the default language used
380 */
381 getDefaultLang() {
382 return this.defaultLang;
383 }
384 /**
385 * Changes the lang currently used
386 */
387 use(lang) {
388 // don't change the language if the language given is already selected
389 if (lang === this.currentLang) {
390 return of(this.translations[lang]);
391 }
392 let pending = this.retrieveTranslations(lang);
393 if (typeof pending !== "undefined") {
394 // on init set the currentLang immediately
395 if (!this.currentLang) {
396 this.currentLang = lang;
397 }
398 pending.pipe(take(1))
399 .subscribe((res) => {
400 this.changeLang(lang);
401 });
402 return pending;
403 }
404 else { // we have this language, return an Observable
405 this.changeLang(lang);
406 return of(this.translations[lang]);
407 }
408 }
409 /**
410 * Retrieves the given translations
411 */
412 retrieveTranslations(lang) {
413 let pending;
414 // if this language is unavailable or extend is true, ask for it
415 if (typeof this.translations[lang] === "undefined" || this.extend) {
416 this._translationRequests[lang] = this._translationRequests[lang] || this.getTranslation(lang);
417 pending = this._translationRequests[lang];
418 }
419 return pending;
420 }
421 /**
422 * Gets an object of translations for a given language with the current loader
423 * and passes it through the compiler
424 */
425 getTranslation(lang) {
426 this.pending = true;
427 const loadingTranslations = this.currentLoader.getTranslation(lang).pipe(shareReplay(1), take(1));
428 this.loadingTranslations = loadingTranslations.pipe(map((res) => this.compiler.compileTranslations(res, lang)), shareReplay(1), take(1));
429 this.loadingTranslations
430 .subscribe({
431 next: (res) => {
432 this.translations[lang] = this.extend && this.translations[lang] ? { ...res, ...this.translations[lang] } : res;
433 this.updateLangs();
434 this.pending = false;
435 },
436 error: (err) => {
437 this.pending = false;
438 }
439 });
440 return loadingTranslations;
441 }
442 /**
443 * Manually sets an object of translations for a given language
444 * after passing it through the compiler
445 */
446 setTranslation(lang, translations, shouldMerge = false) {
447 translations = this.compiler.compileTranslations(translations, lang);
448 if ((shouldMerge || this.extend) && this.translations[lang]) {
449 this.translations[lang] = mergeDeep(this.translations[lang], translations);
450 }
451 else {
452 this.translations[lang] = translations;
453 }
454 this.updateLangs();
455 this.onTranslationChange.emit({ lang: lang, translations: this.translations[lang] });
456 }
457 /**
458 * Returns an array of currently available langs
459 */
460 getLangs() {
461 return this.langs;
462 }
463 /**
464 * Add available langs
465 */
466 addLangs(langs) {
467 langs.forEach((lang) => {
468 if (this.langs.indexOf(lang) === -1) {
469 this.langs.push(lang);
470 }
471 });
472 }
473 /**
474 * Update the list of available langs
475 */
476 updateLangs() {
477 this.addLangs(Object.keys(this.translations));
478 }
479 /**
480 * Returns the parsed result of the translations
481 */
482 getParsedResult(translations, key, interpolateParams) {
483 let res;
484 if (key instanceof Array) {
485 let result = {}, observables = false;
486 for (let k of key) {
487 result[k] = this.getParsedResult(translations, k, interpolateParams);
488 if (isObservable(result[k])) {
489 observables = true;
490 }
491 }
492 if (observables) {
493 const sources = key.map(k => isObservable(result[k]) ? result[k] : of(result[k]));
494 return forkJoin(sources).pipe(map((arr) => {
495 let obj = {};
496 arr.forEach((value, index) => {
497 obj[key[index]] = value;
498 });
499 return obj;
500 }));
501 }
502 return result;
503 }
504 if (translations) {
505 res = this.parser.interpolate(this.parser.getValue(translations, key), interpolateParams);
506 }
507 if (typeof res === "undefined" && this.defaultLang != null && this.defaultLang !== this.currentLang && this.useDefaultLang) {
508 res = this.parser.interpolate(this.parser.getValue(this.translations[this.defaultLang], key), interpolateParams);
509 }
510 if (typeof res === "undefined") {
511 let params = { key, translateService: this };
512 if (typeof interpolateParams !== 'undefined') {
513 params.interpolateParams = interpolateParams;
514 }
515 res = this.missingTranslationHandler.handle(params);
516 }
517 return typeof res !== "undefined" ? res : key;
518 }
519 /**
520 * Gets the translated value of a key (or an array of keys)
521 * @returns the translated key, or an object of translated keys
522 */
523 get(key, interpolateParams) {
524 if (!isDefined(key) || !key.length) {
525 throw new Error(`Parameter "key" required`);
526 }
527 // check if we are loading a new translation to use
528 if (this.pending) {
529 return this.loadingTranslations.pipe(concatMap((res) => {
530 res = this.getParsedResult(res, key, interpolateParams);
531 return isObservable(res) ? res : of(res);
532 }));
533 }
534 else {
535 let res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams);
536 return isObservable(res) ? res : of(res);
537 }
538 }
539 /**
540 * Returns a stream of translated values of a key (or an array of keys) which updates
541 * whenever the translation changes.
542 * @returns A stream of the translated key, or an object of translated keys
543 */
544 getStreamOnTranslationChange(key, interpolateParams) {
545 if (!isDefined(key) || !key.length) {
546 throw new Error(`Parameter "key" required`);
547 }
548 return concat(defer(() => this.get(key, interpolateParams)), this.onTranslationChange.pipe(switchMap((event) => {
549 const res = this.getParsedResult(event.translations, key, interpolateParams);
550 if (typeof res.subscribe === 'function') {
551 return res;
552 }
553 else {
554 return of(res);
555 }
556 })));
557 }
558 /**
559 * Returns a stream of translated values of a key (or an array of keys) which updates
560 * whenever the language changes.
561 * @returns A stream of the translated key, or an object of translated keys
562 */
563 stream(key, interpolateParams) {
564 if (!isDefined(key) || !key.length) {
565 throw new Error(`Parameter "key" required`);
566 }
567 return concat(defer(() => this.get(key, interpolateParams)), this.onLangChange.pipe(switchMap((event) => {
568 const res = this.getParsedResult(event.translations, key, interpolateParams);
569 return isObservable(res) ? res : of(res);
570 })));
571 }
572 /**
573 * Returns a translation instantly from the internal state of loaded translation.
574 * All rules regarding the current language, the preferred language of even fallback languages will be used except any promise handling.
575 */
576 instant(key, interpolateParams) {
577 if (!isDefined(key) || !key.length) {
578 throw new Error(`Parameter "key" required`);
579 }
580 let res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams);
581 if (isObservable(res)) {
582 if (key instanceof Array) {
583 let obj = {};
584 key.forEach((value, index) => {
585 obj[key[index]] = key[index];
586 });
587 return obj;
588 }
589 return key;
590 }
591 else {
592 return res;
593 }
594 }
595 /**
596 * Sets the translated value of a key, after compiling it
597 */
598 set(key, value, lang = this.currentLang) {
599 this.translations[lang][key] = this.compiler.compile(value, lang);
600 this.updateLangs();
601 this.onTranslationChange.emit({ lang: lang, translations: this.translations[lang] });
602 }
603 /**
604 * Changes the current lang
605 */
606 changeLang(lang) {
607 this.currentLang = lang;
608 this.onLangChange.emit({ lang: lang, translations: this.translations[lang] });
609 // if there is no default lang, use the one that we just set
610 if (this.defaultLang == null) {
611 this.changeDefaultLang(lang);
612 }
613 }
614 /**
615 * Changes the default lang
616 */
617 changeDefaultLang(lang) {
618 this.defaultLang = lang;
619 this.onDefaultLangChange.emit({ lang: lang, translations: this.translations[lang] });
620 }
621 /**
622 * Allows to reload the lang file from the file
623 */
624 reloadLang(lang) {
625 this.resetLang(lang);
626 return this.getTranslation(lang);
627 }
628 /**
629 * Deletes inner translation
630 */
631 resetLang(lang) {
632 this._translationRequests[lang] = undefined;
633 this.translations[lang] = undefined;
634 }
635 /**
636 * Returns the language code name from the browser, e.g. "de"
637 */
638 getBrowserLang() {
639 if (typeof window === 'undefined' || typeof window.navigator === 'undefined') {
640 return undefined;
641 }
642 let browserLang = window.navigator.languages ? window.navigator.languages[0] : null;
643 browserLang = browserLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage;
644 if (typeof browserLang === 'undefined') {
645 return undefined;
646 }
647 if (browserLang.indexOf('-') !== -1) {
648 browserLang = browserLang.split('-')[0];
649 }
650 if (browserLang.indexOf('_') !== -1) {
651 browserLang = browserLang.split('_')[0];
652 }
653 return browserLang;
654 }
655 /**
656 * Returns the culture language code name from the browser, e.g. "de-DE"
657 */
658 getBrowserCultureLang() {
659 if (typeof window === 'undefined' || typeof window.navigator === 'undefined') {
660 return undefined;
661 }
662 let browserCultureLang = window.navigator.languages ? window.navigator.languages[0] : null;
663 browserCultureLang = browserCultureLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage;
664 return browserCultureLang;
665 }
666}
667TranslateService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateService, deps: [{ token: TranslateStore }, { token: TranslateLoader }, { token: TranslateCompiler }, { token: TranslateParser }, { token: MissingTranslationHandler }, { token: USE_DEFAULT_LANG }, { token: USE_STORE }, { token: USE_EXTEND }, { token: DEFAULT_LANGUAGE }], target: i0.ɵɵFactoryTarget.Injectable });
668TranslateService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateService });
669i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateService, decorators: [{
670 type: Injectable
671 }], ctorParameters: function () { return [{ type: TranslateStore }, { type: TranslateLoader }, { type: TranslateCompiler }, { type: TranslateParser }, { type: MissingTranslationHandler }, { type: undefined, decorators: [{
672 type: Inject,
673 args: [USE_DEFAULT_LANG]
674 }] }, { type: undefined, decorators: [{
675 type: Inject,
676 args: [USE_STORE]
677 }] }, { type: undefined, decorators: [{
678 type: Inject,
679 args: [USE_EXTEND]
680 }] }, { type: undefined, decorators: [{
681 type: Inject,
682 args: [DEFAULT_LANGUAGE]
683 }] }]; } });
684
685class TranslateDirective {
686 constructor(translateService, element, _ref) {
687 this.translateService = translateService;
688 this.element = element;
689 this._ref = _ref;
690 // subscribe to onTranslationChange event, in case the translations of the current lang change
691 if (!this.onTranslationChangeSub) {
692 this.onTranslationChangeSub = this.translateService.onTranslationChange.subscribe((event) => {
693 if (event.lang === this.translateService.currentLang) {
694 this.checkNodes(true, event.translations);
695 }
696 });
697 }
698 // subscribe to onLangChange event, in case the language changes
699 if (!this.onLangChangeSub) {
700 this.onLangChangeSub = this.translateService.onLangChange.subscribe((event) => {
701 this.checkNodes(true, event.translations);
702 });
703 }
704 // subscribe to onDefaultLangChange event, in case the default language changes
705 if (!this.onDefaultLangChangeSub) {
706 this.onDefaultLangChangeSub = this.translateService.onDefaultLangChange.subscribe((event) => {
707 this.checkNodes(true);
708 });
709 }
710 }
711 set translate(key) {
712 if (key) {
713 this.key = key;
714 this.checkNodes();
715 }
716 }
717 set translateParams(params) {
718 if (!equals(this.currentParams, params)) {
719 this.currentParams = params;
720 this.checkNodes(true);
721 }
722 }
723 ngAfterViewChecked() {
724 this.checkNodes();
725 }
726 checkNodes(forceUpdate = false, translations) {
727 let nodes = this.element.nativeElement.childNodes;
728 // if the element is empty
729 if (!nodes.length) {
730 // we add the key as content
731 this.setContent(this.element.nativeElement, this.key);
732 nodes = this.element.nativeElement.childNodes;
733 }
734 for (let i = 0; i < nodes.length; ++i) {
735 let node = nodes[i];
736 if (node.nodeType === 3) { // node type 3 is a text node
737 let key;
738 if (forceUpdate) {
739 node.lastKey = null;
740 }
741 if (isDefined(node.lookupKey)) {
742 key = node.lookupKey;
743 }
744 else if (this.key) {
745 key = this.key;
746 }
747 else {
748 let content = this.getContent(node);
749 let trimmedContent = content.trim();
750 if (trimmedContent.length) {
751 node.lookupKey = trimmedContent;
752 // we want to use the content as a key, not the translation value
753 if (content !== node.currentValue) {
754 key = trimmedContent;
755 // the content was changed from the user, we'll use it as a reference if needed
756 node.originalContent = content || node.originalContent;
757 }
758 else if (node.originalContent) { // the content seems ok, but the lang has changed
759 // the current content is the translation, not the key, use the last real content as key
760 key = node.originalContent.trim();
761 }
762 else if (content !== node.currentValue) {
763 // we want to use the content as a key, not the translation value
764 key = trimmedContent;
765 // the content was changed from the user, we'll use it as a reference if needed
766 node.originalContent = content || node.originalContent;
767 }
768 }
769 }
770 this.updateValue(key, node, translations);
771 }
772 }
773 }
774 updateValue(key, node, translations) {
775 if (key) {
776 if (node.lastKey === key && this.lastParams === this.currentParams) {
777 return;
778 }
779 this.lastParams = this.currentParams;
780 let onTranslation = (res) => {
781 if (res !== key) {
782 node.lastKey = key;
783 }
784 if (!node.originalContent) {
785 node.originalContent = this.getContent(node);
786 }
787 node.currentValue = isDefined(res) ? res : (node.originalContent || key);
788 // we replace in the original content to preserve spaces that we might have trimmed
789 this.setContent(node, this.key ? node.currentValue : node.originalContent.replace(key, node.currentValue));
790 this._ref.markForCheck();
791 };
792 if (isDefined(translations)) {
793 let res = this.translateService.getParsedResult(translations, key, this.currentParams);
794 if (isObservable(res)) {
795 res.subscribe({ next: onTranslation });
796 }
797 else {
798 onTranslation(res);
799 }
800 }
801 else {
802 this.translateService.get(key, this.currentParams).subscribe(onTranslation);
803 }
804 }
805 }
806 getContent(node) {
807 return isDefined(node.textContent) ? node.textContent : node.data;
808 }
809 setContent(node, content) {
810 if (isDefined(node.textContent)) {
811 node.textContent = content;
812 }
813 else {
814 node.data = content;
815 }
816 }
817 ngOnDestroy() {
818 if (this.onLangChangeSub) {
819 this.onLangChangeSub.unsubscribe();
820 }
821 if (this.onDefaultLangChangeSub) {
822 this.onDefaultLangChangeSub.unsubscribe();
823 }
824 if (this.onTranslationChangeSub) {
825 this.onTranslationChangeSub.unsubscribe();
826 }
827 }
828}
829TranslateDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateDirective, deps: [{ token: TranslateService }, { token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
830TranslateDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.0.0", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: { translate: "translate", translateParams: "translateParams" }, ngImport: i0 });
831i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateDirective, decorators: [{
832 type: Directive,
833 args: [{
834 selector: '[translate],[ngx-translate]'
835 }]
836 }], ctorParameters: function () { return [{ type: TranslateService }, { type: i0.ElementRef }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { translate: [{
837 type: Input
838 }], translateParams: [{
839 type: Input
840 }] } });
841
842class TranslatePipe {
843 constructor(translate, _ref) {
844 this.translate = translate;
845 this._ref = _ref;
846 this.value = '';
847 this.lastKey = null;
848 this.lastParams = [];
849 }
850 updateValue(key, interpolateParams, translations) {
851 let onTranslation = (res) => {
852 this.value = res !== undefined ? res : key;
853 this.lastKey = key;
854 this._ref.markForCheck();
855 };
856 if (translations) {
857 let res = this.translate.getParsedResult(translations, key, interpolateParams);
858 if (isObservable(res.subscribe)) {
859 res.subscribe(onTranslation);
860 }
861 else {
862 onTranslation(res);
863 }
864 }
865 this.translate.get(key, interpolateParams).subscribe(onTranslation);
866 }
867 transform(query, ...args) {
868 if (!query || !query.length) {
869 return query;
870 }
871 // if we ask another time for the same key, return the last value
872 if (equals(query, this.lastKey) && equals(args, this.lastParams)) {
873 return this.value;
874 }
875 let interpolateParams = undefined;
876 if (isDefined(args[0]) && args.length) {
877 if (typeof args[0] === 'string' && args[0].length) {
878 // we accept objects written in the template such as {n:1}, {'n':1}, {n:'v'}
879 // which is why we might need to change it to real JSON objects such as {"n":1} or {"n":"v"}
880 let validArgs = args[0]
881 .replace(/(\')?([a-zA-Z0-9_]+)(\')?(\s)?:/g, '"$2":')
882 .replace(/:(\s)?(\')(.*?)(\')/g, ':"$3"');
883 try {
884 interpolateParams = JSON.parse(validArgs);
885 }
886 catch (e) {
887 throw new SyntaxError(`Wrong parameter in TranslatePipe. Expected a valid Object, received: ${args[0]}`);
888 }
889 }
890 else if (typeof args[0] === 'object' && !Array.isArray(args[0])) {
891 interpolateParams = args[0];
892 }
893 }
894 // store the query, in case it changes
895 this.lastKey = query;
896 // store the params, in case they change
897 this.lastParams = args;
898 // set the value
899 this.updateValue(query, interpolateParams);
900 // if there is a subscription to onLangChange, clean it
901 this._dispose();
902 // subscribe to onTranslationChange event, in case the translations change
903 if (!this.onTranslationChange) {
904 this.onTranslationChange = this.translate.onTranslationChange.subscribe((event) => {
905 if (this.lastKey && event.lang === this.translate.currentLang) {
906 this.lastKey = null;
907 this.updateValue(query, interpolateParams, event.translations);
908 }
909 });
910 }
911 // subscribe to onLangChange event, in case the language changes
912 if (!this.onLangChange) {
913 this.onLangChange = this.translate.onLangChange.subscribe((event) => {
914 if (this.lastKey) {
915 this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated
916 this.updateValue(query, interpolateParams, event.translations);
917 }
918 });
919 }
920 // subscribe to onDefaultLangChange event, in case the default language changes
921 if (!this.onDefaultLangChange) {
922 this.onDefaultLangChange = this.translate.onDefaultLangChange.subscribe(() => {
923 if (this.lastKey) {
924 this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated
925 this.updateValue(query, interpolateParams);
926 }
927 });
928 }
929 return this.value;
930 }
931 /**
932 * Clean any existing subscription to change events
933 */
934 _dispose() {
935 if (typeof this.onTranslationChange !== 'undefined') {
936 this.onTranslationChange.unsubscribe();
937 this.onTranslationChange = undefined;
938 }
939 if (typeof this.onLangChange !== 'undefined') {
940 this.onLangChange.unsubscribe();
941 this.onLangChange = undefined;
942 }
943 if (typeof this.onDefaultLangChange !== 'undefined') {
944 this.onDefaultLangChange.unsubscribe();
945 this.onDefaultLangChange = undefined;
946 }
947 }
948 ngOnDestroy() {
949 this._dispose();
950 }
951}
952TranslatePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslatePipe, deps: [{ token: TranslateService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Pipe });
953TranslatePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslatePipe, name: "translate", pure: false });
954TranslatePipe.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslatePipe });
955i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslatePipe, decorators: [{
956 type: Injectable
957 }, {
958 type: Pipe,
959 args: [{
960 name: 'translate',
961 pure: false // required to update the value when the promise is resolved
962 }]
963 }], ctorParameters: function () { return [{ type: TranslateService }, { type: i0.ChangeDetectorRef }]; } });
964
965class TranslateModule {
966 /**
967 * Use this method in your root module to provide the TranslateService
968 */
969 static forRoot(config = {}) {
970 return {
971 ngModule: TranslateModule,
972 providers: [
973 config.loader || { provide: TranslateLoader, useClass: TranslateFakeLoader },
974 config.compiler || { provide: TranslateCompiler, useClass: TranslateFakeCompiler },
975 config.parser || { provide: TranslateParser, useClass: TranslateDefaultParser },
976 config.missingTranslationHandler || { provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler },
977 TranslateStore,
978 { provide: USE_STORE, useValue: config.isolate },
979 { provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang },
980 { provide: USE_EXTEND, useValue: config.extend },
981 { provide: DEFAULT_LANGUAGE, useValue: config.defaultLanguage },
982 TranslateService
983 ]
984 };
985 }
986 /**
987 * Use this method in your other (non root) modules to import the directive/pipe
988 */
989 static forChild(config = {}) {
990 return {
991 ngModule: TranslateModule,
992 providers: [
993 config.loader || { provide: TranslateLoader, useClass: TranslateFakeLoader },
994 config.compiler || { provide: TranslateCompiler, useClass: TranslateFakeCompiler },
995 config.parser || { provide: TranslateParser, useClass: TranslateDefaultParser },
996 config.missingTranslationHandler || { provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler },
997 { provide: USE_STORE, useValue: config.isolate },
998 { provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang },
999 { provide: USE_EXTEND, useValue: config.extend },
1000 { provide: DEFAULT_LANGUAGE, useValue: config.defaultLanguage },
1001 TranslateService
1002 ]
1003 };
1004 }
1005}
1006TranslateModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1007TranslateModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateModule, declarations: [TranslatePipe,
1008 TranslateDirective], exports: [TranslatePipe,
1009 TranslateDirective] });
1010TranslateModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateModule });
1011i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateModule, decorators: [{
1012 type: NgModule,
1013 args: [{
1014 declarations: [
1015 TranslatePipe,
1016 TranslateDirective
1017 ],
1018 exports: [
1019 TranslatePipe,
1020 TranslateDirective
1021 ]
1022 }]
1023 }] });
1024
1025/**
1026 * Generated bundle index. Do not edit.
1027 */
1028
1029export { DEFAULT_LANGUAGE, FakeMissingTranslationHandler, MissingTranslationHandler, TranslateCompiler, TranslateDefaultParser, TranslateDirective, TranslateFakeCompiler, TranslateFakeLoader, TranslateLoader, TranslateModule, TranslateParser, TranslatePipe, TranslateService, TranslateStore, USE_DEFAULT_LANG, USE_EXTEND, USE_STORE };