Angular + i18n多語系 + ngx-translate

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);
	})
}
語系:en
語系:cs-cz
語系:zh-cn
語系:zh-tw
  • HTML元素內文

在要翻譯的內文加上translate的pipeline。

<span>{{'Ui.Config.ConfigShowHide'|translate}}</span>

如果pipeline無法使用的話

記得去當前元件所屬的Module,把TranslateModule給imiport進去。

測試結果如下

語系:en
語系:zh-tw
語系:zh-cn
語系:zh-cn

 


Ref: