首頁  >  文章  >  web前端  >  使用Angular如何實現國際化(詳細教學)

使用Angular如何實現國際化(詳細教學)

亚连
亚连原創
2018-06-13 10:06:122855瀏覽

本篇文章主要介紹了Angular 專案實現國際化的方法,現在分享給大家,也給大家做個參考。

正如angular官網所說,專案國際化是一件具有挑戰性,需要多方面的努力、持久的奉獻和決心的任務。
本文將介紹angular專案的國際化方案,涉及靜態文件(html)和ts文件文案的國際化。

背景

  1. Angular: 5.0

  2. Angular Cli: 1.6.1(1.5.x也可以)

  3. NG-ZORRO: 0.6.8

Angular i18n

#i18n模板翻譯流程有四個階段:

  1. 在元件範本中標記需要翻譯的靜態文字資訊(即打上i18n標籤)。

  2. Angular的i18n工具將標記的資訊提取到一個行業標準的翻譯源文件(如.xlf文件,使用ng xi18n)。

  3. 翻譯人員編輯該文件,翻譯提取出來的文本信息到目標語言,並將該文件還給你(需要翻譯人員接入,本文採用將xlf文件轉為json格式檔案輸出,最終將json檔案轉換回xlf格式檔案)。

  4. Angular編譯器匯入完成翻譯的文件,使用翻譯的文字取代原始訊息,並產生新的目標語言版本的應用程式。

你可以為每種支援的語言建置和部署單獨的專案版本,只需替換翻譯後的xlf檔案即可。

如何在範本檔案中使用?

i18n提供了幾種使用方式,也專門為單複數提供了翻譯方式(個人沒有使用,感覺不太方便)。接下來以一個單獨的html檔來介紹幾種使用方法。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Angular i18n</title>
</head>
<body>
  <h1 i18n="Site Header|An introduction header for i18n Project@@stTitle">Angular 国际化项目</h1>
  <p>
   <span i18n="@@agDescription">国际化是一项很具有挑战性,需要多方面的努力、持久的奉献和决心的任务。</span>
   <span class="delete" i18n-title="@@agDelete" title="删除"></span>
  </p>
  <p><ng-container i18n=@@agLetGo>让我们现在开始吧!</ng-container>朋友!</p>
</body>
</html>

上述程式碼展示了幾種i18n的使用方式:

1、使用i18n屬性標記(可添加上說明性文案,格式如:title|description@@id,title和description可協助翻譯人員更能理解文案意義,是否新增取決於自身專案情況)

可以在靜態標籤上直接打上i18n的tag,如

<span i18n="@@agDescription"></span>

產生的xlf(xml )欄位格式為

<trans-unit id="agDescription" datatype="html">
 <source>国际化是一项很具有挑战性,需要多方面的努力、持久的奉献和决心的任务。</source>
 <context-group purpose="location">
  <context context-type="sourcefile">xxx.ts</context>
  <context context-type="linenumber">linenum</context>
 </context-group>
</trans-unit>

2、為title新增i18n屬性

對於html標籤屬性,同樣可以新增i18n,如

<span class="delete" i18n-title="@@agDelete" title="删除"></span>

產生的xlf(xml)格式同上

3、翻譯文本,而不必創建元素

我們有時會出現一句話多個斷句情況,如果每次都添加span、label這些元素包裹的話,可能嚴重影響頁面佈局,這時候我們可以使用ng-container來包裹需要翻譯的文案。

<p>
  <ng-container i18n=@@agLetGo>让我们现在开始吧!</ng-container>朋友!
</p>

在頁面顯示為

<p>
  <!---->
  LET&#39;S GO朋友!
</p>

* ng-container變成註解區塊,這樣做不會影響頁面佈局(尤其是應用了style樣式的情況)

貼上標籤後,我們只要執行ng xi18n即可自動建立出xlf文件,通常為message.xlf,如需自訂,可自行前往Angular CLI 官網查看。

XLF與JSON轉換

xlf轉json方法

我個人是採用xml2js庫來操作,簡單程式碼如下:

