Angular 2 多語言 ( 2 )
- 2016-12-28
- Liu, An-Chi 劉安齊
前言
前一篇已經說過為甚麼多語言的網站很重要,也示範了如何達到多語言的效果。但是,我們對於語言翻譯的需求還有很多,例如我如果想要讓一串句子每次搭配不同的變數怎麼辦,像是「你好,Tiger Liu」或是「你好,Tiger」這樣的可變性。或是如果有些語詞在某些語言中翻譯不出來、不想翻譯或是不小心忘記增加字典時,例如「掰掰囉!」在英文翻成「Bye Bye!」可是在日文的字典檔忘記加上這個字,要讓網站還是能夠顯示「Bye Bye!」怎麼做?
目標
- 讓我們的翻譯可以有占位符號 ( placeholder),如此以來可以讓句子的可變性變大,如前言所述一般。
- 當在語言包找不到符合的字句的時候,自動用備用的語言當替補。
我們將繼續用上一篇的程式來修改
完成的 Plunker
開工
語言包
先來看看我們的語言包最後長怎樣
// lang-en.ts
export const LANG_EN_NAME = 'en';
export const LANG_EN_TRANS = {
'hello world': 'hello world',
'hello greet': 'Hello, %0 %1!', // two placeholder
'well done': 'Well done %0', // one placeholder
'good bye': 'bye bye', // 只有在英文中定義
}
// lang-zh.ts
export const LANG_ZH_NAME = 'zh';
export const LANG_ZH_TRANS = {
'hello world': '你好,世界',
'hello greet': '你好, %0 %1!',
'well done': '幹得好, %0',
};
更新 translate 服務
// app/translate/translate.service.ts
...
public instant(key: string, words?: string | string[]) { // 增加可能的變數
const translation: string = this.translate(key);
if (!words) return translation;
return this.replace(translation, words); // 呼叫 replace
}
…
接著來定義replace
// app/translate/translate.service.ts
...
private PLACEHOLDER = '%'; // 我們的佔位符
public replace(word: string = '', words: string | string[] = '') {
let translation: string = word;
const values: string[] = [].concat(words);// 導入輸入的參數字串
values.forEach((e, i) => {
//會用 e 替換掉 %i: %0, %1, ...
translation = translation.replace(this.PLACEHOLDER.concat(<any>i), e);
});
return translation;
}
…
更新 Pipe
// app/translate/translate.pipe.ts
...
transform(value: string, args: string | string[]): any { // args 可以是單一字串或是字串列
if (!value) return;
return this._translate.instant(value, args); // 加入 args
}
…
更新模板
<!--app/app.component.html-->
<!-- 多值通道 –>
<p>
Translate <strong class="text-muted">Hello, %0 %1!</strong>:
<br>
<strong>{{ 'hello greet' | translate:['Jane', 'Doe'] }}</strong>
</p>
<!-- 單一值通道 –>
<p>
Translate <strong class="text-muted">Well done %0</strong>:
<br>
<strong>{{ 'well done' | translate:'John' }}</strong>
</p>
建立事件
// app/translate/translate.service.ts
...
import { Injectable, Inject, EventEmitter } from '@angular/core'; // 引入 event emitter
…
// 增加事件
public onLangChanged: EventEmitter<string> = new EventEmitter<string>();
…
// 選取用甚麼語言
public use(lang: string): void {
…
this.onLangChanged.emit(lang); // 發布變化
}
…
主程式
先移除 selectLang()
中的 refreshText()
,我們要重新架構。
// app/app.component.ts
...
ngOnInit() {
// 載入一些東西
…
this.subscribeToLangChanged(); // subscribe to language changes
// 設定目前語言
this.selectLang('zh');
}
selectLang(lang: string) {
// 設為預設
this._translate.use(lang);
// this.refreshText(); // 刪掉這行
}
subscribeToLangChanged() {
// 刷新文字
return this._translate.onLangChanged.subscribe(x => this.refreshText());
}
…
這樣設定之後,只要語言改變,就會自動更新
預設 & 備用語言
備用語言的概念是,假設預設是英文,當日文沒有字串時,就回去用預設的英文字串。
// app/translate/translate.service.ts
...
// 增加 3 個 properties
private _defaultLang: string;
private _currentLang: string;
private _fallback: boolean;
public get currentLang() {
return this._currentLang || this._defaultLang; // 當沒有當前選取語言時用預設語言
}
public setDefaultLang(lang: string) {
this._defaultLang = lang; // 設定預設
}
public enableFallback(enable: boolean) {
this._fallback = enable; // 是否啟用備用
}
private translate(key: string): string { // 翻譯傳入的字串
let translation = key;
// 發現目前選的語言有這個字串
if (this._translations[this.currentLang] && this._translations[this.currentLang][key]) {
return this._translations[this.currentLang][key];
}
// 不用備用
if (!this._fallback) {
return translation;
}
// 發現預設語言有這字串
if (this._translations[this._defaultLang] && this._translations[this._defaultLang][key]) {
return this._translations[this._defaultLang][key];
}
// 都沒有
return translation;
}
…
當然我們也可以
// app/app.component.ts
...
ngOnInit() {
// 可以做一些事情…
…
this.subscribeToLangChanged();
// 設定預設語言
this._translate.setDefaultLang('en'); // set English as default
this._translate.enableFallback(true); // enable fallback
// 設定目前要用的語言
this.selectLang('zh');
}
…
現在可以來測試語言包沒有字串時,是否會回去找預設的語言字串
<p>
Translate <strong class="text-muted">Good bye (fallback)</strong>:
<br>
<strong>{{ 'good bye' | translate }}</strong>
</p>
上面這一段,不管換西班牙語或是中文,都會回去找英文的字串來用