@jsverse/transloco
Version:
The internationalization (i18n) library for Angular
1 lines • 113 kB
Source Map (JSON)
{"version":3,"file":"jsverse-transloco.mjs","sources":["../../../../libs/transloco/src/lib/transloco.loader.ts","../../../../libs/transloco/src/lib/helpers.ts","../../../../libs/transloco/src/lib/transloco.config.ts","../../../../libs/transloco/src/lib/transloco.transpiler.ts","../../../../libs/transloco/src/lib/transloco-missing-handler.ts","../../../../libs/transloco/src/lib/transloco.interceptor.ts","../../../../libs/transloco/src/lib/transloco-fallback-strategy.ts","../../../../libs/transloco/src/lib/shared.ts","../../../../libs/transloco/src/lib/resolve-loader.ts","../../../../libs/transloco/src/lib/get-fallbacks-loaders.ts","../../../../libs/transloco/src/lib/transloco.service.ts","../../../../libs/transloco/src/lib/loader-component.component.ts","../../../../libs/transloco/src/lib/template-handler.ts","../../../../libs/transloco/src/lib/transloco-lang.ts","../../../../libs/transloco/src/lib/transloco-loading-template.ts","../../../../libs/transloco/src/lib/transloco-scope.ts","../../../../libs/transloco/src/lib/lang-resolver.ts","../../../../libs/transloco/src/lib/scope-resolver.ts","../../../../libs/transloco/src/lib/transloco.directive.ts","../../../../libs/transloco/src/lib/transloco.pipe.ts","../../../../libs/transloco/src/lib/transloco.module.ts","../../../../libs/transloco/src/lib/transloco.providers.ts","../../../../libs/transloco/src/lib/transloco-testing.module.ts","../../../../libs/transloco/src/lib/browser-lang.ts","../../../../libs/transloco/src/jsverse-transloco.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport { Observable, of } from 'rxjs';\n\nimport { Translation } from './types';\n\nexport interface TranslocoLoader {\n getTranslation(\n lang: string,\n data?: TranslocoLoaderData,\n ): Observable<Translation> | Promise<Translation>;\n}\n\nexport type TranslocoLoaderData = {\n scope: string;\n};\n\nexport class DefaultLoader implements TranslocoLoader {\n constructor(private translations: Map<string, Translation>) {}\n\n getTranslation(lang: string): Observable<Translation> {\n return of(this.translations.get(lang) || {});\n }\n}\n\nexport const TRANSLOCO_LOADER = new InjectionToken<TranslocoLoader>(\n 'TRANSLOCO_LOADER',\n);\n","import { flatten as _flatten, unflatten as _unflatten } from 'flat';\n\nimport { ProviderScope, Translation } from './types';\n\nexport function getValue<T>(obj: T, path: keyof T) {\n if (!obj) {\n return obj;\n }\n\n /* For cases where the key is like: 'general.something.thing' */\n if (Object.prototype.hasOwnProperty.call(obj, path)) {\n return obj[path];\n }\n\n return (path as string).split('.').reduce((p, c) => p?.[c], obj as any);\n}\n\nexport function setValue(obj: any, prop: string, val: any) {\n obj = { ...obj };\n\n const split = prop.split('.');\n const lastIndex = split.length - 1;\n\n split.reduce((acc, part, index) => {\n if (index === lastIndex) {\n acc[part] = val;\n } else {\n acc[part] = Array.isArray(acc[part])\n ? acc[part].slice()\n : { ...acc[part] };\n }\n\n return acc && acc[part];\n }, obj);\n\n return obj;\n}\n\nexport function size(collection: any): number {\n if (!collection) {\n return 0;\n }\n\n if (Array.isArray(collection)) {\n return collection.length;\n }\n\n if (isObject(collection)) {\n return Object.keys(collection).length;\n }\n\n return collection ? collection.length : 0;\n}\n\nexport function isEmpty(collection: any): boolean {\n return size(collection) === 0;\n}\n\nexport function isFunction(val: unknown): val is CallableFunction {\n return typeof val === 'function';\n}\n\nexport function isString(val: unknown): val is string {\n return typeof val === 'string';\n}\n\nexport function isNumber(val: unknown): val is number {\n return typeof val === 'number';\n}\n\nexport function isObject(item: unknown): boolean {\n return !!item && typeof item === 'object' && !Array.isArray(item);\n}\n\nexport function coerceArray<T>(value: T | T[]): T[];\nexport function coerceArray<T>(value: T | readonly T[]): readonly T[];\nexport function coerceArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value];\n}\n\n/*\n * @example\n *\n * given: path-to-happiness => pathToHappiness\n * given: path_to_happiness => pathToHappiness\n * given: path-to_happiness => pathToHappiness\n *\n */\nexport function toCamelCase(str: string): string {\n return str\n .replace(/(?:^\\w|[A-Z]|\\b\\w)/g, (word, index) =>\n index == 0 ? word.toLowerCase() : word.toUpperCase(),\n )\n .replace(/\\s+|_|-|\\//g, '');\n}\n\nexport function isBrowser() {\n return typeof window !== 'undefined';\n}\n\nexport function isNil(value: unknown): value is null | undefined {\n return value === null || value === undefined;\n}\n\nexport function isDefined(value: unknown) {\n return isNil(value) === false;\n}\n\nexport function toNumber(value: number | string): number | null {\n if (isNumber(value)) return value;\n\n if (isString(value) && !isNaN(Number(value) - parseFloat(value))) {\n return Number(value);\n }\n\n return null;\n}\n\nexport function isScopeObject(item: any): item is ProviderScope {\n return item && typeof item.scope === 'string';\n}\n\nexport function hasInlineLoader(item: any): item is ProviderScope {\n return item && isObject(item.loader);\n}\n\nexport function unflatten(obj: Translation): Translation {\n return _unflatten(obj);\n}\n\nexport function flatten(obj: Translation): Translation {\n return _flatten(obj, { safe: true });\n}\n","import { InjectionToken } from '@angular/core';\n\nimport { AvailableLangs } from './types';\n\nexport interface TranslocoConfig {\n defaultLang: string;\n reRenderOnLangChange: boolean;\n prodMode: boolean;\n fallbackLang?: string | string[];\n failedRetries: number;\n availableLangs: AvailableLangs;\n flatten: {\n aot: boolean;\n };\n missingHandler: {\n logMissingKey: boolean;\n useFallbackTranslation: boolean;\n allowEmpty: boolean;\n };\n interpolation: [string, string];\n scopes: {\n keepCasing?: boolean;\n };\n}\n\nexport const TRANSLOCO_CONFIG = new InjectionToken<TranslocoConfig>(\n 'TRANSLOCO_CONFIG',\n {\n providedIn: 'root',\n factory: () => defaultConfig,\n },\n);\n\nexport const defaultConfig: TranslocoConfig = {\n defaultLang: 'en',\n reRenderOnLangChange: false,\n prodMode: false,\n failedRetries: 2,\n fallbackLang: [],\n availableLangs: [],\n missingHandler: {\n logMissingKey: true,\n useFallbackTranslation: false,\n allowEmpty: false,\n },\n flatten: {\n aot: false,\n },\n interpolation: ['{{', '}}'],\n scopes: {\n keepCasing: false,\n },\n};\n\ntype DeepPartial<T> =\n T extends Array<any>\n ? T\n : T extends object\n ? { [P in keyof T]?: DeepPartial<T[P]> }\n : T;\n\nexport type PartialTranslocoConfig = DeepPartial<TranslocoConfig>;\n\nexport function translocoConfig(\n config: PartialTranslocoConfig = {},\n): TranslocoConfig {\n return {\n ...defaultConfig,\n ...config,\n missingHandler: {\n ...defaultConfig.missingHandler,\n ...config.missingHandler,\n },\n flatten: {\n ...defaultConfig.flatten,\n ...config.flatten,\n },\n scopes: {\n ...defaultConfig.scopes,\n ...config.scopes,\n },\n };\n}\n","import { inject, Injectable, InjectionToken, Injector } from '@angular/core';\n\nimport { HashMap, Translation } from './types';\nimport { getValue, isDefined, isObject, isString, setValue } from './helpers';\nimport {\n defaultConfig,\n TRANSLOCO_CONFIG,\n TranslocoConfig,\n} from './transloco.config';\n\nexport const TRANSLOCO_TRANSPILER = new InjectionToken<TranslocoTranspiler>(\n 'TRANSLOCO_TRANSPILER',\n);\n\nexport interface TranslocoTranspiler {\n transpile(params: TranspileParams): any;\n\n onLangChanged?(lang: string): void;\n}\n\nexport interface TranspileParams<V = unknown> {\n value: V;\n params?: HashMap;\n translation: Translation;\n key: string;\n}\n\n@Injectable()\nexport class DefaultTranspiler implements TranslocoTranspiler {\n protected config =\n inject(TRANSLOCO_CONFIG, { optional: true }) ?? defaultConfig;\n\n protected get interpolationMatcher() {\n return resolveMatcher(this.config);\n }\n\n transpile({ value, params = {}, translation, key }: TranspileParams): any {\n if (isString(value)) {\n let paramMatch: RegExpExecArray | null;\n let parsedValue = value;\n\n while (\n (paramMatch = this.interpolationMatcher.exec(parsedValue)) !== null\n ) {\n const [match, paramValue] = paramMatch;\n parsedValue = parsedValue.replace(match, () => {\n const match = paramValue.trim();\n\n const param = getValue(params, match);\n if (isDefined(param)) {\n return param;\n }\n\n return isDefined(translation[match])\n ? this.transpile({\n params,\n translation,\n key,\n value: translation[match],\n })\n : '';\n });\n }\n\n return parsedValue;\n } else if (params) {\n if (isObject(value)) {\n value = this.handleObject({\n value: value as Record<any, any>,\n params,\n translation,\n key,\n });\n } else if (Array.isArray(value)) {\n value = this.handleArray({ value, params, translation, key });\n }\n }\n\n return value;\n }\n\n /**\n *\n * @example\n *\n * const en = {\n * a: {\n * b: {\n * c: \"Hello {{ value }}\"\n * }\n * }\n * }\n *\n * const params = {\n * \"b.c\": { value: \"Transloco \"}\n * }\n *\n * service.selectTranslate('a', params);\n *\n * // the first param will be the result of `en.a`.\n * // the second param will be `params`.\n * parser.transpile(value, params, {});\n *\n *\n */\n protected handleObject({\n value,\n params = {},\n translation,\n key,\n }: TranspileParams<Record<any, any>>) {\n let result = value;\n\n Object.keys(params).forEach((p) => {\n // transpile the value => \"Hello Transloco\"\n const transpiled = this.transpile({\n // get the value of \"b.c\" inside \"a\" => \"Hello {{ value }}\"\n value: getValue(result, p),\n // get the params of \"b.c\" => { value: \"Transloco\" }\n params: getValue(params, p),\n translation,\n key,\n });\n\n // set \"b.c\" to `transpiled`\n result = setValue(result, p, transpiled);\n });\n\n return result;\n }\n\n protected handleArray({ value, ...rest }: TranspileParams<unknown[]>) {\n return value.map((v) =>\n this.transpile({\n value: v,\n ...rest,\n }),\n );\n }\n}\n\nfunction resolveMatcher(config: TranslocoConfig): RegExp {\n const [start, end] = config.interpolation;\n\n return new RegExp(`${start}([^${start}${end}]*?)${end}`, 'g');\n}\n\nexport interface TranslocoTranspilerFunction {\n transpile(...args: string[]): any;\n}\n\nexport function getFunctionArgs(argsString: string): string[] {\n const splitted = argsString ? argsString.split(',') : [];\n const args = [];\n for (let i = 0; i < splitted.length; i++) {\n let value = splitted[i].trim();\n while (value[value.length - 1] === '\\\\') {\n i++;\n value = value.replace('\\\\', ',') + splitted[i];\n }\n args.push(value);\n }\n\n return args;\n}\n\n@Injectable()\nexport class FunctionalTranspiler\n extends DefaultTranspiler\n implements TranslocoTranspiler\n{\n protected injector = inject(Injector);\n\n transpile({ value, ...rest }: TranspileParams) {\n let transpiled = value;\n if (isString(value)) {\n transpiled = value.replace(\n /\\[\\[\\s*(\\w+)\\((.*?)\\)\\s*]]/g,\n (match: string, functionName: string, args: string) => {\n try {\n const func: TranslocoTranspilerFunction =\n this.injector.get(functionName);\n\n return func.transpile(...getFunctionArgs(args));\n } catch (e: unknown) {\n let message = `There is an error in: '${value}'. \n Check that the you used the right syntax in your translation and that the implementation of ${functionName} is correct.`;\n if ((e as Error).message.includes('NullInjectorError')) {\n message = `You are using the '${functionName}' function in your translation but no provider was found!`;\n }\n throw new Error(message);\n }\n },\n );\n }\n\n return super.transpile({ value: transpiled, ...rest });\n }\n}\n","import { InjectionToken, Injectable } from '@angular/core';\n\nimport { TranslocoConfig } from './transloco.config';\nimport { HashMap } from './types';\n\nexport const TRANSLOCO_MISSING_HANDLER =\n new InjectionToken<TranslocoMissingHandlerData>('TRANSLOCO_MISSING_HANDLER');\n\nexport interface TranslocoMissingHandlerData extends TranslocoConfig {\n activeLang: string;\n}\n\nexport interface TranslocoMissingHandler {\n handle(key: string, data: TranslocoMissingHandlerData, params?: HashMap): any;\n}\n\n@Injectable()\nexport class DefaultMissingHandler implements TranslocoMissingHandler {\n handle(key: string, config: TranslocoConfig) {\n if (config.missingHandler.logMissingKey && !config.prodMode) {\n const msg = `Missing translation for '${key}'`;\n console.warn(`%c ${msg}`, 'font-size: 12px; color: red');\n }\n\n return key;\n }\n}\n","import { InjectionToken, Injectable } from '@angular/core';\n\nimport { Translation } from './types';\n\nexport const TRANSLOCO_INTERCEPTOR = new InjectionToken<TranslocoInterceptor>(\n 'TRANSLOCO_INTERCEPTOR',\n);\n\nexport interface TranslocoInterceptor {\n preSaveTranslation(translation: Translation, lang: string): Translation;\n\n preSaveTranslationKey(key: string, value: string, lang: string): string;\n}\n\n@Injectable()\nexport class DefaultInterceptor implements TranslocoInterceptor {\n preSaveTranslation(translation: Translation): Translation {\n return translation;\n }\n\n preSaveTranslationKey(_: string, value: string): string {\n return value;\n }\n}\n","import { Inject, Injectable, InjectionToken } from '@angular/core';\n\nimport { TRANSLOCO_CONFIG, TranslocoConfig } from './transloco.config';\n\nexport const TRANSLOCO_FALLBACK_STRATEGY =\n new InjectionToken<TranslocoFallbackStrategy>('TRANSLOCO_FALLBACK_STRATEGY');\n\nexport interface TranslocoFallbackStrategy {\n getNextLangs(failedLang: string): string[];\n}\n\n@Injectable()\nexport class DefaultFallbackStrategy implements TranslocoFallbackStrategy {\n constructor(@Inject(TRANSLOCO_CONFIG) private userConfig: TranslocoConfig) {}\n\n getNextLangs() {\n const fallbackLang = this.userConfig.fallbackLang;\n if (!fallbackLang) {\n throw new Error(\n 'When using the default fallback, a fallback language must be provided in the config!',\n );\n }\n\n return Array.isArray(fallbackLang) ? fallbackLang : [fallbackLang];\n }\n}\n","import { Observable, OperatorFunction, take } from 'rxjs';\n\nimport { TranslocoService } from './transloco.service';\nimport { hasInlineLoader, isString } from './helpers';\nimport { InlineLoader, LoadedEvent, TranslocoScope } from './types';\n\n/*\n * @example\n *\n * given: lazy-page/en => lazy-page\n *\n */\nexport function getScopeFromLang(lang: string): string {\n if (!lang) {\n return '';\n }\n\n const split = lang.split('/');\n split.pop();\n\n return split.join('/');\n}\n\n/*\n * @example\n *\n * given: lazy-page/en => en\n *\n */\nexport function getLangFromScope(lang: string): string {\n if (!lang) {\n return '';\n }\n\n return lang.split('/').pop()!;\n}\n\n/**\n * @example\n *\n * getPipeValue('todos|scoped', 'scoped') [true, 'todos']\n * getPipeValue('en|static', 'static') [true, 'en']\n * getPipeValue('en', 'static') [false, 'en']\n */\nexport function getPipeValue(\n str: string | undefined,\n value: string,\n char = '|',\n): [boolean, string] {\n if (isString(str)) {\n const splitted = str.split(char);\n const lastItem = splitted.pop()!;\n\n return lastItem === value ? [true, splitted.toString()] : [false, lastItem];\n }\n\n return [false, ''];\n}\n\nexport function shouldListenToLangChanges(\n service: TranslocoService,\n lang?: string,\n) {\n const [hasStatic] = getPipeValue(lang, 'static');\n if (!hasStatic) {\n // If we didn't get 'lang|static' check if it's set in the global level\n return !!service.config.reRenderOnLangChange;\n }\n\n // We have 'lang|static' so don't listen to lang changes\n return false;\n}\n\nexport function listenOrNotOperator<T>(\n listenToLangChange?: boolean,\n): OperatorFunction<T, T> {\n return listenToLangChange ? (source: Observable<T>) => source : take<T>(1);\n}\n\nfunction prependScope(inlineLoader: InlineLoader, scope: string) {\n return Object.keys(inlineLoader).reduce(\n (acc, lang) => {\n acc[`${scope}/${lang}`] = inlineLoader[lang];\n\n return acc;\n },\n {} as Record<string, InlineLoader[keyof InlineLoader]>,\n );\n}\n\nexport function resolveInlineLoader(\n providerScope: TranslocoScope | null,\n scope?: string,\n): InlineLoader | undefined {\n return hasInlineLoader(providerScope)\n ? prependScope(providerScope.loader!, scope!)\n : undefined;\n}\n\nexport function getEventPayload(lang: string): LoadedEvent['payload'] {\n return {\n scope: getScopeFromLang(lang) || null,\n langName: getLangFromScope(lang),\n };\n}\n","import { TranslocoLoader, TranslocoLoaderData } from './transloco.loader';\nimport { InlineLoader } from './types';\nimport { isFunction } from './helpers';\n\ninterface Options {\n inlineLoader?: InlineLoader;\n path: string;\n mainLoader: TranslocoLoader;\n data?: TranslocoLoaderData;\n}\n\nexport function resolveLoader(options: Options) {\n const { path, inlineLoader, mainLoader, data } = options;\n\n if (inlineLoader) {\n const pathLoader = inlineLoader[path];\n if (isFunction(pathLoader) === false) {\n throw `You're using an inline loader but didn't provide a loader for ${path}`;\n }\n\n return inlineLoader[path]().then((res) =>\n res.default ? res.default : res,\n );\n }\n\n return mainLoader.getTranslation(path, data);\n}\n","import { from, map } from 'rxjs';\n\nimport { resolveLoader } from './resolve-loader';\nimport { TranslocoLoader, TranslocoLoaderData } from './transloco.loader';\nimport { InlineLoader } from './types';\n\ninterface Options {\n path: string;\n fallbackPath?: string;\n inlineLoader?: InlineLoader;\n mainLoader: TranslocoLoader;\n data?: TranslocoLoaderData;\n}\n\nexport function getFallbacksLoaders({\n mainLoader,\n path,\n data,\n fallbackPath,\n inlineLoader,\n}: Options) {\n const paths = fallbackPath ? [path, fallbackPath] : [path];\n\n return paths.map((path) => {\n const loader = resolveLoader({ path, mainLoader, inlineLoader, data });\n\n return from(loader).pipe(\n map((translation) => ({\n translation,\n lang: path,\n })),\n );\n });\n}\n","import { Inject, Injectable, OnDestroy, Optional } from '@angular/core';\nimport {\n BehaviorSubject,\n catchError,\n combineLatest,\n EMPTY,\n forkJoin,\n from,\n map,\n Observable,\n of,\n retry,\n shareReplay,\n Subject,\n switchMap,\n tap,\n} from 'rxjs';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport {\n DefaultLoader,\n TRANSLOCO_LOADER,\n TranslocoLoader,\n} from './transloco.loader';\nimport {\n TRANSLOCO_TRANSPILER,\n TranslocoTranspiler,\n} from './transloco.transpiler';\nimport {\n AvailableLangs,\n HashMap,\n InlineLoader,\n LangDefinition,\n LoadOptions,\n SetTranslationOptions,\n TranslateObjectParams,\n TranslateParams,\n Translation,\n TranslocoEvents,\n TranslocoScope,\n} from './types';\nimport {\n flatten,\n isEmpty,\n isNil,\n isScopeObject,\n isString,\n size,\n toCamelCase,\n unflatten,\n} from './helpers';\nimport { TRANSLOCO_CONFIG, TranslocoConfig } from './transloco.config';\nimport {\n TRANSLOCO_MISSING_HANDLER,\n TranslocoMissingHandler,\n TranslocoMissingHandlerData,\n} from './transloco-missing-handler';\nimport {\n TRANSLOCO_INTERCEPTOR,\n TranslocoInterceptor,\n} from './transloco.interceptor';\nimport {\n TRANSLOCO_FALLBACK_STRATEGY,\n TranslocoFallbackStrategy,\n} from './transloco-fallback-strategy';\nimport {\n getEventPayload,\n getLangFromScope,\n getScopeFromLang,\n resolveInlineLoader,\n} from './shared';\nimport { getFallbacksLoaders } from './get-fallbacks-loaders';\nimport { resolveLoader } from './resolve-loader';\n\nlet service: TranslocoService;\n\nexport function translate<T = string>(\n key: TranslateParams,\n params: HashMap = {},\n lang?: string,\n): T {\n return service.translate<T>(key, params, lang);\n}\n\nexport function translateObject<T>(\n key: TranslateParams,\n params: HashMap = {},\n lang?: string,\n): T | T[] {\n return service.translateObject<T>(key, params, lang);\n}\n\n@Injectable({ providedIn: 'root' })\nexport class TranslocoService implements OnDestroy {\n langChanges$: Observable<string>;\n\n private translations = new Map<string, Translation>();\n private cache = new Map<string, Observable<Translation>>();\n private firstFallbackLang: string | undefined;\n private defaultLang = '';\n private availableLangs: AvailableLangs = [];\n private isResolvedMissingOnce = false;\n private lang: BehaviorSubject<string>;\n private failedLangs = new Set<string>();\n private events = new Subject<TranslocoEvents>();\n\n events$ = this.events.asObservable();\n readonly config: TranslocoConfig & {\n scopeMapping?: HashMap<string>;\n };\n\n constructor(\n @Optional() @Inject(TRANSLOCO_LOADER) private loader: TranslocoLoader,\n @Inject(TRANSLOCO_TRANSPILER) private parser: TranslocoTranspiler,\n @Inject(TRANSLOCO_MISSING_HANDLER)\n private missingHandler: TranslocoMissingHandler,\n @Inject(TRANSLOCO_INTERCEPTOR) private interceptor: TranslocoInterceptor,\n @Inject(TRANSLOCO_CONFIG) userConfig: TranslocoConfig,\n @Inject(TRANSLOCO_FALLBACK_STRATEGY)\n private fallbackStrategy: TranslocoFallbackStrategy,\n ) {\n if (!this.loader) {\n this.loader = new DefaultLoader(this.translations);\n }\n service = this;\n this.config = JSON.parse(JSON.stringify(userConfig));\n\n this.setAvailableLangs(this.config.availableLangs || []);\n this.setFallbackLangForMissingTranslation(this.config);\n this.setDefaultLang(this.config.defaultLang);\n this.lang = new BehaviorSubject<string>(this.getDefaultLang());\n // Don't use distinctUntilChanged as we need the ability to update\n // the value when using setTranslation or setTranslationKeys\n this.langChanges$ = this.lang.asObservable();\n\n /**\n * When we have a failure, we want to define the next language that succeeded as the active\n */\n this.events$.pipe(takeUntilDestroyed()).subscribe((e) => {\n if (e.type === 'translationLoadSuccess' && e.wasFailure) {\n this.setActiveLang(e.payload.langName);\n }\n });\n }\n\n getDefaultLang() {\n return this.defaultLang;\n }\n\n setDefaultLang(lang: string) {\n this.defaultLang = lang;\n }\n\n getActiveLang() {\n return this.lang.getValue();\n }\n\n setActiveLang(lang: string) {\n this.parser.onLangChanged?.(lang);\n this.lang.next(lang);\n this.events.next({\n type: 'langChanged',\n payload: getEventPayload(lang),\n });\n return this;\n }\n\n setAvailableLangs(langs: AvailableLangs) {\n this.availableLangs = langs;\n }\n\n /**\n * Gets the available languages.\n *\n * @returns\n * An array of the available languages. Can be either a `string[]` or a `{ id: string; label: string }[]`\n * depending on how the available languages are set in your module.\n */\n getAvailableLangs() {\n return this.availableLangs;\n }\n\n load(path: string, options: LoadOptions = {}): Observable<Translation> {\n const cached = this.cache.get(path);\n if (cached) {\n return cached;\n }\n\n let loadTranslation: Observable<\n Translation | { translation: Translation; lang: string }[]\n >;\n const isScope = this._isLangScoped(path);\n let scope: string;\n if (isScope) {\n scope = getScopeFromLang(path);\n }\n\n const loadersOptions = {\n path,\n mainLoader: this.loader,\n inlineLoader: options.inlineLoader,\n data: isScope ? { scope: scope! } : undefined,\n };\n\n if (this.useFallbackTranslation(path)) {\n // if the path is scope the fallback should be `scope/fallbackLang`;\n const fallback = isScope\n ? `${scope!}/${this.firstFallbackLang}`\n : this.firstFallbackLang;\n\n const loaders = getFallbacksLoaders({\n ...loadersOptions,\n fallbackPath: fallback!,\n });\n loadTranslation = forkJoin(loaders);\n } else {\n const loader = resolveLoader(loadersOptions);\n loadTranslation = from(loader);\n }\n\n const load$ = loadTranslation.pipe(\n retry(this.config.failedRetries),\n tap((translation) => {\n if (Array.isArray(translation)) {\n translation.forEach((t) => {\n this.handleSuccess(t.lang, t.translation);\n // Save the fallback in cache so we'll not create a redundant request\n if (t.lang !== path) {\n this.cache.set(t.lang, of({}));\n }\n });\n return;\n }\n this.handleSuccess(path, translation);\n }),\n catchError((error) => {\n if (!this.config.prodMode) {\n console.error(`Error while trying to load \"${path}\"`, error);\n }\n\n return this.handleFailure(path, options);\n }),\n shareReplay(1),\n );\n\n this.cache.set(path, load$);\n\n return load$;\n }\n\n /**\n * Gets the instant translated value of a key\n *\n * @example\n *\n * translate<string>('hello')\n * translate('hello', { value: 'value' })\n * translate<string[]>(['hello', 'key'])\n * translate('hello', { }, 'en')\n * translate('scope.someKey', { }, 'en')\n */\n translate<T = string>(\n key: TranslateParams,\n params: HashMap = {},\n lang = this.getActiveLang(),\n ): T {\n if (!key) return key as any;\n\n const { scope, resolveLang } = this.resolveLangAndScope(lang);\n\n if (Array.isArray(key)) {\n return key.map((k) =>\n this.translate(scope ? `${scope}.${k}` : k, params, resolveLang),\n ) as any;\n }\n\n key = scope ? `${scope}.${key}` : key;\n\n const translation = this.getTranslation(resolveLang);\n const value = translation[key];\n\n if (!value) {\n return this._handleMissingKey(key, value, params);\n }\n\n return this.parser.transpile({\n value,\n params,\n translation,\n key,\n });\n }\n\n /**\n * Gets the translated value of a key as observable\n *\n * @example\n *\n * selectTranslate<string>('hello').subscribe(value => ...)\n * selectTranslate<string>('hello', {}, 'es').subscribe(value => ...)\n * selectTranslate<string>('hello', {}, 'todos').subscribe(value => ...)\n * selectTranslate<string>('hello', {}, { scope: 'todos' }).subscribe(value => ...)\n *\n */\n selectTranslate<T = any>(\n key: TranslateParams,\n params?: HashMap,\n lang?: string | TranslocoScope | TranslocoScope[],\n _isObject = false,\n ): Observable<T> {\n let inlineLoader: InlineLoader | undefined;\n const load = (lang: string, options?: LoadOptions) =>\n this.load(lang, options).pipe(\n map(() =>\n _isObject\n ? this.translateObject(key, params, lang)\n : this.translate(key, params, lang),\n ),\n );\n if (isNil(lang)) {\n return this.langChanges$.pipe(switchMap((lang) => load(lang)));\n }\n\n lang = Array.isArray(lang) ? lang[0] : lang;\n if (isScopeObject(lang)) {\n // it's a scope object.\n const providerScope = lang;\n lang = providerScope.scope;\n inlineLoader = resolveInlineLoader(providerScope, providerScope.scope);\n }\n\n lang = lang as string;\n if (this.isLang(lang) || this.isScopeWithLang(lang)) {\n return load(lang);\n }\n // it's a scope\n const scope = lang;\n return this.langChanges$.pipe(\n switchMap((lang) => load(`${scope}/${lang}`, { inlineLoader })),\n );\n }\n\n /**\n * Whether the scope with lang\n *\n * @example\n *\n * todos/en => true\n * todos => false\n */\n private isScopeWithLang(lang: string) {\n return this.isLang(getLangFromScope(lang));\n }\n\n /**\n * Translate the given path that returns an object\n *\n * @example\n *\n * service.translateObject('path.to.object', {'subpath': { value: 'someValue'}}) => returns translated object\n *\n */\n translateObject<T = any>(key: string, params?: HashMap, lang?: string): T;\n translateObject<T = any>(key: string[], params?: HashMap, lang?: string): T[];\n translateObject<T = any>(\n key: TranslateParams,\n params?: HashMap,\n lang?: string,\n ): T | T[];\n translateObject<T = any>(\n key: HashMap | Map<string, HashMap>,\n params?: null,\n lang?: string,\n ): T[];\n translateObject<T = any>(\n key: TranslateObjectParams,\n params: HashMap | null = {},\n lang = this.getActiveLang(),\n ): T | T[] {\n if (isString(key) || Array.isArray(key)) {\n const { resolveLang, scope } = this.resolveLangAndScope(lang);\n if (Array.isArray(key)) {\n return key.map((k) =>\n this.translateObject(\n scope ? `${scope}.${k}` : k,\n params!,\n resolveLang,\n ),\n ) as any;\n }\n\n const translation = this.getTranslation(resolveLang);\n key = scope ? `${scope}.${key}` : key;\n\n const value = unflatten(this.getObjectByKey(translation, key));\n /* If an empty object was returned we want to try and translate the key as a string and not an object */\n return isEmpty(value)\n ? this.translate(key, params!, lang)\n : this.parser.transpile({ value, params: params!, translation, key });\n }\n\n const translations: T[] = [];\n for (const [_key, _params] of this.getEntries(key)) {\n translations.push(this.translateObject(_key, _params, lang));\n }\n\n return translations;\n }\n\n selectTranslateObject<T = any>(\n key: string,\n params?: HashMap,\n lang?: string,\n ): Observable<T>;\n selectTranslateObject<T = any>(\n key: string[],\n params?: HashMap,\n lang?: string,\n ): Observable<T[]>;\n selectTranslateObject<T = any>(\n key: TranslateParams,\n params?: HashMap,\n lang?: string,\n ): Observable<T> | Observable<T[]>;\n selectTranslateObject<T = any>(\n key: HashMap | Map<string, HashMap>,\n params?: null,\n lang?: string,\n ): Observable<T[]>;\n selectTranslateObject<T = any>(\n key: TranslateObjectParams,\n params?: HashMap | null,\n lang?: string,\n ): Observable<T> | Observable<T[]> {\n if (isString(key) || Array.isArray(key)) {\n return this.selectTranslate<T>(key, params!, lang, true);\n }\n\n const [[firstKey, firstParams], ...rest] = this.getEntries(key);\n\n /* In order to avoid subscribing multiple times to the load language event by calling selectTranslateObject for each pair,\n * we listen to when the first key has been translated (the language is loaded) and translate the rest synchronously */\n return this.selectTranslateObject<T>(firstKey, firstParams, lang).pipe(\n map((value) => {\n const translations = [value];\n for (const [_key, _params] of rest) {\n translations.push(this.translateObject<T>(_key, _params, lang));\n }\n\n return translations;\n }),\n );\n }\n\n /**\n * Gets an object of translations for a given language\n *\n * @example\n *\n * getTranslation()\n * getTranslation('en')\n * getTranslation('admin-page/en')\n */\n getTranslation(): Map<string, Translation>;\n getTranslation(langOrScope: string): Translation;\n getTranslation(langOrScope?: string): Map<string, Translation> | Translation {\n if (langOrScope) {\n if (this.isLang(langOrScope)) {\n return this.translations.get(langOrScope) || {};\n } else {\n // This is a scope, build the scope value from the translation object\n const { scope, resolveLang } = this.resolveLangAndScope(langOrScope);\n const translation = this.translations.get(resolveLang) || {};\n\n return this.getObjectByKey(translation, scope);\n }\n }\n\n return this.translations;\n }\n\n /**\n * Gets an object of translations for a given language\n *\n * @example\n *\n * selectTranslation().subscribe() - will return the current lang translation\n * selectTranslation('es').subscribe()\n * selectTranslation('admin-page').subscribe() - will return the current lang scope translation\n * selectTranslation('admin-page/es').subscribe()\n */\n selectTranslation(lang?: string): Observable<Translation> {\n let language$ = this.langChanges$;\n if (lang) {\n const scopeLangSpecified = getLangFromScope(lang) !== lang;\n if (this.isLang(lang) || scopeLangSpecified) {\n language$ = of(lang);\n } else {\n language$ = this.langChanges$.pipe(\n map((currentLang) => `${lang}/${currentLang}`),\n );\n }\n }\n\n return language$.pipe(\n switchMap((language) =>\n this.load(language).pipe(map(() => this.getTranslation(language))),\n ),\n );\n }\n\n /**\n * Sets or merge a given translation object to current lang\n *\n * @example\n *\n * setTranslation({ ... })\n * setTranslation({ ... }, 'en')\n * setTranslation({ ... }, 'es', { merge: false } )\n * setTranslation({ ... }, 'todos/en', { merge: false } )\n */\n setTranslation(\n translation: Translation,\n lang = this.getActiveLang(),\n options: SetTranslationOptions = {},\n ) {\n const defaults = { merge: true, emitChange: true };\n const mergedOptions = { ...defaults, ...options };\n const scope = getScopeFromLang(lang);\n\n /**\n * If this isn't a scope we use the whole translation as is\n * otherwise we need to flat the scope and use it\n */\n let flattenScopeOrTranslation = translation;\n\n // Merged the scoped language into the active language\n if (scope) {\n const key = this.getMappedScope(scope);\n flattenScopeOrTranslation = flatten({ [key]: translation });\n }\n\n const currentLang = scope ? getLangFromScope(lang) : lang;\n\n const mergedTranslation = {\n ...(mergedOptions.merge && this.getTranslation(currentLang)),\n ...flattenScopeOrTranslation,\n };\n\n const flattenTranslation = this.config.flatten!.aot\n ? mergedTranslation\n : flatten(mergedTranslation);\n const withHook = this.interceptor.preSaveTranslation(\n flattenTranslation,\n currentLang,\n );\n this.translations.set(currentLang, withHook);\n mergedOptions.emitChange && this.setActiveLang(this.getActiveLang());\n }\n\n /**\n * Sets translation key with given value\n *\n * @example\n *\n * setTranslationKey('key', 'value')\n * setTranslationKey('key.nested', 'value')\n * setTranslationKey('key.nested', 'value', 'en')\n * setTranslationKey('key.nested', 'value', 'en', { emitChange: false } )\n */\n setTranslationKey(\n key: string,\n value: string,\n options: Omit<SetTranslationOptions, 'merge'> = {},\n ) {\n const lang = options.lang || this.getActiveLang();\n const withHook = this.interceptor.preSaveTranslationKey(key, value, lang);\n const newValue = {\n [key]: withHook,\n };\n\n this.setTranslation(newValue, lang, { ...options, merge: true });\n }\n\n /**\n * Sets the fallback lang for the currently active language\n * @param fallbackLang\n */\n setFallbackLangForMissingTranslation({\n fallbackLang,\n }: Pick<TranslocoConfig, 'fallbackLang'>) {\n const lang = Array.isArray(fallbackLang) ? fallbackLang[0] : fallbackLang;\n if (fallbackLang && this.useFallbackTranslation(lang)) {\n this.firstFallbackLang = lang!;\n }\n }\n\n /**\n * @internal\n */\n _handleMissingKey(key: string, value: any, params?: HashMap) {\n if (this.config.missingHandler!.allowEmpty && value === '') {\n return '';\n }\n\n if (!this.isResolvedMissingOnce && this.useFallbackTranslation()) {\n // We need to set it to true to prevent a loop\n this.isResolvedMissingOnce = true;\n const fallbackValue = this.translate(\n key,\n params,\n this.firstFallbackLang!,\n );\n this.isResolvedMissingOnce = false;\n\n return fallbackValue;\n }\n\n return this.missingHandler.handle(\n key,\n this.getMissingHandlerData(),\n params,\n );\n }\n\n /**\n * @internal\n */\n _isLangScoped(lang: string) {\n return this.getAvailableLangsIds().indexOf(lang) === -1;\n }\n\n /**\n * Checks if a given string is one of the specified available languages.\n * @returns\n * True if the given string is an available language.\n * False if the given string is not an available language.\n */\n isLang(lang: string): boolean {\n return this.getAvailableLangsIds().indexOf(lang) !== -1;\n }\n\n /**\n * @internal\n *\n * We always want to make sure the global lang is loaded\n * before loading the scope since you can access both via the pipe/directive.\n */\n _loadDependencies(\n path: string,\n inlineLoader?: InlineLoader,\n ): Observable<Translation | Translation[]> {\n const mainLang = getLangFromScope(path);\n\n if (this._isLangScoped(path) && !this.isLoadedTranslation(mainLang)) {\n return combineLatest([\n this.load(mainLang),\n this.load(path, { inlineLoader }),\n ]);\n }\n return this.load(path, { inlineLoader });\n }\n\n /**\n * @internal\n */\n _completeScopeWithLang(langOrScope: string) {\n if (\n this._isLangScoped(langOrScope) &&\n !this.isLang(getLangFromScope(langOrScope))\n ) {\n return `${langOrScope}/${this.getActiveLang()}`;\n }\n return langOrScope;\n }\n\n /**\n * @internal\n */\n _setScopeAlias(scope: string, alias: string) {\n if (!this.config.scopeMapping) {\n this.config.scopeMapping = {};\n }\n this.config.scopeMapping[scope] = alias;\n }\n\n ngOnDestroy() {\n // Caretaker note: since this is the root provider, it'll be destroyed when the `NgModuleRef.destroy()` is run.\n // Cached values capture `this`, thus leading to a circular reference and preventing the `TranslocoService` from\n // being GC'd. This would lead to a memory leak when server-side rendering is used since the service is created\n // and destroyed per each HTTP request, but any service is not getting GC'd.\n this.cache.clear();\n }\n\n private isLoadedTranslation(lang: string) {\n return size(this.getTranslation(lang));\n }\n\n private getAvailableLangsIds(): string[] {\n const first = this.getAvailableLangs()[0];\n\n if (isString(first)) {\n return this.getAvailableLangs() as string[];\n }\n\n return (this.getAvailableLangs() as LangDefinition[]).map((l) => l.id);\n }\n\n private getMissingHandlerData(): TranslocoMissingHandlerData {\n return {\n ...this.config,\n activeLang: this.getActiveLang(),\n availableLangs: this.availableLangs,\n defaultLang: this.defaultLang,\n };\n }\n\n /**\n * Use a fallback translation set for missing keys of the primary language\n * This is unrelated to the fallback language (which changes the active language)\n */\n private useFallbackTranslation(lang?: string) {\n return (\n this.config.missingHandler!.useFallbackTranslation &&\n lang !== this.firstFallbackLang\n );\n }\n\n private handleSuccess(lang: string, translation: Translation) {\n this.setTranslation(translation, lang, { emitChange: false });\n this.events.next({\n wasFailure: !!this.failedLangs.size,\n type: 'translationLoadSuccess',\n payload: getEventPayload(lang),\n });\n this.failedLangs.forEach((l) => this.cache.delete(l));\n this.failedLangs.clear();\n }\n\n private handleFailure(lang: string, loadOptions: LoadOptions) {\n // When starting to load a first choice language, initialize\n // the failed counter and resolve the fallback langs.\n if (isNil(loadOptions.failedCounter)) {\n loadOptions.failedCounter = 0;\n\n if (!loadOptions.fallbackLangs) {\n loadOptions.fallbackLangs = this.fallbackStrategy.getNextLangs(lang);\n }\n }\n\n const splitted = lang.split('/');\n const fallbacks = loadOptions.fallbackLangs;\n const nextLang = fallbacks![loadOptions.failedCounter!];\n this.failedLangs.add(lang);\n\n // This handles the case where a loaded fallback language is requested again\n if (this.cache.has(nextLang)) {\n this.handleSuccess(nextLang, this.getTranslation(nextLang));\n return EMPTY;\n }\n\n const isFallbackLang = nextLang === splitted[splitted.length - 1];\n\n if (!nextLang || isFallbackLang) {\n let msg = `Unable to load translation and all the fallback languages`;\n if (splitted.length > 1) {\n msg += `, did you misspelled the scope name?`;\n }\n\n throw new Error(msg);\n }\n\n let resolveLang = nextLang;\n // if it's scoped lang\n if (splitted.length > 1) {\n // We need to resolve it to:\n // todos/langNotExists => todos/nextLang\n splitted[splitted.length - 1] = nextLang;\n resolveLang = splitted.join('/');\n }\n\n loadOptions.failedCounter!++;\n this.events.next({\n type: 'translationLoadFailure',\n payload: getEventPayload(lang),\n });\n\n return this.load(resolveLang, loadOptions);\n }\n\n private getMappedScope(scope: string): string {\n const { scopeMapping = {}, scopes = { keepCasing: false } } = this.config;\n return (\n scopeMapping[scope] || (scopes.keepCasing ? scope : toCamelCase(scope))\n );\n }\n\n /**\n * If lang is scope we need to check the following cases:\n * todos/es => in this case we should take `es` as lang\n * todos => in this case we should set the active lang as lang\n */\n private resolveLangAndScope(lang: string) {\n let resolveLang = lang;\n let scope;\n\n if (this._isLangScoped(lang)) {\n // en for example\n const langFromScope = getLangFromScope(lang);\n // en is lang\n const hasLang = this.isLang(langFromScope);\n // take en\n resolveLang = hasLang ? langFromScope : this.getActiveLang();\n // find the scope\n scope = this.getMappedScope(hasLang ? getScopeFromLang(lang) : lang);\n }\n\n return { scope, resolveLang };\n }\n\n private getObjectByKey(translation: Translation, key?: string) {\n const result: Translation = {};\n const prefix = `${key}.`;\n\n for (const currentKey in translation) {\n if (currentKey.startsWith(prefix)) {\n result[currentKey.replace(prefix, '')] = translation[currentKey];\n }\n }\n\n return result;\n }\n\n private getEntries(key: HashMap | Map<string, HashMap>) {\n return key instanceof Map ? key.entries() : Object.entries(key);\n }\n}\n","import { Component, Input } from '@angular/core';\n\n@Component({\n template: `\n <div class=\"transloco-loader-template\" [innerHTML]=\"html\"></div>\n `,\n standalone: true,\n})\nexport class TranslocoLoaderComponent {\n @Input() html: string | undefined;\n}\n","import { TemplateRef, Type, ViewContainerRef } from '@angular/core';\n\nimport { isString } from './helpers';\nimport { TranslocoLoaderComponent } from './loader-component.component';\n\nexport type Content = string | TemplateRef<unknown> | Type<unknown>;\n\nexport class TemplateHandler {\n constructor(\n private view: Content,\n private vcr: ViewContainerRef,\n ) {}\n\n attachView() {\n if (this.view instanceof TemplateRef) {\n this.vcr.createEmbeddedView(this.view);\n } else if (isString(this.view)) {\n const componentRef = this.vcr.createComponent(TranslocoLoaderComponent);\n componentRef.instance.html = this.view;\n componentRef.hostView.detectChanges();\n } else {\n this.vcr.createComponent(this.view);\n }\n }\n\n detachView() {\n this.vcr.clear();\n }\n}\n","import { InjectionToken } from '@angular/core';\n\nexport const TRANSLOCO_LANG = new InjectionToken<string>('TRANSLOCO_LANG');\n","import { InjectionToken } from '@angular/core';\n\nimport { Content } from './template-handler';\n\nexport const TRANSLOCO_LOADING_TEMPLATE = new InjectionToken<Content>(\n 'TRANSLOCO_LOADING_TEMPLATE',\n);\n","import { InjectionToken } from '@angular/core';\n\nimport { TranslocoScope } from './types';\n\nexport const TRANSLOCO_SCOPE = new InjectionToken<TranslocoScope>(\n 'TRANSLOCO_SCOPE',\n);\n","import { getLangFromScope, getPipeValue, getScopeFromLang } from './shared';\n\ninterface LangResolverParams {\n inline?: string;\n provider: string | null | undefined;\n active: string;\n}\n\nexport class LangResolver {\n initialized = false;\n\n // inline => provider => active\n resolve({ inline, provider, active }: LangResolverParams): string {\n let lang = active;\n /**\n * When the user changes the lang we need to update\n * the view. Otherwise, the lang will remain the inline/provided lang\n */\n if (this.initialized) {\n lang = active;\n\n return lang;\n }\n\n if (provider) {\n const [, extracted] = getPipeValue(provider, 'static');\n lang = extracted;\n }\n\n if (inline) {\n const [, extracted] = getPipeValue(inline, 'static');\n lang = extracted;\n }\n\n this.initialized = true;\n\n return lang;\n }\n\n /**\n *\n * Resolve the lang\n *\n * @example\n *\n * resolveLangBasedOnScope('todos/en') => en\n * resolveLangBasedOnScope('en') => en\n *\n */\n resolveLangBasedOnScope(lang: string) {\n const scope = getScopeFromLang(lang);\n\n return scope ? getLangFromScope(lang) : lang;\n }\n\n /**\n *\n * Resolve the lang path for loading\n *\n * @example\n *\n * resolveLangPath('todos', 'en') => todos/en\n * resolveLangPath('en') => en\n *\n */\n resolveLangPath(lang: string, scope?: string) {\n return scope ? `${scope}/${lang}` : lang;\n }\n}\n","import { TranslocoScope, ProviderScope, OrArray } from './types';\nimport { TranslocoService } from './transloco.service';\nimport { isScopeObject, toCamelCase } from './helpers';\n\ntype ScopeResolverParams = {\n inline: string | undefined;\n provider: OrArray<TranslocoScope> | null;\n};\n\nexport class ScopeResolver {\n constructor(private service: TranslocoService) {}\n\n // inline => provider\n resolve(params: ScopeResolverParams): string | undefined {\n const { inline, provider } = params;\n if (inline) {\n return inline;\n }\n\n if (provider) {\n if (isScopeObject(provider)) {\n const {\n scope,\n alias = this.service.config.scopes.keepCasing\n ? scope\n : toCamelCase(scope),\n } = provider as ProviderScope;\n this.service._setScopeAlias(scope, alias);\n\n return scope;\n }\n\n return provider as string;\n }\n\n return undefined;\n }\n}\n","import {\n ChangeDetectorRef,\n DestroyRef,\n Directive,\n ElementRef,\n EmbeddedViewRef,\n inject,\n Input,\n OnChanges,\n OnDestroy,\n OnInit,\n Renderer2,\n SimpleChanges,\n TemplateRef,\n ViewContainerRef,\n} from '@angular/core';\nimport { forkJoin, Observable, switchMap } from 'rxjs';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { Content, TemplateHandler } from './template-handler';\nimport { TRANSLOCO_LANG } from './transloco-lang';\nimport { TRANSLOCO_LOADING_TEMPLATE } from './transloco-loading-template';\nimport { TRANSLOCO_SCOPE } from './transloco-scope';\nimport { TranslocoService } from './transloco.service';\nimport { HashMap, OrArray, Translation, TranslocoScope } from './types';\nimport {\n listenOrNotOperator,\n resolveInlineLoader,\n shouldListenToLangChanges,\n} from './shared';\nimport { LangResolver } from './lang-resolver';\nimport { ScopeResolver } from './scope-resolver';\n\ntype TranslateFn = (key: string, params?: HashMap) => any;\ninterface ViewContext {\n $implicit: TranslateFn;\n currentLang: string;\n}\n\n@Directive({\n selector: '[transloco]',\n standalone: true,\n})\nexport class TranslocoDirective implements OnInit, OnDestroy, OnChanges {\n private destroyRef = inject(DestroyRef);\n private service = inject(TranslocoService);\n private tpl = inject<TemplateRef<ViewContext>>(TemplateRef, {\n optional: true,\n });\n private providerLang = inject(TRANSLOCO_LANG, { optional: true });\n private providerScope: OrArray<TranslocoScope> | null = inject(\n TRANSLOCO_SCOPE,\n { optional: true },\n );\n private providedLoadingTpl = inject(TRANSLOCO_LOADING_TEMPLATE, {\n optional: true,\n });\n private cdr = inject(ChangeDetectorRef);\n private host = inject(ElementRef);\n private vcr = inject(ViewContainerRef);\n private renderer = inject(Renderer2);\n\n view: EmbeddedViewRef<ViewContext> | undefined;\n\n private memo = new Map<string, any>();\n\n @Input('transloco') key: string | undefined;\n @Input('translocoParams') params: HashMap = {};\n @Input('translocoScope') inlineScope: string | undefined;\n /** @deprec