const fs = require(&#39;fs&#39;);
xml2js = require(&#39;xml2js&#39;);
var parser = new xml2js.Parser();
fs.readFile(fileName, &#39;utf8&#39;, (err, data) => {
 parser.parseString(data, function (err, result) {
  // 读取新文件全部需要翻译的数据,并对比已翻译的进行取舍,具体转换成的格式结构可自行查看
  result[&#39;xliff&#39;][&#39;file&#39;][0][&#39;body&#39;][0][&#39;trans-unit&#39;].forEach((item) => {
   var itemFormat = {
    "key" : item[&#39;$&#39;][&#39;id&#39;],
    "value": item[&#39;source&#39;][0]
   };
   // 执行相关操作,key-value形式是为了统一翻译文件结构,可按需定义
  })
 });
});

json轉xlf方法

function backToXLF(translatedParams) {
 // 文件格式可自行参考angular.cn官网的例子
 var xlfFormat = {
  "xliff": {
   "$"  : {
    "version": "1.2",
    "xmlns" : "urn:oasis:names:tc:xliff:document:1.2"
   },
   "file": [
    {
     "$"  : {
      "source-language": "en",
      "datatype"    : "plaintext",
      "original"    : "ng2.template"
     },
     "body": [
      {
       "trans-unit": []
      }
     ]
    }
   ]
  }
 };
 if (translatedParams instanceof Array) {
  // 获取原始名称
  translatedParams.forEach((data) => {
   var tmp = {
    "$"   : {
     "id"   : data.key,
     "datatype": "html"
    },
    "source": [i18nItemsOrigin[data.key]], // 这里的i18nItemsOrigin是json格式,属性名为key值,表示原始文案
    "target": [data.value]
   };
   // 数组,json项
   xlfFormat[&#39;xliff&#39;][&#39;file&#39;][0][&#39;body&#39;][0][&#39;trans-unit&#39;].push(tmp);
  });
 }
 var builder = new xml2js.Builder();
 var xml = builder.buildObject(xlfFormat);
 return xml;
}

這樣提取文案資訊和轉換翻譯後的檔案就完成了,接下來我們需要把翻譯好的文案應用到專案中去。

部署翻譯檔案

src目錄下新建locale資料夾,將翻譯轉換後的demo.en-US.xlf檔案存在改目錄下

app資料夾下新建i18n-providers.ts

import {
 LOCALE_ID,
 MissingTranslationStrategy,
 StaticProvider,
 TRANSLATIONS,
 TRANSLATIONS_FORMAT
} from &#39;@angular/core&#39;;
import { CompilerConfig } from &#39;@angular/compiler&#39;;
import { Observable } from &#39;rxjs/Observable&#39;;
import { LOCALE_LANGUAGE } from &#39;./app.config&#39;; // 自行定义配置位置

export function getTranslationProviders(): Promise<StaticProvider[]> {

 // get the locale string from the document
 const locale = LOCALE_LANGUAGE.toString();

 // return no providers
 const noProviders: StaticProvider[] = [];

 // no locale or zh-CN: no translation providers
 if (!locale || locale === &#39;zh-CN&#39;) {
  return Promise.resolve(noProviders);
 }

 // Ex: &#39;locale/demo.zh-MO.xlf`
 const translationFile = `./locale/demo.${locale}.xlf`;

 return getTranslationsWithSystemJs(translationFile)
  .then((translations: string) => [
   { provide: TRANSLATIONS, useValue: translations },
   { provide: TRANSLATIONS_FORMAT, useValue: &#39;xlf&#39; },
   { provide: LOCALE_ID, useValue: locale },
   {
    provide: CompilerConfig,
    useValue: new CompilerConfig({ missingTranslation: MissingTranslationStrategy.Error })
   }
  ]).catch(() => noProviders); // ignore if file not found
}

declare var System: any;
// 获取locale文件
function getTranslationsWithSystemJs(file: string) {
 let text = &#39;&#39;;
 const fileRequest = new XMLHttpRequest();
 fileRequest.open(&#39;GET&#39;, file, false);
 fileRequest.onerror = function (err) {
  console.log(err);
 };
 fileRequest.onreadystatechange = function () {
  if (fileRequest.readyState === 4) {
   if (fileRequest.status === 200 || fileRequest.status === 0) {
    text = fileRequest.responseText;
   }
  }
 };
 fileRequest.send();
 const observable = Observable.of(text);
 const prom = observable.toPromise();
 return prom;
}

main.ts檔案修改為

import { enableProdMode } from &#39;@angular/core&#39;;
import { platformBrowserDynamic } from &#39;@angular/platform-browser-dynamic&#39;;

import { AppModule } from &#39;./app/app.module&#39;;
import { environment } from &#39;./environments/environment&#39;;
import { getTranslationProviders } from &#39;./app/i18n-providers&#39;;

if (environment.production) {
 enableProdMode();
}

getTranslationProviders().then(providers => {
 const options = { providers };
 platformBrowserDynamic().bootstrapModule(AppModule, options)
  .catch(err => console.log(err));
});

別忘了將locale目錄加入.angular-cli.json裡,來單獨打包。

這樣我們對靜態文案的翻譯工作基本上已經完成了,但是有些動態文案如ts文件裡的文案或者第三方框架屬性該如何翻譯呢?以下會介紹針對 ts 檔案和 NG-ZORRO 框架實作動態文案翻譯的方案。

ts檔案文案和NG-ZORRO框架文案翻譯

#具體思路

透過Pipe呼叫Service方法,根據對應的唯一id值來匹配json物件裡的翻譯結果,進而返回渲染到前端,參考於NG-ZORRO框架的國際化實現方案。

首先我們定義一下json翻譯物件的格式,全部為三層結構,動態變數需要按%%包裹,這樣做的原因是和項目結構相關聯,也便於後期和i18n方式格式統一。

{
  "app": {
    "base": {
      "hello": "文件文案",
      "userCount": "一共%num%人"
    }
  }
}

格式已定,我們繼續定義Service處理方式

這裡復用NG-ZORRO的國際化方案 ,可以簡化我們的開發,有興趣的可以參考一下其原始碼。

*** TranslateService ***
import { Injectable } from &#39;@angular/core&#39;;
// 引入语言配置和国际化文件文案对象
import { LOCALE_LANGUAGE } from &#39;../app.config&#39;;
import { enUS } from &#39;../locales/demo.en-US&#39;;
import { zhCN } from &#39;../locales/stream.zh-CN&#39;;

@Injectable()
export class TranslateService {

 private _locale = LOCALE_LANGUAGE.toString() === &#39;zh-CN&#39; ? zhCN : enUS;

 constructor() {
 }
 // path为app.base.hello格式的字符串,这里按json层级取匹配改变量
 translate(path: string, data?: any): string {
  let content = this._getObjectPath(this._locale, path) as string;
  if (typeof content === &#39;string&#39;) {
   if (data) {
    Object.keys(data).forEach((key) => content = content.replace(new RegExp(`%${key}%`, &#39;g&#39;), data[key]));
   }
   return content;
  }
  return path;
 }

 private _getObjectPath(obj: object, path: string): string | object {
  let res = obj;
  const paths = path.split(&#39;.&#39;);
  const depth = paths.length;
  let index = 0;
  while (res && index < depth) {
   res = res[paths[index++]];
  }
  return index === depth ? res : null;
 }
}

這樣,只需要在Pipe中呼叫Service的translate方法即可

*** NzTranslateLocalePipe ***
import { Pipe, PipeTransform } from &#39;@angular/core&#39;;
import { TranslateService } from &#39;../services/translate.service&#39;;

@Pipe({
 name: &#39;nzTranslateLocale&#39;
})
export class NzTranslateLocalePipe implements PipeTransform {
 constructor(private _locale: TranslateService) {
 }

 transform(path: string, keyValue?: object): string {
  return this._locale.translate(path, keyValue);
 }
}

好了,現在我們處理邏輯已經完全結束了,下面介紹一下如何使用

*** NG-ZORRO 控件 ***
<nz-input [nzPlaceHolder]="&#39;app.base.hello&#39;|nzTranslateLocale"></nz-input> // 无动态参数
<nz-popconfirm [nzTitle]="&#39;app.base.userCount&#39;|nzTranslateLocale: {num:users.length}" ...>
... // 有动态参数
</nz-popconfirm>

*** ts文件 ***
export class AppComponent implements OnInit {
 demoTitle=&#39;&#39;;
 users = [&#39;Jack&#39;, &#39;Johnson&#39;, &#39;Lucy&#39;];
 constructor(privete translateService: TranslateService) {
 }
 ngOnInit() {
  this.demoTitle = this.translateService.translate(&#39;app.base.hello&#39;);
 }
}

以上流程基本上能滿足大部分angular專案的國際化需求,如果需要更加複雜的國際化情況,歡迎討論。

總結

Angular到5.0的國際化已經相對來說簡便了很多,我們只需要在合適的地方打上i18n的tag即可方便快速地提取需要翻譯文案,具體如何處理翻譯後的檔案因人而異,多種方法可協助我們轉換(如本文透過nodejs)。

複雜一點的是無法透過打i18n標籤來翻譯的文本,NG-ZORRO的國際化方案彌補了這方面的不足,結合起來可以很方便地完成專案的國際化。國際化如果沒有專門的團隊支持,翻譯難度很大,需要考慮的東西很多,比如繁體還有澳門繁體、台灣繁體等,語法也不盡相同。

參考目錄

Angular的國際化(i18n)線上範例
NG-ZORRO Locale 國際化

上面是我整理給大家的,希望今後對大家有幫助。

相關文章:

用JQuery如何實作表單驗證,具體該怎麼做?

使用Vue如何設定多個Class

在SpringMVC中post如何取得多選框value的值(程式碼實例)

jQuery SpringMVC中的複選框選擇與傳值實例_jquery

以上是使用Angular如何實現國際化(詳細教學)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn