UNPKG

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