@ogs-gmbh/ngx-translate
Version:
A lightweight, REST-based Angular i18n library designed for seamless internationalization with minimal setup. It supports dynamic language switching & flexible translation management via RESTful APIs.
1 lines • 110 kB
Source Map (JSON)
{"version":3,"file":"ogs-gmbh-ngx-translate.mjs","sources":["../../../src/enums/collecting-strategy.enum.ts","../../../src/enums/preloading-strategy.enum.ts","../../../src/errors/scope-not-defined.error.ts","../../../src/errors/locale-not-defined.error.ts","../../../src/errors/translation-not-defined.error.ts","../../../src/errors/source-locale-not-defined.error.ts","../../../src/tokens/config.token.ts","../../../src/services/translation-store.service.ts","../../../src/interceptors/translation.interceptor.ts","../../../src/tokens/scope.token.ts","../../../src/utils/file.util.ts","../../../src/utils/translate.util.ts","../../../src/tokens/http.token.ts","../../../src/services/translation-http.serivce.ts","../../../src/services/translation.service.ts","../../../src/pipes/translation.pipe.ts","../../../src/tokens/preload.token.ts","../../../src/providers/scope.provider.ts","../../../src/providers/preload.provider.ts","../../../src/providers/config.provider.ts","../../../src/providers/interceptor.provider.ts","../../../src/providers/http.provider.ts","../../../src/lib.module.ts","../../../src/public-api.ts","../../../src/ogs-gmbh-ngx-translate.ts"],"sourcesContent":["/**\n * Strategies for collecting translations in the application, so that they don't have to be fetched multiple times.\n * @remarks Each option describes which {@link LocaleConfig} should be collected.\n * @deprecated Use {@link CollectingStrategy} instead. It'll be removed in upcoming releases.\n * @category NG config\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport enum CollectingStrategies {\n /**\n * Collect every translation, regardless of the current {@link LocaleConfig}\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n ALL = \"all\",\n /**\n * Collect only translations of the current locale\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n CURRENT = \"current\"\n}\n\n/**\n * Strategies for collecting translations in the application, so that they don't have to be fetched multiple times.\n * @remarks Each option describes which {@link LocaleConfig} should be collected.\n * @category NG config\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport enum CollectingStrategy {\n /**\n * Collect every translation, regardless of the current locale\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n ALL = \"all\",\n /**\n * Collect only translations of the current locale\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n CURRENT = \"current\"\n}\n","/**\n * Strategies for preloading translations\n * @remarks Each option describes when the preload should happen.\n * @category NG config\n * @deprecated Use {@link PreloadingStrategy} instead. It'll be removed in upcoming releases.\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport enum PreloadingStrategies {\n /**\n * Preload translations during application initialization\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n INITIALIZATION = \"initialization\",\n /**\n * Preload translations during application runtime\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n RUNTIME = \"runtime\"\n}\n\n/**\n * Strategies for preloading translations\n * @remarks Each option describes when the preload should happen.\n * @category NG config\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\nexport enum PreloadingStrategy {\n /**\n * Preload translations during application initialization\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n INITIALIZATION = \"initialization\",\n /**\n * Preload translations during application runtime\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n RUNTIME = \"runtime\"\n}\n","import { LocaleConfig } from \"../public-api\";\n\n/**\n * Snapshot of the {@link ScopeNotDefinedError} properties for serialization purposes\n * @category Types\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\nexport type ScopeNotDefinedStateSnapshot = {\n /**\n * Indicates whether the default scope is not defined\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n isDefaultScope: boolean;\n /**\n * Name of the scope that is not defined\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n scope?: string;\n /**\n * {@link LocaleConfig} of the scope that is not defined in the translations\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n locale?: LocaleConfig;\n}\n\n/**\n * Error thrown when a scope is not defined\n * @category Errors\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport class ScopeNotDefinedError extends Error {\n public override readonly name: string;\n\n constructor (\n snapshot: ScopeNotDefinedStateSnapshot\n ) {\n super(\n snapshot.isDefaultScope\n ? `The default scope does not exists. Please check if the given scope is provided.`\n : `Scope does not exists. Please check if the given scope is provided.\\n`\n + `State: ${ JSON.stringify(snapshot) }`\n );\n this.name = \"ScopeNotDefinedError\";\n }\n}\n","import { LocaleConfig } from \"../public-api\";\n\n/**\n * Snapshot of the {@link LocaleNotDefinedError} properties for serialization purposes.\n * @category Types\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\nexport type LocaleNotDefinedStateSnapshot = {\n /**\n * {@link LocaleConfig}, that is not defined\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n locale: LocaleConfig;\n}\n\n/**\n * Error thrown when a requested {@link LocaleConfig} is not defined\n * @category Errors\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport class LocaleNotDefinedError extends Error {\n public override readonly name: string;\n\n constructor (locale: LocaleConfig) {\n super(`Locale \"${ locale.value }\" does not exists. Please check if the locale is provided in the translation config.`);\n this.name = \"LocaleNotDefinedError\";\n }\n}\n\n","import { LocaleConfig } from \"../public-api\";\n\n/**\n * Snapshot of the translation that is not defined\n * @category Types\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\nexport type TranslationNotDefinedStateSnapshot = {\n /**\n * Token for which the translation is not defined in the translations\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n token: string;\n /**\n * Value of the translation that is not defined in the translations\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n value?: string;\n /**\n * Scope of the translation that is not defined in the translations\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n scope?: ReadonlyArray<string | null> | string | null;\n /**\n * {@link LocaleConfig} of the translation that is not defined in the translations\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n locale?: LocaleConfig;\n}\n\n/**\n * Error thrown when a translation for a given token is not defined\n * @category Errors\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport class TranslationNotDefinedError extends Error {\n public override name: string;\n\n constructor (\n snapshot: TranslationNotDefinedStateSnapshot\n ) {\n super(`Translation does not exists.\n Please check if the translation for the given token is provided in the translations.\n \\n\n State: ${ JSON.stringify(snapshot) }`);\n this.name = \"TranslationNotDefinedError\";\n }\n}\n","import { LocaleConfig } from \"../public-api\";\n\n/**\n * Snapshot of the {@link SourceLocaleNotDefinedError} properties for serialization purposes\n * @category Types\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\nexport type SourceLocaleNotDefinedStateSnapshot = {\n /**\n * {@link LocaleConfig} of the source locale that is not defined in the translation configuration\n *\n * @since 2.0.0\n * @author Simon Kovtyk\n */\n locale?: LocaleConfig;\n}\n\n/**\n * Error thrown when a {@link LocaleConfig} as source locale is not defined\n * @category Errors\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport class SourceLocaleNotDefinedError extends Error {\n public override readonly name: string;\n\n constructor (\n snapshot?: SourceLocaleNotDefinedStateSnapshot\n ) {\n super(\n `A source locale does not exists. Please check if a source locale is provided in the translation config.\\n`\n + `State: ${ JSON.stringify(snapshot) }`\n );\n this.name = \"SourceLocaleNotDefinedError\";\n }\n}\n\n","import { InjectionToken } from \"@angular/core\";\nimport { SpecificTranslateConfig } from \"../types/config.type\";\n\n/**\n * Injection token for {@link SpecificTranslateConfig}\n * @readonly\n * @category NG config\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport const TRANSLATION_CONFIG_TOKEN: InjectionToken<SpecificTranslateConfig> = new InjectionToken<SpecificTranslateConfig>(\"translation-config-token\");\n","/* eslint-disable-next-line @tseslint/no-shadow */\nimport { BehaviorSubject, Observable, Subject, distinctUntilChanged } from \"rxjs\";\nimport { Injectable, inject } from \"@angular/core\";\nimport { LoadedScope, LoadedScopes, LocaleLoadedScopes, NotifierScope, NotifierScopes, ScopedFile } from \"../types/store.type\";\nimport { LocaleConfig, SpecificTranslateConfig } from \"../types/config.type\";\nimport { CollectingStrategy } from \"../enums/collecting-strategy.enum\";\nimport { LocaleNotDefinedError } from \"../errors/locale-not-defined.error\";\nimport { SourceLocaleNotDefinedError } from \"../errors/source-locale-not-defined.error\";\nimport { TRANSLATION_CONFIG_TOKEN } from \"../tokens/config.token\";\n\ntype StorageAttributes = {\n collectingStrategy: CollectingStrategy;\n locale: {\n type: Storage;\n key: string;\n };\n translations: {\n type: Storage;\n key: string;\n };\n};\n\ntype StoreTranslations = LoadedScopes | LocaleLoadedScopes;\n\n/**\n * Core service as abstraction layer for translaton storage and state management\n * @category NG services\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n@Injectable({\n providedIn: \"root\"\n})\nexport class TranslationStoreService {\n private _loadedScopes: LoadedScopes | LocaleLoadedScopes | null;\n\n private _notifierScopes: NotifierScopes | null = null;\n\n private readonly _translationConfig: SpecificTranslateConfig = inject(TRANSLATION_CONFIG_TOKEN);\n\n private readonly _locale: BehaviorSubject<LocaleConfig>;\n\n private readonly _locale$: Observable<LocaleConfig>;\n\n private readonly _storageAttributes: StorageAttributes = {\n collectingStrategy: this._translationConfig.storageConfig?.collectingStrategy ?? CollectingStrategy.CURRENT,\n locale: {\n type: this._translationConfig.storageConfig?.locale?.type ?? window.localStorage,\n key: this._translationConfig.storageConfig?.locale?.key ?? \"locale\"\n },\n translations: {\n type: this._translationConfig.storageConfig?.translations?.type ?? window.sessionStorage,\n key: this._translationConfig.storageConfig?.translations?.key ?? \"translations\"\n }\n };\n\n constructor () {\n const sourceLocale: LocaleConfig | undefined = this.getSourceLocale();\n\n if (sourceLocale === undefined)\n throw new SourceLocaleNotDefinedError();\n\n if (this._translationConfig.storageConfig?.locale?.store) {\n const localeStorageValue: LocaleConfig | null = this._getStorageValue(\"locale\") as LocaleConfig | null;\n const localeExists: boolean = localeStorageValue ? this._checkIfLocaleExists(localeStorageValue) : false;\n const isSourceLocale: boolean = localeStorageValue ? this._checkIfLocaleIsSourceLocale(localeStorageValue) : false;\n\n if (!localeExists || isSourceLocale)\n this._setStorageValue(\"locale\", null);\n\n this._locale = new BehaviorSubject<LocaleConfig>(localeStorageValue ?? sourceLocale);\n } else\n this._locale = new BehaviorSubject<LocaleConfig>(sourceLocale);\n\n if (this._translationConfig.storageConfig?.translations?.store) {\n const translationsStorageValue: StoreTranslations | null = this._getStorageValue(\"translations\") as StoreTranslations | null;\n\n this._loadedScopes = translationsStorageValue ?? null;\n } else\n this._loadedScopes = null;\n\n this._locale$ = this._locale.asObservable();\n }\n\n /**\n * Get the current {@link LocaleConfig}\n *\n * @returns The current {@link LocaleConfig}\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public getCurrentLocale (): LocaleConfig {\n return this._locale.value;\n }\n\n /**\n * Get an `Observable` of the current {@link LocaleConfig}\n *\n * @returns An `Observable` of the current {@link LocaleConfig}\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public getLocale$ (): Observable<LocaleConfig> {\n return this._locale$.pipe(distinctUntilChanged());\n }\n\n /**\n * Set the current {@link LocaleConfig}\n *\n * @param locale - The locale to set\n * @throws {@link LocaleNotDefinedError} if the provided locale is not defined\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public setLocale (locale: LocaleConfig): void {\n if (!this._checkIfLocaleExists(locale))\n throw new LocaleNotDefinedError(locale);\n\n if (this._translationConfig.storageConfig?.locale?.store) {\n this._checkIfLocaleIsSourceLocale(locale)\n ? this._setStorageValue(\"locale\", null)\n : this._setStorageValue(\"locale\", locale);\n }\n\n if (this._translationConfig.storageConfig?.translations?.store && this._translationConfig.storageConfig.collectingStrategy === CollectingStrategy.CURRENT) this._setStorageValue(\"translations\", null);\n\n this._locale.next(locale);\n }\n\n /**\n * Get all defined locales\n *\n * @returns An `Array` of all defined locales\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public getLocales (): LocaleConfig[] {\n return this._translationConfig.locales;\n }\n\n /**\n * Set a scoped file\n *\n * @param scopeName - The name of the scope\n * @param file - The scoped file to set\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public setScopedFile (scopeName: string | null, file: ScopedFile): void {\n const existingScope: LoadedScope | undefined = scopeName ? this._getScopeByScopeName(scopeName) : undefined;\n\n if (existingScope === undefined)\n this._appendScope(scopeName, file);\n else\n existingScope.file = file;\n\n\n if (this._translationConfig.storageConfig?.translations?.store) this._setStorageValue(\"translations\", this._loadedScopes);\n }\n\n /**\n * Get a scoped file\n *\n * @param scopeName - The name of the scope, the {@link ScopedFile} belongs to\n * @returns if found {@link ScopedFile}, otherwise `undefined`\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public getScopedFile (scopeName: string | null): ScopedFile | undefined {\n const existingScope: LoadedScope | undefined = this._getScopeByScopeName(scopeName);\n\n return existingScope ? existingScope.file : undefined;\n }\n\n /**\n * Check if a scoped file exists\n *\n * @param scopeName - The name of the scope, the {@link ScopedFile} belongs to\n * @returns `true` if found, otherwise `false`\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public hasScopedFile (scopeName: string | null): boolean {\n const existingScope: LoadedScope | undefined = this._getScopeByScopeName(scopeName);\n\n return Boolean(existingScope);\n }\n\n /**\n * Check if scoped files exist\n *\n * @param scopeNames - An `Array` of scope names, the ScopedFiles belong to\n * @returns `true` if all could be found, otherwise `false`\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public hasScopedFiles (scopeNames: ReadonlyArray<string | null>): boolean {\n const existingScopes: LoadedScopes | undefined = this._getScopeByScopeNames(scopeNames);\n\n return Boolean(existingScopes);\n }\n\n /**\n * Check if a scope exists\n *\n * @param scopeName - The name of the scope\n * @returns `true` if found, otherwise `false`\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public isScopeExisting (scopeName: string | null): boolean {\n const existingScope: LoadedScope | undefined = this._getScopeByScopeName(scopeName);\n\n return existingScope !== undefined;\n }\n\n /**\n * Check which scopes exist\n *\n * @param scopeNames - An `Array` of scope names, that should be checked\n * @returns An `Array` of existing scope names\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public getExistingScopes (scopeNames: ReadonlyArray<string | null>): Array<string | null> {\n return scopeNames.filter((scopeName: string | null): boolean => this.isScopeExisting(scopeName));\n }\n\n /**\n * Check if scope name is in notifier scopes\n *\n * @param scopeName - The name of the scope\n * @returns `true` if found, otherwise `false`\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public isScopeNameInNotifierScopes (scopeName: string | null): boolean {\n return Boolean(this._notifierScopes?.find((notifierScope: Readonly<NotifierScope>): boolean => notifierScope.scope === scopeName));\n }\n\n /**\n * Check if scope names are in notifier scopes\n *\n * @param scopeNames - An `Array` of scope names, that should be checked\n * @returns `true` if all are found, otherwise `false`\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public areScopeNamesInNotifierScopes (scopeNames: ReadonlyArray<string | null>): boolean {\n return scopeNames.map((scopeName: string | null): boolean => this.isScopeNameInNotifierScopes(scopeName))\n .reduce((previous: boolean, current: boolean): boolean => previous && current);\n }\n\n /**\n * Check which scope names are in notifier scopes\n *\n * @param scopeNames - An `Array` of scope names, that should be checked\n * @returns An `Array` of existing scope names\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public getExistingNotifierScopes (scopeNames: ReadonlyArray<string | null>): Array<string | null> {\n return scopeNames.filter((scopeName: string | null): boolean => this.isScopeNameInNotifierScopes(scopeName));\n }\n\n /**\n * Get {@link NotifierScope} by scope name\n *\n * @param scopeName - The name of the scope\n * @returns if found {@link NotifierScope}, otherwise `undefined`\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public getNotiferScope (scopeName: string | null): NotifierScope | undefined {\n return this._notifierScopes?.find((notiferScope: NotifierScope): boolean => notiferScope.scope === scopeName);\n }\n\n /**\n * Get {@link NotifierScopes} by scope names\n *\n * @param scopeNames - An `Array` of scope names\n * @returns if found {@link NotifierScopes}, otherwise `undefined`\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public getNotifierScopes (scopeNames: ReadonlyArray<string | null>): NotifierScopes | undefined {\n const filteredScopes: NotifierScopes | [] = scopeNames.map((scopeName: string | null): NotifierScope | undefined => this.getNotiferScope(scopeName))\n .filter((notifierScope: Readonly<NotifierScope> | undefined): boolean => notifierScope !== undefined) as NotifierScopes | [];\n\n return filteredScopes.length === 0 ? filteredScopes : undefined;\n }\n\n /**\n * Add a notifier scope\n *\n * @param notifierScope - The notifier scope to add\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public addNotifierScope (notifierScope: Readonly<NotifierScope>): void {\n this._notifierScopes === null\n ? this._notifierScopes = [ notifierScope ]\n : this._notifierScopes.push(notifierScope);\n }\n\n /**\n * Add multiple notifier scopes\n *\n * @param notifierScopes - The notifier scopes to add\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public addNotifierScopes (notifierScopes: NotifierScopes): void {\n this._notifierScopes === null\n ? this._notifierScopes = notifierScopes\n : this._notifierScopes.push(...notifierScopes);\n }\n\n /**\n * Add a notifier to a notifier scope\n *\n * @param scopeName - The name of the scope\n * @param notifier - The notifier to add\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public addNotifierToNotifierScope (scopeName: string | null, notifier: Subject<ScopedFile>): void {\n this._notifierScopes = this._notifierScopes?.map((notifierScope: NotifierScope): NotifierScope => {\n if (notifierScope.scope !== scopeName) return notifierScope;\n\n notifierScope.notifiers === null\n ? notifierScope.notifiers = [ notifier ]\n : notifierScope.notifiers.push(notifier);\n\n return notifierScope;\n }) ?? null;\n }\n\n /**\n * Remove a notifier scope\n *\n * @param scopeName - The name of the scope\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public removeNotifierScope (scopeName: string | null): void {\n if (this._notifierScopes === null)\n return;\n\n this._notifierScopes = this._notifierScopes.filter((notifierScope: Readonly<NotifierScope>): boolean => notifierScope.scope !== scopeName);\n }\n\n /**\n * Get the source {@link LocaleConfig}\n *\n * @returns if defined {@link LocaleConfig}, otherwise `undefined`\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public getSourceLocale (): LocaleConfig | undefined {\n return this._translationConfig.locales.find((localeConfig: LocaleConfig) => localeConfig.isSource);\n }\n\n private _checkIfLocaleExists (locale: LocaleConfig): boolean {\n return this._translationConfig.locales.some((configLocale: LocaleConfig): boolean => configLocale.value === locale.value);\n }\n\n private _checkIfLocaleIsSourceLocale (locale: LocaleConfig): boolean {\n return this._translationConfig.locales.some((localeConfig: LocaleConfig) => localeConfig.value === locale.value && localeConfig.isSource);\n }\n\n private _setStorageValue (dataType: \"locale\" | \"translations\", storageValue: unknown): void {\n storageValue === null\n ? this._storageAttributes[ dataType ].type.removeItem(this._storageAttributes[ dataType ].key)\n : this._storageAttributes[ dataType ].type.setItem(this._storageAttributes[ dataType ].key, JSON.stringify(storageValue));\n }\n\n private _getStorageValue (dataType: \"locale\" | \"translations\"): unknown {\n const serializedStorageValue: string | null = this._storageAttributes[ dataType ].type.getItem(this._storageAttributes[ dataType ].key);\n\n return serializedStorageValue ? JSON.parse(serializedStorageValue) : null;\n }\n\n private _getScopeByScopeName (scopeName: string | null): LoadedScope | undefined {\n if (this._storageAttributes.collectingStrategy === CollectingStrategy.ALL) {\n const localeLoadedScopes: LocaleLoadedScopes | null = this._loadedScopes as LocaleLoadedScopes;\n\n if (this._loadedScopes === null) return undefined;\n\n const _loadedScopes: LoadedScopes | undefined = localeLoadedScopes[ this._locale.value.value ];\n\n return _loadedScopes?.find((loadedScope: LoadedScope): boolean => loadedScope.scope === scopeName);\n }\n\n const loadedScopes: LoadedScopes | null = this._loadedScopes as LoadedScopes | null;\n\n return loadedScopes?.find((loadedScope: LoadedScope): boolean => loadedScope.scope === scopeName);\n }\n\n private _getScopeByScopeNames (scopeNames: ReadonlyArray<string | null>): LoadedScopes | undefined {\n if (this._storageAttributes.collectingStrategy === CollectingStrategy.ALL) {\n const localeLoadedScopes: LocaleLoadedScopes | null = this._loadedScopes as LocaleLoadedScopes;\n\n if (this._loadedScopes === null) return undefined;\n\n const _loadedScopes: LoadedScopes | undefined = localeLoadedScopes[ this._locale.value.value ];\n\n return _loadedScopes?.filter((loadedScope: LoadedScope): boolean => scopeNames.includes(loadedScope.scope));\n }\n\n const loadedScopes: LoadedScopes = this._loadedScopes as LoadedScopes;\n\n return loadedScopes.filter((loadedScope: LoadedScope): boolean => scopeNames.includes(loadedScope.scope));\n }\n\n private _appendScope (scopeName: string | null, file: ScopedFile): void {\n if (this._storageAttributes.collectingStrategy === CollectingStrategy.ALL) {\n const localeLoadedScopes: LocaleLoadedScopes | null = this._loadedScopes as LocaleLoadedScopes | null;\n\n if (localeLoadedScopes === null) {\n this._loadedScopes = { [ this._locale.value.value ]: [ { scope: scopeName, file } ] };\n\n return;\n }\n\n const loadedScope: LoadedScopes | undefined = localeLoadedScopes[ this._locale.value.value ];\n\n if (loadedScope === undefined) {\n localeLoadedScopes[ this._locale.value.value ] = [ { scope: scopeName, file } ];\n\n return;\n }\n\n loadedScope.push({ scope: scopeName, file });\n\n return;\n }\n\n this._loadedScopes === null\n ? this._loadedScopes = [ { scope: scopeName, file } ]\n : (this._loadedScopes as LoadedScopes).push({ scope: scopeName, file });\n }\n}\n","import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from \"@angular/common/http\";\nimport { Injectable, inject } from \"@angular/core\";\nimport { LocaleConfig } from \"../types/config.type\";\n/* eslint-disable-next-line @tseslint/no-shadow */\nimport { Observable } from \"rxjs\";\nimport { TranslationStoreService } from \"../services/translation-store.service\";\n\n/**\n * Interceptor to add the current locale language header to HTTP requests.\n * @remarks Adds a `language` header with `value` of the current {@link LocaleConfig} to each outgoing HTTP request.\n * @category NG interceptors\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n@Injectable({\n providedIn: \"root\"\n})\nexport class TranslationInterceptor implements HttpInterceptor {\n private readonly _translationStoreService: TranslationStoreService = inject(TranslationStoreService);\n\n public intercept (httpRequest: Readonly<HttpRequest<unknown>>, httpHandler: Readonly<HttpHandler>): Observable<HttpEvent<unknown>> {\n const currentLocale: LocaleConfig = this._translationStoreService.getCurrentLocale();\n const clonedHttpRequest: HttpRequest<unknown> = httpRequest.clone({\n setHeaders: {\n language: currentLocale.value\n }\n });\n\n return httpHandler.handle(clonedHttpRequest);\n }\n}\n","import { InjectionToken } from \"@angular/core\";\n\n/**\n * Injection token that holds a translation scope\n * @readonly\n * @category NG config\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport const TRANSLATION_SCOPE_TOKEN: InjectionToken<string> = new InjectionToken<string>(\"translation-scope-token\");\n","import { MultiScopedFile, ParsedMultiScopedFiles, ScopedFile } from \"../types/store.type\";\nimport { ScopeNotDefinedError } from \"../errors/scope-not-defined.error\";\nimport { TranslationNotDefinedError } from \"../errors/translation-not-defined.error\";\n\n/**\n * Parse a multi scoped file by the defined scopes\n * @param multiScopedFile - The multi scoped file, that should be parsed\n * @returns A partial scoped file for processing it further\n */\nexport const parseMultiScopedFile = (multiScopedFile: MultiScopedFile): ParsedMultiScopedFiles => {\n const splittedMultiScopedFile: ParsedMultiScopedFiles = [];\n const globalScopedFile: Record<string, string> = {};\n\n Object.keys(multiScopedFile).forEach((key: string): void => {\n const currentItem: ScopedFile | undefined = multiScopedFile[ key ];\n\n if (!(currentItem !== undefined && !Array.isArray(currentItem)))\n return;\n\n if (typeof currentItem === \"object\") {\n return void splittedMultiScopedFile.push({\n scopeName: key,\n file: currentItem\n });\n }\n\n globalScopedFile[ key ] = currentItem;\n });\n splittedMultiScopedFile.push({\n scopeName: null,\n file: globalScopedFile\n });\n\n return splittedMultiScopedFile;\n};\nexport const splitMultiScopedFile = (multiScopedFile: MultiScopedFile): ScopedFile[] => {\n const scopedFile: ScopedFile[] = [];\n const globalScopedFile: Record<string, string> = {};\n\n Object.keys(multiScopedFile).forEach((key: string): void => {\n const currentItem: Readonly<Record<string, string>> | undefined = multiScopedFile[ key ];\n\n if (!(currentItem !== undefined && !Array.isArray(currentItem)))\n return;\n\n if (typeof currentItem === \"object\")\n return void scopedFile.push(currentItem);\n\n globalScopedFile[ key ] = currentItem;\n });\n scopedFile.push(globalScopedFile);\n\n return scopedFile;\n};\n/**\n * Find a scope inside a multi scoped file\\\n * Throws an error if the scope could not be resolved out of the multi scoped file\n * @param multiScopedFile - The multi scoped file, that should include the scope\n * @param scopeName - The scope name, that'll be searched\n * @returns The found scoped file\n * @throws {@link ScopeNotDefinedError} When the scope could not be found in the multi scoped file\n */\nexport const findScopeInMultiScopedFile = (multiScopedFile: MultiScopedFile, scopeName: string | null): ScopedFile => {\n // Filter all not-nested elements to represent the global scope.\n if (scopeName === null) {\n const newScopedFile: Record<string, string> = {};\n\n Object.keys(multiScopedFile).forEach((key: string): void => {\n const currentItem: Readonly<Record<string, string>> | undefined = multiScopedFile[ key ];\n\n if (!(currentItem !== undefined && typeof currentItem !== \"object\" && !Array.isArray(currentItem)))\n return;\n\n newScopedFile[ key ] = currentItem;\n });\n\n return newScopedFile;\n }\n\n // Get the specific scoped file\n const scopedFile: ScopedFile | undefined = multiScopedFile[ scopeName ];\n\n if (scopedFile === undefined) {\n throw new ScopeNotDefinedError({\n isDefaultScope: false,\n scope: scopeName\n });\n }\n\n return scopedFile;\n};\n/**\n * Parse a multi scoped file\n * @param multiScopedFile - The multi scoped file, that'll be parsed\n * @param scopeName - The scope name, that'll be resolved\n * @returns All scoped files, that match the scope name\n */\nexport const parseMultiScopedFileByScope = (multiScopedFile: MultiScopedFile, scopeName: string[] | string): ScopedFile[] => {\n if (Array.isArray(scopeName))\n return scopeName.map((_scopeName: string): ScopedFile => findScopeInMultiScopedFile(multiScopedFile, _scopeName));\n\n\n return [ findScopeInMultiScopedFile(multiScopedFile, scopeName) ];\n};\n/**\n * Find a token in the provided scoped file\n * @param scopedFiles - The scoped file, that should include the token\n * @param token - The token, that'll be searched\n * @returns The scoped file with the found token\n * @throws {@link TranslationNotDefinedError} When the token could not be found in the scoped file\n */\nexport const findTokenInScopedFiles = (scopedFiles: readonly ScopedFile[], token: string): ScopedFile => {\n const foundScopedFile: ScopedFile | undefined = scopedFiles.find((scopedFile: ScopedFile): boolean => Object.keys(scopedFile).includes(token));\n\n if (foundScopedFile === undefined) {\n throw new TranslationNotDefinedError({\n token\n });\n }\n\n return foundScopedFile;\n};\n\n","import { SpecificTranslateConfig } from \"../public-api\";\nimport { MultiScopedFile, ScopedFile } from \"../types/store.type\";\nimport { findScopeInMultiScopedFile } from \"./file.util\";\n\n/**\n * Translate token by a scoped file\n * @param scopedFile - The scoped file, that should include the translation\n * @param token - The token, that'll be translated\n * @returns A `string`, that represents the translated token. If no translation was found `undefined`.\n */\nexport const translateTokenByScopedFile = (scopedFile: ScopedFile, token: string): string | undefined => {\n const isTokenValid: boolean = Object.keys(scopedFile).includes(token);\n\n if (!isTokenValid) return;\n\n return scopedFile[ token ];\n};\nexport const translateTokenByScopedFiles = (scopedFiles: ScopedFile[], token: string): string | undefined => {\n let translation: string | undefined;\n\n scopedFiles.some((scopedFile: ScopedFile): boolean => {\n const isTokenValid: boolean = Object.keys(scopedFile).includes(token);\n\n if (!isTokenValid) return false;\n\n translation = scopedFile[ token ];\n\n return true;\n });\n\n return translation;\n};\n/**\n * Translate token by a multi scoped file by first resolving the scope out of the multi scoped file\n * @param multiScopedFile - The MultiScopedFile, that should includes the scope to search and the token\n * @param scopeName - The scope name which should include the translation for the token\n * @param token - The token, that'll be translated\n * @returns A `string`, that represents the translated token. If no translation was found `undefined`.\n */\nexport const translateTokenByMultiScopedFile = (multiScopedFile: MultiScopedFile, scopeName: string | null, token: string): string | undefined => {\n const scopedFile: ScopedFile = findScopeInMultiScopedFile(multiScopedFile, scopeName);\n\n return translateTokenByScopedFile(scopedFile, token);\n};\nexport function resolveScope<T extends ReadonlyArray<string | null> | string | null> (translationConfig: SpecificTranslateConfig | null, scopeName?: T): T {\n return (scopeName ?? translationConfig?.defaultScope ?? null) as T;\n}\n\n","import { InjectionToken } from \"@angular/core\";\nimport { HttpHeadersOption, HttpOptions } from \"@ogs-gmbh/ngx-http\";\n\n/**\n * Injection token for HTTP configuration\n * @readonly\n * @category NG config\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport const TRANSLATION_HTTP_CONFIG: InjectionToken<string> = new InjectionToken<string>(\"translation-http-config\");\n/**\n * Injection token for additional HTTP configuration\n * @readonly\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\nexport const TRANSLATION_HTTP_OPTIONS: InjectionToken<HttpOptions<never, HttpHeadersOption, never>> = new InjectionToken<HttpOptions<never, HttpHeadersOption, never>>(\"translation-http-options\");\n","import { HttpHeadersOption, HttpOptions, mergeHttpHeaders } from \"@ogs-gmbh/ngx-http\";\nimport { HttpClient, HttpHeaders } from \"@angular/common/http\";\nimport { Injectable, inject } from \"@angular/core\";\n/* eslint-disable-next-line @tseslint/no-shadow */\nimport { Observable, retry, timeout } from \"rxjs\";\nimport { SpecificTranslateConfig } from \"../types/config.type\";\nimport { TRANSLATION_CONFIG_TOKEN } from \"../tokens/config.token\";\nimport { TRANSLATION_HTTP_CONFIG, TRANSLATION_HTTP_OPTIONS } from \"../tokens/http.token\";\n\n/**\n * Core service as abstraction layer for HTTP handling\n * @category NG services\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n@Injectable({\n providedIn: \"root\"\n})\nexport class TranslationHttpService {\n private readonly _translationHttpConfig: string = inject(TRANSLATION_HTTP_CONFIG);\n\n private readonly _translationConfig: SpecificTranslateConfig | null = inject(TRANSLATION_CONFIG_TOKEN, { optional: true });\n\n private readonly _translationHttpOptions: HttpOptions<never, HttpHeadersOption, never> | null = inject(TRANSLATION_HTTP_OPTIONS, { optional: true });\n\n /**\n * Gets translations with the provided `HttpClient` reference.\n *\n * @param httpClientRef - The `HttpClient` reference to use for the request.\n * @param scopeName - The scope name(s) for the translations.\n * @param httpOptions - Optional `HttpOptions` to customize the request.\n * @returns An `Observable` of the requested translations.\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public getWithRef$<T>(\n httpClientRef: Readonly<HttpClient>,\n scopeName: ReadonlyArray<string | null> | string | null,\n httpOptions?: HttpOptions<never, HttpHeadersOption, never>\n ): Observable<T> {\n let path: string = this._translationHttpConfig;\n\n if (typeof scopeName === \"string\" || scopeName === null) {\n if (scopeName !== null)\n path += `?scope=${ encodeURIComponent(scopeName) }`;\n } else {\n const urlSearchParams: URLSearchParams = new URLSearchParams();\n\n scopeName.forEach((scopeNameItem: string | null): void => {\n if (scopeNameItem === null) return;\n\n urlSearchParams.append(\"scope\", encodeURIComponent(scopeNameItem));\n });\n path += `?${ urlSearchParams.toString() }`;\n }\n\n let headers: HttpHeaders = new HttpHeaders();\n\n if (this._translationHttpOptions?.headers !== undefined)\n headers = mergeHttpHeaders(headers, this._translationHttpOptions.headers);\n\n if (httpOptions?.headers !== undefined)\n headers = mergeHttpHeaders(headers, httpOptions.headers);\n\n return httpClientRef.get<T>(path, {\n responseType: \"json\",\n observe: \"body\",\n headers\n }).pipe(\n timeout(this._translationConfig?.timeout ?? 3000),\n retry(0)\n );\n }\n}\n","import { HttpRequestStatus, HttpOptions, HttpHeadersOption } from \"@ogs-gmbh/ngx-http\";\n/* eslint-disable-next-line @tseslint/no-shadow */\nimport { BehaviorSubject, EMPTY, Observable, Subject, catchError, combineLatestWith, distinctUntilChanged, filter, map, of, switchMap, tap } from \"rxjs\";\nimport { Injectable, inject } from \"@angular/core\";\nimport { LocaleConfig, SpecificTranslateConfig } from \"../types/config.type\";\nimport { MultiScopedFile, NotifierScope, ParsedMultiScopedFile, ParsedMultiScopedFiles, ScopedFile } from \"../types/store.type\";\nimport { findScopeInMultiScopedFile, findTokenInScopedFiles, parseMultiScopedFile, splitMultiScopedFile } from \"../utils/file.util\";\nimport { resolveScope, translateTokenByScopedFile, translateTokenByScopedFiles } from \"../utils/translate.util\";\nimport { HttpClient } from \"@angular/common/http\";\nimport { TRANSLATION_CONFIG_TOKEN } from \"../tokens/config.token\";\nimport { TranslationHttpService } from \"./translation-http.serivce\";\nimport { TranslationNotDefinedError } from \"../errors/translation-not-defined.error\";\nimport { TranslationStoreService } from \"./translation-store.service\";\n\n/**\n * Core service for translation handling\n * @category NG services\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n@Injectable({\n providedIn: \"root\"\n})\nexport class TranslationService {\n private readonly _httpClient: HttpClient = inject(HttpClient, { host: true });\n\n private readonly _translationConfig: SpecificTranslateConfig = inject(TRANSLATION_CONFIG_TOKEN);\n\n private readonly _translationStoreService: TranslationStoreService = inject(TranslationStoreService);\n\n private readonly _translationHttpService: TranslationHttpService = inject(TranslationHttpService);\n\n private readonly _isHttpLoading: BehaviorSubject<HttpRequestStatus | null> = new BehaviorSubject<HttpRequestStatus | null>(null);\n\n private readonly _isHttpLoading$: Observable<HttpRequestStatus | null> = this._isHttpLoading.asObservable();\n\n /**\n * An `Observable`, that will emit only when a HTTP-Request is made\n * @returns An `Observable` with the `HttpRequestStatus` if HTTP is currently under use. Otherwise an `Observable` with null inside.\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public isHttpLoading$ (): Observable<HttpRequestStatus | null> {\n return this._isHttpLoading$.pipe(\n distinctUntilChanged(),\n filter((isHttpLoading: HttpRequestStatus | null): boolean => isHttpLoading !== null)\n );\n }\n\n /**\n * Preload multiple translations with references\n * @param httpClient - The `HttpClient`, that'll be used\n * @param translationStoreService - The {@link TranslationStoreService}, that'll be used\n * @param translationHttpService - The {@link TranslationHttpService}, that'll be used\n * @param _locale - The {@link LocaleConfig}, that should be preloaded\n * @param scopeName - The scope name for the lookup of the translation\n * @param httpOptions - Additional HTTP Options for the request\n * @returns An `Observable` to handle the status\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n /* eslint-disable-next-line @tseslint/class-methods-use-this, @tseslint/max-params */\n public _preloadWithRef$ (\n httpClient: Readonly<HttpClient>,\n translationStoreService: Readonly<TranslationStoreService>,\n translationHttpService: Readonly<TranslationHttpService>,\n _locale: LocaleConfig,\n scopeName: ReadonlyArray<string | null> | string | null,\n httpOptions?: HttpOptions<never, HttpHeadersOption, never>\n ): Observable<void> {\n /*\n * Handle resolve by store\n * Check if all scopes are in store\n */\n if ((typeof scopeName === \"string\" || scopeName === null) && translationStoreService.hasScopedFile(scopeName))\n return EMPTY;\n\n if (Array.isArray(scopeName) && translationStoreService.hasScopedFiles(scopeName))\n return EMPTY;\n\n // Check if scopes are in notifer scopes\n if ((typeof scopeName === \"string\" || scopeName === null) && translationStoreService.isScopeNameInNotifierScopes(scopeName))\n return EMPTY;\n\n if (Array.isArray(scopeName) && translationStoreService.areScopeNamesInNotifierScopes(scopeName as ReadonlyArray<string | null>))\n return EMPTY;\n\n // Check if scopes are not in store\n if (typeof scopeName === \"string\" || scopeName === null) {\n return translationHttpService.getWithRef$<ScopedFile>(\n httpClient,\n scopeName,\n httpOptions\n ).pipe(\n tap((scopedFile: ScopedFile): void => {\n translationStoreService.setScopedFile(scopeName, scopedFile);\n }),\n map((): void => void 0)\n );\n } else {\n const existingScopes: Array<string | null> = translationStoreService.getExistingScopes(scopeName);\n\n if (existingScopes.length === scopeName.length)\n return EMPTY;\n\n const extinctScopes: Array<string | null> = scopeName.filter((_scopeName: string | null): boolean => !existingScopes.includes(_scopeName));\n\n if (extinctScopes.length === 0)\n return EMPTY;\n\n return translationHttpService.getWithRef$<MultiScopedFile>(\n httpClient,\n extinctScopes,\n httpOptions\n ).pipe(\n tap((multiScopedFile: MultiScopedFile): void => {\n const parsedMultiScopedFiles: ParsedMultiScopedFiles = parseMultiScopedFile(multiScopedFile);\n\n parsedMultiScopedFiles.forEach((parsedScopedFile: ParsedMultiScopedFile): void => {\n translationStoreService.setScopedFile(parsedScopedFile.scopeName, parsedScopedFile.file);\n });\n }),\n map((): void => void 0)\n );\n }\n }\n\n /**\n * Translates a token by the locale reactive\n * @param token - The token to resolve the translation\n * @param value - The default value of the translation\n * @param scopeName - A scope name to resolve the lookup\n * @param httpOptions - Additional HTTP Options for the request\n * @returns An `Observable` with the current translation as `string`\n *\n * @remarks\n * If the {@link LocaleConfig}, a new translation based on the new locale will be emitted.\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public translateTokenByLocale$ (\n token: string,\n value: string,\n scopeName?: ReadonlyArray<string | null> | string | null,\n httpOptions?: HttpOptions<never, HttpHeadersOption, never>\n ): Observable<string> {\n return this._translationStoreService.getLocale$().pipe(switchMap((locale: LocaleConfig): Observable<string> => this._translateWithRef$(\n this._httpClient,\n this._translationStoreService,\n this._translationHttpService,\n locale,\n token,\n value,\n this._resolveScope(scopeName),\n httpOptions\n )));\n }\n\n /**\n * Translates a token by the current locale\n * @param token - The token to resolve the translation\n * @param value - The default value of the translation\n * @param scopeName - A scope name to resolve the lookup\n * @param httpOptions - Additional HTTP Options for the request\n * @returns An `Observable` with the current translation as `string`\n *\n * @remarks\n * If the {@link LocaleConfig}, no new translation will be emitted.\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n public translateTokenByCurrentLocale$ (\n token: string,\n value: string,\n scopeName?: ReadonlyArray<string | null> | string | null,\n httpOptions?: HttpOptions<never, HttpHeadersOption, never>\n ): Observable<string> {\n const currentLocale: LocaleConfig = this._translationStoreService.getCurrentLocale();\n\n return this._translateWithRef$(\n this._httpClient,\n this._translationStoreService,\n this._translationHttpService,\n currentLocale,\n token,\n value,\n scopeName,\n httpOptions\n );\n }\n\n /**\n * Preload translations by locale\n * @param httpClient - The `HttpClient`, that'll be used\n * @param translationStoreService - The {@link TranslationStoreService}, that'll be used\n * @param translationHttpService - The {@link TranslationHttpService}, that'll be used\n * @param scopeName - The scope name for the lookup of the translation\n * @param httpOptions - Additional HTTP Options for the request\n * @returns An `Observable` to handle the status\n *\n * @remarks\n * If the {@link LocaleConfig} changes, the scope will be preloaded again.\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n /* eslint-disable-next-line @tseslint/max-params */\n public preloadByLocale (\n httpClient: Readonly<HttpClient>,\n translationStoreService: Readonly<TranslationStoreService>,\n translationHttpService: Readonly<TranslationHttpService>,\n scopeName: string | null | ReadonlyArray<string | null>,\n httpOptions?: HttpOptions<never, HttpHeadersOption, never>\n ): Observable<void> {\n return translationStoreService.getLocale$().pipe(switchMap((locale: LocaleConfig): Observable<void> => this._preloadWithRef$(\n httpClient,\n translationStoreService,\n translationHttpService,\n locale,\n scopeName,\n httpOptions\n )));\n }\n\n /**\n * Preload translations by locale\n * @param httpClient - The `HttpClient`, that'll be used\n * @param translationStoreService - The {@link TranslationStoreService}, that'll be used\n * @param translationHttpService - The {@link TranslationHttpService}, that'll be used\n * @param scopeName - The scope name for the lookup of the translation\n * @param httpOptions - Additional HTTP Options for the request\n * @returns An `Observable` to handle the status\n *\n * @remarks\n * If the {@link LocaleConfig} changes, no new preloading will be made.\n *\n * @since 1.0.0\n * @author Simon Kovtyk\n */\n /* eslint-disable-next-line @tseslint/max-params */\n public preloadByCurrentLocale (\n httpClient: Readonly<HttpClient>,\n translationStoreService: Readonly<TranslationStoreService>,\n translationHttpService: Readonly<TranslationHttpService>,\n scopeName: string | null | ReadonlyArray<string | null>,\n httpOptions?: HttpOptions<never, HttpHeadersOption, never>\n ): Observable<void> {\n const currentLocale: LocaleConfig = translationStoreService.getCurrentLocale();\n\n return this._preloadWithRef$(\n httpClient,\n translationStoreService,\n translationHttpService,\n currentLocale,\n scopeName,\n httpOptions\n );\n }\n\n private _resolveScope<T extends ReadonlyArray<string | null> | string | null>(scopeName?: T): T {\n return resolveScope(this._translationConfig, scopeName);\n }\n\n /* eslint-disable-next-line @tseslint/class-methods-use-this */\n private _translateBySourceLocale (token: string, value?: string): Observable<string> {\n if (!value) {\n throw new TranslationNotDefinedError({\n token\n });\n }\n\n return of(value);\n }\n\n private _translateByStore (scope: ReadonlyArray<string | null> | string | null, token: string): Observable<string> {\n if (typeof scope === \"string\" || scope === null) {\n /* eslint-disable-next-line @tseslint/no-non-null-assertion */\n const scopedFile: ScopedFile = this._translationStoreService.getScopedFile(scope)!;\n const _translatedToken: string | undefined = translateTokenByScopedFile(scopedFile, token);\n\n if (_translatedToken === undefined) {\n throw new TranslationNotDefinedError({\n token,\n scope\n });\n } \n\n return of(_translatedToken);\n }\n\n let translatedToken: string | undefined;\n\n scope.map((_scope: string | null): ScopedFile | undefined => this._translationStoreService.getScopedFile(_scope))\n .some((scopedFile: ScopedFile | undefined): boolean => {\n if (scopedFile === undefined) return false;\n\n const _translatedToken: string | undefined = translateTokenByScopedFile(scopedFile, token);\n\n if (_translatedToken === undefined) return false;\n\n translatedToken = _translatedToken;\n\n return true;\n });\n\n if (translatedToken === undefined) {\n throw new TranslationNotDefinedError({\n token,\n scope\n });\n };\n\n return of(translatedToken);\n }\n\n private _getScopedFileByNotifier (scope: string | null): Observable<ScopedFile> {\n const currentNotifier: Subject<ScopedFile> = new Subject<ScopedFile>();\n\n this._translationStoreService.addNotifierToNotifierScope(scope, currentNotifier);\n\n return currentNotifier.asObservable();\n }\n\n private _getScopedFileByAddingNotifie