UNPKG

42.7 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] ? Object.assign(Object.assign({}, 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 () {
672 return [{ type: TranslateStore }, { type: TranslateLoader }, { type: TranslateCompiler }, { type: TranslateParser }, { type: MissingTranslationHandler }, { type: undefined, decorators: [{
673 type: Inject,
674 args: [USE_DEFAULT_LANG]
675 }] }, { type: undefined, decorators: [{
676 type: Inject,
677 args: [USE_STORE]
678 }] }, { type: undefined, decorators: [{
679 type: Inject,
680 args: [USE_EXTEND]
681 }] }, { type: undefined, decorators: [{
682 type: Inject,
683 args: [DEFAULT_LANGUAGE]
684 }] }];
685 } });
686
687class TranslateDirective {
688 constructor(translateService, element, _ref) {
689 this.translateService = translateService;
690 this.element = element;
691 this._ref = _ref;
692 // subscribe to onTranslationChange event, in case the translations of the current lang change
693 if (!this.onTranslationChangeSub) {
694 this.onTranslationChangeSub = this.translateService.onTranslationChange.subscribe((event) => {
695 if (event.lang === this.translateService.currentLang) {
696 this.checkNodes(true, event.translations);
697 }
698 });
699 }
700 // subscribe to onLangChange event, in case the language changes
701 if (!this.onLangChangeSub) {
702 this.onLangChangeSub = this.translateService.onLangChange.subscribe((event) => {
703 this.checkNodes(true, event.translations);
704 });
705 }
706 // subscribe to onDefaultLangChange event, in case the default language changes
707 if (!this.onDefaultLangChangeSub) {
708 this.onDefaultLangChangeSub = this.translateService.onDefaultLangChange.subscribe((event) => {
709 this.checkNodes(true);
710 });
711 }
712 }
713 set translate(key) {
714 if (key) {
715 this.key = key;
716 this.checkNodes();
717 }
718 }
719 set translateParams(params) {
720 if (!equals(this.currentParams, params)) {
721 this.currentParams = params;
722 this.checkNodes(true);
723 }
724 }
725 ngAfterViewChecked() {
726 this.checkNodes();
727 }
728 checkNodes(forceUpdate = false, translations) {
729 let nodes = this.element.nativeElement.childNodes;
730 // if the element is empty
731 if (!nodes.length) {
732 // we add the key as content
733 this.setContent(this.element.nativeElement, this.key);
734 nodes = this.element.nativeElement.childNodes;
735 }
736 for (let i = 0; i < nodes.length; ++i) {
737 let node = nodes[i];
738 if (node.nodeType === 3) { // node type 3 is a text node
739 let key;
740 if (forceUpdate) {
741 node.lastKey = null;
742 }
743 if (isDefined(node.lookupKey)) {
744 key = node.lookupKey;
745 }
746 else if (this.key) {
747 key = this.key;
748 }
749 else {
750 let content = this.getContent(node);
751 let trimmedContent = content.trim();
752 if (trimmedContent.length) {
753 node.lookupKey = trimmedContent;
754 // we want to use the content as a key, not the translation value
755 if (content !== node.currentValue) {
756 key = trimmedContent;
757 // the content was changed from the user, we'll use it as a reference if needed
758 node.originalContent = content || node.originalContent;
759 }
760 else if (node.originalContent) { // the content seems ok, but the lang has changed
761 // the current content is the translation, not the key, use the last real content as key
762 key = node.originalContent.trim();
763 }
764 else if (content !== node.currentValue) {
765 // we want to use the content as a key, not the translation value
766 key = trimmedContent;
767 // the content was changed from the user, we'll use it as a reference if needed
768 node.originalContent = content || node.originalContent;
769 }
770 }
771 }
772 this.updateValue(key, node, translations);
773 }
774 }
775 }
776 updateValue(key, node, translations) {
777 if (key) {
778 if (node.lastKey === key && this.lastParams === this.currentParams) {
779 return;
780 }
781 this.lastParams = this.currentParams;
782 let onTranslation = (res) => {
783 if (res !== key) {
784 node.lastKey = key;
785 }
786 if (!node.originalContent) {
787 node.originalContent = this.getContent(node);
788 }
789 node.currentValue = isDefined(res) ? res : (node.originalContent || key);
790 // we replace in the original content to preserve spaces that we might have trimmed
791 this.setContent(node, this.key ? node.currentValue : node.originalContent.replace(key, node.currentValue));
792 this._ref.markForCheck();
793 };
794 if (isDefined(translations)) {
795 let res = this.translateService.getParsedResult(translations, key, this.currentParams);
796 if (isObservable(res)) {
797 res.subscribe({ next: onTranslation });
798 }
799 else {
800 onTranslation(res);
801 }
802 }
803 else {
804 this.translateService.get(key, this.currentParams).subscribe(onTranslation);
805 }
806 }
807 }
808 getContent(node) {
809 return isDefined(node.textContent) ? node.textContent : node.data;
810 }
811 setContent(node, content) {
812 if (isDefined(node.textContent)) {
813 node.textContent = content;
814 }
815 else {
816 node.data = content;
817 }
818 }
819 ngOnDestroy() {
820 if (this.onLangChangeSub) {
821 this.onLangChangeSub.unsubscribe();
822 }
823 if (this.onDefaultLangChangeSub) {
824 this.onDefaultLangChangeSub.unsubscribe();
825 }
826 if (this.onTranslationChangeSub) {
827 this.onTranslationChangeSub.unsubscribe();
828 }
829 }
830}
831TranslateDirective.ɵ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 });
832TranslateDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.0.0", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: { translate: "translate", translateParams: "translateParams" }, ngImport: i0 });
833i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateDirective, decorators: [{
834 type: Directive,
835 args: [{
836 selector: '[translate],[ngx-translate]'
837 }]
838 }], ctorParameters: function () { return [{ type: TranslateService }, { type: i0.ElementRef }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { translate: [{
839 type: Input
840 }], translateParams: [{
841 type: Input
842 }] } });
843
844class TranslatePipe {
845 constructor(translate, _ref) {
846 this.translate = translate;
847 this._ref = _ref;
848 this.value = '';
849 this.lastKey = null;
850 this.lastParams = [];
851 }
852 updateValue(key, interpolateParams, translations) {
853 let onTranslation = (res) => {
854 this.value = res !== undefined ? res : key;
855 this.lastKey = key;
856 this._ref.markForCheck();
857 };
858 if (translations) {
859 let res = this.translate.getParsedResult(translations, key, interpolateParams);
860 if (isObservable(res.subscribe)) {
861 res.subscribe(onTranslation);
862 }
863 else {
864 onTranslation(res);
865 }
866 }
867 this.translate.get(key, interpolateParams).subscribe(onTranslation);
868 }
869 transform(query, ...args) {
870 if (!query || !query.length) {
871 return query;
872 }
873 // if we ask another time for the same key, return the last value
874 if (equals(query, this.lastKey) && equals(args, this.lastParams)) {
875 return this.value;
876 }
877 let interpolateParams = undefined;
878 if (isDefined(args[0]) && args.length) {
879 if (typeof args[0] === 'string' && args[0].length) {
880 // we accept objects written in the template such as {n:1}, {'n':1}, {n:'v'}
881 // which is why we might need to change it to real JSON objects such as {"n":1} or {"n":"v"}
882 let validArgs = args[0]
883 .replace(/(\')?([a-zA-Z0-9_]+)(\')?(\s)?:/g, '"$2":')
884 .replace(/:(\s)?(\')(.*?)(\')/g, ':"$3"');
885 try {
886 interpolateParams = JSON.parse(validArgs);
887 }
888 catch (e) {
889 throw new SyntaxError(`Wrong parameter in TranslatePipe. Expected a valid Object, received: ${args[0]}`);
890 }
891 }
892 else if (typeof args[0] === 'object' && !Array.isArray(args[0])) {
893 interpolateParams = args[0];
894 }
895 }
896 // store the query, in case it changes
897 this.lastKey = query;
898 // store the params, in case they change
899 this.lastParams = args;
900 // set the value
901 this.updateValue(query, interpolateParams);
902 // if there is a subscription to onLangChange, clean it
903 this._dispose();
904 // subscribe to onTranslationChange event, in case the translations change
905 if (!this.onTranslationChange) {
906 this.onTranslationChange = this.translate.onTranslationChange.subscribe((event) => {
907 if (this.lastKey && event.lang === this.translate.currentLang) {
908 this.lastKey = null;
909 this.updateValue(query, interpolateParams, event.translations);
910 }
911 });
912 }
913 // subscribe to onLangChange event, in case the language changes
914 if (!this.onLangChange) {
915 this.onLangChange = this.translate.onLangChange.subscribe((event) => {
916 if (this.lastKey) {
917 this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated
918 this.updateValue(query, interpolateParams, event.translations);
919 }
920 });
921 }
922 // subscribe to onDefaultLangChange event, in case the default language changes
923 if (!this.onDefaultLangChange) {
924 this.onDefaultLangChange = this.translate.onDefaultLangChange.subscribe(() => {
925 if (this.lastKey) {
926 this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated
927 this.updateValue(query, interpolateParams);
928 }
929 });
930 }
931 return this.value;
932 }
933 /**
934 * Clean any existing subscription to change events
935 */
936 _dispose() {
937 if (typeof this.onTranslationChange !== 'undefined') {
938 this.onTranslationChange.unsubscribe();
939 this.onTranslationChange = undefined;
940 }
941 if (typeof this.onLangChange !== 'undefined') {
942 this.onLangChange.unsubscribe();
943 this.onLangChange = undefined;
944 }
945 if (typeof this.onDefaultLangChange !== 'undefined') {
946 this.onDefaultLangChange.unsubscribe();
947 this.onDefaultLangChange = undefined;
948 }
949 }
950 ngOnDestroy() {
951 this._dispose();
952 }
953}
954TranslatePipe.ɵ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 });
955TranslatePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslatePipe, name: "translate", pure: false });
956TranslatePipe.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslatePipe });
957i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslatePipe, decorators: [{
958 type: Injectable
959 }, {
960 type: Pipe,
961 args: [{
962 name: 'translate',
963 pure: false // required to update the value when the promise is resolved
964 }]
965 }], ctorParameters: function () { return [{ type: TranslateService }, { type: i0.ChangeDetectorRef }]; } });
966
967class TranslateModule {
968 /**
969 * Use this method in your root module to provide the TranslateService
970 */
971 static forRoot(config = {}) {
972 return {
973 ngModule: TranslateModule,
974 providers: [
975 config.loader || { provide: TranslateLoader, useClass: TranslateFakeLoader },
976 config.compiler || { provide: TranslateCompiler, useClass: TranslateFakeCompiler },
977 config.parser || { provide: TranslateParser, useClass: TranslateDefaultParser },
978 config.missingTranslationHandler || { provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler },
979 TranslateStore,
980 { provide: USE_STORE, useValue: config.isolate },
981 { provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang },
982 { provide: USE_EXTEND, useValue: config.extend },
983 { provide: DEFAULT_LANGUAGE, useValue: config.defaultLanguage },
984 TranslateService
985 ]
986 };
987 }
988 /**
989 * Use this method in your other (non root) modules to import the directive/pipe
990 */
991 static forChild(config = {}) {
992 return {
993 ngModule: TranslateModule,
994 providers: [
995 config.loader || { provide: TranslateLoader, useClass: TranslateFakeLoader },
996 config.compiler || { provide: TranslateCompiler, useClass: TranslateFakeCompiler },
997 config.parser || { provide: TranslateParser, useClass: TranslateDefaultParser },
998 config.missingTranslationHandler || { provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler },
999 { provide: USE_STORE, useValue: config.isolate },
1000 { provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang },
1001 { provide: USE_EXTEND, useValue: config.extend },
1002 { provide: DEFAULT_LANGUAGE, useValue: config.defaultLanguage },
1003 TranslateService
1004 ]
1005 };
1006 }
1007}
1008TranslateModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1009TranslateModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateModule, declarations: [TranslatePipe,
1010 TranslateDirective], exports: [TranslatePipe,
1011 TranslateDirective] });
1012TranslateModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateModule });
1013i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: TranslateModule, decorators: [{
1014 type: NgModule,
1015 args: [{
1016 declarations: [
1017 TranslatePipe,
1018 TranslateDirective
1019 ],
1020 exports: [
1021 TranslatePipe,
1022 TranslateDirective
1023 ]
1024 }]
1025 }] });
1026
1027/**
1028 * Generated bundle index. Do not edit.
1029 */
1030
1031export { DEFAULT_LANGUAGE, FakeMissingTranslationHandler, MissingTranslationHandler, TranslateCompiler, TranslateDefaultParser, TranslateDirective, TranslateFakeCompiler, TranslateFakeLoader, TranslateLoader, TranslateModule, TranslateParser, TranslatePipe, TranslateService, TranslateStore, USE_DEFAULT_LANG, USE_EXTEND, USE_STORE };