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