Skip to content

Commit

Permalink
feat(TranslateService): added new OnTranslationChange event
Browse files Browse the repository at this point in the history
  • Loading branch information
mrkosima authored and ocombe committed Aug 13, 2016
1 parent 655c607 commit 7ddc0da
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 28 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@ Or use the `TranslatePipe` in any template:
// do something
});
```
- `onTranslationChange`: An EventEmitter to listen to translation change events. A `TranslationChangeEvent` is an object with the properties `lang: string` & `translations: any` (an object containing your translations).

example:
```ts
onTranslationChange.subscribe((event: TranslationChangeEvent) => {
// do something
});
```

#### Methods:
- `setDefaultLang(lang: string)`: Sets the default language to use as a fallback
Expand Down
29 changes: 27 additions & 2 deletions bundles/ng2-translate.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,29 @@ System.registerDynamic("src/translate.pipe", ["@angular/core", "./translate.serv
this.lastParams = args;
this.updateValue(query, interpolateParams);
this._dispose();
if (!this.onTranslationChange) {
this.onTranslationChange = this.translate.onTranslationChange.subscribe(function(event) {
if (_this.lastKey && event.lang === _this.translate.currentLang) {
_this.lastKey = null;
_this.updateValue(query, interpolateParams);
}
});
}
if (!this.onLangChange) {
this.onLangChange = this.translate.onLangChange.subscribe(function(event) {
_this.lastKey = null;
_this.updateValue(query, interpolateParams);
if (_this.lastKey) {
_this.lastKey = null;
_this.updateValue(query, interpolateParams);
}
});
}
return this.value;
};
TranslatePipe.prototype._dispose = function() {
if (lang_1.isPresent(this.onTranslationChange)) {
this.onTranslationChange.unsubscribe();
this.onTranslationChange = undefined;
}
if (lang_1.isPresent(this.onLangChange)) {
this.onLangChange.unsubscribe();
this.onLangChange = undefined;
Expand Down Expand Up @@ -206,8 +220,10 @@ System.registerDynamic("src/translate.service", ["@angular/core", "rxjs/Observab
this.currentLoader = currentLoader;
this.missingTranslationHandler = missingTranslationHandler;
this.currentLang = this.defaultLang;
this.onTranslationChange = new core_1.EventEmitter();
this.onLangChange = new core_1.EventEmitter();
this.translations = {};
this.langs = [];
this.parser = new translate_parser_1.Parser();
}
TranslateService.prototype.setDefaultLang = function(lang) {
Expand Down Expand Up @@ -248,6 +264,10 @@ System.registerDynamic("src/translate.service", ["@angular/core", "rxjs/Observab
}
if (shouldMerge && this.translations[lang]) {
Object.assign(this.translations[lang], translations);
this.onTranslationChange.emit({
translations: translations,
lang: lang
});
} else {
this.translations[lang] = translations;
}
Expand Down Expand Up @@ -361,6 +381,11 @@ System.registerDynamic("src/translate.service", ["@angular/core", "rxjs/Observab
}
this.translations[lang][key] = value;
this.updateLangs();
this.onTranslationChange.emit({
translations: (_a = {}, _a[key] = value, _a),
lang: lang
});
var _a;
};
TranslateService.prototype.changeLang = function(lang) {
this.currentLang = lang;
Expand Down
25 changes: 21 additions & 4 deletions src/translate.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {PipeTransform, Pipe, Injectable, EventEmitter, OnDestroy, ChangeDetectorRef} from '@angular/core';
import {TranslateService, LangChangeEvent} from './translate.service';
import {TranslateService, LangChangeEvent, TranslationChangeEvent} from './translate.service';
import {isPresent, isArray} from "@angular/core/src/facade/lang";

@Injectable()
Expand All @@ -11,6 +11,7 @@ export class TranslatePipe implements PipeTransform, OnDestroy {
value: string = '';
lastKey: string;
lastParams: any[];
onTranslationChange: EventEmitter<TranslationChangeEvent>;
onLangChange: EventEmitter<LangChangeEvent>;

constructor(private translate: TranslateService, private _ref: ChangeDetectorRef) {
Expand Down Expand Up @@ -112,22 +113,38 @@ export class TranslatePipe implements PipeTransform, OnDestroy {
// 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: TranslationChangeEvent) => {
if (this.lastKey && event.lang === this.translate.currentLang) {
this.lastKey = null;
this.updateValue(query, interpolateParams);
}
});
}

// subscribe to onLangChange event, in case the language changes
if(!this.onLangChange) {
this.onLangChange = this.translate.onLangChange.subscribe((event: LangChangeEvent) => {
this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated
this.updateValue(query, interpolateParams);
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 onLangChange events
* Clean any existing subscription to change events
* @private
*/
_dispose(): void {
if(isPresent(this.onTranslationChange)) {
this.onTranslationChange.unsubscribe();
this.onTranslationChange = undefined;
}
if(isPresent(this.onLangChange)) {
this.onLangChange.unsubscribe();
this.onLangChange = undefined;
Expand Down
54 changes: 35 additions & 19 deletions src/translate.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import "rxjs/add/operator/toArray";

import {Parser} from "./translate.parser";

export interface TranslationChangeEvent {
translations: any;
lang: string;
}

export interface LangChangeEvent {
lang: string;
translations: any;
Expand Down Expand Up @@ -54,7 +59,16 @@ export class TranslateService {
public currentLang: string = this.defaultLang;

/**
* An EventEmitter to listen to lang changes events
* An EventEmitter to listen to translation change events
* onTranslationChange.subscribe((params: TranslationChangeEvent) => {
* // do something
* });
* @type {ng.EventEmitter<TranslationChangeEvent>}
*/
public onTranslationChange: EventEmitter<TranslationChangeEvent> = new EventEmitter<TranslationChangeEvent>();

/**
* An EventEmitter to listen to lang change events
* onLangChange.subscribe((params: LangChangeEvent) => {
* // do something
* });
Expand Down Expand Up @@ -93,12 +107,12 @@ export class TranslateService {
public use(lang: string): Observable<any> {
let pending: Observable<any>;
// check if this language is available
if(typeof this.translations[lang] === "undefined") {
if (typeof this.translations[lang] === "undefined") {
// not available, ask for it
pending = this.getTranslation(lang);
}

if(typeof pending !== "undefined") {
if (typeof pending !== "undefined") {
pending.subscribe((res: any) => {
this.changeLang(lang);
});
Expand Down Expand Up @@ -139,6 +153,7 @@ 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: translations, lang: lang});
} else {
this.translations[lang] = translations;
}
Expand Down Expand Up @@ -178,20 +193,20 @@ export class TranslateService {
private getParsedResult(translations: any, key: any, interpolateParams?: Object): any {
let res: string|Observable<string>;

if(key instanceof Array) {
if (key instanceof Array) {
let result: any = {},
observables: boolean = false;
for(let k of key) {
for (let k of key) {
result[k] = this.getParsedResult(translations, k, interpolateParams);
if(typeof result[k].subscribe === "function") {
if (typeof result[k].subscribe === "function") {
observables = true;
}
}
if(observables) {
if (observables) {
let mergedObs: any;
for(let k of key) {
for (let k of key) {
let obs = typeof result[k].subscribe === "function" ? result[k] : Observable.of(result[k]);
if(typeof mergedObs === "undefined") {
if (typeof mergedObs === "undefined") {
mergedObs = obs;
} else {
mergedObs = mergedObs.merge(obs);
Expand All @@ -208,15 +223,15 @@ export class TranslateService {
return result;
}

if(translations) {
if (translations) {
res = this.parser.interpolate(this.parser.getValue(translations, key), interpolateParams);
}

if(typeof res === "undefined" && this.defaultLang && this.defaultLang !== this.currentLang) {
if (typeof res === "undefined" && this.defaultLang && this.defaultLang !== this.currentLang) {
res = this.parser.interpolate(this.parser.getValue(this.translations[this.defaultLang], key), interpolateParams);
}

if(!res && this.missingTranslationHandler) {
if (!res && this.missingTranslationHandler) {
res = this.missingTranslationHandler.handle(key);
}

Expand All @@ -230,19 +245,19 @@ export class TranslateService {
* @returns {any} the translated key, or an object of translated keys
*/
public get(key: string|Array<string>, interpolateParams?: Object): Observable<string|any> {
if(!key) {
if (!key) {
throw new Error(`Parameter "key" required`);
}
// check if we are loading a new translation to use
if(this.pending) {
if (this.pending) {
return Observable.create((observer: Observer<string>) => {
let onComplete = (res: string) => {
observer.next(res);
observer.complete();
};
this.pending.subscribe((res: any) => {
res = this.getParsedResult(res, key, interpolateParams);
if(typeof res.subscribe === "function") {
if (typeof res.subscribe === "function") {
res.subscribe(onComplete);
} else {
onComplete(res);
Expand All @@ -251,7 +266,7 @@ export class TranslateService {
});
} else {
let res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams);
if(typeof res.subscribe === "function") {
if (typeof res.subscribe === "function") {
return res;
} else {
return Observable.of(res);
Expand All @@ -267,13 +282,13 @@ export class TranslateService {
* @returns {string}
*/
public instant(key: string|Array<string>, interpolateParams?: Object): string|any {
if(!key) {
if (!key) {
throw new Error(`Parameter "key" required`);
}

let res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams);
if(typeof res.subscribe !== "undefined") {
if(key instanceof Array) {
if (typeof res.subscribe !== "undefined") {
if (key instanceof Array) {
let obj: any = {};
key.forEach((value: string, index: number) => {
obj[key[index]] = key[index];
Expand All @@ -295,6 +310,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});
}

/**
Expand Down
25 changes: 23 additions & 2 deletions tests/translate.pipe.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {MockConnection, MockBackend} from "@angular/http/testing/mock_backend";
import {TRANSLATE_PROVIDERS, TranslateService} from "./../ng2-translate";
import {ResponseOptions, Response, XHRBackend, HTTP_PROVIDERS} from "@angular/http";
import {provide, Injector, ReflectiveInjector, ChangeDetectorRef} from "@angular/core";
import {LangChangeEvent} from "../src/translate.service";
import {LangChangeEvent, TranslationChangeEvent} from "../src/translate.service";

class FakeChangeDetectorRef extends ChangeDetectorRef {
markForCheck(): void {}
Expand Down Expand Up @@ -118,6 +118,27 @@ export function main() {
}).toThrowError(`Wrong parameter in TranslatePipe. Expected a valid Object, received: ${param}`)
});

describe('should update translations on translation by key change', () => {
it('with static loader', (done) => {
translate.setTranslation('en', {"TEST": "This is a test"});
translate.use('en');

expect(translatePipe.transform('TEST')).toEqual("This is a test");

// this will be resolved at the next key's translation change
let subscription = translate.onTranslationChange.subscribe(
(res: TranslationChangeEvent) => {
expect(res.translations['TEST']).toBeDefined();
expect(res.translations['TEST']).toEqual("This is new test value");
expect(translatePipe.transform('TEST')).toEqual("This is new test value");
subscription.unsubscribe();
done();
});

translate.set('TEST', 'This is new test value', 'en');
});
});

describe('should update translations on lang change', () => {
it('with static loader', (done) => {
translate.setTranslation('en', {"TEST": "This is a test"});
Expand Down Expand Up @@ -158,4 +179,4 @@ export function main() {
});
});
});
}
}
13 changes: 12 additions & 1 deletion tests/translate.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
MissingTranslationHandler,
TranslateLoader,
TranslateStaticLoader,
LangChangeEvent
LangChangeEvent,
TranslationChangeEvent
} from './../ng2-translate';
import {Observable} from "rxjs/Observable";

Expand Down Expand Up @@ -194,6 +195,16 @@ export function main() {
expect(translate.instant('TEST2')).toEqual('TEST2');
});

it('should trigger an event when the translation value changes', () => {
translate.setTranslation('en', {});
translate.onTranslationChange.subscribe((event: TranslationChangeEvent) => {
expect(event.translations).toBeDefined();
expect(event.translations["TEST"]).toEqual("This is a test");
expect(event.lang).toBe('en');
});
translate.set("TEST", "This is a test", 'en');
});

it('should trigger an event when the lang changes', () => {
var tr = {"TEST": "This is a test"};
translate.setTranslation('en', tr);
Expand Down

0 comments on commit 7ddc0da

Please sign in to comment.