UNPKG

20.1 kBJavaScriptView Raw
1import { Injectable, EventEmitter, Inject, InjectionToken } from "@angular/core";
2import { Observable } from "rxjs/Observable";
3import "rxjs/add/observable/of";
4import "rxjs/add/operator/concat";
5import "rxjs/add/operator/share";
6import "rxjs/add/operator/map";
7import "rxjs/add/operator/merge";
8import "rxjs/add/operator/switchMap";
9import "rxjs/add/operator/toArray";
10import "rxjs/add/operator/take";
11import { TranslateStore } from "./translate.store";
12import { TranslateLoader } from "./translate.loader";
13import { TranslateCompiler } from "./translate.compiler";
14import { MissingTranslationHandler } from "./missing-translation-handler";
15import { TranslateParser } from "./translate.parser";
16import { mergeDeep, isDefined } from "./util";
17export var USE_STORE = new InjectionToken('USE_STORE');
18export var USE_DEFAULT_LANG = new InjectionToken('USE_DEFAULT_LANG');
19var TranslateService = (function () {
20 /**
21 *
22 * @param store an instance of the store (that is supposed to be unique)
23 * @param currentLoader An instance of the loader currently used
24 * @param compiler An instance of the compiler currently used
25 * @param parser An instance of the parser currently used
26 * @param missingTranslationHandler A handler for missing translations.
27 * @param isolate whether this service should use the store or not
28 * @param useDefaultLang whether we should use default language translation when current language translation is missing.
29 */
30 function TranslateService(store, currentLoader, compiler, parser, missingTranslationHandler, useDefaultLang, isolate) {
31 if (useDefaultLang === void 0) { useDefaultLang = true; }
32 if (isolate === void 0) { isolate = false; }
33 this.store = store;
34 this.currentLoader = currentLoader;
35 this.compiler = compiler;
36 this.parser = parser;
37 this.missingTranslationHandler = missingTranslationHandler;
38 this.useDefaultLang = useDefaultLang;
39 this.isolate = isolate;
40 this.pending = false;
41 this._onTranslationChange = new EventEmitter();
42 this._onLangChange = new EventEmitter();
43 this._onDefaultLangChange = new EventEmitter();
44 this._langs = [];
45 this._translations = {};
46 this._translationRequests = {};
47 }
48 Object.defineProperty(TranslateService.prototype, "onTranslationChange", {
49 /**
50 * An EventEmitter to listen to translation change events
51 * onTranslationChange.subscribe((params: TranslationChangeEvent) => {
52 * // do something
53 * });
54 * @type {EventEmitter<TranslationChangeEvent>}
55 */
56 get: function () {
57 return this.isolate ? this._onTranslationChange : this.store.onTranslationChange;
58 },
59 enumerable: true,
60 configurable: true
61 });
62 Object.defineProperty(TranslateService.prototype, "onLangChange", {
63 /**
64 * An EventEmitter to listen to lang change events
65 * onLangChange.subscribe((params: LangChangeEvent) => {
66 * // do something
67 * });
68 * @type {EventEmitter<LangChangeEvent>}
69 */
70 get: function () {
71 return this.isolate ? this._onLangChange : this.store.onLangChange;
72 },
73 enumerable: true,
74 configurable: true
75 });
76 Object.defineProperty(TranslateService.prototype, "onDefaultLangChange", {
77 /**
78 * An EventEmitter to listen to default lang change events
79 * onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => {
80 * // do something
81 * });
82 * @type {EventEmitter<DefaultLangChangeEvent>}
83 */
84 get: function () {
85 return this.isolate ? this._onDefaultLangChange : this.store.onDefaultLangChange;
86 },
87 enumerable: true,
88 configurable: true
89 });
90 Object.defineProperty(TranslateService.prototype, "defaultLang", {
91 /**
92 * The default lang to fallback when translations are missing on the current lang
93 */
94 get: function () {
95 return this.isolate ? this._defaultLang : this.store.defaultLang;
96 },
97 set: function (defaultLang) {
98 if (this.isolate) {
99 this._defaultLang = defaultLang;
100 }
101 else {
102 this.store.defaultLang = defaultLang;
103 }
104 },
105 enumerable: true,
106 configurable: true
107 });
108 Object.defineProperty(TranslateService.prototype, "currentLang", {
109 /**
110 * The lang currently used
111 * @type {string}
112 */
113 get: function () {
114 return this.isolate ? this._currentLang : this.store.currentLang;
115 },
116 set: function (currentLang) {
117 if (this.isolate) {
118 this._currentLang = currentLang;
119 }
120 else {
121 this.store.currentLang = currentLang;
122 }
123 },
124 enumerable: true,
125 configurable: true
126 });
127 Object.defineProperty(TranslateService.prototype, "langs", {
128 /**
129 * an array of langs
130 * @type {Array}
131 */
132 get: function () {
133 return this.isolate ? this._langs : this.store.langs;
134 },
135 set: function (langs) {
136 if (this.isolate) {
137 this._langs = langs;
138 }
139 else {
140 this.store.langs = langs;
141 }
142 },
143 enumerable: true,
144 configurable: true
145 });
146 Object.defineProperty(TranslateService.prototype, "translations", {
147 /**
148 * a list of translations per lang
149 * @type {{}}
150 */
151 get: function () {
152 return this.isolate ? this._translations : this.store.translations;
153 },
154 set: function (translations) {
155 if (this.isolate) {
156 this._currentLang = translations;
157 }
158 else {
159 this.store.translations = translations;
160 }
161 },
162 enumerable: true,
163 configurable: true
164 });
165 /**
166 * Sets the default language to use as a fallback
167 * @param lang
168 */
169 TranslateService.prototype.setDefaultLang = function (lang) {
170 var _this = this;
171 if (lang === this.defaultLang) {
172 return;
173 }
174 var pending = this.retrieveTranslations(lang);
175 if (typeof pending !== "undefined") {
176 // on init set the defaultLang immediately
177 if (!this.defaultLang) {
178 this.defaultLang = lang;
179 }
180 pending.take(1)
181 .subscribe(function (res) {
182 _this.changeDefaultLang(lang);
183 });
184 }
185 else {
186 this.changeDefaultLang(lang);
187 }
188 };
189 /**
190 * Gets the default language used
191 * @returns string
192 */
193 TranslateService.prototype.getDefaultLang = function () {
194 return this.defaultLang;
195 };
196 /**
197 * Changes the lang currently used
198 * @param lang
199 * @returns {Observable<*>}
200 */
201 TranslateService.prototype.use = function (lang) {
202 var _this = this;
203 // don't change the language if the language given is already selected
204 if (lang === this.currentLang) {
205 return Observable.of(this.translations[lang]);
206 }
207 var pending = this.retrieveTranslations(lang);
208 if (typeof pending !== "undefined") {
209 // on init set the currentLang immediately
210 if (!this.currentLang) {
211 this.currentLang = lang;
212 }
213 pending.take(1)
214 .subscribe(function (res) {
215 _this.changeLang(lang);
216 });
217 return pending;
218 }
219 else {
220 this.changeLang(lang);
221 return Observable.of(this.translations[lang]);
222 }
223 };
224 /**
225 * Retrieves the given translations
226 * @param lang
227 * @returns {Observable<*>}
228 */
229 TranslateService.prototype.retrieveTranslations = function (lang) {
230 var pending;
231 // if this language is unavailable, ask for it
232 if (typeof this.translations[lang] === "undefined") {
233 this._translationRequests[lang] = this._translationRequests[lang] || this.getTranslation(lang);
234 pending = this._translationRequests[lang];
235 }
236 return pending;
237 };
238 /**
239 * Gets an object of translations for a given language with the current loader
240 * and passes it through the compiler
241 * @param lang
242 * @returns {Observable<*>}
243 */
244 TranslateService.prototype.getTranslation = function (lang) {
245 var _this = this;
246 this.pending = true;
247 this.loadingTranslations = this.currentLoader.getTranslation(lang).share();
248 this.loadingTranslations.take(1)
249 .subscribe(function (res) {
250 _this.translations[lang] = _this.compiler.compileTranslations(res, lang);
251 _this.updateLangs();
252 _this.pending = false;
253 }, function (err) {
254 _this.pending = false;
255 });
256 return this.loadingTranslations;
257 };
258 /**
259 * Manually sets an object of translations for a given language
260 * after passing it through the compiler
261 * @param lang
262 * @param translations
263 * @param shouldMerge
264 */
265 TranslateService.prototype.setTranslation = function (lang, translations, shouldMerge) {
266 if (shouldMerge === void 0) { shouldMerge = false; }
267 translations = this.compiler.compileTranslations(translations, lang);
268 if (shouldMerge && this.translations[lang]) {
269 this.translations[lang] = mergeDeep(this.translations[lang], translations);
270 }
271 else {
272 this.translations[lang] = translations;
273 }
274 this.updateLangs();
275 this.onTranslationChange.emit({ lang: lang, translations: this.translations[lang] });
276 };
277 /**
278 * Returns an array of currently available langs
279 * @returns {any}
280 */
281 TranslateService.prototype.getLangs = function () {
282 return this.langs;
283 };
284 /**
285 * @param langs
286 * Add available langs
287 */
288 TranslateService.prototype.addLangs = function (langs) {
289 var _this = this;
290 langs.forEach(function (lang) {
291 if (_this.langs.indexOf(lang) === -1) {
292 _this.langs.push(lang);
293 }
294 });
295 };
296 /**
297 * Update the list of available langs
298 */
299 TranslateService.prototype.updateLangs = function () {
300 this.addLangs(Object.keys(this.translations));
301 };
302 /**
303 * Returns the parsed result of the translations
304 * @param translations
305 * @param key
306 * @param interpolateParams
307 * @returns {any}
308 */
309 TranslateService.prototype.getParsedResult = function (translations, key, interpolateParams) {
310 var res;
311 if (key instanceof Array) {
312 var result = {}, observables = false;
313 for (var _i = 0, key_1 = key; _i < key_1.length; _i++) {
314 var k = key_1[_i];
315 result[k] = this.getParsedResult(translations, k, interpolateParams);
316 if (typeof result[k].subscribe === "function") {
317 observables = true;
318 }
319 }
320 if (observables) {
321 var mergedObs = void 0;
322 for (var _a = 0, key_2 = key; _a < key_2.length; _a++) {
323 var k = key_2[_a];
324 var obs = typeof result[k].subscribe === "function" ? result[k] : Observable.of(result[k]);
325 if (typeof mergedObs === "undefined") {
326 mergedObs = obs;
327 }
328 else {
329 mergedObs = mergedObs.merge(obs);
330 }
331 }
332 return mergedObs.toArray().map(function (arr) {
333 var obj = {};
334 arr.forEach(function (value, index) {
335 obj[key[index]] = value;
336 });
337 return obj;
338 });
339 }
340 return result;
341 }
342 if (translations) {
343 res = this.parser.interpolate(this.parser.getValue(translations, key), interpolateParams);
344 }
345 if (typeof res === "undefined" && this.defaultLang && this.defaultLang !== this.currentLang && this.useDefaultLang) {
346 res = this.parser.interpolate(this.parser.getValue(this.translations[this.defaultLang], key), interpolateParams);
347 }
348 if (typeof res === "undefined") {
349 var params = { key: key, translateService: this };
350 if (typeof interpolateParams !== 'undefined') {
351 params.interpolateParams = interpolateParams;
352 }
353 res = this.missingTranslationHandler.handle(params);
354 }
355 return typeof res !== "undefined" ? res : key;
356 };
357 /**
358 * Gets the translated value of a key (or an array of keys)
359 * @param key
360 * @param interpolateParams
361 * @returns {any} the translated key, or an object of translated keys
362 */
363 TranslateService.prototype.get = function (key, interpolateParams) {
364 var _this = this;
365 if (!isDefined(key) || !key.length) {
366 throw new Error("Parameter \"key\" required");
367 }
368 // check if we are loading a new translation to use
369 if (this.pending) {
370 return Observable.create(function (observer) {
371 var onComplete = function (res) {
372 observer.next(res);
373 observer.complete();
374 };
375 var onError = function (err) {
376 observer.error(err);
377 };
378 _this.loadingTranslations.subscribe(function (res) {
379 res = _this.getParsedResult(res, key, interpolateParams);
380 if (typeof res.subscribe === "function") {
381 res.subscribe(onComplete, onError);
382 }
383 else {
384 onComplete(res);
385 }
386 }, onError);
387 });
388 }
389 else {
390 var res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams);
391 if (typeof res.subscribe === "function") {
392 return res;
393 }
394 else {
395 return Observable.of(res);
396 }
397 }
398 };
399 /**
400 * Returns a stream of translated values of a key (or an array of keys) which updates
401 * whenever the language changes.
402 * @param key
403 * @param interpolateParams
404 * @returns {any} A stream of the translated key, or an object of translated keys
405 */
406 TranslateService.prototype.stream = function (key, interpolateParams) {
407 var _this = this;
408 if (!isDefined(key) || !key.length) {
409 throw new Error("Parameter \"key\" required");
410 }
411 return this
412 .get(key, interpolateParams)
413 .concat(this.onLangChange.switchMap(function (event) {
414 var res = _this.getParsedResult(event.translations, key, interpolateParams);
415 if (typeof res.subscribe === "function") {
416 return res;
417 }
418 else {
419 return Observable.of(res);
420 }
421 }));
422 };
423 /**
424 * Returns a translation instantly from the internal state of loaded translation.
425 * All rules regarding the current language, the preferred language of even fallback languages will be used except any promise handling.
426 * @param key
427 * @param interpolateParams
428 * @returns {string}
429 */
430 TranslateService.prototype.instant = function (key, interpolateParams) {
431 if (!isDefined(key) || !key.length) {
432 throw new Error("Parameter \"key\" required");
433 }
434 var res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams);
435 if (typeof res.subscribe !== "undefined") {
436 if (key instanceof Array) {
437 var obj_1 = {};
438 key.forEach(function (value, index) {
439 obj_1[key[index]] = key[index];
440 });
441 return obj_1;
442 }
443 return key;
444 }
445 else {
446 return res;
447 }
448 };
449 /**
450 * Sets the translated value of a key, after compiling it
451 * @param key
452 * @param value
453 * @param lang
454 */
455 TranslateService.prototype.set = function (key, value, lang) {
456 if (lang === void 0) { lang = this.currentLang; }
457 this.translations[lang][key] = this.compiler.compile(value, lang);
458 this.updateLangs();
459 this.onTranslationChange.emit({ lang: lang, translations: this.translations[lang] });
460 };
461 /**
462 * Changes the current lang
463 * @param lang
464 */
465 TranslateService.prototype.changeLang = function (lang) {
466 this.currentLang = lang;
467 this.onLangChange.emit({ lang: lang, translations: this.translations[lang] });
468 // if there is no default lang, use the one that we just set
469 if (!this.defaultLang) {
470 this.changeDefaultLang(lang);
471 }
472 };
473 /**
474 * Changes the default lang
475 * @param lang
476 */
477 TranslateService.prototype.changeDefaultLang = function (lang) {
478 this.defaultLang = lang;
479 this.onDefaultLangChange.emit({ lang: lang, translations: this.translations[lang] });
480 };
481 /**
482 * Allows to reload the lang file from the file
483 * @param lang
484 * @returns {Observable<any>}
485 */
486 TranslateService.prototype.reloadLang = function (lang) {
487 this.resetLang(lang);
488 return this.getTranslation(lang);
489 };
490 /**
491 * Deletes inner translation
492 * @param lang
493 */
494 TranslateService.prototype.resetLang = function (lang) {
495 this._translationRequests[lang] = undefined;
496 this.translations[lang] = undefined;
497 };
498 /**
499 * Returns the language code name from the browser, e.g. "de"
500 *
501 * @returns string
502 */
503 TranslateService.prototype.getBrowserLang = function () {
504 if (typeof window === 'undefined' || typeof window.navigator === 'undefined') {
505 return undefined;
506 }
507 var browserLang = window.navigator.languages ? window.navigator.languages[0] : null;
508 browserLang = browserLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage;
509 if (browserLang.indexOf('-') !== -1) {
510 browserLang = browserLang.split('-')[0];
511 }
512 if (browserLang.indexOf('_') !== -1) {
513 browserLang = browserLang.split('_')[0];
514 }
515 return browserLang;
516 };
517 /**
518 * Returns the culture language code name from the browser, e.g. "de-DE"
519 *
520 * @returns string
521 */
522 TranslateService.prototype.getBrowserCultureLang = function () {
523 if (typeof window === 'undefined' || typeof window.navigator === 'undefined') {
524 return undefined;
525 }
526 var browserCultureLang = window.navigator.languages ? window.navigator.languages[0] : null;
527 browserCultureLang = browserCultureLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage;
528 return browserCultureLang;
529 };
530 return TranslateService;
531}());
532export { TranslateService };
533TranslateService.decorators = [
534 { type: Injectable },
535];
536/** @nocollapse */
537TranslateService.ctorParameters = function () { return [
538 { type: TranslateStore, },
539 { type: TranslateLoader, },
540 { type: TranslateCompiler, },
541 { type: TranslateParser, },
542 { type: MissingTranslationHandler, },
543 { type: undefined, decorators: [{ type: Inject, args: [USE_DEFAULT_LANG,] },] },
544 { type: undefined, decorators: [{ type: Inject, args: [USE_STORE,] },] },
545]; };