UNPKG

@ngx-translate/core

Version:

The internationalization (i18n) library for Angular

1,278 lines (1,265 loc) 124 kB
import { Injectable, EventEmitter, Inject, InjectionToken, ChangeDetectorRef, Directive, ElementRef, Input, Pipe, NgModule } from '@angular/core'; import { of, concat, merge, Observable } from 'rxjs'; import { map, share, switchMap, take, toArray } from 'rxjs/operators'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @abstract */ class TranslateLoader { } /** * This loader is just a placeholder that does nothing, in case you don't need a loader at all */ class TranslateFakeLoader extends TranslateLoader { /** * @param {?} lang * @return {?} */ getTranslation(lang) { return of({}); } } TranslateFakeLoader.decorators = [ { type: Injectable } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @abstract */ class MissingTranslationHandler { } /** * This handler is just a placeholder that does nothing, in case you don't need a missing translation handler at all */ class FakeMissingTranslationHandler { /** * @param {?} params * @return {?} */ handle(params) { return params.key; } } FakeMissingTranslationHandler.decorators = [ { type: Injectable } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @abstract */ class TranslateCompiler { } /** * This compiler is just a placeholder that does nothing, in case you don't need a compiler at all */ class TranslateFakeCompiler extends TranslateCompiler { /** * @param {?} value * @param {?} lang * @return {?} */ compile(value, lang) { return value; } /** * @param {?} translations * @param {?} lang * @return {?} */ compileTranslations(translations, lang) { return translations; } } TranslateFakeCompiler.decorators = [ { type: Injectable } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /* tslint:disable */ /** * Determines if two objects or two values are equivalent. * * Two objects or values are considered equivalent if at least one of the following is true: * * * Both objects or values pass `===` comparison. * * Both objects or values are of the same type and all of their properties are equal by * comparing them with `equals`. * * @param {?} o1 Object or value to compare. * @param {?} o2 Object or value to compare. * @return {?} true if arguments are equal. */ function equals(o1, o2) { if (o1 === o2) return true; if (o1 === null || o2 === null) return false; if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN // NaN === NaN /** @type {?} */ let t1 = typeof o1; /** @type {?} */ let t2 = typeof o2; /** @type {?} */ let length; /** @type {?} */ let key; /** @type {?} */ let keySet; if (t1 == t2 && t1 == 'object') { if (Array.isArray(o1)) { if (!Array.isArray(o2)) return false; if ((length = o1.length) == o2.length) { for (key = 0; key < length; key++) { if (!equals(o1[key], o2[key])) return false; } return true; } } else { if (Array.isArray(o2)) { return false; } keySet = Object.create(null); for (key in o1) { if (!equals(o1[key], o2[key])) { return false; } keySet[key] = true; } for (key in o2) { if (!(key in keySet) && typeof o2[key] !== 'undefined') { return false; } } return true; } } return false; } /* tslint:enable */ /** * @param {?} value * @return {?} */ function isDefined(value) { return typeof value !== 'undefined' && value !== null; } /** * @param {?} item * @return {?} */ function isObject(item) { return (item && typeof item === 'object' && !Array.isArray(item)); } /** * @param {?} target * @param {?} source * @return {?} */ function mergeDeep(target, source) { /** @type {?} */ let output = Object.assign({}, target); if (isObject(target) && isObject(source)) { Object.keys(source).forEach((key) => { if (isObject(source[key])) { if (!(key in target)) { Object.assign(output, { [key]: source[key] }); } else { output[key] = mergeDeep(target[key], source[key]); } } else { Object.assign(output, { [key]: source[key] }); } }); } return output; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @abstract */ class TranslateParser { } class TranslateDefaultParser extends TranslateParser { constructor() { super(...arguments); this.templateMatcher = /{{\s?([^{}\s]*)\s?}}/g; } /** * @param {?} expr * @param {?=} params * @return {?} */ interpolate(expr, params) { /** @type {?} */ let result; if (typeof expr === 'string') { result = this.interpolateString(expr, params); } else if (typeof expr === 'function') { result = this.interpolateFunction(expr, params); } else { // this should not happen, but an unrelated TranslateService test depends on it result = (/** @type {?} */ (expr)); } return result; } /** * @param {?} target * @param {?} key * @return {?} */ getValue(target, key) { /** @type {?} */ let keys = key.split('.'); key = ''; do { key += keys.shift(); if (isDefined(target) && isDefined(target[key]) && (typeof target[key] === 'object' || !keys.length)) { target = target[key]; key = ''; } else if (!keys.length) { target = undefined; } else { key += '.'; } } while (keys.length); return target; } /** * @param {?} fn * @param {?=} params * @return {?} */ interpolateFunction(fn, params) { return fn(params); } /** * @param {?} expr * @param {?=} params * @return {?} */ interpolateString(expr, params) { if (!params) { return expr; } return expr.replace(this.templateMatcher, (substring, b) => { /** @type {?} */ let r = this.getValue(params, b); return isDefined(r) ? r : substring; }); } } TranslateDefaultParser.decorators = [ { type: Injectable } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class TranslateStore { constructor() { /** * The lang currently used */ this.currentLang = this.defaultLang; /** * a list of translations per lang */ this.translations = {}; /** * an array of langs */ this.langs = []; /** * An EventEmitter to listen to translation change events * onTranslationChange.subscribe((params: TranslationChangeEvent) => { * // do something * }); */ this.onTranslationChange = new EventEmitter(); /** * An EventEmitter to listen to lang change events * onLangChange.subscribe((params: LangChangeEvent) => { * // do something * }); */ this.onLangChange = new EventEmitter(); /** * An EventEmitter to listen to default lang change events * onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => { * // do something * }); */ this.onDefaultLangChange = new EventEmitter(); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** @type {?} */ const USE_STORE = new InjectionToken('USE_STORE'); /** @type {?} */ const USE_DEFAULT_LANG = new InjectionToken('USE_DEFAULT_LANG'); class TranslateService { /** * * @param {?} store an instance of the store (that is supposed to be unique) * @param {?} currentLoader An instance of the loader currently used * @param {?} compiler An instance of the compiler currently used * @param {?} parser An instance of the parser currently used * @param {?} missingTranslationHandler A handler for missing translations. * @param {?=} useDefaultLang whether we should use default language translation when current language translation is missing. * @param {?=} isolate whether this service should use the store or not */ constructor(store, currentLoader, compiler, parser, missingTranslationHandler, useDefaultLang = true, isolate = false) { this.store = store; this.currentLoader = currentLoader; this.compiler = compiler; this.parser = parser; this.missingTranslationHandler = missingTranslationHandler; this.useDefaultLang = useDefaultLang; this.isolate = isolate; this.pending = false; this._onTranslationChange = new EventEmitter(); this._onLangChange = new EventEmitter(); this._onDefaultLangChange = new EventEmitter(); this._langs = []; this._translations = {}; this._translationRequests = {}; } /** * An EventEmitter to listen to translation change events * onTranslationChange.subscribe((params: TranslationChangeEvent) => { * // do something * }); * @return {?} */ get onTranslationChange() { return this.isolate ? this._onTranslationChange : this.store.onTranslationChange; } /** * An EventEmitter to listen to lang change events * onLangChange.subscribe((params: LangChangeEvent) => { * // do something * }); * @return {?} */ get onLangChange() { return this.isolate ? this._onLangChange : this.store.onLangChange; } /** * An EventEmitter to listen to default lang change events * onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => { * // do something * }); * @return {?} */ get onDefaultLangChange() { return this.isolate ? this._onDefaultLangChange : this.store.onDefaultLangChange; } /** * The default lang to fallback when translations are missing on the current lang * @return {?} */ get defaultLang() { return this.isolate ? this._defaultLang : this.store.defaultLang; } /** * @param {?} defaultLang * @return {?} */ set defaultLang(defaultLang) { if (this.isolate) { this._defaultLang = defaultLang; } else { this.store.defaultLang = defaultLang; } } /** * The lang currently used * @return {?} */ get currentLang() { return this.isolate ? this._currentLang : this.store.currentLang; } /** * @param {?} currentLang * @return {?} */ set currentLang(currentLang) { if (this.isolate) { this._currentLang = currentLang; } else { this.store.currentLang = currentLang; } } /** * an array of langs * @return {?} */ get langs() { return this.isolate ? this._langs : this.store.langs; } /** * @param {?} langs * @return {?} */ set langs(langs) { if (this.isolate) { this._langs = langs; } else { this.store.langs = langs; } } /** * a list of translations per lang * @return {?} */ get translations() { return this.isolate ? this._translations : this.store.translations; } /** * @param {?} translations * @return {?} */ set translations(translations) { if (this.isolate) { this._translations = translations; } else { this.store.translations = translations; } } /** * Sets the default language to use as a fallback * @param {?} lang * @return {?} */ setDefaultLang(lang) { if (lang === this.defaultLang) { return; } /** @type {?} */ let pending = this.retrieveTranslations(lang); if (typeof pending !== "undefined") { // on init set the defaultLang immediately if (!this.defaultLang) { this.defaultLang = lang; } pending.pipe(take(1)) .subscribe((res) => { this.changeDefaultLang(lang); }); } else { // we already have this language this.changeDefaultLang(lang); } } /** * Gets the default language used * @return {?} */ getDefaultLang() { return this.defaultLang; } /** * Changes the lang currently used * @param {?} lang * @return {?} */ use(lang) { // don't change the language if the language given is already selected if (lang === this.currentLang) { return of(this.translations[lang]); } /** @type {?} */ let pending = this.retrieveTranslations(lang); if (typeof pending !== "undefined") { // on init set the currentLang immediately if (!this.currentLang) { this.currentLang = lang; } pending.pipe(take(1)) .subscribe((res) => { this.changeLang(lang); }); return pending; } else { // we have this language, return an Observable this.changeLang(lang); return of(this.translations[lang]); } } /** * Retrieves the given translations * @param {?} lang * @return {?} */ retrieveTranslations(lang) { /** @type {?} */ let pending; // if this language is unavailable, ask for it if (typeof this.translations[lang] === "undefined") { this._translationRequests[lang] = this._translationRequests[lang] || this.getTranslation(lang); pending = this._translationRequests[lang]; } return pending; } /** * Gets an object of translations for a given language with the current loader * and passes it through the compiler * @param {?} lang * @return {?} */ getTranslation(lang) { this.pending = true; /** @type {?} */ const loadingTranslations = this.currentLoader.getTranslation(lang).pipe(share()); this.loadingTranslations = loadingTranslations.pipe(take(1), map((res) => this.compiler.compileTranslations(res, lang)), share()); this.loadingTranslations .subscribe((res) => { this.translations[lang] = res; this.updateLangs(); this.pending = false; }, (err) => { this.pending = false; }); return loadingTranslations; } /** * Manually sets an object of translations for a given language * after passing it through the compiler * @param {?} lang * @param {?} translations * @param {?=} shouldMerge * @return {?} */ setTranslation(lang, translations, shouldMerge = false) { translations = this.compiler.compileTranslations(translations, lang); if (shouldMerge && this.translations[lang]) { this.translations[lang] = mergeDeep(this.translations[lang], translations); } else { this.translations[lang] = translations; } this.updateLangs(); this.onTranslationChange.emit({ lang: lang, translations: this.translations[lang] }); } /** * Returns an array of currently available langs * @return {?} */ getLangs() { return this.langs; } /** * Add available langs * @param {?} langs * @return {?} */ addLangs(langs) { langs.forEach((lang) => { if (this.langs.indexOf(lang) === -1) { this.langs.push(lang); } }); } /** * Update the list of available langs * @return {?} */ updateLangs() { this.addLangs(Object.keys(this.translations)); } /** * Returns the parsed result of the translations * @param {?} translations * @param {?} key * @param {?=} interpolateParams * @return {?} */ getParsedResult(translations, key, interpolateParams) { /** @type {?} */ let res; if (key instanceof Array) { /** @type {?} */ let result = {}; /** @type {?} */ let observables = false; for (let k of key) { result[k] = this.getParsedResult(translations, k, interpolateParams); if (typeof result[k].subscribe === "function") { observables = true; } } if (observables) { /** @type {?} */ let mergedObs; for (let k of key) { /** @type {?} */ let obs = typeof result[k].subscribe === "function" ? result[k] : of((/** @type {?} */ (result[k]))); if (typeof mergedObs === "undefined") { mergedObs = obs; } else { mergedObs = merge(mergedObs, obs); } } return mergedObs.pipe(toArray(), map((arr) => { /** @type {?} */ let obj = {}; arr.forEach((value, index) => { obj[key[index]] = value; }); return obj; })); } return result; } if (translations) { res = this.parser.interpolate(this.parser.getValue(translations, key), interpolateParams); } if (typeof res === "undefined" && this.defaultLang && this.defaultLang !== this.currentLang && this.useDefaultLang) { res = this.parser.interpolate(this.parser.getValue(this.translations[this.defaultLang], key), interpolateParams); } if (typeof res === "undefined") { /** @type {?} */ let params = { key, translateService: this }; if (typeof interpolateParams !== 'undefined') { params.interpolateParams = interpolateParams; } res = this.missingTranslationHandler.handle(params); } return typeof res !== "undefined" ? res : key; } /** * Gets the translated value of a key (or an array of keys) * @param {?} key * @param {?=} interpolateParams * @return {?} the translated key, or an object of translated keys */ get(key, interpolateParams) { if (!isDefined(key) || !key.length) { throw new Error(`Parameter "key" required`); } // check if we are loading a new translation to use if (this.pending) { return Observable.create((observer) => { /** @type {?} */ let onComplete = (res) => { observer.next(res); observer.complete(); }; /** @type {?} */ let onError = (err) => { observer.error(err); }; this.loadingTranslations.subscribe((res) => { res = this.getParsedResult(res, key, interpolateParams); if (typeof res.subscribe === "function") { res.subscribe(onComplete, onError); } else { onComplete(res); } }, onError); }); } else { /** @type {?} */ let res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams); if (typeof res.subscribe === "function") { return res; } else { return of(res); } } } /** * Returns a stream of translated values of a key (or an array of keys) which updates * whenever the language changes. * @param {?} key * @param {?=} interpolateParams * @return {?} A stream of the translated key, or an object of translated keys */ stream(key, interpolateParams) { if (!isDefined(key) || !key.length) { throw new Error(`Parameter "key" required`); } return concat(this.get(key, interpolateParams), this.onLangChange.pipe(switchMap((event) => { /** @type {?} */ const res = this.getParsedResult(event.translations, key, interpolateParams); if (typeof res.subscribe === "function") { return res; } else { return of(res); } }))); } /** * Returns a translation instantly from the internal state of loaded translation. * All rules regarding the current language, the preferred language of even fallback languages will be used except any promise handling. * @param {?} key * @param {?=} interpolateParams * @return {?} */ instant(key, interpolateParams) { if (!isDefined(key) || !key.length) { throw new Error(`Parameter "key" required`); } /** @type {?} */ let res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams); if (typeof res.subscribe !== "undefined") { if (key instanceof Array) { /** @type {?} */ let obj = {}; key.forEach((value, index) => { obj[key[index]] = key[index]; }); return obj; } return key; } else { return res; } } /** * Sets the translated value of a key, after compiling it * @param {?} key * @param {?} value * @param {?=} lang * @return {?} */ set(key, value, lang = this.currentLang) { this.translations[lang][key] = this.compiler.compile(value, lang); this.updateLangs(); this.onTranslationChange.emit({ lang: lang, translations: this.translations[lang] }); } /** * Changes the current lang * @param {?} lang * @return {?} */ changeLang(lang) { this.currentLang = lang; this.onLangChange.emit({ lang: lang, translations: this.translations[lang] }); // if there is no default lang, use the one that we just set if (!this.defaultLang) { this.changeDefaultLang(lang); } } /** * Changes the default lang * @param {?} lang * @return {?} */ changeDefaultLang(lang) { this.defaultLang = lang; this.onDefaultLangChange.emit({ lang: lang, translations: this.translations[lang] }); } /** * Allows to reload the lang file from the file * @param {?} lang * @return {?} */ reloadLang(lang) { this.resetLang(lang); return this.getTranslation(lang); } /** * Deletes inner translation * @param {?} lang * @return {?} */ resetLang(lang) { this._translationRequests[lang] = undefined; this.translations[lang] = undefined; } /** * Returns the language code name from the browser, e.g. "de" * @return {?} */ getBrowserLang() { if (typeof window === 'undefined' || typeof window.navigator === 'undefined') { return undefined; } /** @type {?} */ let browserLang = window.navigator.languages ? window.navigator.languages[0] : null; browserLang = browserLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage; if (browserLang.indexOf('-') !== -1) { browserLang = browserLang.split('-')[0]; } if (browserLang.indexOf('_') !== -1) { browserLang = browserLang.split('_')[0]; } return browserLang; } /** * Returns the culture language code name from the browser, e.g. "de-DE" * @return {?} */ getBrowserCultureLang() { if (typeof window === 'undefined' || typeof window.navigator === 'undefined') { return undefined; } /** @type {?} */ let browserCultureLang = window.navigator.languages ? window.navigator.languages[0] : null; browserCultureLang = browserCultureLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage; return browserCultureLang; } } TranslateService.decorators = [ { type: Injectable } ]; /** @nocollapse */ TranslateService.ctorParameters = () => [ { type: TranslateStore }, { type: TranslateLoader }, { type: TranslateCompiler }, { type: TranslateParser }, { type: MissingTranslationHandler }, { type: Boolean, decorators: [{ type: Inject, args: [USE_DEFAULT_LANG,] }] }, { type: Boolean, decorators: [{ type: Inject, args: [USE_STORE,] }] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class TranslateDirective { /** * @param {?} translateService * @param {?} element * @param {?} _ref */ constructor(translateService, element, _ref) { this.translateService = translateService; this.element = element; this._ref = _ref; // subscribe to onTranslationChange event, in case the translations of the current lang change if (!this.onTranslationChangeSub) { this.onTranslationChangeSub = this.translateService.onTranslationChange.subscribe((event) => { if (event.lang === this.translateService.currentLang) { this.checkNodes(true, event.translations); } }); } // subscribe to onLangChange event, in case the language changes if (!this.onLangChangeSub) { this.onLangChangeSub = this.translateService.onLangChange.subscribe((event) => { this.checkNodes(true, event.translations); }); } // subscribe to onDefaultLangChange event, in case the default language changes if (!this.onDefaultLangChangeSub) { this.onDefaultLangChangeSub = this.translateService.onDefaultLangChange.subscribe((event) => { this.checkNodes(true); }); } } /** * @param {?} key * @return {?} */ set translate(key) { if (key) { this.key = key; this.checkNodes(); } } /** * @param {?} params * @return {?} */ set translateParams(params) { if (!equals(this.currentParams, params)) { this.currentParams = params; this.checkNodes(true); } } /** * @return {?} */ ngAfterViewChecked() { this.checkNodes(); } /** * @param {?=} forceUpdate * @param {?=} translations * @return {?} */ checkNodes(forceUpdate = false, translations) { /** @type {?} */ let nodes = this.element.nativeElement.childNodes; // if the element is empty if (!nodes.length) { // we add the key as content this.setContent(this.element.nativeElement, this.key); nodes = this.element.nativeElement.childNodes; } for (let i = 0; i < nodes.length; ++i) { /** @type {?} */ let node = nodes[i]; if (node.nodeType === 3) { // node type 3 is a text node // node type 3 is a text node /** @type {?} */ let key; if (this.key) { key = this.key; if (forceUpdate) { node.lastKey = null; } } else { /** @type {?} */ let content = this.getContent(node); /** @type {?} */ let trimmedContent = content.trim(); if (trimmedContent.length) { // we want to use the content as a key, not the translation value if (content !== node.currentValue) { key = trimmedContent; // the content was changed from the user, we'll use it as a reference if needed node.originalContent = this.getContent(node); } else if (node.originalContent && forceUpdate) { // the content seems ok, but the lang has changed node.lastKey = null; // the current content is the translation, not the key, use the last real content as key key = node.originalContent.trim(); } } } this.updateValue(key, node, translations); } } } /** * @param {?} key * @param {?} node * @param {?} translations * @return {?} */ updateValue(key, node, translations) { if (key) { if (node.lastKey === key && this.lastParams === this.currentParams) { return; } this.lastParams = this.currentParams; /** @type {?} */ let onTranslation = (res) => { if (res !== key) { node.lastKey = key; } if (!node.originalContent) { node.originalContent = this.getContent(node); } node.currentValue = isDefined(res) ? res : (node.originalContent || key); // we replace in the original content to preserve spaces that we might have trimmed this.setContent(node, this.key ? node.currentValue : node.originalContent.replace(key, node.currentValue)); this._ref.markForCheck(); }; if (isDefined(translations)) { /** @type {?} */ let res = this.translateService.getParsedResult(translations, key, this.currentParams); if (typeof res.subscribe === "function") { res.subscribe(onTranslation); } else { onTranslation(res); } } else { this.translateService.get(key, this.currentParams).subscribe(onTranslation); } } } /** * @param {?} node * @return {?} */ getContent(node) { return isDefined(node.textContent) ? node.textContent : node.data; } /** * @param {?} node * @param {?} content * @return {?} */ setContent(node, content) { if (isDefined(node.textContent)) { node.textContent = content; } else { node.data = content; } } /** * @return {?} */ ngOnDestroy() { if (this.onLangChangeSub) { this.onLangChangeSub.unsubscribe(); } if (this.onDefaultLangChangeSub) { this.onDefaultLangChangeSub.unsubscribe(); } if (this.onTranslationChangeSub) { this.onTranslationChangeSub.unsubscribe(); } } } TranslateDirective.decorators = [ { type: Directive, args: [{ selector: '[translate],[ngx-translate]' },] } ]; /** @nocollapse */ TranslateDirective.ctorParameters = () => [ { type: TranslateService }, { type: ElementRef }, { type: ChangeDetectorRef } ]; TranslateDirective.propDecorators = { translate: [{ type: Input }], translateParams: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class TranslatePipe { /** * @param {?} translate * @param {?} _ref */ constructor(translate, _ref) { this.translate = translate; this._ref = _ref; this.value = ''; } /** * @param {?} key * @param {?=} interpolateParams * @param {?=} translations * @return {?} */ updateValue(key, interpolateParams, translations) { /** @type {?} */ let onTranslation = (res) => { this.value = res !== undefined ? res : key; this.lastKey = key; this._ref.markForCheck(); }; if (translations) { /** @type {?} */ let res = this.translate.getParsedResult(translations, key, interpolateParams); if (typeof res.subscribe === 'function') { res.subscribe(onTranslation); } else { onTranslation(res); } } this.translate.get(key, interpolateParams).subscribe(onTranslation); } /** * @param {?} query * @param {...?} args * @return {?} */ transform(query, ...args) { if (!query || query.length === 0) { return query; } // if we ask another time for the same key, return the last value if (equals(query, this.lastKey) && equals(args, this.lastParams)) { return this.value; } /** @type {?} */ let interpolateParams; if (isDefined(args[0]) && args.length) { if (typeof args[0] === 'string' && args[0].length) { // we accept objects written in the template such as {n:1}, {'n':1}, {n:'v'} // which is why we might need to change it to real JSON objects such as {"n":1} or {"n":"v"} /** @type {?} */ let validArgs = args[0] .replace(/(\')?([a-zA-Z0-9_]+)(\')?(\s)?:/g, '"$2":') .replace(/:(\s)?(\')(.*?)(\')/g, ':"$3"'); try { interpolateParams = JSON.parse(validArgs); } catch (e) { throw new SyntaxError(`Wrong parameter in TranslatePipe. Expected a valid Object, received: ${args[0]}`); } } else if (typeof args[0] === 'object' && !Array.isArray(args[0])) { interpolateParams = args[0]; } } // store the query, in case it changes this.lastKey = query; // store the params, in case they change this.lastParams = args; // set the value this.updateValue(query, interpolateParams); // if there is a subscription to onLangChange, clean it this._dispose(); // subscribe to onTranslationChange event, in case the translations change if (!this.onTranslationChange) { this.onTranslationChange = this.translate.onTranslationChange.subscribe((event) => { if (this.lastKey && event.lang === this.translate.currentLang) { this.lastKey = null; this.updateValue(query, interpolateParams, event.translations); } }); } // subscribe to onLangChange event, in case the language changes if (!this.onLangChange) { this.onLangChange = this.translate.onLangChange.subscribe((event) => { if (this.lastKey) { this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated this.updateValue(query, interpolateParams, event.translations); } }); } // subscribe to onDefaultLangChange event, in case the default language changes if (!this.onDefaultLangChange) { this.onDefaultLangChange = this.translate.onDefaultLangChange.subscribe(() => { if (this.lastKey) { this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated this.updateValue(query, interpolateParams); } }); } return this.value; } /** * Clean any existing subscription to change events * @return {?} */ _dispose() { if (typeof this.onTranslationChange !== 'undefined') { this.onTranslationChange.unsubscribe(); this.onTranslationChange = undefined; } if (typeof this.onLangChange !== 'undefined') { this.onLangChange.unsubscribe(); this.onLangChange = undefined; } if (typeof this.onDefaultLangChange !== 'undefined') { this.onDefaultLangChange.unsubscribe(); this.onDefaultLangChange = undefined; } } /** * @return {?} */ ngOnDestroy() { this._dispose(); } } TranslatePipe.decorators = [ { type: Injectable }, { type: Pipe, args: [{ name: 'translate', pure: false // required to update the value when the promise is resolved },] } ]; /** @nocollapse */ TranslatePipe.ctorParameters = () => [ { type: TranslateService }, { type: ChangeDetectorRef } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class TranslateModule { /** * Use this method in your root module to provide the TranslateService * @param {?=} config * @return {?} */ static forRoot(config = {}) { return { ngModule: TranslateModule, providers: [ config.loader || { provide: TranslateLoader, useClass: TranslateFakeLoader }, config.compiler || { provide: TranslateCompiler, useClass: TranslateFakeCompiler }, config.parser || { provide: TranslateParser, useClass: TranslateDefaultParser }, config.missingTranslationHandler || { provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler }, TranslateStore, { provide: USE_STORE, useValue: config.isolate }, { provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang }, TranslateService ] }; } /** * Use this method in your other (non root) modules to import the directive/pipe * @param {?=} config * @return {?} */ static forChild(config = {}) { return { ngModule: TranslateModule, providers: [ config.loader || { provide: TranslateLoader, useClass: TranslateFakeLoader }, config.compiler || { provide: TranslateCompiler, useClass: TranslateFakeCompiler }, config.parser || { provide: TranslateParser, useClass: TranslateDefaultParser }, config.missingTranslationHandler || { provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler }, { provide: USE_STORE, useValue: config.isolate }, { provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang }, TranslateService ] }; } } TranslateModule.decorators = [ { type: NgModule, args: [{ declarations: [ TranslatePipe, TranslateDirective ], exports: [ TranslatePipe, TranslateDirective ] },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ export { TranslateModule, TranslateLoader, TranslateFakeLoader, USE_STORE, USE_DEFAULT_LANG, TranslateService, MissingTranslationHandler, FakeMissingTranslationHandler, TranslateParser, TranslateDefaultParser, TranslateCompiler, TranslateFakeCompiler, TranslateDirective, TranslatePipe, TranslateStore }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LXRyYW5zbGF0ZS1jb3JlLmpzLm1hcCIsInNvdXJjZXMiOlsibmc6Ly9Abmd4LXRyYW5zbGF0ZS9jb3JlL2xpYi90cmFuc2xhdGUubG9hZGVyLnRzIiwibmc6Ly9Abmd4LXRyYW5zbGF0ZS9jb3JlL2xpYi9taXNzaW5nLXRyYW5zbGF0aW9uLWhhbmRsZXIudHMiLCJuZzovL0BuZ3gtdHJhbnNsYXRlL2NvcmUvbGliL3RyYW5zbGF0ZS5jb21waWxlci50cyIsIm5nOi8vQG5neC10cmFuc2xhdGUvY29yZS9saWIvdXRpbC50cyIsIm5nOi8vQG5neC10cmFuc2xhdGUvY29yZS9saWIvdHJhbnNsYXRlLnBhcnNlci50cyIsIm5nOi8vQG5neC10cmFuc2xhdGUvY29yZS9saWIvdHJhbnNsYXRlLnN0b3JlLnRzIiwibmc6Ly9Abmd4LXRyYW5zbGF0ZS9jb3JlL2xpYi90cmFuc2xhdGUuc2VydmljZS50cyIsIm5nOi8vQG5neC10cmFuc2xhdGUvY29yZS9saWIvdHJhbnNsYXRlLmRpcmVjdGl2ZS50cyIsIm5nOi8vQG5neC10cmFuc2xhdGUvY29yZS9saWIvdHJhbnNsYXRlLnBpcGUudHMiLCJuZzovL0BuZ3gtdHJhbnNsYXRlL2NvcmUvcHVibGljX2FwaS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge0luamVjdGFibGV9IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5pbXBvcnQge09ic2VydmFibGUsIG9mfSBmcm9tIFwicnhqc1wiO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgVHJhbnNsYXRlTG9hZGVyIHtcbiAgYWJzdHJhY3QgZ2V0VHJhbnNsYXRpb24obGFuZzogc3RyaW5nKTogT2JzZXJ2YWJsZTxhbnk+O1xufVxuXG4vKipcbiAqIFRoaXMgbG9hZGVyIGlzIGp1c3QgYSBwbGFjZWhvbGRlciB0aGF0IGRvZXMgbm90aGluZywgaW4gY2FzZSB5b3UgZG9uJ3QgbmVlZCBhIGxvYWRlciBhdCBhbGxcbiAqL1xuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIFRyYW5zbGF0ZUZha2VMb2FkZXIgZXh0ZW5kcyBUcmFuc2xhdGVMb2FkZXIge1xuICBnZXRUcmFuc2xhdGlvbihsYW5nOiBzdHJpbmcpOiBPYnNlcnZhYmxlPGFueT4ge1xuICAgIHJldHVybiBvZih7fSk7XG4gIH1cbn1cbiIsImltcG9ydCB7SW5qZWN0YWJsZX0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7VHJhbnNsYXRlU2VydmljZX0gZnJvbSBcIi4vdHJhbnNsYXRlLnNlcnZpY2VcIjtcblxuZXhwb3J0IGludGVyZmFjZSBNaXNzaW5nVHJhbnNsYXRpb25IYW5kbGVyUGFyYW1zIHtcbiAgLyoqXG4gICAqIHRoZSBrZXkgdGhhdCdzIG1pc3NpbmcgaW4gdHJhbnNsYXRpb24gZmlsZXNcbiAgICovXG4gIGtleTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBhbiBpbnN0YW5jZSBvZiB0aGUgc2VydmljZSB0aGF0IHdhcyB1bmFibGUgdG8gdHJhbnNsYXRlIHRoZSBrZXkuXG4gICAqL1xuICB0cmFuc2xhdGVTZXJ2aWNlOiBUcmFuc2xhdGVTZXJ2aWNlO1xuXG4gIC8qKlxuICAgKiBpbnRlcnBvbGF0aW9uIHBhcmFtcyB0aGF0IHdlcmUgcGFzc2VkIGFsb25nIGZvciB0cmFuc2xhdGluZyB0aGUgZ2l2ZW4ga2V5LlxuICAgKi9cbiAgaW50ZXJwb2xhdGVQYXJhbXM/OiBPYmplY3Q7XG59XG5cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBNaXNzaW5nVHJhbnNsYXRpb25IYW5kbGVyIHtcbiAgLyoqXG4gICAqIEEgZnVuY3Rpb24gdGhhdCBoYW5kbGVzIG1pc3NpbmcgdHJhbnNsYXRpb25zLlxuICAgKlxuICAgKiBAcGFyYW0gcGFyYW1zIGNvbnRleHQgZm9yIHJlc29sdmluZyBhIG1pc3NpbmcgdHJhbnNsYXRpb25cbiAgICogQHJldHVybnMgYSB2YWx1ZSBvciBhbiBvYnNlcnZhYmxlXG4gICAqIElmIGl0IHJldHVybnMgYSB2YWx1ZSwgdGhlbiB0aGlzIHZhbHVlIGlzIHVzZWQuXG4gICAqIElmIGl0IHJldHVybiBhbiBvYnNlcnZhYmxlLCB0aGUgdmFsdWUgcmV0dXJuZWQgYnkgdGhpcyBvYnNlcnZhYmxlIHdpbGwgYmUgdXNlZCAoZXhjZXB0IGlmIHRoZSBtZXRob2Qgd2FzIFwiaW5zdGFudFwiKS5cbiAgICogSWYgaXQgZG9lc24ndCByZXR1cm4gdGhlbiB0aGUga2V5IHdpbGwgYmUgdXNlZCBhcyBhIHZhbHVlXG4gICAqL1xuICBhYnN0cmFjdCBoYW5kbGUocGFyYW1zOiBNaXNzaW5nVHJhbnNsYXRpb25IYW5kbGVyUGFyYW1zKTogYW55O1xufVxuXG4vKipcbiAqIFRoaXMgaGFuZGxlciBpcyBqdXN0IGEgcGxhY2Vob2xkZXIgdGhhdCBkb2VzIG5vdGhpbmcsIGluIGNhc2UgeW91IGRvbid0IG5lZWQgYSBtaXNzaW5nIHRyYW5zbGF0aW9uIGhhbmRsZXIgYXQgYWxsXG4gKi9cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBGYWtlTWlzc2luZ1RyYW5zbGF0aW9uSGFuZGxlciBpbXBsZW1lbnRzIE1pc3NpbmdUcmFuc2xhdGlvbkhhbmRsZXIge1xuICBoYW5kbGUocGFyYW1zOiBNaXNzaW5nVHJhbnNsYXRpb25IYW5kbGVyUGFyYW1zKTogc3RyaW5nIHtcbiAgICByZXR1cm4gcGFyYW1zLmtleTtcbiAgfVxufVxuIiwiaW1wb3J0IHtJbmplY3RhYmxlfSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgVHJhbnNsYXRlQ29tcGlsZXIge1xuICBhYnN0cmFjdCBjb21waWxlKHZhbHVlOiBzdHJpbmcsIGxhbmc6IHN0cmluZyk6IHN0cmluZyB8IEZ1bmN0aW9uO1xuXG4gIGFic3RyYWN0IGNvbXBpbGVUcmFuc2xhdGlvbnModHJhbnNsYXRpb25zOiBhbnksIGxhbmc6IHN0cmluZyk6IGFueTtcbn1cblxuLyoqXG4gKiBUaGlzIGNvbXBpbGVyIGlzIGp1c3QgYSBwbGFjZWhvbGRlciB0aGF0IGRvZXMgbm90aGluZywgaW4gY2FzZSB5b3UgZG9uJ3QgbmVlZCBhIGNvbXBpbGVyIGF0IGFsbFxuICovXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgVHJhbnNsYXRlRmFrZUNvbXBpbGVyIGV4dGVuZHMgVHJhbnNsYXRlQ29tcGlsZXIge1xuICBjb21waWxlKHZhbHVlOiBzdHJpbmcsIGxhbmc6IHN0cmluZyk6IHN0cmluZyB8IEZ1bmN0aW9uIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cblxuICBjb21waWxlVHJhbnNsYXRpb25zKHRyYW5zbGF0aW9uczogYW55LCBsYW5nOiBzdHJpbmcpOiBhbnkge1xuICAgIHJldHVybiB0cmFuc2xhdGlvbnM7XG4gIH1cbn1cbiIsIi8qIHRzbGludDpkaXNhYmxlICovXG4vKipcbiAqIERldGVybWluZXMgaWYgdHdvIG9iamVjdHMgb3IgdHdvIHZhbHVlcyBhcmUgZXF1aXZhbGVudC5cbiAqXG4gKiBUd28gb2JqZWN0cyBvciB2YWx1ZXMgYXJlIGNvbnNpZGVyZWQgZXF1aXZhbGVudCBpZiBhdCBsZWFzdCBvbmUgb2YgdGhlIGZvbGxvd2luZyBpcyB0cnVlOlxuICpcbiAqICogQm90aCBvYmplY3RzIG9yIHZhbHVlcyBwYXNzIGA9PT1gIGNvbXBhcmlzb24uXG4gKiAqIEJvdGggb2JqZWN0cyBvciB2YWx1ZXMgYXJlIG9mIHRoZSBzYW1lIHR5cGUgYW5kIGFsbCBvZiB0aGVpciBwcm9wZXJ0aWVzIGFyZSBlcXVhbCBieVxuICogICBjb21wYXJpbmcgdGhlbSB3aXRoIGBlcXVhbHNgLlxuICpcbiAqIEBwYXJhbSBvMSBPYmplY3Qgb3IgdmFsdWUgdG8gY29tcGFyZS5cbiAqIEBwYXJhbSBvMiBPYmplY3Qgb3IgdmFsdWUgdG8gY29tcGFyZS5cbiAqIEByZXR1cm5zIHRydWUgaWYgYXJndW1lbnRzIGFyZSBlcXVhbC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGVxdWFscyhvMTogYW55LCBvMjogYW55KTogYm9vbGVhbiB7XG4gIGlmIChvMSA9PT0gbzIpIHJldHVybiB0cnVlO1xuICBpZiAobzEgPT09IG51bGwgfHwgbzIgPT09IG51bGwpIHJldHVybiBmYWxzZTtcbiAgaWYgKG8xICE9PSBvMSAmJiBvMiAhPT0gbzIpIHJldHVybiB0cnVlOyAvLyBOYU4gPT09IE5hTlxuICBsZXQgdDEgPSB0eXBlb2YgbzEsIHQyID0gdHlwZW9mIG8yLCBsZW5ndGg6IG51bWJlciwga2V5OiBhbnksIGtleVNldDogYW55O1xuICBpZiAodDEgPT0gdDIgJiYgdDEgPT0gJ29iamVjdCcpIHtcbiAgICBpZiAoQXJyYXkuaXNBcnJheShvMSkpIHtcbiAgICAgIGlmICghQXJyYXkuaXNBcnJheShvMikpIHJldHVybiBmYWxzZTtcbiAgICAgIGlmICgobGVuZ3RoID0gbzEubGVuZ3RoKSA9PSBvMi5sZW5ndGgpIHtcbiAgICAgICAgZm9yIChrZXkgPSAwOyBrZXkgPCBsZW5ndGg7IGtleSsrKSB7XG4gICAgICAgICAgaWYgKCFlcXVhbHMobzFba2V5XSwgbzJba2V5XSkpIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkobzIpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIGtleVNldCA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gICAgICBmb3IgKGtleSBpbiBvMSkge1xuICAgICAgICBpZiAoIWVxdWFscyhvMVtrZXldLCBvMltrZXldKSkge1xuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBrZXlTZXRba2V5XSA9IHRydWU7XG4gICAgICB9XG4gICAgICBmb3IgKGtleSBpbiBvMikge1xuICAgICAgICBpZiAoIShrZXkgaW4ga2V5U2V0KSAmJiB0eXBlb2YgbzJba2V5XSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG4vKiB0c2xpbnQ6ZW5hYmxlICovXG5cbmV4cG9ydCBmdW5jdGlvbiBpc0RlZmluZWQodmFsdWU6IGFueSk6IGJvb2xlYW4ge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlICE9PSAndW5kZWZpbmVkJyAmJiB2YWx1ZSAhPT0gbnVsbDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzT2JqZWN0KGl0ZW06IGFueSk6IGJvb2xlYW4ge1xuICByZXR1cm4gKGl0ZW0gJiYgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGl0ZW0pKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlRGVlcCh0YXJnZXQ6IGFueSwgc291cmNlOiBhbnkpOiBhbnkge1xuICBsZXQgb3V0cHV0ID0gT2JqZWN0LmFzc2lnbih7fSwgdGFyZ2V0KTtcbiAgaWYgKGlzT2JqZWN0KHRhcmdldCkgJiYgaXNPYmplY3Qoc291cmNlKSkge1xuICAgIE9iamVjdC5rZXlzKHNvdXJjZSkuZm9yRWFjaCgoa2V5OiBhbnkpID0+IHtcbiAgICAgIGlmIChpc09iamVjdChzb3VyY2Vba2V5XSkpIHtcbiAgICAgICAgaWYgKCEoa2V5IGluIHRhcmdldCkpIHtcbiAgICAgICAgICBPYmplY3QuYXNzaWduKG91dHB1dCwge1trZXldOiBzb3VyY2Vba2V5XX0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG91dHB1dFtrZXldID0gbWVyZ2VEZWVwKHRhcmdldFtrZXldLCBzb3VyY2Vba2V5XSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIE9iamVjdC5hc3NpZ24ob3V0cHV0LCB7W2tleV06IHNvdXJjZVtrZXldfSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIG91dHB1dDtcbn1cbiIsImltcG9ydCB7SW5qZWN0YWJsZX0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7aXNEZWZpbmVkfSBmcm9tIFwiLi91dGlsXCI7XG5cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBUcmFuc2xhdGVQYXJzZXIge1xuICAvKipcbiAgICogSW50ZXJwb2xhdGVzIGEgc3RyaW5nIHRvIHJlcGxhY2UgcGFyYW1ldGVyc1xuICAgKiBcIlRoaXMgaXMgYSB7eyBrZXkgfX1cIiA9PT4gXCJUaGlzIGlzIGEgdmFsdWVcIiwgd2l0aCBwYXJhbXMgPSB7IGtleTogXCJ2YWx1ZVwiIH1cbiAgICogQHBhcmFtIGV4cHJcbiAgICogQHBhcmFtIHBhcmFtc1xuICAgKi9cbiAgYWJzdHJhY3QgaW50ZXJwb2xhdGUoZXhwcjogc3RyaW5nIHwgRnVuY3Rpb24sIHBhcmFtcz86IGFueSk6IHN0cmluZztcblxuICAvKipcbiAgICogR2V0cyBhIHZhbHVlIGZyb20gYW4gb2JqZWN0IGJ5IGNvbXBvc2VkIGtleVxuICAgKiBwYXJzZXIuZ2V0VmFsdWUoeyBrZXkxOiB7IGtleUE6ICd2YWx1ZUknIH19LCAna2V5MS5rZXlBJykgPT0+ICd2YWx1ZUknXG4gICAqIEBwYXJhbSB0YXJnZXRcbiAgICogQHBhcmFtIGtleVxuICAgKi9cbiAgYWJzdHJhY3QgZ2V0VmFsdWUodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nKTogYW55XG59XG5cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBUcmFuc2xhdGVEZWZhdWx0UGFyc2VyIGV4dGVuZHMgVHJhbnNsYXRlUGFyc2VyIHtcbiAgdGVtcGxhdGVNYXRjaGVyOiBSZWdFeHAgPSAve3tcXHM/KFtee31cXHNdKilcXHM/fX0vZztcblxuICBwdWJsaWMgaW50ZXJwb2xhdGUoZXhwcjogc3RyaW5nIHwgRnVuY3Rpb24sIHBhcmFtcz86IGFueSk6IHN0cmluZyB7XG4gICAgbGV0IHJlc3VsdDogc3RyaW5nO1xuXG4gICAgaWYgKHR5cGVvZiBleHByID09PSAnc3RyaW5nJykge1xuICAgICAgcmVzdWx0ID0gdGhpcy5pbnRlcnBvbGF0ZVN0cmluZyhleHByLCBwYXJhbXMpO1xuICAgIH0gZWxzZSBpZiAodHlwZW9mIGV4cHIgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIHJlc3VsdCA9IHRoaXMuaW50ZXJwb2xhdGVGdW5jdGlvbihleHByLCBwYXJhbXMpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyB0aGlzIHNob3VsZCBub3QgaGFwcGVuLCBidXQgYW4gdW5yZWxhdGVkIFRyYW5zbGF0ZVNlcnZpY2UgdGVzdCBkZXBlbmRzIG9uIGl0XG4gICAgICByZXN1bHQgPSBleHByIGFzIHN0cmluZztcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZ2V0VmFsdWUodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nKTogYW55IHtcbiAgICBsZXQga2V5cyA9IGtleS5zcGxpdCgnLicpO1xuICAgIGtleSA9ICcnO1xuICAgIGRvIHtcbiAgICAgIGtleSArPSBrZXlzLnNoaWZ0KCk7XG4gICAgICBpZiAoaXNEZWZpbmVkKHRhcmdldCkgJiYgaXNEZWZpbmVkKHRhcmdldFtrZXldKSAmJiAodHlwZW9mIHRhcmdldFtrZXldID0