I am doing a i18n service system for my Angular 2 application. To avoid impure pipes, I am adding to the translation pipe an additional parameter, which change will trigger the pipe value to update, so it will update the translated text.
What would be the best practice: Raise an event from the service and subscribe in each component to detect the language change
vs
Set the service variable lang
as public and add to it to the pipe as parameter?
I have tested and both of them works.
Using Event emitter:
component.html
<button md-button class="btn-register">{{'Register' | _:lang}}</button>
component.ts
export class LoginComponent implements OnInit {
loginForm: FormGroup;
public lang: string;
onLangChange: EventEmitter<LangChangeEvent>;
constructor(private _fb: FormBuilder, private _authService: AuthService, private tr:TranslateService) {
}
ngOnInit():any {
//monitor language change
this.onLangChange = this.tr.onLangChange.subscribe( (event: LangChangeEvent) => {
this.lang = event.lang;
});
this.loginForm = this._fb.group({
email: ['', Validators.required],
password: ['', Validators.compose( [Validators.required, hasNumbers] ) ]
});
}
onSubmit(form) {
this._authService.login( this.loginForm.value );
}
}
translate.service.ts
import {Injectable, EventEmitter} from '@angular/core';
import {Http} from '@angular/http';
import 'rxjs/add/operator/toPromise';
import {Languages} from "../config/Constants";
export interface LangChangeEvent {
lang: string;
}
@Injectable()
export class TranslateService {
public lang: string;
dictionary: any = {};
/**
* An EventEmitter to listen to lang change events
* onLangChange.subscribe((params: LangChangeEvent) => {
* // do something
* });
* @type {ng.EventEmitter<LangChangeEvent>}
*/
public onLangChange: EventEmitter<LangChangeEvent> = new EventEmitter<LangChangeEvent>();
constructor(private http: Http, private path:string) {
let lang = this.detectLang();
this.setLanguage(lang);
}
/**
* A standard alias used for the translate function
* @param val
* @returns {string}
* @private
*/
public _(val: string) {
return this.dictionary[val] ? this.dictionary[val] : val;
}
public setLanguage(lang: string) {
if(Languages[lang]) {
this.loadLanguage(lang, Languages[lang].path);
}
}
private loadLanguage(lang:string, url: string) {
return this.http.get(url).toPromise()
.then( dictionary =>
{
this.dictionary = dictionary.json();
this.lang = lang;
this.onLangChange.emit({ lang: this.lang });
})
.catch( error => console.log(error) );
}
private detectLang(): string {
if (typeof window === 'undefined' || typeof window.navigator === 'undefined') {
return 'en';
}
let browserLang: any;
if (typeof window.navigator['languages'] !== 'undefined' && window.navigator['languages'].length > 0) {
browserLang = window.navigator['languages'][0].indexOf('-') !== -1 || window.navigator['languages'].length < 2 ? window.navigator['languages'][0] : window.navigator['languages'][1];
} else {
browserLang = window.navigator['language'] || window.navigator['browserLanguage'];
}
return browserLang && browserLang.length ? browserLang.split('-')[0] : 'en'; // use navigator lang if available
}
}
Using a public variable:
<button md-button class="btn-register">{{'Register' | _:tr.lang}}</button>
component.ts
export class LoginComponent implements OnInit {
loginForm: FormGroup;
constructor(private _fb: FormBuilder, private _authService: AuthService, private tr:TranslateService) {
}
ngOnInit():any {
this.loginForm = this._fb.group({
email: ['', Validators.required],
password: ['', Validators.compose( [Validators.required, hasNumbers] ) ]
});
}
onSubmit(form) {
this._authService.login( this.loginForm.value );
}
}
translate.service.ts
import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import 'rxjs/add/operator/toPromise';
import {Languages} from "../config/Constants";
export interface LangChangeEvent {
lang: string;
}
@Injectable()
export class TranslateService {
public lang: string;
dictionary: any = {};
constructor(private http: Http, private path:string) {
let lang = this.detectLang();
this.setLanguage(lang);
}
/**
* A standard alias used for the translate function
* @param val
* @returns {string}
* @private
*/
public _(val: string) {
return this.dictionary[val] ? this.dictionary[val] : val;
}
public setLanguage(lang: string) {
if(Languages[lang]) {
this.loadLanguage(lang, Languages[lang].path);
}
}
private loadLanguage(lang:string, url: string) {
return this.http.get(url).toPromise()
.then( dictionary =>
{
this.dictionary = dictionary.json();
this.lang = lang;
})
.catch( error => console.log(error) );
}
private detectLang(): string {
if (typeof window === 'undefined' || typeof window.navigator === 'undefined') {
return 'en';
}
let browserLang: any;
if (typeof window.navigator['languages'] !== 'undefined' && window.navigator['languages'].length > 0) {
browserLang = window.navigator['languages'][0].indexOf('-') !== -1 || window.navigator['languages'].length < 2 ? window.navigator['languages'][0] : window.navigator['languages'][1];
} else {
browserLang = window.navigator['language'] || window.navigator['browserLanguage'];
}
return browserLang && browserLang.length ? browserLang.split('-')[0] : 'en'; // use navigator lang if available
}
}