微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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文件来介绍几种使用方法

rush:xhtml;"> <Meta charset="utf-8"> Angular i18n

删除">

让我们现在开始吧!朋友!

上述代码展示了几种i18n的使用方式:

1、使用i18n属性标记(可添加上说明性文案,格式如:title|description@@id,title和description可帮助翻译人员更好地理解文案含义,是否添加取决于自身项目情况)

可以在静态标签上直接打上i18n的tag,如

rush:xhtml;">

生成的xlf(xml)字段格式为

rush:xml;"> 国际化是一项很具有挑战性,需要多方面的努力、持久的奉献和决心的任务。

2、为title添加i18n属性

对于html标签属性,同样可以添加i18n,如

rush:xhtml;"> 删除">

生成的xlf(xml)格式同上

3、翻译文本,而不必创建元素

我们有时候会出现一句话多个断句情况,如果每次都添加span、label这些元素包裹的话,可能严重影响页面布局,这时候我们可以使用ng-container来包裹需要翻译的文案。

rush:xhtml;">

让我们现在开始吧!朋友!

页面显示

rush:xhtml;">

LET'S GO朋友!

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

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

XLF与JSON转换

xlf转json方法

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

{ 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方法

{ 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

rush:js;"> 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 { 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方式格式统一。

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

格式已定,我们继续定义Service处理方式

这里复用NG-ZORRO的国际化方案,可以简化我们的开发,有兴趣的可以参看一下其源码。

@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方法即可

rush:js;"> *** 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);
}
}

好了,现在我们处理逻辑已经完全结束了,下面介绍一下如何使用

rush:js;"> *** NG-ZORRO 控件 *** // 无动态参数 ... // 有动态参数

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 国际化

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