diff --git a/src/translate.directive.ts b/src/translate.directive.ts index 4b8381e5..3c7b4a61 100644 --- a/src/translate.directive.ts +++ b/src/translate.directive.ts @@ -2,6 +2,8 @@ import {Directive, ElementRef, AfterViewChecked, Input, OnDestroy} from '@angula import {Subscription} from 'rxjs'; import {isDefined} from './util'; import {TranslateService, LangChangeEvent} from './translate.service'; +import {TranslationChangeEvent} from "./translate.service"; +import {DefaultLangChangeEvent} from "./translate.service"; @Directive({ selector: '[translate],[ng2-translate]' @@ -11,6 +13,7 @@ export class TranslateDirective implements AfterViewChecked, OnDestroy { lastParams: any; onLangChangeSub: Subscription; onDefaultLangChangeSub: Subscription; + onTranslationChangeSub: Subscription; @Input() set translate(key: string) { if(key) { @@ -22,17 +25,26 @@ export class TranslateDirective implements AfterViewChecked, OnDestroy { @Input() translateParams: any; constructor(private translateService: TranslateService, private element: ElementRef) { + // subscribe to onTranslationChange event, in case the translations of the current lang change + if(!this.onTranslationChangeSub) { + this.onTranslationChangeSub = this.translateService.onTranslationChange.subscribe((event: TranslationChangeEvent) => { + 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: LangChangeEvent) => { - this.checkNodes(event.translations); + this.checkNodes(true, event.translations); }); } // subscribe to onDefaultLangChange event, in case the default language changes if(!this.onDefaultLangChangeSub) { - this.onDefaultLangChangeSub = this.translateService.onDefaultLangChange.subscribe(() => { - this.checkNodes(); + this.onDefaultLangChangeSub = this.translateService.onDefaultLangChange.subscribe((event: DefaultLangChangeEvent) => { + this.checkNodes(true); }); } } @@ -41,7 +53,7 @@ export class TranslateDirective implements AfterViewChecked, OnDestroy { this.checkNodes(); } - checkNodes(translations?: any) { + checkNodes(forceUpdate = false, translations?: any) { let nodes: NodeList = this.element.nativeElement.childNodes; for(let i = 0; i < nodes.length; ++i) { let node: any = nodes[i]; @@ -57,7 +69,7 @@ export class TranslateDirective implements AfterViewChecked, OnDestroy { key = content; // the content was changed from the user, we'll use it as a reference if needed node.originalContent = node.textContent; - } else if(node.originalContent && isDefined(translations)) { // the content seems ok, but the lang has changed + } else if(node.originalContent && forceUpdate) { // the content seems ok, but the lang has changed node.lastKey = null; // the current content is the translation, not the key, use the last real content as key key = node.originalContent.trim(); @@ -111,5 +123,9 @@ export class TranslateDirective implements AfterViewChecked, OnDestroy { if(this.onDefaultLangChangeSub) { this.onDefaultLangChangeSub.unsubscribe(); } + + if(this.onTranslationChangeSub) { + this.onTranslationChangeSub.unsubscribe(); + } } } diff --git a/src/translate.service.ts b/src/translate.service.ts index b51a8542..017e2058 100644 --- a/src/translate.service.ts +++ b/src/translate.service.ts @@ -7,6 +7,7 @@ import "rxjs/add/operator/share"; import "rxjs/add/operator/map"; import "rxjs/add/operator/merge"; import "rxjs/add/operator/toArray"; +import "rxjs/add/operator/take"; import {TranslateParser} from "./translate.parser"; import {isDefined} from "./util"; @@ -129,6 +130,7 @@ export class TranslateService { /** * * @param currentLoader An instance of the loader currently used + * @param parser An instance of the parser currently used * @param missingTranslationHandler A handler for missing translations. */ constructor( @@ -142,6 +144,10 @@ export class TranslateService { * @param lang */ public setDefaultLang(lang: string): void { + if(lang === this.defaultLang) { + return; + } + let pending: Observable = this.retrieveTranslations(lang); if(typeof pending !== "undefined") { @@ -150,10 +156,10 @@ export class TranslateService { this.defaultLang = lang; } - pending.subscribe((res: any) => { - this.changeDefaultLang(lang); - }, (err: any) => { - }); + pending.take(1) + .subscribe((res: any) => { + this.changeDefaultLang(lang); + }); } else { // we already have this language this.changeDefaultLang(lang); } @@ -180,10 +186,11 @@ export class TranslateService { if(!this.currentLang) { this.currentLang = lang; } - pending.subscribe((res: any) => { - this.changeLang(lang); - }, (err: any) => { - }); + + pending.take(1) + .subscribe((res: any) => { + this.changeLang(lang); + }); return pending; } else { // we have this language, return an Observable @@ -200,11 +207,12 @@ export class TranslateService { */ private retrieveTranslations(lang: string): Observable { let pending: Observable; - // check if this language is available + + // if this language is unavailable, ask for it if(typeof this.translations[lang] === "undefined") { - // not available, ask for it pending = this.getTranslation(lang); } + return pending; } @@ -215,13 +223,15 @@ export class TranslateService { */ public getTranslation(lang: string): Observable { this.pending = this.currentLoader.getTranslation(lang).share(); - this.pending.subscribe((res: Object) => { - this.translations[lang] = res; - this.updateLangs(); - this.pending = undefined; - }, (err: any) => { - this.pending = undefined; - }); + + this.pending.take(1) + .subscribe((res: Object) => { + this.translations[lang] = res; + this.updateLangs(); + this.pending = undefined; + }, (err: any) => { + this.pending = undefined; + }); return this.pending; } @@ -235,11 +245,11 @@ export class TranslateService { public setTranslation(lang: string, translations: Object, shouldMerge: boolean = false): void { if(shouldMerge && this.translations[lang]) { Object.assign(this.translations[lang], translations); - this.onTranslationChange.emit({translations: this.translations[lang], lang: lang}); } else { this.translations[lang] = translations; } this.updateLangs(); + this.onTranslationChange.emit({lang: lang, translations: this.translations[lang]}); } /** @@ -317,15 +327,15 @@ export class TranslateService { res = this.parser.interpolate(this.parser.getValue(this.translations[this.defaultLang], key), interpolateParams); } - if (!res && this.missingTranslationHandler) { - let params: MissingTranslationHandlerParams = { key, translateService: this }; - if (typeof interpolateParams !== 'undefined') { + if(!res && this.missingTranslationHandler) { + let params: MissingTranslationHandlerParams = {key, translateService: this}; + if(typeof interpolateParams !== 'undefined') { params.interpolateParams = interpolateParams; } res = this.missingTranslationHandler.handle(params); } - return res !== undefined ? res : key; + return typeof res !== "undefined" ? res : key; } /** @@ -403,7 +413,7 @@ export class TranslateService { public set(key: string, value: string, lang: string = this.currentLang): void { this.translations[lang][key] = value; this.updateLangs(); - this.onTranslationChange.emit({translations: {[key]: value}, lang: lang}); + this.onTranslationChange.emit({lang: lang, translations: this.translations[lang]}); } /** @@ -413,6 +423,11 @@ export class TranslateService { private changeLang(lang: string): void { 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); + } } /** @@ -472,7 +487,7 @@ export class TranslateService { * @returns string */ public getBrowserCultureLang(): string { - if (typeof window === 'undefined' || typeof window.navigator === 'undefined') { + if(typeof window === 'undefined' || typeof window.navigator === 'undefined') { return undefined; } diff --git a/tests/translate.directive.spec.ts b/tests/translate.directive.spec.ts index 950a5538..47737ec9 100644 --- a/tests/translate.directive.spec.ts +++ b/tests/translate.directive.spec.ts @@ -117,12 +117,11 @@ describe('TranslateDirective', () => { // Test (temporarily) disabled as the directive tests manipulate the DOM manually which breaks this test. // https://github.com/ocombe/ng2-translate/pull/336 - xit('should update the DOM when the default lang changes', () => { + it('should update the DOM when the default lang changes', () => { expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual('TEST'); translate.setTranslation('en', {"TEST": "This is a test"}); translate.setTranslation('fr', {"TEST": "C'est un test"}); - translate.setDefaultLang('en'); expect(fixture.componentInstance.noKey.nativeElement.innerHTML).toEqual('This is a test');