UNPKG

pretty-ngx-translate

Version:

The internationalization (i18n) library for Angular 2+

546 lines (545 loc) 20.1 kB
import { Injectable, EventEmitter, Inject, OpaqueToken } from "@angular/core"; import { Observable } from "rxjs/Observable"; import "rxjs/add/observable/of"; import "rxjs/add/operator/concat"; import "rxjs/add/operator/share"; import "rxjs/add/operator/map"; import "rxjs/add/operator/merge"; import "rxjs/add/operator/switchMap"; import "rxjs/add/operator/toArray"; import "rxjs/add/operator/take"; import { TranslateStore } from "./translate.store"; import { TranslateLoader } from "./translate.loader"; import { TranslateCompiler } from "./translate.compiler"; import { MissingTranslationHandler } from "./missing-translation-handler"; import { TranslateParser } from "./translate.parser"; import { mergeDeep, isDefined } from "./util"; export var USE_STORE = new OpaqueToken('USE_STORE'); export var USE_DEFAULT_LANG = new OpaqueToken('USE_DEFAULT_LANG'); var TranslateService = (function () { /** * * @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 isolate whether this service should use the store or not * @param useDefaultLang whether we should use default language translation when current language translation is missing. */ function TranslateService(store, currentLoader, compiler, parser, missingTranslationHandler, useDefaultLang, isolate) { if (useDefaultLang === void 0) { useDefaultLang = true; } if (isolate === void 0) { isolate = false; } this.store = store; this.currentLoader = currentLoader; this.compiler = compiler; this.parser = parser; this.missingTranslationHandler = missingTranslationHandler; this.useDefaultLang = useDefaultLang; this.isolate = isolate; this.pending = false; this._onTranslationChange = new EventEmitter(); this._onLangChange = new EventEmitter(); this._onDefaultLangChange = new EventEmitter(); this._langs = []; this._translations = {}; this._translationRequests = {}; } Object.defineProperty(TranslateService.prototype, "onTranslationChange", { /** * An EventEmitter to listen to translation change events * onTranslationChange.subscribe((params: TranslationChangeEvent) => { * // do something * }); * @type {EventEmitter<TranslationChangeEvent>} */ get: function () { return this.isolate ? this._onTranslationChange : this.store.onTranslationChange; }, enumerable: true, configurable: true }); Object.defineProperty(TranslateService.prototype, "onLangChange", { /** * An EventEmitter to listen to lang change events * onLangChange.subscribe((params: LangChangeEvent) => { * // do something * }); * @type {EventEmitter<LangChangeEvent>} */ get: function () { return this.isolate ? this._onLangChange : this.store.onLangChange; }, enumerable: true, configurable: true }); Object.defineProperty(TranslateService.prototype, "onDefaultLangChange", { /** * An EventEmitter to listen to default lang change events * onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => { * // do something * }); * @type {EventEmitter<DefaultLangChangeEvent>} */ get: function () { return this.isolate ? this._onDefaultLangChange : this.store.onDefaultLangChange; }, enumerable: true, configurable: true }); Object.defineProperty(TranslateService.prototype, "defaultLang", { /** * The default lang to fallback when translations are missing on the current lang */ get: function () { return this.isolate ? this._defaultLang : this.store.defaultLang; }, set: function (defaultLang) { if (this.isolate) { this._defaultLang = defaultLang; } else { this.store.defaultLang = defaultLang; } }, enumerable: true, configurable: true }); Object.defineProperty(TranslateService.prototype, "currentLang", { /** * The lang currently used * @type {string} */ get: function () { return this.isolate ? this._currentLang : this.store.currentLang; }, set: function (currentLang) { if (this.isolate) { this._currentLang = currentLang; } else { this.store.currentLang = currentLang; } }, enumerable: true, configurable: true }); Object.defineProperty(TranslateService.prototype, "langs", { /** * an array of langs * @type {Array} */ get: function () { return this.isolate ? this._langs : this.store.langs; }, set: function (langs) { if (this.isolate) { this._langs = langs; } else { this.store.langs = langs; } }, enumerable: true, configurable: true }); Object.defineProperty(TranslateService.prototype, "translations", { /** * a list of translations per lang * @type {{}} */ get: function () { return this.isolate ? this._translations : this.store.translations; }, set: function (translations) { if (this.isolate) { this._currentLang = translations; } else { this.store.translations = translations; } }, enumerable: true, configurable: true }); /** * Sets the default language to use as a fallback * @param lang */ TranslateService.prototype.setDefaultLang = function (lang) { var _this = this; if (lang === this.defaultLang) { return; } var pending = this.retrieveTranslations(lang); if (typeof pending !== "undefined") { // on init set the defaultLang immediately if (!this.defaultLang) { this.defaultLang = lang; } pending.take(1) .subscribe(function (res) { _this.changeDefaultLang(lang); }); } else { this.changeDefaultLang(lang); } }; /** * Gets the default language used * @returns string */ TranslateService.prototype.getDefaultLang = function () { return this.defaultLang; }; /** * Changes the lang currently used * @param lang * @returns {Observable<*>} */ TranslateService.prototype.use = function (lang) { var _this = this; // don't change the language if the language given is already selected if (lang === this.currentLang) { return Observable.of(this.translations[lang]); } var pending = this.retrieveTranslations(lang); if (typeof pending !== "undefined") { // on init set the currentLang immediately if (!this.currentLang) { this.currentLang = lang; } pending.take(1) .subscribe(function (res) { _this.changeLang(lang); }); return pending; } else { this.changeLang(lang); return Observable.of(this.translations[lang]); } }; /** * Retrieves the given translations * @param lang * @returns {Observable<*>} */ TranslateService.prototype.retrieveTranslations = function (lang) { var pending; // if this language is unavailable, ask for it if (typeof this.translations[lang] === "undefined") { this._translationRequests[lang] = this._translationRequests[lang] || this.getTranslation(lang); pending = this._translationRequests[lang]; } return pending; }; /** * Gets an object of translations for a given language with the current loader * and passes it through the compiler * @param lang * @returns {Observable<*>} */ TranslateService.prototype.getTranslation = function (lang) { var _this = this; this.pending = true; this.loadingTranslations = this.currentLoader.getTranslation(lang).share(); this.loadingTranslations.take(1) .subscribe(function (res) { _this.translations[lang] = _this.compiler.compileTranslations(res, lang); _this.updateLangs(); _this.pending = false; }, function (err) { _this.pending = false; }); return this.loadingTranslations; }; /** * Manually sets an object of translations for a given language * after passing it through the compiler * @param lang * @param translations * @param shouldMerge */ TranslateService.prototype.setTranslation = function (lang, translations, shouldMerge) { if (shouldMerge === void 0) { shouldMerge = false; } translations = this.compiler.compileTranslations(translations, lang); if (shouldMerge && this.translations[lang]) { this.translations[lang] = mergeDeep(this.translations[lang], translations); } else { this.translations[lang] = translations; } this.updateLangs(); this.onTranslationChange.emit({ lang: lang, translations: this.translations[lang] }); }; /** * Returns an array of currently available langs * @returns {any} */ TranslateService.prototype.getLangs = function () { return this.langs; }; /** * @param langs * Add available langs */ TranslateService.prototype.addLangs = function (langs) { var _this = this; langs.forEach(function (lang) { if (_this.langs.indexOf(lang) === -1) { _this.langs.push(lang); } }); }; /** * Update the list of available langs */ TranslateService.prototype.updateLangs = function () { this.addLangs(Object.keys(this.translations)); }; /** * Returns the parsed result of the translations * @param translations * @param key * @param interpolateParams * @returns {any} */ TranslateService.prototype.getParsedResult = function (translations, key, interpolateParams) { var res; if (key instanceof Array) { var result = {}, observables = false; for (var _i = 0, key_1 = key; _i < key_1.length; _i++) { var k = key_1[_i]; result[k] = this.getParsedResult(translations, k, interpolateParams); if (typeof result[k].subscribe === "function") { observables = true; } } if (observables) { var mergedObs = void 0; for (var _a = 0, key_2 = key; _a < key_2.length; _a++) { var k = key_2[_a]; var obs = typeof result[k].subscribe === "function" ? result[k] : Observable.of(result[k]); if (typeof mergedObs === "undefined") { mergedObs = obs; } else { mergedObs = mergedObs.merge(obs); } } return mergedObs.toArray().map(function (arr) { var obj = {}; arr.forEach(function (value, index) { obj[key[index]] = value; }); return obj; }); } return result; } if (translations) { res = this.parser.interpolate(this.parser.getValue(translations, key), interpolateParams); } if (typeof res === "undefined" && this.defaultLang && this.defaultLang !== this.currentLang && this.useDefaultLang) { res = this.parser.interpolate(this.parser.getValue(this.translations[this.defaultLang], key), interpolateParams); } if (typeof res === "undefined") { var params = { key: key, translateService: this }; if (typeof interpolateParams !== 'undefined') { params.interpolateParams = interpolateParams; } res = this.missingTranslationHandler.handle(params); } return typeof res !== "undefined" ? res : key; }; /** * Gets the translated value of a key (or an array of keys) * @param key * @param interpolateParams * @returns {any} the translated key, or an object of translated keys */ TranslateService.prototype.get = function (key, interpolateParams) { var _this = this; if (!isDefined(key) || !key.length) { throw new Error("Parameter \"key\" required"); } // check if we are loading a new translation to use if (this.pending) { return Observable.create(function (observer) { var onComplete = function (res) { observer.next(res); observer.complete(); }; var onError = function (err) { observer.error(err); }; _this.loadingTranslations.subscribe(function (res) { res = _this.getParsedResult(res, key, interpolateParams); if (typeof res.subscribe === "function") { res.subscribe(onComplete, onError); } else { onComplete(res); } }, onError); }); } else { var res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams); if (typeof res.subscribe === "function") { return res; } else { return Observable.of(res); } } }; /** * Returns a stream of translated values of a key (or an array of keys) which updates * whenever the language changes. * @param key * @param interpolateParams * @returns {any} A stream of the translated key, or an object of translated keys */ TranslateService.prototype.stream = function (key, interpolateParams) { var _this = this; if (!isDefined(key) || !key.length) { throw new Error("Parameter \"key\" required"); } return this .get(key, interpolateParams) .concat(this.onLangChange.switchMap(function (event) { var res = _this.getParsedResult(event.translations, key, interpolateParams); if (typeof res.subscribe === "function") { return res; } else { return Observable.of(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. * @param key * @param interpolateParams * @returns {string} */ TranslateService.prototype.instant = function (key, interpolateParams) { if (!isDefined(key) || !key.length) { throw new Error("Parameter \"key\" required"); } var res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams); if (typeof res.subscribe !== "undefined") { if (key instanceof Array) { var obj_1 = {}; key.forEach(function (value, index) { obj_1[key[index]] = key[index]; }); return obj_1; } return key; } else { return res; } }; /** * Sets the translated value of a key, after compiling it * @param key * @param value * @param lang */ TranslateService.prototype.set = function (key, value, lang) { if (lang === void 0) { lang = this.currentLang; } this.translations[lang][key] = this.compiler.compile(value, lang); this.updateLangs(); this.onTranslationChange.emit({ lang: lang, translations: this.translations[lang] }); }; /** * Changes the current lang * @param lang */ TranslateService.prototype.changeLang = function (lang) { 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) { this.changeDefaultLang(lang); } }; /** * Changes the default lang * @param lang */ TranslateService.prototype.changeDefaultLang = function (lang) { this.defaultLang = lang; this.onDefaultLangChange.emit({ lang: lang, translations: this.translations[lang] }); }; /** * Allows to reload the lang file from the file * @param lang * @returns {Observable<any>} */ TranslateService.prototype.reloadLang = function (lang) { this.resetLang(lang); return this.getTranslation(lang); }; /** * Deletes inner translation * @param lang */ TranslateService.prototype.resetLang = function (lang) { this._translationRequests[lang] = undefined; this.translations[lang] = undefined; }; /** * Returns the language code name from the browser, e.g. "de" * * @returns string */ TranslateService.prototype.getBrowserLang = function () { if (typeof window === 'undefined' || typeof window.navigator === 'undefined') { return undefined; } var browserLang = window.navigator.languages ? window.navigator.languages[0] : null; browserLang = browserLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage; if (browserLang.indexOf('-') !== -1) { browserLang = browserLang.split('-')[0]; } if (browserLang.indexOf('_') !== -1) { browserLang = browserLang.split('_')[0]; } return browserLang; }; /** * Returns the culture language code name from the browser, e.g. "de-DE" * * @returns string */ TranslateService.prototype.getBrowserCultureLang = function () { if (typeof window === 'undefined' || typeof window.navigator === 'undefined') { return undefined; } var browserCultureLang = window.navigator.languages ? window.navigator.languages[0] : null; browserCultureLang = browserCultureLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage; return browserCultureLang; }; return TranslateService; }()); export { TranslateService }; TranslateService.decorators = [ { type: Injectable }, ]; /** @nocollapse */ TranslateService.ctorParameters = function () { return [ { 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: [USE_STORE,] },] }, ]; };