NG-ZORRO: 0.6.8

Angular i18n

i18n模板翻译流程有四个阶段:

在组件模板中标记需要翻译的静态文本信息(即打上i18n标签)。

Angular的i18n工具将标记的信息提取到一个行业标准的翻译源文件(如.xlf文件,使用ng xi18n)。

翻译人员编辑该文件,翻译提取出来的文本信息到目标语言,并将该文件还给你(需要翻译人员接入,本文采用将xlf文件转为json格式文件输出,最终将json文件转换回xlf格式文件)。

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'S GO朋友! </p>

* ng-container变为了注释块,这样做不会影响页面布局(尤其是应用了style样式的情况)

打上标签后,我们只要执行ng xi18n即可自动创建出xlf文件,通常为message.xlf,如需自定义,可自行前往 Angular CLI 官网查看。

XLF与JSON转换

xlf转json方法

我个人是采用xml2js库进行操作,简单代码如下:

const fs = require('fs'); xml2js = require('xml2js'); var parser = new xml2js.Parser(); fs.readFile(fileName, 'utf8', (err, data) => { parser.parseString(data, function (err, result) { // 读取新文件全部需要翻译的数据,并对比已翻译的进行取舍,具体转换成的格式结构可自行查看 result['xliff']['file'][0]['body'][0]['trans-unit'].forEach((item) => { var itemFormat = { "key" : item['$']['id'], "value": item['source'][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['xliff']['file'][0]['body'][0]['trans-unit'].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 '@angular/core'; import { CompilerConfig } from '@angular/compiler'; import { Observable } from 'rxjs/Observable'; import { LOCALE_LANGUAGE } from './app.config'; // 自行定义配置位置 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 === 'zh-CN') { return Promise.resolve(noProviders); } // Ex: '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: 'xlf' }, { 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 = ''; const fileRequest = new XMLHttpRequest(); fileRequest.open('GET', 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; }