Angular + i18n多語系 + ngx-translate
前篇文章使用的是Angular + i18n。這篇則是在另外搭配ngx-translate。使用起來後也是覺得ngx-translate使用起來更為方便。
個人覺得與原生的Angular i18n有些差異的地方如下:
- 不必在元素上再加上i18n的attribute
- 翻譯檔改由json格式編輯,感覺更直觀. 畫面更加整潔。
- 建置後不會因多語系而分別產生對應的資料夾。試想,如果有3.40個語系,那一次建置要產生3.40個資料夾也是很可怕
套件安裝
npm install @ngx-translate/core @ngx-translate/http-loader
AppModule引用TranslateModule
這邊有一點要特別注意,就是i18n資料夾路徑的設定。以我的狀況來說,本機開發環境透過Angular渲染網頁,直接讀取根目錄下的assets/i18n資料夾即可,遠端測試機因為是透過MVC來渲染網頁,assets/i18n資料夾是放在Areas下,就不能使用本機環境的路徑,如果沒有換的話,到時發佈到遠端Server時,會一直出現找不到xxx.json的404錯誤。建議可以把路徑寫在環境變數裡。
本機測試環境:'./assets/i18n/'
遠端測試機Server:'PnpTest/Areas/Viewer/Scripts/wwwroot/assets/i18n/'
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { AppComponent } from './app.component';
// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
建立翻譯檔
在/assets資料夾下建立i18n資料夾,並在裡面建立不同語系的翻譯檔。
cs-cz.json(捷克語)
{
"Ui" : {
"Config" : {
"ConfigShowHide" : "Zobrazit/Skrýt"
}
},
"Message" : {
"NetNotPresent" : "Nalezený díl není na této vrstvě, změnit na opačnou ?"
}
}
en.json(英文)
{
"Ui" : {
"Config" : {
"ConfigShowHide" : "ConfigShowHide"
}
},
"Message" : {
"NetNotPresent" : "This NET is not present, whether to change the layer?"
}
}
zh-cn.json(簡體中文)
{
"Ui" : {
"Config" : {
"ConfigShowHide" : "显示/隐藏"
}
},
"Message" : {
"NetNotPresent" : "该NET不在目前层面,是否变更层面?"
}
}
zh-tw.json(繁體中文)
{
"Ui" : {
"Config" : {
"ConfigShowHide" : "顯示/隱藏"
}
},
"Message" : {
"NetNotPresent" : "該NET不在目前層面,是否變更層面?"
}
}
建立LanguageService處理多語系相關邏輯
this._translateService.addLangs(['en', 'cs-cz', 'zh-tw', 'zh-cn']);參數會對應到我們剛剛建立的.json翻譯檔(/assets/i18n/*.json)
import { Injectable } from '@angular/core';
import { MvcService } from './mvc.service';
import { TranslateService as NgxTranslateService } from '@ngx-translate/core';
import { Language } from '../models/static-model/language';
import { LocalStorageService } from './local-storage.service';
import { LocalStorage } from '../models/static-model/local-storage';
@Injectable({
providedIn: 'root'
})
export class LanguageService {
constructor(
private _translateService: NgxTranslateService,
private _localstorageService: LocalStorageService
) { }
setInitState()
{
this._translateService.addLangs(['en', 'cs-cz', 'zh-tw', 'zh-cn']);
this._translateService.setDefaultLang('en');
this.setLang();
}
setLang(lang: string)
{
this._translateService.use(lang);
}
translateKey(key: string): Promise<string> {
return new Promise((resolve) => {
this._translateService.get(key).subscribe((value) => {
resolve(value);
});
});
}
}
前端UI變更語系
正常來說應該是會有一個下拉選單來變更語系。大概會長這樣子
//config.component.html
<select id="ddlCultrue" (change)="onCultureChange($event)">
<option value="cs-cz"> čeština (Česko) </option>
<option value="en"> English </option>
<option value="zh-cn"> 中文(简体) </option>
<option value="zh-tw"> 中文(繁體) </option>
</select>
/**
* 切換語系
* @param data
*/
onCultureChange(locale: any)
{
const language = locale.target.value;
this._languageService.setLang(language);
window.location.href = `${environment.apiBaseUrl}/ChangeLang?mID=${this._mvcService.indexViewModel.MappingID}&lang=${language}&isNewPNP=1`;
}
setLang()
的參數一定要跟json翻譯檔的檔名要能對應! 不然會回應404找不到對應的json翻譯檔。下面的錯誤就是我把onCultureChange
的參數改成zh-tww,找不到zh-tw.json而導致的錯誤。
在app.component.ts裡面執行SetInitState()
//app.component.ts
constructor(
private _languageService: LanguageService
){}
ngOnInit()
{
省略...
this._languageService.setInitState();
}
測試
translateKey
的參數要記得按照json檔裡面的屬性層級來設定!
- Alert彈跳訊息
//app.component.ts
constructor(
private _languageService: LanguageService
){}
ngOnInit()
{
省略...
this._languageService.setInitState();
/**test */
this._languageService.translateKey("Message.NetNotPresent").then(msg => {
alert(msg);
})
}
- HTML元素內文
在要翻譯的內文加上translate的pipeline。
<span>{{'Ui.Config.ConfigShowHide'|translate}}</span>
如果pipeline無法使用的話
記得去當前元件所屬的Module,把TranslateModule給imiport進去。
測試結果如下
Ref: