正如 angular 官网所说,项目国际化是一件具有挑战性,需要多方面的努力、持久的奉献和决心的任务。
本文将介绍 angular 项目的国际化方案,涉及静态文件 (html) 和 ts 文件文案的国际化。
i18n 模板翻译流程有四个阶段:
- 你可以为每种支持的语言构建和部署单独的项目版本,仅需替换翻译后的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 方法
- 我个人是采用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[''
- 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;
- }
- main.ts文件修改为
- import { enableProdMode } from '@angular/core';
- import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
- import { AppModule } from './app/app.module';
- import { environment } from './environments/environment';
- import { getTranslationProviders } from './app/i18n-providers';
- 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 '@angular/core';
- // 引入语言配置和国际化文件文案对象
- import { LOCALE_LANGUAGE } from '../app.config';
- import { enUS } from '../locales/demo.en-US';
- import { zhCN } from '../locales/stream.zh-CN';
- @Injectable()
- export class TranslateService {
- private _locale = LOCALE_LANGUAGE.toString() === 'zh-CN' ? 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 === 'string') {
- if (data) {
- Object.keys(data).forEach((key) => content = content.replace(new RegExp(`%${key}%`, 'g'), data[key]));
- }
- return content;
- }
- return path;
- }
- private _getObjectPath(obj: object, path: string): string | object {
- let res = obj;
- const paths = path.split('.');
- 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 '@angular/core';
- import { TranslateService } from '../services/translate.service';
- @Pipe({
- name: 'nzTranslateLocale'
- })
- 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]="'app.base.hello'|nzTranslateLocale"></nz-input> // 无动态参数
- <nz-popconfirm [nzTitle]="'app.base.userCount'|nzTranslateLocale: {num:users.length}" ...>
- ... // 有动态参数
- </nz-popconfirm>
- *** ts文件 ***
- export class AppComponent implements OnInit {
- demoTitle='';
- users = ['Jack', 'Johnson', 'Lucy'];
- constructor(privete translateService: TranslateService) {
- }
- ngOnInit() {
- this.demoTitle = this.translateService.translate('app.base.hello');
- }
- }
- 以上流程基本上能满足大部分angular项目的国际化需求,如果需要更加复杂的国际化情况,欢迎讨论。
- 总结
- Angular到5.0的国际化已经相对来说简便了很多,我们只需要在合适的地方打上i18n的tag即可方便快速地提取需要翻译文案,具体如何处理翻译后的文件因人而异,多种方法可帮助我们转换(如本文通过nodejs)。
- 复杂一点的是无法通过打i18n标签来翻译的文本,NG-ZORRO的国际化方案弥补了这方面的不足,结合起来可以很方便地完成项目的国际化。 国际化如果没有专门的团队支持,翻译难度很大,需要考虑的东西很多,比如繁体还有澳门繁体、台湾繁体等,语法也不尽相同。
- 参考目录
- Angular的国际化(i18n)在线例子
- NG-ZORRO Locale 国际化
- 1 天前发布
- 赞 | 1 收藏 | 1
- 你可能感兴趣的文章
- 使用Profile创建第一个Grails+Angular2应用
- 1.1k
- 浏览
- Elixir Phoenix: Gettext I18N
- 720
- 浏览
- 一个简单且灵活易用的 React 格式化和 i18n 工具
- 716
- 浏览
- 评论
- 默认排序 时间排序
- 发布评论
- 关注作者
- Jason
- 6 声望
- ]['id'],
- "value": item['source'][0]
- };
- // 执行相关操作,key-value形式是为了统一翻译文件结构,可按需定义
- })
- });
- });
{$76} {$75} {$73}
src 目录下新建 locale 文件夹,将翻译转换后的 demo.en-US.xlf 文件存在改目录下
app 文件夹下新建 i18n-providers.ts
{$72}
main.ts 文件修改为
{$70}
这样我们对静态文案的翻译工作基本已经完成了,但是有些动态文案如 ts 文件里的文案或者第三方框架属性该如何翻译呢?下面会介绍针对 ts 文件和 {$68} 框架实现动态文案翻译的方案。
{$67} {$66} {$65}
首先我们定义一下 json 翻译对象的格式, 全部为三层结构, 动态变量需要按 %% 包裹,这样做的原因是和项目结构相关联,也便于后期和 i18n 方式格式统一。
{$63}
格式已定,我们继续定义 Service 处理方式
这里复用 {$61} , 可以简化我们的开发, 有兴趣的可以参看一下其源码。
{$60}
这样,只需要在 Pipe 中调用 Service 的 translate 方法即可
{$58}
好了,现在我们处理逻辑已经完全结束了,下面介绍一下如何使用
{$56}
以上流程基本上能满足大部分 angular 项目的国际化需求,如果需要更加复杂的国际化情况,欢迎讨论。
{$54}
Angular 到 5.0 的国际化已经相对来说简便了很多,我们只需要在合适的地方打上 i18n 的 tag 即可方便快速地提取需要翻译文案,具体如何处理翻译后的文件因人而异,多种方法可帮助我们转换 (如本文通过 nodejs)。
复杂一点的是无法通过打 i18n 标签来翻译的文本,NG-ZORRO 的国际化方案弥补了这方面的不足,结合起来可以很方便地完成项目的国际化。 国际化如果没有专门的团队支持,翻译难度很大,需要考虑的东西很多,比如繁体还有澳门繁体、台湾繁体等,语法也不尽相同。
{$53} {$52}
{$47}
{$38}{$34}
{$30}
{$29} {$28} {$18}
{$17}
{$16} {$15}
{$13}{$12}{$5}
{$4} {$2}
6 声望
来源: https://segmentfault.com/a/1190000012720393