@ngx-translate/core
Version:
Translation library (i18n) for Angular
1 lines • 75.8 kB
Source Map (JSON)
{"version":3,"file":"ngx-translate-core.mjs","sources":["../../../projects/ngx-translate/src/lib/translate.loader.ts","../../../projects/ngx-translate/src/lib/missing-translation-handler.ts","../../../projects/ngx-translate/src/lib/util.ts","../../../projects/ngx-translate/src/lib/translate.parser.ts","../../../projects/ngx-translate/src/lib/translate.compiler.ts","../../../projects/ngx-translate/src/lib/translate.store.ts","../../../projects/ngx-translate/src/lib/translate.service.ts","../../../projects/ngx-translate/src/lib/translate.directive.ts","../../../projects/ngx-translate/src/lib/translate.pipe.ts","../../../projects/ngx-translate/src/lib/extraction-marker.ts","../../../projects/ngx-translate/src/public-api.ts","../../../projects/ngx-translate/src/ngx-translate-core.ts"],"sourcesContent":["import {Injectable} from \"@angular/core\";\nimport {Observable, of} from \"rxjs\";\nimport {TranslationObject} from \"./translate.service\";\n\nexport abstract class TranslateLoader {\n abstract getTranslation(lang: string): Observable<TranslationObject>;\n}\n\n/**\n * This loader is just a placeholder that does nothing, in case you don't need a loader at all\n */\n@Injectable()\nexport class TranslateFakeLoader extends TranslateLoader {\n getTranslation(lang: string): Observable<TranslationObject> {\n void lang;\n return of({});\n }\n}\n","import {Injectable} from \"@angular/core\";\nimport {Observable} from \"rxjs\";\nimport {TranslateService, Translation} from \"./translate.service\";\n\nexport interface MissingTranslationHandlerParams {\n /**\n * the key that's missing in translation files\n */\n key: string;\n\n /**\n * an instance of the service that was unable to translate the key.\n */\n translateService: TranslateService;\n\n /**\n * interpolation params that were passed along for translating the given key.\n */\n interpolateParams?: object;\n}\n\nexport abstract class MissingTranslationHandler {\n /**\n * A function that handles missing translations.\n *\n * @param params context for resolving a missing translation\n * @returns a value or an observable\n *\n * If it returns a value, then this value is used.\n * If it returns an observable, the value returned by this observable will be used (except if the method was \"instant\").\n * If it returns undefined, the key will be used as a value\n */\n abstract handle(params: MissingTranslationHandlerParams): Translation|Observable<Translation>;\n}\n\n/**\n * This handler is just a placeholder that does nothing, in case you don't need a missing translation handler at all\n */\n@Injectable()\nexport class FakeMissingTranslationHandler implements MissingTranslationHandler {\n handle(params: MissingTranslationHandlerParams): string {\n return params.key;\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Determines if two objects or two values are equivalent.\n *\n * Two objects or values are considered equivalent if at least one of the following is true:\n *\n * * Both objects or values pass `===` comparison.\n * * Both objects or values are of the same type and all of their properties are equal by\n * comparing them with `equals`.\n *\n * @param o1 Object or value to compare.\n * @param o2 Object or value to compare.\n * @returns true if arguments are equal.\n */\nexport function equals(o1: any, o2: any): boolean {\n if (o1 === o2) return true;\n if (o1 === null || o2 === null) return false;\n if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN\n const t1 = typeof o1, t2 = typeof o2;\n let length: number, key: any, keySet: any;\n if (t1 == t2 && t1 == 'object') {\n if (Array.isArray(o1)) {\n if (!Array.isArray(o2)) return false;\n if ((length = o1.length) == o2.length) {\n for (key = 0; key < length; key++) {\n if (!equals(o1[key], o2[key])) return false;\n }\n return true;\n }\n } else {\n if (Array.isArray(o2)) {\n return false;\n }\n keySet = Object.create(null);\n for (key in o1) {\n if (!equals(o1[key], o2[key])) {\n return false;\n }\n keySet[key] = true;\n }\n for (key in o2) {\n if (!(key in keySet) && typeof o2[key] !== 'undefined') {\n return false;\n }\n }\n return true;\n }\n }\n return false;\n}\n\nexport function isDefined(value: any): boolean {\n return typeof value !== 'undefined' && value !== null;\n}\n\n\nexport function isDict(value: any): boolean {\n return isObject(value) && !isArray(value) && value !== null;\n}\n\n\nexport function isObject(value: any): boolean {\n return typeof value === 'object';\n}\n\nexport function isArray(value: any): boolean {\n return Array.isArray(value);\n}\n\nexport function isString(value: any): boolean {\n return typeof value === 'string';\n}\n\nexport function isFunction(value: any):boolean {\n return typeof value === \"function\"\n}\n\n\nexport function mergeDeep(target: any, source: any): any {\n const output = Object.assign({}, target);\n\n if (!isObject(target)) {\n return mergeDeep({}, source);\n }\n\n if (isObject(target) && isObject(source)) {\n Object.keys(source).forEach((key: any) => {\n if (isDict(source[key])) {\n if (key in target) {\n output[key] = mergeDeep(target[key], source[key]);\n } else {\n Object.assign(output, {[key]: source[key]});\n }\n } else {\n Object.assign(output, {[key]: source[key]});\n }\n });\n }\n return output;\n}\n\n\n/**\n * Gets a value from an object by composed key\n * getValue({ key1: { keyA: 'valueI' }}, 'key1.keyA') ==> 'valueI'\n * @param target\n * @param key\n */\nexport function getValue(target: any, key: string): any\n{\n const keys = key.split(\".\");\n\n key = \"\";\n do\n {\n key += keys.shift();\n if (isDefined(target) && isDefined(target[key]) && (isDict(target[key]) ||isArray(target[key]) || !keys.length))\n {\n target = target[key];\n key = \"\";\n }\n else if (!keys.length)\n {\n target = undefined;\n }\n else\n {\n key += \".\";\n }\n } while (keys.length);\n\n return target;\n}\n\n/**\n * Gets a value from an object by composed key\n * parser.setValue({a:{b:{c: \"test\"}}}, 'a.b.c', \"test2\") ==> {a:{b:{c: \"test2\"}}}\n * @param target an object\n * @param key E.g. \"a.b.c\"\n * @param value to set\n */\nexport function setValue(target: any, key: string, value: any): void {\n const keys = key.split('.');\n let current = target;\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n\n // If we're at the last key, set the value\n if (i === keys.length - 1) {\n current[key] = value;\n } else {\n // If the key doesn't exist or isn't an object, create an empty object\n if (!current[key] || !isDict(current[key])) {\n current[key] = {};\n }\n current = current[key];\n }\n }\n}\n\n\n","import {Injectable} from \"@angular/core\";\nimport {InterpolationParameters} from \"./translate.service\";\nimport {getValue, isDefined, isString, isFunction} from \"./util\";\n\n\nexport type InterpolateFunction = (params?: InterpolationParameters) => string;\n\n\nexport abstract class TranslateParser\n{\n /**\n * Interpolates a string to replace parameters\n * \"This is a {{ key }}\" ==> \"This is a value\", with params = { key: \"value\" }\n * @param expr\n * @param params\n */\n abstract interpolate(expr: InterpolateFunction|string, params?: InterpolationParameters): string|undefined;\n}\n\n\n@Injectable()\nexport class TranslateDefaultParser extends TranslateParser\n{\n templateMatcher = /{{\\s?([^{}\\s]*)\\s?}}/g;\n\n public interpolate(expr: InterpolateFunction|string, params?: InterpolationParameters): string|undefined\n {\n if (isString(expr))\n {\n return this.interpolateString(expr as string, params);\n }\n else if (isFunction(expr))\n {\n return this.interpolateFunction(expr as InterpolateFunction, params);\n }\n return undefined;\n }\n\n private interpolateFunction(fn: InterpolateFunction, params?: InterpolationParameters): string\n {\n return fn(params);\n }\n\n private interpolateString(expr: string, params?: InterpolationParameters): string\n {\n if (!params)\n {\n return expr;\n }\n\n return expr.replace(this.templateMatcher, (substring: string, b: string) =>\n {\n const r = getValue(params, b);\n return isDefined(r)\n ? r\n : substring;\n });\n }\n}\n","import {Injectable} from \"@angular/core\";\nimport {InterpolateFunction} from \"./translate.parser\";\nimport {InterpolatableTranslation, InterpolatableTranslationObject, Translation} from \"./translate.service\";\n\nexport abstract class TranslateCompiler {\n abstract compile(value: string, lang: string): InterpolatableTranslation;\n\n abstract compileTranslations(translations: Translation, lang: string): InterpolatableTranslationObject;\n}\n\n/**\n * This compiler is just a placeholder that does nothing, in case you don't need a compiler at all\n */\n@Injectable()\nexport class TranslateFakeCompiler extends TranslateCompiler {\n compile(value: string, lang: string): string | InterpolateFunction {\n void lang;\n return value;\n }\n\n compileTranslations(translations: InterpolatableTranslationObject, lang: string): InterpolatableTranslationObject {\n void lang;\n return translations;\n }\n}\n","import {EventEmitter} from \"@angular/core\";\nimport {\n InterpolatableTranslationObject,\n DefaultLangChangeEvent,\n LangChangeEvent,\n TranslationChangeEvent\n} from \"./translate.service\";\n\nexport class TranslateStore {\n /**\n * The default lang to fallback when translations are missing on the current lang\n */\n public defaultLang!: string;\n\n /**\n * The lang currently used\n */\n public currentLang: string = this.defaultLang;\n\n /**\n * a list of translations per lang\n */\n public translations: Record<string, InterpolatableTranslationObject> = {};\n\n /**\n * an array of langs\n */\n public langs: string[] = [];\n\n /**\n * An EventEmitter to listen to translation change events\n * onTranslationChange.subscribe((params: TranslationChangeEvent) => {\n * // do something\n * });\n */\n public onTranslationChange: EventEmitter<TranslationChangeEvent> = new EventEmitter<TranslationChangeEvent>();\n\n /**\n * An EventEmitter to listen to lang change events\n * onLangChange.subscribe((params: LangChangeEvent) => {\n * // do something\n * });\n */\n public onLangChange: EventEmitter<LangChangeEvent> = new EventEmitter<LangChangeEvent>();\n\n /**\n * An EventEmitter to listen to default lang change events\n * onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => {\n * // do something\n * });\n */\n public onDefaultLangChange: EventEmitter<DefaultLangChangeEvent> = new EventEmitter<DefaultLangChangeEvent>();\n}\n","import {EventEmitter, Inject, Injectable, InjectionToken} from \"@angular/core\";\nimport {concat, forkJoin, isObservable, Observable, of, defer} from \"rxjs\";\nimport {concatMap, map, shareReplay, switchMap, take} from \"rxjs/operators\";\nimport {MissingTranslationHandler, MissingTranslationHandlerParams} from \"./missing-translation-handler\";\nimport {TranslateCompiler} from \"./translate.compiler\";\nimport {TranslateLoader} from \"./translate.loader\";\nimport {InterpolateFunction, TranslateParser} from \"./translate.parser\";\nimport {TranslateStore} from \"./translate.store\";\nimport {getValue, isDefined, isArray, isString, mergeDeep, setValue, isDict} from \"./util\";\n\nexport const ISOLATE_TRANSLATE_SERVICE = new InjectionToken<string>('ISOLATE_TRANSLATE_SERVICE');\nexport const USE_DEFAULT_LANG = new InjectionToken<string>('USE_DEFAULT_LANG');\nexport const DEFAULT_LANGUAGE = new InjectionToken<string>('DEFAULT_LANGUAGE');\nexport const USE_EXTEND = new InjectionToken<string>('USE_EXTEND');\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type InterpolationParameters = Record<string, any>;\n\nexport type Translation =\n string |\n Translation[] |\n TranslationObject |\n\n // required to prevent error \"Type instantiation is excessively deep and possibly infinite.\"\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n ;\n\n\n// using Record<> does not work because TS does not support recursive definitions\n// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style\nexport interface TranslationObject {\n [key: string]: Translation\n}\n\n\nexport type InterpolatableTranslation =\n string |\n InterpolatableTranslation[] |\n InterpolateFunction |\n InterpolatableTranslationObject |\n\n // required to prevent error \"Type instantiation is excessively deep and possibly infinite.\"\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n ;\n\n\n// using Record<> does not work because TS does not support recursive definitions\n// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style\nexport interface InterpolatableTranslationObject {\n [key: string]: InterpolatableTranslation\n}\n\n\nexport type Language = string;\n\nexport interface TranslationChangeEvent {\n translations: InterpolatableTranslationObject;\n lang: string;\n}\n\nexport interface LangChangeEvent {\n lang: string;\n translations: InterpolatableTranslationObject;\n}\n\nexport interface DefaultLangChangeEvent {\n lang: string;\n translations: InterpolatableTranslationObject;\n}\n\ndeclare interface Window {\n navigator: {\n languages?: string[],\n language?: string,\n browserLanguage?: string,\n userLanguage?: string,\n };\n}\n\ndeclare const window: Window;\n\nconst makeObservable = <T>(value: T | Observable<T>): Observable<T> => {\n return isObservable(value) ? value : of(value);\n};\n\n@Injectable({\n providedIn: 'root'\n})\nexport class TranslateService {\n private loadingTranslations!: Observable<InterpolatableTranslationObject>;\n private pending = false;\n private _translationRequests: Record<string, Observable<TranslationObject>> = {};\n private lastUseLanguage: string|null = null;\n\n\n /**\n * An EventEmitter to listen to translation change events\n * onTranslationChange.subscribe((params: TranslationChangeEvent) => {\n * // do something\n * });\n */\n get onTranslationChange(): EventEmitter<TranslationChangeEvent> {\n return this.store.onTranslationChange;\n }\n\n /**\n * An EventEmitter to listen to lang change events\n * onLangChange.subscribe((params: LangChangeEvent) => {\n * // do something\n * });\n */\n get onLangChange(): EventEmitter<LangChangeEvent> {\n return this.store.onLangChange;\n }\n\n /**\n * An EventEmitter to listen to default lang change events\n * onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => {\n * // do something\n * });\n */\n get onDefaultLangChange() {\n return this.store.onDefaultLangChange;\n }\n\n /**\n * The default lang to fallback when translations are missing on the current lang\n */\n get defaultLang(): string {\n return this.store.defaultLang;\n }\n\n set defaultLang(defaultLang: string) {\n this.store.defaultLang = defaultLang;\n }\n\n /**\n * The lang currently used\n */\n get currentLang(): string {\n return this.store.currentLang;\n }\n\n set currentLang(currentLang: string) {\n this.store.currentLang = currentLang;\n }\n\n /**\n * an array of langs\n */\n get langs(): string[] {\n return this.store.langs;\n }\n\n set langs(langs: string[]) {\n this.store.langs = langs;\n }\n\n /**\n * a list of translations per lang\n */\n get translations(): Record<string, InterpolatableTranslationObject> {\n return this.store.translations;\n }\n\n set translations(translations: Record<string, InterpolatableTranslationObject>) {\n this.store.translations = translations;\n }\n\n /**\n *\n * @param store an instance of the store (that is supposed to be unique)\n * @param currentLoader An instance of the loader currently used\n * @param compiler An instance of the compiler currently used\n * @param parser An instance of the parser currently used\n * @param missingTranslationHandler A handler for missing translations.\n * @param useDefaultLang whether we should use default language translation when current language translation is missing.\n * @param isolate whether this service should use the store or not\n * @param extend To make a child module extend (and use) translations from parent modules.\n * @param defaultLanguage Set the default language using configuration\n */\n constructor(public store: TranslateStore,\n public currentLoader: TranslateLoader,\n public compiler: TranslateCompiler,\n public parser: TranslateParser,\n public missingTranslationHandler: MissingTranslationHandler,\n @Inject(USE_DEFAULT_LANG) private useDefaultLang = true,\n @Inject(ISOLATE_TRANSLATE_SERVICE) isolate = false,\n @Inject(USE_EXTEND) private extend = false,\n @Inject(DEFAULT_LANGUAGE) defaultLanguage: string\n )\n {\n if(isolate)\n {\n this.store = new TranslateStore();\n }\n\n if (defaultLanguage) {\n this.setDefaultLang(defaultLanguage);\n }\n }\n\n /**\n * Sets the default language to use as a fallback\n */\n public setDefaultLang(lang: string): void {\n if (lang === this.defaultLang) {\n return;\n }\n\n const pending = this.retrieveTranslations(lang);\n\n if (typeof pending !== \"undefined\") {\n // on init set the defaultLang immediately\n if (this.defaultLang == null) {\n this.defaultLang = lang;\n }\n\n pending.pipe(take(1))\n .subscribe(() => {\n this.changeDefaultLang(lang);\n });\n } else { // we already have this language\n this.changeDefaultLang(lang);\n }\n }\n\n /**\n * Gets the default language used\n */\n public getDefaultLang(): string {\n return this.defaultLang;\n }\n\n /**\n * Changes the lang currently used\n */\n public use(lang: string): Observable<InterpolatableTranslationObject> {\n\n // remember the language that was called\n // we need this with multiple fast calls to use()\n // where translation loads might complete in random order\n this.lastUseLanguage = lang;\n\n // don't change the language if the language given is already selected\n if (lang === this.currentLang) {\n return of(this.translations[lang]);\n }\n\n // on init set the currentLang immediately\n if (!this.currentLang) {\n this.currentLang = lang;\n }\n\n const pending = this.retrieveTranslations(lang);\n\n if (isObservable(pending)) {\n\n pending.pipe(take(1))\n .subscribe(() => {\n this.changeLang(lang);\n });\n\n return pending;\n }\n else {\n // we have this language, return an Observable\n this.changeLang(lang);\n return of(this.translations[lang]);\n }\n }\n\n\n /**\n * Changes the current lang\n */\n private changeLang(lang: string): void {\n\n // received a new language file\n // but this was not the one requested last\n if(lang !== this.lastUseLanguage)\n {\n return;\n }\n\n this.currentLang = lang;\n\n this.onLangChange.emit({lang: lang, translations: this.translations[lang]});\n\n // if there is no default lang, use the one that we just set\n if (this.defaultLang == null) {\n this.changeDefaultLang(lang);\n }\n }\n\n\n /**\n * Retrieves the given translations\n */\n private retrieveTranslations(lang: string): Observable<TranslationObject> | undefined {\n\n // if this language is unavailable or extend is true, ask for it\n if (typeof this.translations[lang] === \"undefined\" || this.extend) {\n this._translationRequests[lang] = this._translationRequests[lang] || this.loadAndCompileTranslations(lang);\n return this._translationRequests[lang];\n }\n\n return undefined;\n }\n\n\n\n /**\n * Gets an object of translations for a given language with the current loader\n * and passes it through the compiler\n *\n * @deprecated This function is meant for internal use. There should\n * be no reason to use outside this service. You can plug into this\n * functionality by using a customer TranslateLoader or TranslateCompiler.\n * To load a new language use setDefaultLang() and/or use()\n */\n public getTranslation(lang: string): Observable<InterpolatableTranslationObject>\n {\n return this.loadAndCompileTranslations(lang);\n }\n\n private loadAndCompileTranslations(lang: string): Observable<InterpolatableTranslationObject> {\n\n this.pending = true;\n\n const loadingTranslations = this.currentLoader.getTranslation(lang).pipe(\n shareReplay(1),\n take(1),\n );\n\n this.loadingTranslations = loadingTranslations.pipe(\n map((res:TranslationObject) => this.compiler.compileTranslations(res, lang)),\n shareReplay(1),\n take(1),\n );\n\n this.loadingTranslations\n .subscribe({\n next: (res: InterpolatableTranslationObject) => {\n this.translations[lang] = (this.extend && this.translations[lang]) ? { ...res, ...this.translations[lang] } : res;\n this.updateLangs();\n this.pending = false;\n },\n error: (err) => {\n void err;\n this.pending = false;\n }\n });\n\n return loadingTranslations;\n }\n\n /**\n * Manually sets an object of translations for a given language\n * after passing it through the compiler\n */\n public setTranslation(lang: string, translations: InterpolatableTranslationObject, shouldMerge = false): void {\n const interpolatableTranslations = this.compiler.compileTranslations(translations, lang);\n if ((shouldMerge || this.extend) && this.translations[lang]) {\n this.translations[lang] = mergeDeep(this.translations[lang], interpolatableTranslations);\n } else {\n this.translations[lang] = interpolatableTranslations;\n }\n this.updateLangs();\n this.onTranslationChange.emit({lang: lang, translations: this.translations[lang]});\n }\n\n /**\n * Returns an array of currently available langs\n */\n public getLangs(): string[] {\n return this.langs;\n }\n\n /**\n * Add available languages\n */\n public addLangs(langs: string[]): void\n {\n const newLangs = langs.filter(lang => !this.langs.includes(lang));\n if (newLangs.length > 0) {\n this.langs = [...this.langs, ...newLangs];\n }\n }\n\n /**\n * Update the list of available languages\n */\n private updateLangs(): void {\n this.addLangs(Object.keys(this.translations));\n }\n\n private getParsedResultForKey(translations: InterpolatableTranslation, key: string, interpolateParams?: InterpolationParameters): Translation|Observable<Translation>\n {\n let res: Translation | Observable<Translation> | undefined;\n\n if (translations) {\n res = this.runInterpolation(getValue(translations, key), interpolateParams);\n }\n\n if (res === undefined && this.defaultLang != null && this.defaultLang !== this.currentLang && this.useDefaultLang) {\n res = this.runInterpolation(getValue(this.translations[this.defaultLang], key), interpolateParams);\n }\n\n if (res === undefined) {\n const params: MissingTranslationHandlerParams = {key, translateService: this};\n if (typeof interpolateParams !== 'undefined') {\n params.interpolateParams = interpolateParams;\n }\n res = this.missingTranslationHandler.handle(params);\n }\n\n return res !== undefined ? res : key;\n }\n\n private runInterpolation(translations: InterpolatableTranslation, interpolateParams?: InterpolationParameters): Translation\n {\n if(isArray(translations))\n {\n return (translations as Translation[]).map((translation) => this.runInterpolation(translation, interpolateParams));\n }\n else if (isDict(translations))\n {\n const result: TranslationObject = {};\n for (const key in translations) {\n const res = this.runInterpolation(translations[key], interpolateParams);\n if(res !== undefined)\n {\n result[key] = res;\n }\n }\n return result;\n }\n else\n {\n return this.parser.interpolate(translations, interpolateParams);\n }\n }\n\n /**\n * Returns the parsed result of the translations\n */\n public getParsedResult(translations: InterpolatableTranslation, key: string | string[], interpolateParams?: InterpolationParameters): Translation|TranslationObject|Observable<Translation|TranslationObject> {\n\n // handle a bunch of keys\n if (key instanceof Array) {\n const result: Record<string, Translation|Observable<Translation>> = {};\n\n let observables = false;\n for (const k of key) {\n result[k] = this.getParsedResultForKey(translations, k, interpolateParams);\n observables = observables || isObservable(result[k]);\n }\n\n if (!observables) {\n return result as TranslationObject;\n }\n\n const sources: Observable<Translation>[] = key.map(k => makeObservable(result[k]));\n return forkJoin(sources).pipe(\n map((arr: (Translation)[]) => {\n const obj: TranslationObject = {};\n arr.forEach((value:Translation, index: number) => {\n obj[key[index]] = value;\n });\n return obj;\n })\n );\n }\n\n return this.getParsedResultForKey(translations, key, interpolateParams);\n }\n\n /**\n * Gets the translated value of a key (or an array of keys)\n * @returns the translated key, or an object of translated keys\n */\n public get(key: string | string[], interpolateParams?: InterpolationParameters): Observable<Translation|TranslationObject> {\n if (!isDefined(key) || !key.length) {\n throw new Error(`Parameter \"key\" is required and cannot be empty`);\n }\n // check if we are loading a new translation to use\n if (this.pending) {\n return this.loadingTranslations.pipe(\n concatMap((res: InterpolatableTranslation) => {\n return makeObservable(this.getParsedResult(res, key, interpolateParams));\n }),\n );\n }\n\n return makeObservable(this.getParsedResult(this.translations[this.currentLang], key, interpolateParams));\n }\n\n /**\n * Returns a stream of translated values of a key (or an array of keys) which updates\n * whenever the translation changes.\n * @returns A stream of the translated key, or an object of translated keys\n */\n public getStreamOnTranslationChange(key: string | string[], interpolateParams?: InterpolationParameters): Observable<Translation|TranslationObject> {\n if (!isDefined(key) || !key.length) {\n throw new Error(`Parameter \"key\" is required and cannot be empty`);\n }\n\n return concat(\n defer(() => this.get(key, interpolateParams)),\n this.onTranslationChange.pipe(\n switchMap((event: TranslationChangeEvent) => {\n const res = this.getParsedResult(event.translations, key, interpolateParams);\n return makeObservable(res);\n })\n )\n );\n }\n\n /**\n * Returns a stream of translated values of a key (or an array of keys) which updates\n * whenever the language changes.\n * @returns A stream of the translated key, or an object of translated keys\n */\n public stream(key: string | string[], interpolateParams?: InterpolationParameters): Observable<Translation|TranslationObject> {\n if (!isDefined(key) || !key.length) {\n throw new Error(`Parameter \"key\" required`);\n }\n\n return concat(\n defer(() => this.get(key, interpolateParams)),\n this.onLangChange.pipe(\n switchMap((event: LangChangeEvent) => {\n const res = this.getParsedResult(event.translations, key, interpolateParams);\n return makeObservable(res);\n })\n ));\n }\n\n /**\n * Returns a translation instantly from the internal state of loaded translation.\n * All rules regarding the current language, the preferred language of even fallback languages\n * will be used except any promise handling.\n */\n public instant(key: string | string[], interpolateParams?: InterpolationParameters): Translation|TranslationObject\n {\n if (!isDefined(key) || key.length === 0) {\n throw new Error('Parameter \"key\" is required and cannot be empty');\n }\n\n const result = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams);\n\n if (isObservable(result)) {\n if (Array.isArray(key)) {\n return key.reduce((acc: Record<string, string>, currKey: string) => {\n acc[currKey] = currKey;\n return acc;\n }, {});\n }\n return key;\n }\n\n return result;\n }\n\n /**\n * Sets the translated value of a key, after compiling it\n */\n public set(key: string, translation: Translation, lang: string = this.currentLang): void {\n setValue(this.translations[lang], key,\n isString(translation)\n ? this.compiler.compile(translation, lang)\n : this.compiler.compileTranslations(translation, lang)\n );\n this.updateLangs();\n this.onTranslationChange.emit({lang: lang, translations: this.translations[lang]});\n }\n\n /**\n * Changes the default lang\n */\n private changeDefaultLang(lang: string): void {\n this.defaultLang = lang;\n this.onDefaultLangChange.emit({lang: lang, translations: this.translations[lang]});\n }\n\n /**\n * Allows to reload the lang file from the file\n */\n public reloadLang(lang: string): Observable<InterpolatableTranslationObject> {\n this.resetLang(lang);\n return this.loadAndCompileTranslations(lang);\n }\n\n /**\n * Deletes inner translation\n */\n public resetLang(lang: string): void {\n delete this._translationRequests[lang];\n delete this.translations[lang];\n }\n\n /**\n * Returns the language code name from the browser, e.g. \"de\"\n */\n public getBrowserLang(): string | undefined {\n if (typeof window === 'undefined' || !window.navigator) {\n return undefined;\n }\n\n const browserLang = this.getBrowserCultureLang();\n\n return browserLang ? browserLang.split(/[-_]/)[0] : undefined;\n }\n\n /**\n * Returns the culture language code name from the browser, e.g. \"de-DE\"\n */\n public getBrowserCultureLang(): string | undefined {\n if (typeof window === 'undefined' || typeof window.navigator === 'undefined') {\n return undefined;\n }\n\n return window.navigator.languages\n ? window.navigator.languages[0]\n : (window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage);\n }\n}\n","import {AfterViewChecked, ChangeDetectorRef, Directive, ElementRef, Input, OnDestroy} from '@angular/core';\nimport {Subscription, isObservable} from 'rxjs';\nimport {\n DefaultLangChangeEvent,\n InterpolatableTranslation,\n LangChangeEvent,\n TranslateService,\n TranslationChangeEvent,\n Translation,\n InterpolationParameters\n} from \"./translate.service\";\nimport {equals, isDefined} from './util';\n\ninterface ExtendedNode extends Text {\n originalContent: string;\n currentValue: Translation;\n lookupKey: string;\n lastKey: string|null;\n data: string;\n}\n\n\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: '[translate],[ngx-translate]',\n standalone: true\n})\nexport class TranslateDirective implements AfterViewChecked, OnDestroy {\n key!: string;\n lastParams?: InterpolationParameters;\n currentParams?: InterpolationParameters;\n onLangChangeSub!: Subscription;\n onDefaultLangChangeSub!: Subscription;\n onTranslationChangeSub!: Subscription;\n\n @Input() set translate(key: string) {\n if (key) {\n this.key = key;\n this.checkNodes();\n }\n }\n\n @Input() set translateParams(params: InterpolationParameters) {\n if (!equals(this.currentParams, params)) {\n this.currentParams = params;\n this.checkNodes(true);\n }\n }\n\n constructor(private translateService: TranslateService, private element: ElementRef, private _ref: ChangeDetectorRef) {\n // subscribe to onTranslationChange event, in case the translations of the current lang change\n if (!this.onTranslationChangeSub) {\n this.onTranslationChangeSub = this.translateService.onTranslationChange.subscribe((event: TranslationChangeEvent) => {\n if (event.lang === this.translateService.currentLang) {\n this.checkNodes(true, event.translations);\n }\n });\n }\n\n // subscribe to onLangChange event, in case the language changes\n if (!this.onLangChangeSub) {\n this.onLangChangeSub = this.translateService.onLangChange.subscribe((event: LangChangeEvent) => {\n this.checkNodes(true, event.translations);\n });\n }\n\n // subscribe to onDefaultLangChange event, in case the default language changes\n if (!this.onDefaultLangChangeSub) {\n this.onDefaultLangChangeSub = this.translateService.onDefaultLangChange.subscribe((event: DefaultLangChangeEvent) => {\n void event;\n this.checkNodes(true);\n });\n }\n }\n\n ngAfterViewChecked() {\n this.checkNodes();\n }\n\n\n checkNodes(forceUpdate = false, translations?: InterpolatableTranslation) {\n let nodes: NodeList = this.element.nativeElement.childNodes;\n // if the element is empty\n if (!nodes.length) {\n // we add the key as content\n this.setContent(this.element.nativeElement, this.key);\n nodes = this.element.nativeElement.childNodes;\n }\n\n nodes.forEach(( n) => {\n const node= n as ExtendedNode;\n if (node.nodeType === 3) { // node type 3 is a text node\n let key!: string;\n if (forceUpdate) {\n node.lastKey = null;\n }\n if(isDefined(node.lookupKey)) {\n key = node.lookupKey;\n } else if (this.key) {\n key = this.key;\n } else {\n const content = this.getContent(node);\n const trimmedContent = content.trim();\n if (trimmedContent.length) {\n node.lookupKey = trimmedContent;\n // we want to use the content as a key, not the translation value\n if (content !== node.currentValue) {\n key = trimmedContent;\n // the content was changed from the user, we'll use it as a reference if needed\n node.originalContent = content || node.originalContent;\n } else if (node.originalContent) { // the content seems ok, but the lang has changed\n // the current content is the translation, not the key, use the last real content as key\n key = node.originalContent.trim();\n }\n }\n }\n this.updateValue(key, node, translations);\n }\n })\n }\n\n updateValue(key: string, node: ExtendedNode, translations?: InterpolatableTranslation) {\n if (key) {\n if (node.lastKey === key && this.lastParams === this.currentParams) {\n return;\n }\n\n this.lastParams = this.currentParams;\n\n const onTranslation = (res: Translation) => {\n if (res !== key || !node.lastKey) {\n node.lastKey = key;\n }\n if (!node.originalContent) {\n node.originalContent = this.getContent(node);\n }\n node.currentValue = isDefined(res) ? res : (node.originalContent || key);\n // we replace in the original content to preserve spaces that we might have trimmed\n this.setContent(node, this.key ? node.currentValue : node.originalContent.replace(key, node.currentValue));\n this._ref.markForCheck();\n };\n\n if (isDefined(translations)) {\n const res = this.translateService.getParsedResult(translations as InterpolatableTranslation, key, this.currentParams);\n if (isObservable(res)) {\n res.subscribe({next: onTranslation});\n } else {\n onTranslation(res);\n }\n } else {\n this.translateService.get(key, this.currentParams).subscribe(onTranslation);\n }\n }\n }\n\n getContent(node: ExtendedNode): string {\n return (isDefined(node.textContent) ? node.textContent : node.data) as string;\n }\n\n setContent(node: ExtendedNode, content: string): void {\n if (isDefined(node.textContent)) {\n node.textContent = content;\n } else {\n node.data = content;\n }\n }\n\n ngOnDestroy() {\n if (this.onLangChangeSub) {\n this.onLangChangeSub.unsubscribe();\n }\n\n if (this.onDefaultLangChangeSub) {\n this.onDefaultLangChangeSub.unsubscribe();\n }\n\n if (this.onTranslationChangeSub) {\n this.onTranslationChangeSub.unsubscribe();\n }\n }\n}\n","import {ChangeDetectorRef, Injectable, OnDestroy, Pipe, PipeTransform} from '@angular/core';\nimport {isObservable, Subscription} from 'rxjs';\nimport {\n InterpolatableTranslationObject,\n LangChangeEvent,\n TranslateService,\n TranslationChangeEvent,\n Translation,\n InterpolationParameters\n} from \"./translate.service\";\nimport {equals, isDefined, isDict, isString} from \"./util\";\n\n@Injectable()\n@Pipe({\n name: 'translate',\n standalone: true,\n pure: false // required to update the value when the promise is resolved\n})\nexport class TranslatePipe implements PipeTransform, OnDestroy {\n value:Translation = '';\n lastKey: string | null = null;\n lastParams: InterpolationParameters[] = [];\n onTranslationChange: Subscription | undefined;\n onLangChange: Subscription | undefined;\n onDefaultLangChange: Subscription | undefined;\n\n constructor(private translate: TranslateService, private _ref: ChangeDetectorRef) {\n }\n\n updateValue(key: string, interpolateParams?: object, translations?: InterpolatableTranslationObject): void {\n const onTranslation = (res: Translation) => {\n this.value = res !== undefined ? res : key;\n this.lastKey = key;\n this._ref.markForCheck();\n };\n if (translations) {\n const res = this.translate.getParsedResult(translations, key, interpolateParams);\n if (isObservable(res)) {\n res.subscribe(onTranslation);\n } else {\n onTranslation(res);\n }\n }\n this.translate.get(key, interpolateParams).subscribe(onTranslation);\n }\n\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n transform(query: string, ...args: any[]): any {\n if (!query || !query.length) {\n return query;\n }\n\n // if we ask another time for the same key, return the last value\n if (equals(query, this.lastKey) && equals(args, this.lastParams)) {\n return this.value;\n }\n\n let interpolateParams: object | undefined = undefined;\n if (isDefined(args[0]) && args.length) {\n if (isString(args[0]) && args[0].length) {\n // we accept objects written in the template such as {n:1}, {'n':1}, {n:'v'}\n // which is why we might need to change it to real JSON objects such as {\"n\":1} or {\"n\":\"v\"}\n const validArgs: string = args[0]\n .replace(/(')?([a-zA-Z0-9_]+)(')?(\\s)?:/g, '\"$2\":')\n .replace(/:(\\s)?(')(.*?)(')/g, ':\"$3\"');\n try {\n interpolateParams = JSON.parse(validArgs);\n } catch (e) {\n void e;\n throw new SyntaxError(`Wrong parameter in TranslatePipe. Expected a valid Object, received: ${args[0]}`);\n }\n } else if (isDict(args[0])) {\n interpolateParams = args[0];\n }\n }\n\n // store the query, in case it changes\n this.lastKey = query;\n\n // store the params, in case they change\n this.lastParams = args;\n\n // set the value\n this.updateValue(query, interpolateParams);\n\n // if there is a subscription to onLangChange, clean it\n this._dispose();\n\n // subscribe to onTranslationChange event, in case the translations change\n if (!this.onTranslationChange) {\n this.onTranslationChange = this.translate.onTranslationChange.subscribe((event: TranslationChangeEvent) => {\n if (this.lastKey && event.lang === this.translate.currentLang) {\n this.lastKey = null;\n this.updateValue(query, interpolateParams, event.translations);\n }\n });\n }\n\n // subscribe to onLangChange event, in case the language changes\n if (!this.onLangChange) {\n this.onLangChange = this.translate.onLangChange.subscribe((event: LangChangeEvent) => {\n if (this.lastKey) {\n this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated\n this.updateValue(query, interpolateParams, event.translations);\n }\n });\n }\n\n // subscribe to onDefaultLangChange event, in case the default language changes\n if (!this.onDefaultLangChange) {\n this.onDefaultLangChange = this.translate.onDefaultLangChange.subscribe(() => {\n if (this.lastKey) {\n this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated\n this.updateValue(query, interpolateParams);\n }\n });\n }\n\n return this.value;\n }\n\n /**\n * Clean any existing subscription to change events\n */\n private _dispose(): void {\n if (typeof this.onTranslationChange !== 'undefined') {\n this.onTranslationChange.unsubscribe();\n this.onTranslationChange = undefined;\n }\n if (typeof this.onLangChange !== 'undefined') {\n this.onLangChange.unsubscribe();\n this.onLangChange = undefined;\n }\n if (typeof this.onDefaultLangChange !== 'undefined') {\n this.onDefaultLangChange.unsubscribe();\n this.onDefaultLangChange = undefined;\n }\n }\n\n ngOnDestroy(): void {\n this._dispose();\n }\n}\n","export function _<T extends string | string[]>(key: T): T {\n return key;\n}\n","import {NgModule, ModuleWithProviders, Provider, EnvironmentProviders, makeEnvironmentProviders} from \"@angular/core\";\nimport {TranslateLoader, TranslateFakeLoader} from \"./lib/translate.loader\";\nimport {MissingTranslationHandler, FakeMissingTranslationHandler} from \"./lib/missing-translation-handler\";\nimport {TranslateParser, TranslateDefaultParser} from \"./lib/translate.parser\";\nimport {TranslateCompiler, TranslateFakeCompiler} from \"./lib/translate.compiler\";\nimport {TranslateDirective} from \"./lib/translate.directive\";\nimport {TranslatePipe} from \"./lib/translate.pipe\";\nimport {TranslateStore} from \"./lib/translate.store\";\nimport {\n USE_DEFAULT_LANG,\n DEFAULT_LANGUAGE,\n USE_EXTEND,\n ISOLATE_TRANSLATE_SERVICE,\n TranslateService\n} from \"./lib/translate.service\";\n\nexport * from \"./lib/translate.loader\";\nexport * from \"./lib/translate.service\";\nexport * from \"./lib/missing-translation-handler\";\nexport * from \"./lib/translate.parser\";\nexport * from \"./lib/translate.compiler\";\nexport * from \"./lib/translate.directive\";\nexport * from \"./lib/translate.pipe\";\nexport * from \"./lib/translate.store\";\nexport * from \"./lib/extraction-marker\";\nexport * from \"./lib/util\"\n\nexport interface TranslateModuleConfig {\n loader?: Provider;\n compiler?: Provider;\n parser?: Provider;\n missingTranslationHandler?: Provider;\n // isolate the service instance, only works for lazy loaded modules or components with the \"providers\" property\n isolate?: boolean;\n // extends translations for a given language instead of ignoring them if present\n extend?: boolean;\n useDefaultLang?: boolean;\n defaultLanguage?: string;\n}\n\nexport const provideTranslateService = (config: TranslateModuleConfig = {}): EnvironmentProviders =>\n{\n return makeEnvironmentProviders([\n config.loader || {provide: TranslateLoader, useClass: TranslateFakeLoader},\n config.compiler || {provide: TranslateCompiler, useClass: TranslateFakeCompiler},\n config.parser || {provide: TranslateParser, useClass: TranslateDefaultParser},\n config.missingTranslationHandler || {provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler},\n TranslateStore,\n {provide: ISOLATE_TRANSLATE_SERVICE, useValue: config.isolate},\n {provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang},\n {provide: USE_EXTEND, useValue: config.extend},\n {provide: DEFAULT_LANGUAGE, useValue: config.defaultLanguage},\n TranslateService\n ]);\n}\n\n\n@NgModule({\n imports: [\n TranslatePipe,\n TranslateDirective\n ],\n exports: [\n TranslatePipe,\n TranslateDirective\n ]\n})\nexport class TranslateModule {\n /**\n * Use this method in your root module to provide the TranslateService\n */\n static forRoot(config: TranslateModuleConfig = {}): ModuleWithProviders<TranslateModule> {\n return {\n ngModule: TranslateModule,\n providers: [\n config.loader || {provide: TranslateLoader, useClass: TranslateFakeLoader},\n config.compiler || {provide: TranslateCompiler, useClass: TranslateFakeCompiler},\n config.parser || {provide: TranslateParser, useClass: TranslateDefaultParser},\n config.missingTranslationHandler || {provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler},\n TranslateStore,\n {provide: ISOLATE_TRANSLATE_SERVICE, useValue: config.isolate},\n {provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang},\n {provide: USE_EXTEND, useValue: config.extend},\n {provide: DEFAULT_LANGUAGE, useValue: config.defaultLanguage},\n TranslateService\n ]\n };\n }\n\n /**\n * Use this method in your other (non-root) modules to import the directive/pipe\n */\n static forChild(config: TranslateModuleConfig = {}): ModuleWithProviders<TranslateModule> {\n return {\n ngModule: TranslateModule,\n providers: [\n config.loader || {provide: TranslateLoader, useClass: TranslateFakeLoader},\n config.compiler || {provide: TranslateCompiler, useClass: TranslateFakeCompiler},\n config.parser || {provide: TranslateParser, useClass: TranslateDefaultParser},\n config.missingTranslationHandler || {provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler},\n {provide: ISOLATE_TRANSLATE_SERVICE, useValue: config.isolate},\n {provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang},\n {provide: USE_EXTEND, useValue: config.extend},\n {provide: DEFAULT_LANGUAGE, useValue: config.defaultLanguage},\n TranslateService\n ]\n };\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.TranslateService"],"mappings":"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