UNPKG

@ngx-easy-i18n-js/core

Version:

The easy internationalization (i18n) library for Angular

1,124 lines (1,107 loc) 49.4 kB
import * as i0 from '@angular/core'; import { Injectable, InjectionToken, Inject, LOCALE_ID, Pipe, Input, Directive, DEFAULT_CURRENCY_CODE, ContentChildren, NgModule, provideAppInitializer, inject } from '@angular/core'; import { registerLocaleData, formatDate, formatNumber, formatPercent, getCurrencySymbol, formatCurrency } from '@angular/common'; import { of, defer, BehaviorSubject, ReplaySubject, switchMap, forkJoin, firstValueFrom, filter, merge } from 'rxjs'; import { installEasyI18n, setEasyI18nMessages, tr, plural } from 'easy-i18n-js'; import * as lodash from 'lodash'; import { tap, catchError, map } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; class EasyI18nLoader { } /** * This loader is just a placeholder that does nothing, in case you don't need a loader at all */ class EmptyEasyI18nLoader extends EasyI18nLoader { getMessages(locale) { return of({}); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EmptyEasyI18nLoader, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EmptyEasyI18nLoader }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EmptyEasyI18nLoader, decorators: [{ type: Injectable }] }); class EasyI18nStore { } /** * This store, get stored culture */ class EmptyEasyI18nStore extends EasyI18nStore { get() { return of(null); } save(locale) { return of(true); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EmptyEasyI18nStore, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EmptyEasyI18nStore }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EmptyEasyI18nStore, decorators: [{ type: Injectable }] }); /** * Use localStorage */ class LocalStorageEasyI18nStore extends EasyI18nStore { constructor(key) { super(); this.key = key; } get() { return defer(() => of(localStorage.getItem(this.key))); } save(locale) { return defer(() => { localStorage.setItem(this.key, locale); return of(true); }); } } const EASY_I18N_OPTIONS = new InjectionToken('EASY_I18N_OPTIONS'); const NG_LOCALES = new InjectionToken('NG_LOCALES'); const USE_BROWSER_LANGUAGE = new InjectionToken('USE_BROWSER_LANGUAGE'); const DEFAULT_LANGUAGE = new InjectionToken('DEFAULT_LANGUAGE'); const FALLBACK_LANGUAGE = new InjectionToken('FALLBACK_LANGUAGE'); const DISCOVER = new InjectionToken('DISCOVER'); const cultureRegex = new RegExp('^([a-z]{2,3})(?:-([A-Z]{2,3})(?:-([a-zA-Z]{4}))?)?$'); function getPossibleLocales(culture, discover) { if (!culture) { return []; } if (discover === 'exact') { return [culture]; } const match = culture.match(cultureRegex); if (discover === 'minimum') { return lodash.compact([match?.[1]]); } return lodash.compact(lodash.uniq([match?.[0], lodash.compact([match?.[1], match?.[2]]).join('-'), match?.[1]])); } class EasyI18nService { // Get status observable of loading locale get localeStatus$() { return this._localeStatusSubject.asObservable(); } // Get locale observable get locale$() { return this._localeSubject.asObservable(); } // Current locale get currentLocale() { return this._currentLocale; } // Current Angular locale get ngLocale() { return this._ngLocale; } constructor(options, ngLocales, useBrowserLanguage, defaultLanguage, fallbackLanguage, discover, loader, store, destroyRef) { this.options = options; this.ngLocales = ngLocales; this.useBrowserLanguage = useBrowserLanguage; this.defaultLanguage = defaultLanguage; this.fallbackLanguage = fallbackLanguage; this.discover = discover; this.loader = loader; this.store = store; this.destroyRef = destroyRef; this._localeStatusSubject = new BehaviorSubject('none'); this._localeSubject = new ReplaySubject(1); this._currentLocale = null; this._ngLocale = null; this.otherLoaders = []; this.currentMsg = {}; installEasyI18n(options); } async initialize() { this._localeSubject.asObservable().pipe(takeUntilDestroyed(this.destroyRef), tap(() => this._localeStatusSubject.next('loading')), switchMap(culture => { const ngLocale = getPossibleLocales(culture, 'all').find(l => this.ngLocales?.[l]); if (ngLocale != null) { registerLocaleData(this.ngLocales[ngLocale]); this._ngLocale = ngLocale; } else { console.warn(`Not found locale data for currentLocale ${culture}`); if (culture !== (this.fallbackLanguage ?? this.defaultLanguage)) { const defaultNgLocale = getPossibleLocales(this.fallbackLanguage ?? this.defaultLanguage, 'all').find(l => this.ngLocales?.[l]); if (defaultNgLocale != null) { console.warn(`Use locale data with ${this.fallbackLanguage ?? this.defaultLanguage}`); registerLocaleData(this.ngLocales[defaultNgLocale]); this._ngLocale = defaultNgLocale; } else { console.warn(`Not found locale data for defaultLanguage ${this.fallbackLanguage ?? this.defaultLanguage}`); this._ngLocale = null; } } } const locales = lodash.uniq([...getPossibleLocales(culture, this.discover), ...getPossibleLocales(this.fallbackLanguage ?? this.defaultLanguage, this.discover)]); return forkJoin(locales.flatMap(locale => [ this.loader.getMessages(locale).pipe(catchError(() => { return of({}); })), ...this.otherLoaders.map(l => l.getMessages(locale).pipe(catchError(() => { return of({}); }))) ])).pipe(map(res => lodash.defaultsDeep({}, ...lodash.compact(res))), tap(msg => { this.currentMsg = msg ?? {}; setEasyI18nMessages(this.currentMsg, culture); this._currentLocale = lodash.head(locales) ?? culture; })); }), tap(() => this._localeStatusSubject.next('ready'))).subscribe(); this.store.get().pipe(takeUntilDestroyed(this.destroyRef), tap(stored => { const culture = stored ?? (this.useBrowserLanguage ? this.getBrowserCulture() : null) ?? this.defaultLanguage; if (!culture || !cultureRegex.test(culture)) { console.error(`Culture ${culture} is wrong format`); return; } this._localeSubject.next(culture); })).subscribe(); return firstValueFrom(this._localeStatusSubject.asObservable().pipe(filter(s => s === 'ready'))); } /** * Get plain message by key * * @param key key */ getPlainMessage(key) { return lodash.get(this.currentMsg, key); } /** * Change current culture * * @param culture new culture * @param options */ registerCulture(culture, options) { if (!culture || !cultureRegex.test(culture)) { console.error(`Culture ${culture} is wrong format`); return; } this.store.save(culture).pipe(tap(() => { if (options?.reload) { location.reload(); } else { this._localeSubject.next(culture); } })).subscribe(); } /** * Get browser culture */ getBrowserCulture() { if (typeof window === 'undefined' || typeof window.navigator === 'undefined') { return null; } let browserCultureLang = window.navigator.languages ? window.navigator.languages[0] : null; return browserCultureLang ?? window.navigator.language ?? window.navigator.browserLanguage ?? window.navigator.userLanguage; } async appendLoader(loader) { this.otherLoaders.push(loader); return firstValueFrom(this._localeStatusSubject.asObservable().pipe(filter(s => s === 'ready'), switchMap(() => { const locales = lodash.uniq([...getPossibleLocales(this._currentLocale, this.discover), ...getPossibleLocales(this.fallbackLanguage ?? this.defaultLanguage, this.discover)]); return forkJoin(locales.map(locale => loader.getMessages(locale).pipe(catchError(() => { return of({}); })))).pipe(map(res => lodash.defaultsDeep(this.currentMsg, ...lodash.compact(res))), tap(msg => { setEasyI18nMessages(msg, this._currentLocale); }), map(() => true)); })), { defaultValue: false }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EasyI18nService, deps: [{ token: EASY_I18N_OPTIONS }, { token: NG_LOCALES }, { token: USE_BROWSER_LANGUAGE }, { token: DEFAULT_LANGUAGE }, { token: FALLBACK_LANGUAGE }, { token: DISCOVER }, { token: EasyI18nLoader }, { token: EasyI18nStore }, { token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EasyI18nService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EasyI18nService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [EASY_I18N_OPTIONS] }] }, { type: undefined, decorators: [{ type: Inject, args: [NG_LOCALES] }] }, { type: undefined, decorators: [{ type: Inject, args: [USE_BROWSER_LANGUAGE] }] }, { type: undefined, decorators: [{ type: Inject, args: [DEFAULT_LANGUAGE] }] }, { type: undefined, decorators: [{ type: Inject, args: [FALLBACK_LANGUAGE] }] }, { type: undefined, decorators: [{ type: Inject, args: [DISCOVER] }] }, { type: EasyI18nLoader }, { type: EasyI18nStore }, { type: i0.DestroyRef }] }); class LocaleDatePipe { constructor(easyI18nService, localeId) { this.easyI18nService = easyI18nService; this.localeId = localeId; } transform(value, format, timezone) { return formatDate(value, format, this.easyI18nService.ngLocale ?? this.localeId, timezone); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: LocaleDatePipe, deps: [{ token: EasyI18nService }, { token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.7", ngImport: i0, type: LocaleDatePipe, isStandalone: true, name: "localeDate" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: LocaleDatePipe, decorators: [{ type: Pipe, args: [{ name: 'localeDate', standalone: true }] }], ctorParameters: () => [{ type: EasyI18nService }, { type: undefined, decorators: [{ type: Inject, args: [LOCALE_ID] }] }] }); class LocaleNumberPipe { constructor(easyI18nService, localeId) { this.easyI18nService = easyI18nService; this.localeId = localeId; } transform(value, digitsInfo) { return formatNumber(value, this.easyI18nService.ngLocale ?? this.localeId, digitsInfo); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: LocaleNumberPipe, deps: [{ token: EasyI18nService }, { token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.7", ngImport: i0, type: LocaleNumberPipe, isStandalone: true, name: "localeNumber" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: LocaleNumberPipe, decorators: [{ type: Pipe, args: [{ name: 'localeNumber', standalone: true }] }], ctorParameters: () => [{ type: EasyI18nService }, { type: undefined, decorators: [{ type: Inject, args: [LOCALE_ID] }] }] }); class TrDirective { set trKey(key) { if (key !== this.currentKey) { this.currentKey = key; this.render(true); } } set trNamespace(namespace) { if (namespace !== this.currentParams?.namespace) { this.currentParams = { ...(this.currentParams ?? {}), namespace }; this.render(true); } } set trGender(gender) { if (gender !== this.currentParams?.gender) { this.currentParams = { ...(this.currentParams ?? {}), gender }; this.render(true); } } set trArgs(args) { if (!lodash.isEqual(args, this.currentParams?.args)) { this.currentParams = { ...(this.currentParams ?? {}), args }; this.render(true); } } set trNamedArgs(namedArgs) { if (!lodash.isEqual(namedArgs, this.currentParams?.namedArgs)) { this.currentParams = { ...(this.currentParams ?? {}), namedArgs }; this.render(true); } } constructor(el) { this.el = el; this.changes = new MutationObserver(() => { this.render(); }); this.changes.observe(el.nativeElement, { childList: true, characterData: true, subtree: true }); } ngOnDestroy() { this.changes.disconnect(); } render(paramsOnly = false) { const nodes = this.el.nativeElement.childNodes; nodes.forEach((node) => { if (node.nodeType === 3) { // Seulement les node de type 3, text if (node.lookupKey != null) { this.updateValue(node.lookupKey, node); } else if (this.currentKey != null) { this.updateValue(this.currentKey, this.el.nativeElement); } else { let key = null; const content = this.getContent(node); node.lookupKey = content.trim(); // Si le contenu actuel est différent de la valeur traduite, la clef a changé if (content !== node.currentValue) { key = node.lookupKey; // On stocke la valeur originale qui doit ête la clef node.originalContent = content ?? node.originalContent; } else if (node.originalContent && paramsOnly) { // Le contenu actuel est la version traduite, si on a l'original // On prend la clef originale et on va vérifier si un paramètre a changé key = node.originalContent.trim(); } this.updateValue(key, node); } } }); } updateValue(key, node) { if (!key) { return; } // Si rien n'a changé on arrête if (node.lastKey === key && this.lastParams === this.currentParams) { return; } node.lastKey = key; this.lastParams = this.currentParams; if (!node.originalContent) { node.originalContent = this.getContent(node); } const res = tr(key, this.currentParams); if (this.currentKey && res === key) { return; } node.currentValue = res ?? node.originalContent ?? key; this.setContent(node, this.currentKey ? node.currentValue : node.originalContent.replace(key, node.currentValue)); } getContent(node) { return node.textContent != null ? node.textContent : node.data; } setContent(node, content) { if (node.textContent != null) { node.textContent = content; } else { node.data = content; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TrDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.7", type: TrDirective, isStandalone: true, selector: "[tr]", inputs: { trKey: "trKey", trNamespace: "trNamespace", trGender: "trGender", trArgs: "trArgs", trNamedArgs: "trNamedArgs" }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TrDirective, decorators: [{ type: Directive, args: [{ selector: '[tr]', standalone: true }] }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { trKey: [{ type: Input }], trNamespace: [{ type: Input }], trGender: [{ type: Input }], trArgs: [{ type: Input }], trNamedArgs: [{ type: Input }] } }); class TrPipe { transform(text, options) { if (!text) { return text; } return tr(text, options); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TrPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.7", ngImport: i0, type: TrPipe, isStandalone: true, name: "tr" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TrPipe, decorators: [{ type: Pipe, args: [{ name: 'tr', standalone: true }] }] }); class PluralPipe { transform(text, value, options) { if (!text) { return text; } return plural(text, value, options); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PluralPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.7", ngImport: i0, type: PluralPipe, isStandalone: true, name: "plural" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PluralPipe, decorators: [{ type: Pipe, args: [{ name: 'plural', standalone: true }] }] }); class PluralDirective { set plural(value) { if (value !== this.currentValue) { this.currentValue = value; this.render(true); } } set pluralKey(key) { if (key !== this.currentKey) { this.currentKey = key; this.render(true); } } set pluralNamespace(namespace) { if (namespace !== this.currentParams?.namespace) { this.currentParams = { ...(this.currentParams ?? {}), namespace }; this.render(true); } } set pluralGender(gender) { if (gender !== this.currentParams?.gender) { this.currentParams = { ...(this.currentParams ?? {}), gender }; this.render(true); } } set pluralArgs(args) { if (!lodash.isEqual(args, this.currentParams?.args)) { this.currentParams = { ...(this.currentParams ?? {}), args }; this.render(true); } } set pluralNamedArgs(namedArgs) { if (!lodash.isEqual(namedArgs, this.currentParams?.namedArgs)) { this.currentParams = { ...(this.currentParams ?? {}), namedArgs }; this.render(true); } } set pluralName(name) { if (name !== this.currentParams?.name) { this.currentParams = { ...(this.currentParams ?? {}), name }; this.render(true); } } set pluralNumberFormatterFn(numberFormatterFn) { if (numberFormatterFn !== this.currentParams?.numberFormatterFn) { this.currentParams = { ...(this.currentParams ?? {}), numberFormatterFn }; this.render(true); } } constructor(el) { this.el = el; this.changes = new MutationObserver(() => { this.render(); }); this.changes.observe(el.nativeElement, { childList: true, characterData: true, subtree: true }); } ngOnDestroy() { this.changes.disconnect(); } render(paramsOnly = false) { const nodes = this.el.nativeElement.childNodes; if (this.currentKey) { this.updateValue(this.currentKey, this.el.nativeElement); } else { nodes.forEach((node) => { if (node.nodeType === 3) { // Seulement les node de type 3, text let key = null; const content = this.getContent(node); // Si le contenu actuel est différent de la valeur traduite, la clef a changé if (content !== node.currentValue) { key = content.trim(); // On stocke la valeur originale qui doit ête la clef node.originalContent = content ?? node.originalContent; } else if (node.originalContent && paramsOnly) { // Le contenu actuel est la version traduite, si on a l'original // On prend la clef originale et on va vérifier si un paramètre a changé key = node.originalContent.trim(); } this.updateValue(key, node); } }); } } updateValue(key, node) { if (!key) { return; } // Si rien n'a changé on arrête if (node.lastKey === key && this.currentValue === this.lastValue && this.lastParams === this.currentParams) { return; } node.lastKey = key; this.lastValue = this.currentValue; this.lastParams = this.currentParams; if (!node.originalContent) { node.originalContent = this.getContent(node); } const res = this.currentValue != null ? plural(key, this.currentValue, this.currentParams) : ''; // Même clef, donc non traduit if (this.currentKey && res === key) { return; } node.currentValue = res ?? node.originalContent ?? key; this.setContent(node, this.currentKey ? node.currentValue : node.originalContent.replace(key, node.currentValue)); } getContent(node) { return node.textContent != null ? node.textContent : node.data; } setContent(node, content) { if (node.textContent != null) { node.textContent = content; } else { node.data = content; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PluralDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.7", type: PluralDirective, isStandalone: true, selector: "[plural]", inputs: { plural: "plural", pluralKey: "pluralKey", pluralNamespace: "pluralNamespace", pluralGender: "pluralGender", pluralArgs: "pluralArgs", pluralNamedArgs: "pluralNamedArgs", pluralName: "pluralName", pluralNumberFormatterFn: "pluralNumberFormatterFn" }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PluralDirective, decorators: [{ type: Directive, args: [{ selector: '[plural]', standalone: true }] }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { plural: [{ type: Input, args: ['plural'] }], pluralKey: [{ type: Input }], pluralNamespace: [{ type: Input }], pluralGender: [{ type: Input }], pluralArgs: [{ type: Input }], pluralNamedArgs: [{ type: Input }], pluralName: [{ type: Input, args: ['pluralName'] }], pluralNumberFormatterFn: [{ type: Input, args: ['pluralNumberFormatterFn'] }] } }); class LocalePercentPipe { constructor(easyI18nService, localeId) { this.easyI18nService = easyI18nService; this.localeId = localeId; } transform(value, digitsInfo) { return formatPercent(value, this.easyI18nService.ngLocale ?? this.localeId, digitsInfo); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: LocalePercentPipe, deps: [{ token: EasyI18nService }, { token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.7", ngImport: i0, type: LocalePercentPipe, isStandalone: true, name: "localePercent" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: LocalePercentPipe, decorators: [{ type: Pipe, args: [{ name: 'localePercent', standalone: true }] }], ctorParameters: () => [{ type: EasyI18nService }, { type: undefined, decorators: [{ type: Inject, args: [LOCALE_ID] }] }] }); class LocaleCurrencyPipe { constructor(easyI18nService, localeId, defaultCurrencyCode = 'USD') { this.easyI18nService = easyI18nService; this.localeId = localeId; this.defaultCurrencyCode = defaultCurrencyCode; } transform(value, currencyCode, display = 'symbol', digitsInfo) { const locale = this.easyI18nService.ngLocale ?? this.localeId; let currency = currencyCode ?? this.defaultCurrencyCode; if (display !== 'code') { if (display === 'symbol' || display === 'symbol-narrow') { currency = getCurrencySymbol(currency, display === 'symbol' ? 'wide' : 'narrow', locale); } else { currency = display; } } return formatCurrency(value, locale, currency, currencyCode, digitsInfo); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: LocaleCurrencyPipe, deps: [{ token: EasyI18nService }, { token: LOCALE_ID }, { token: DEFAULT_CURRENCY_CODE }], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.7", ngImport: i0, type: LocaleCurrencyPipe, isStandalone: true, name: "localeCurrency" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: LocaleCurrencyPipe, decorators: [{ type: Pipe, args: [{ name: 'localeCurrency', standalone: true }] }], ctorParameters: () => [{ type: EasyI18nService }, { type: undefined, decorators: [{ type: Inject, args: [LOCALE_ID] }] }, { type: undefined, decorators: [{ type: Inject, args: [DEFAULT_CURRENCY_CODE] }] }] }); class PluralElementDirective { constructor(viewRef, templateRef) { this.viewRef = viewRef; this.templateRef = templateRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PluralElementDirective, deps: [{ token: i0.ViewContainerRef }, { token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.7", type: PluralElementDirective, isStandalone: true, selector: "[pluralElement]", inputs: { elementKey: ["pluralElement", "elementKey"] }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PluralElementDirective, decorators: [{ type: Directive, args: [{ selector: '[pluralElement]', standalone: true }] }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.TemplateRef }], propDecorators: { elementKey: [{ type: Input, args: ['pluralElement'] }] } }); class PluralContentDirective { set pluralContent(key) { if (key !== this.currentKey) { this.currentKey = key; this.render(); } } set pluralValue(value) { if (value !== this.currentValue) { this.currentValue = value; this.render(); } } set pluralNamespace(namespace) { if (namespace !== this.currentParams?.namespace) { this.currentParams = { ...(this.currentParams ?? {}), namespace }; this.render(); } } set pluralGender(gender) { if (gender !== this.currentParams?.gender) { this.currentParams = { ...(this.currentParams ?? {}), gender }; this.render(); } } set pluralArgs(args) { if (!lodash.isEqual(args, this.currentParams?.args)) { this.currentParams = { ...(this.currentParams ?? {}), args }; this.render(); } } set pluralNamedArgs(namedArgs) { if (!lodash.isEqual(namedArgs, this.currentParams?.namedArgs)) { this.currentParams = { ...(this.currentParams ?? {}), namedArgs }; this.render(); } } set pluralName(name) { if (name !== this.currentParams?.name) { this.currentParams = { ...(this.currentParams ?? {}), name }; this.render(); } } set pluralNumberFormatterFn(numberFormatterFn) { if (numberFormatterFn !== this.currentParams?.numberFormatterFn) { this.currentParams = { ...(this.currentParams ?? {}), numberFormatterFn }; this.render(); } } set demarc(demarc) { if (!lodash.isEqual(demarc, this._demarc)) { this.demarc = { ...this._demarc, ...demarc }; this.render(); } } constructor(viewRef, renderer, changeDetectorRef) { this.viewRef = viewRef; this.renderer = renderer; this.changeDetectorRef = changeDetectorRef; this._demarc = { start: '{', end: '}' }; } ngAfterContentInit() { if (this.elements) { this.subscription = merge(new BehaviorSubject(this.elements.toArray()), this.elements.changes).subscribe(next => this.render()); } } ngOnDestroy() { this.subscription?.unsubscribe(); } render() { if (this.elements == null || !this.currentKey || this.currentValue == null || !this._demarc?.start || !this._demarc?.end) { return; } this.viewRef.clear(); const childElements = this.viewRef.element.nativeElement.children; for (const child of childElements) { this.renderer.removeChild(this.viewRef.element.nativeElement, child); } this.viewRef.element.nativeElement.textContent = ''; const raw = plural(this.currentKey, this.currentValue, this.currentParams); let lastTokenEnd = 0; while (lastTokenEnd < raw.length) { const tokenStartDemarc = raw.indexOf(this._demarc?.start, lastTokenEnd); if (tokenStartDemarc < 0) { break; } const tokenStart = tokenStartDemarc + this._demarc?.start.length; const tokenEnd = raw.indexOf(this._demarc?.end, tokenStart); if (tokenEnd < 0) { throw new Error(`Encountered unterminated token in translation string '${this.currentKey}'`); } const tokenEndDemarc = tokenEnd + this._demarc?.end.length; const precedingText = raw.substring(lastTokenEnd, tokenStartDemarc); const precedingTextElement = this.renderer.createText(precedingText.replace(/ /g, '\u00A0')); this.renderer.appendChild(this.viewRef.element.nativeElement, precedingTextElement); const elementKey = raw.substring(tokenStart, tokenEnd); const embeddedElementTemplate = this.elements.toArray().find(element => element.elementKey === elementKey); if (embeddedElementTemplate) { const embeddedElementView = embeddedElementTemplate.viewRef.createEmbeddedView(embeddedElementTemplate.templateRef); this.renderer.appendChild(this.viewRef.element.nativeElement, embeddedElementView.rootNodes[0]); } else { const missingTokenText = raw.substring(tokenStartDemarc, tokenEndDemarc); const missingTokenElement = this.renderer.createText(missingTokenText); this.renderer.appendChild(this.viewRef.element.nativeElement, missingTokenElement); } lastTokenEnd = tokenEndDemarc; } const trailingText = raw.substring(lastTokenEnd); const trailingTextElement = this.renderer.createText(trailingText.replace(/ /g, '\u00A0')); this.renderer.appendChild(this.viewRef.element.nativeElement, trailingTextElement); this.changeDetectorRef.detectChanges(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PluralContentDirective, deps: [{ token: i0.ViewContainerRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.7", type: PluralContentDirective, isStandalone: true, selector: "[pluralContent]", inputs: { pluralContent: "pluralContent", pluralValue: "pluralValue", pluralNamespace: "pluralNamespace", pluralGender: "pluralGender", pluralArgs: "pluralArgs", pluralNamedArgs: "pluralNamedArgs", pluralName: "pluralName", pluralNumberFormatterFn: "pluralNumberFormatterFn", demarc: "demarc" }, queries: [{ propertyName: "elements", predicate: PluralElementDirective }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PluralContentDirective, decorators: [{ type: Directive, args: [{ selector: '[pluralContent]', standalone: true }] }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }], propDecorators: { elements: [{ type: ContentChildren, args: [PluralElementDirective] }], pluralContent: [{ type: Input, args: ['pluralContent'] }], pluralValue: [{ type: Input, args: ['pluralValue'] }], pluralNamespace: [{ type: Input }], pluralGender: [{ type: Input }], pluralArgs: [{ type: Input }], pluralNamedArgs: [{ type: Input }], pluralName: [{ type: Input, args: ['pluralName'] }], pluralNumberFormatterFn: [{ type: Input, args: ['pluralNumberFormatterFn'] }], demarc: [{ type: Input }] } }); class TrElementDirective { constructor(viewRef, templateRef) { this.viewRef = viewRef; this.templateRef = templateRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TrElementDirective, deps: [{ token: i0.ViewContainerRef }, { token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.7", type: TrElementDirective, isStandalone: true, selector: "[trElement]", inputs: { elementKey: ["trElement", "elementKey"] }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TrElementDirective, decorators: [{ type: Directive, args: [{ selector: '[trElement]', standalone: true }] }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.TemplateRef }], propDecorators: { elementKey: [{ type: Input, args: ['trElement'] }] } }); class TrContentDirective { set trContent(key) { if (key !== this.currentKey) { this.currentKey = key; this.render(); } } set trNamespace(namespace) { if (namespace !== this.currentParams?.namespace) { this.currentParams = { ...(this.currentParams ?? {}), namespace }; this.render(); } } set trGender(gender) { if (gender !== this.currentParams?.gender) { this.currentParams = { ...(this.currentParams ?? {}), gender }; this.render(); } } set trArgs(args) { if (!lodash.isEqual(args, this.currentParams?.args)) { this.currentParams = { ...(this.currentParams ?? {}), args }; this.render(); } } set trNamedArgs(namedArgs) { if (!lodash.isEqual(namedArgs, this.currentParams?.namedArgs)) { this.currentParams = { ...(this.currentParams ?? {}), namedArgs }; this.render(); } } set demarc(demarc) { if (!lodash.isEqual(demarc, this._demarc)) { this.demarc = { ...this._demarc, ...demarc }; this.render(); } } constructor(viewRef, renderer, changeDetectorRef) { this.viewRef = viewRef; this.renderer = renderer; this.changeDetectorRef = changeDetectorRef; this._demarc = { start: '{', end: '}' }; } ngAfterContentInit() { if (this.elements) { this.subscription = merge(new BehaviorSubject(this.elements.toArray()), this.elements.changes).subscribe(next => this.render()); } } ngOnDestroy() { this.subscription?.unsubscribe(); } render() { if (this.elements == null || !this.currentKey || !this._demarc?.start || !this._demarc?.end) { return; } this.viewRef.clear(); const childElements = this.viewRef.element.nativeElement.children; for (const child of childElements) { this.renderer.removeChild(this.viewRef.element.nativeElement, child); } this.viewRef.element.nativeElement.textContent = ''; const raw = tr(this.currentKey, this.currentParams); let lastTokenEnd = 0; while (lastTokenEnd < raw.length) { const tokenStartDemarc = raw.indexOf(this._demarc?.start, lastTokenEnd); if (tokenStartDemarc < 0) { break; } const tokenStart = tokenStartDemarc + this._demarc?.start.length; const tokenEnd = raw.indexOf(this._demarc?.end, tokenStart); if (tokenEnd < 0) { throw new Error(`Encountered unterminated token in translation string '${this.currentKey}'`); } const tokenEndDemarc = tokenEnd + this._demarc?.end.length; const precedingText = raw.substring(lastTokenEnd, tokenStartDemarc); const precedingTextElement = this.renderer.createText(precedingText.replace(/ /g, '\u00A0')); this.renderer.appendChild(this.viewRef.element.nativeElement, precedingTextElement); const elementKey = raw.substring(tokenStart, tokenEnd); const embeddedElementTemplate = this.elements.toArray().find(element => element.elementKey === elementKey); if (embeddedElementTemplate) { const embeddedElementView = embeddedElementTemplate.viewRef.createEmbeddedView(embeddedElementTemplate.templateRef); this.renderer.appendChild(this.viewRef.element.nativeElement, embeddedElementView.rootNodes[0]); } else { const missingTokenText = raw.substring(tokenStartDemarc, tokenEndDemarc); const missingTokenElement = this.renderer.createText(missingTokenText); this.renderer.appendChild(this.viewRef.element.nativeElement, missingTokenElement); } lastTokenEnd = tokenEndDemarc; } const trailingText = raw.substring(lastTokenEnd); const trailingTextElement = this.renderer.createText(trailingText.replace(/ /g, '\u00A0')); this.renderer.appendChild(this.viewRef.element.nativeElement, trailingTextElement); this.changeDetectorRef.detectChanges(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TrContentDirective, deps: [{ token: i0.ViewContainerRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.7", type: TrContentDirective, isStandalone: true, selector: "[trContent]", inputs: { trContent: "trContent", trNamespace: "trNamespace", trGender: "trGender", trArgs: "trArgs", trNamedArgs: "trNamedArgs", demarc: "demarc" }, queries: [{ propertyName: "elements", predicate: TrElementDirective }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TrContentDirective, decorators: [{ type: Directive, args: [{ selector: '[trContent]', standalone: true }] }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }], propDecorators: { elements: [{ type: ContentChildren, args: [TrElementDirective] }], trContent: [{ type: Input, args: ['trContent'] }], trNamespace: [{ type: Input }], trGender: [{ type: Input }], trArgs: [{ type: Input }], trNamedArgs: [{ type: Input }], demarc: [{ type: Input }] } }); class EasyI18nModule { /** * Use this method in your root module to provide the EasyI18nService */ static forRoot(config) { return { ngModule: EasyI18nModule, providers: provideEasyI18n(config) }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EasyI18nModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.7", ngImport: i0, type: EasyI18nModule, imports: [LocaleDatePipe, LocaleNumberPipe, TrDirective, TrPipe, PluralPipe, PluralDirective, LocalePercentPipe, LocaleCurrencyPipe, PluralElementDirective, PluralContentDirective, TrElementDirective, TrContentDirective], exports: [LocaleDatePipe, LocaleNumberPipe, TrDirective, TrPipe, PluralPipe, PluralDirective, LocalePercentPipe, LocaleCurrencyPipe, PluralElementDirective, PluralContentDirective, TrElementDirective, TrContentDirective] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EasyI18nModule }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: EasyI18nModule, decorators: [{ type: NgModule, args: [{ imports: [ LocaleDatePipe, LocaleNumberPipe, TrDirective, TrPipe, PluralPipe, PluralDirective, LocalePercentPipe, LocaleCurrencyPipe, PluralElementDirective, PluralContentDirective, TrElementDirective, TrContentDirective ], exports: [ LocaleDatePipe, LocaleNumberPipe, TrDirective, TrPipe, PluralPipe, PluralDirective, LocalePercentPipe, LocaleCurrencyPipe, PluralElementDirective, PluralContentDirective, TrElementDirective, TrContentDirective ] }] }] }); function provideEasyI18n(config) { return [ config.loader || { provide: EasyI18nLoader, useClass: EmptyEasyI18nLoader }, config.store || { provide: EasyI18nStore, useClass: EmptyEasyI18nStore }, { provide: EASY_I18N_OPTIONS, useValue: config.options }, { provide: NG_LOCALES, useValue: config.ngLocales }, { provide: USE_BROWSER_LANGUAGE, useValue: config.useBrowserLanguage ?? true }, { provide: DEFAULT_LANGUAGE, useValue: config.defaultLanguage ?? 'en-US' }, { provide: FALLBACK_LANGUAGE, useValue: config.fallbackLanguage }, { provide: DISCOVER, useValue: config.discover ?? 'all' }, EasyI18nService, provideAppInitializer(() => { const initializerFn = ((easyI18nService) => { return async () => easyI18nService.initialize(); })(inject(EasyI18nService)); return initializerFn(); }) ]; } /** * Generated bundle index. Do not edit. */ export { DEFAULT_LANGUAGE, DISCOVER, EASY_I18N_OPTIONS, EasyI18nLoader, EasyI18nModule, EasyI18nService, EasyI18nStore, EmptyEasyI18nLoader, EmptyEasyI18nStore, FALLBACK_LANGUAGE, LocalStorageEasyI18nStore, LocaleCurrencyPipe, LocaleDatePipe, LocaleNumberPipe, LocalePercentPipe, NG_LOCALES, PluralContentDirective, PluralDirective, PluralElementDirective, PluralPipe, TrContentDirective, TrDirective, TrElementDirective, TrPipe, USE_BROWSER_LANGUAGE, provideEasyI18n }; //# sourceMappingURL=ngx-easy-i18n-js-core.mjs.map