Skip to content

Commit

Permalink
feat(TranslationService): getting translations for default lang when …
Browse files Browse the repository at this point in the history
…it's set

Fixes #332
Fixes #336

BREAKING CHANGE: we will now fetch the translations for the default lang when it is set (if they are not already available). Also the default lang is now automatically set to the first lang that receives translations (if it is still undefined at that time). Setting the default lang will override this default behavior, so this will probably not break anything for you, but if you set the translations for the default lang first then you don't even have to call `setDefaultLang` anymore.
  • Loading branch information
ocombe committed Dec 20, 2016
1 parent 38e0cd9 commit 2088195
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 31 deletions.
26 changes: 21 additions & 5 deletions src/translate.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]'
Expand All @@ -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) {
Expand All @@ -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);
});
}
}
Expand All @@ -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];
Expand All @@ -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();
Expand Down Expand Up @@ -111,5 +123,9 @@ export class TranslateDirective implements AfterViewChecked, OnDestroy {
if(this.onDefaultLangChangeSub) {
this.onDefaultLangChangeSub.unsubscribe();
}

if(this.onTranslationChangeSub) {
this.onTranslationChangeSub.unsubscribe();
}
}
}
63 changes: 39 additions & 24 deletions src/translate.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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(
Expand All @@ -142,6 +144,10 @@ export class TranslateService {
* @param lang
*/
public setDefaultLang(lang: string): void {
if(lang === this.defaultLang) {
return;
}

let pending: Observable<any> = this.retrieveTranslations(lang);

if(typeof pending !== "undefined") {
Expand All @@ -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);
}
Expand All @@ -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
Expand All @@ -200,11 +207,12 @@ export class TranslateService {
*/
private retrieveTranslations(lang: string): Observable<any> {
let pending: Observable<any>;
// 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;
}

Expand All @@ -215,13 +223,15 @@ export class TranslateService {
*/
public getTranslation(lang: string): Observable<any> {
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;
}
Expand All @@ -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]});
}

/**
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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]});
}

/**
Expand All @@ -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);
}
}

/**
Expand Down Expand Up @@ -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;
}

Expand Down
3 changes: 1 addition & 2 deletions tests/translate.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down

0 comments on commit 2088195

Please sign in to comment.