Angular 项目实现国际化的方法
正如angular官网所说,项目国际化是一件具有挑战性,需要多方面的努力、持久的奉献和决心的任务。
本文将介绍angular项目的国际化方案,涉及静态文件(html)和ts文件文案的国际化。
背景
- Angular:5.0
- AngularCli:1.6.1(1.5.x也可以)
- NG-ZORRO:0.6.8
Angulari18n
i18n模板翻译流程有四个阶段:
- 在组件模板中标记需要翻译的静态文本信息(即打上i18n标签)。
- Angular的i18n工具将标记的信息提取到一个行业标准的翻译源文件(如.xlf文件,使用ngxi18n)。
- 翻译人员编辑该文件,翻译提取出来的文本信息到目标语言,并将该文件还给你(需要翻译人员接入,本文采用将xlf文件转为json格式文件输出,最终将json文件转换回xlf格式文件)。
- Angular编译器导入完成翻译的文件,使用翻译的文本替换原始信息,并生成新的目标语言版本的应用程序。
你可以为每种支持的语言构建和部署单独的项目版本,仅需替换翻译后的xlf文件即可。
如何在模板文件中使用?
i18n提供了几种使用方式,还专门为单复数提供了翻译方式(个人没有使用,感觉不太方便)。接下来以一个单独的html文件来介绍几种使用方法。
Angulari18n Angular国际化项目
国际化是一项很具有挑战性,需要多方面的努力、持久的奉献和决心的任务。
让我们现在开始吧!朋友!
上述代码展示了几种i18n的使用方式:
1、使用i18n属性标记(可添加上说明性文案,格式如:title|description@@id,title和description可帮助翻译人员更好地理解文案含义,是否添加取决于自身项目情况)
可以在静态标签上直接打上i18n的tag,如
生成的xlf(xml)字段格式为
xxx.ts linenum
2、为title添加i18n属性
对于html标签属性,同样可以添加i18n,如
生成的xlf(xml)格式同上
3、翻译文本,而不必创建元素
我们有时候会出现一句话多个断句情况,如果每次都添加span、label这些元素包裹的话,可能严重影响页面布局,这时候我们可以使用ng-container来包裹需要翻译的文案。
让我们现在开始吧!朋友!
在页面显示为
LET'SGO朋友!
*ng-container变为了注释块,这样做不会影响页面布局(尤其是应用了style样式的情况)
打上标签后,我们只要执行ngxi18n即可自动创建出xlf文件,通常为message.xlf,如需自定义,可自行前往AngularCLI官网查看。
XLF与JSON转换
xlf转json方法
我个人是采用xml2js库进行操作,简单代码如下:
constfs=require('fs'); xml2js=require('xml2js'); varparser=newxml2js.Parser(); fs.readFile(fileName,'utf8',(err,data)=>{ parser.parseString(data,function(err,result){ //读取新文件全部需要翻译的数据,并对比已翻译的进行取舍,具体转换成的格式结构可自行查看 result['xliff']['file'][0]['body'][0]['trans-unit'].forEach((item)=>{ varitemFormat={ "key":item['$']['id'], "value":item['source'][0] }; //执行相关操作,key-value形式是为了统一翻译文件结构,可按需定义 }) }); });
json转xlf方法
functionbackToXLF(translatedParams){ //文件格式可自行参考angular.cn官网的例子 varxlfFormat={ "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(translatedParamsinstanceofArray){ //获取原始名称 translatedParams.forEach((data)=>{ vartmp={ "$":{ "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); }); } varbuilder=newxml2js.Builder(); varxml=builder.buildObject(xlfFormat); returnxml; }
这样提取文案信息和转换翻译后的文件就完成了,接下来我们需要把翻译好的文案应用到项目中去。
部署翻译文件
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';//自行定义配置位置 exportfunctiongetTranslationProviders():Promise{ //getthelocalestringfromthedocument constlocale=LOCALE_LANGUAGE.toString(); //returnnoproviders constnoProviders:StaticProvider[]=[]; //nolocaleorzh-CN:notranslationproviders if(!locale||locale==='zh-CN'){ returnPromise.resolve(noProviders); } //Ex:'locale/demo.zh-MO.xlf` consttranslationFile=`./locale/demo.${locale}.xlf`; returngetTranslationsWithSystemJs(translationFile) .then((translations:string)=>[ {provide:TRANSLATIONS,useValue:translations}, {provide:TRANSLATIONS_FORMAT,useValue:'xlf'}, {provide:LOCALE_ID,useValue:locale}, { provide:CompilerConfig, useValue:newCompilerConfig({missingTranslation:MissingTranslationStrategy.Error}) } ]).catch(()=>noProviders);//ignoreiffilenotfound } declarevarSystem:any; //获取locale文件 functiongetTranslationsWithSystemJs(file:string){ lettext=''; constfileRequest=newXMLHttpRequest(); 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(); constobservable=Observable.of(text); constprom=observable.toPromise(); returnprom; }
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=>{ constoptions={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() exportclassTranslateService{ private_locale=LOCALE_LANGUAGE.toString()==='zh-CN'?zhCN:enUS; constructor(){ } //path为app.base.hello格式的字符串,这里按json层级取匹配改变量 translate(path:string,data?:any):string{ letcontent=this._getObjectPath(this._locale,path)asstring; if(typeofcontent==='string'){ if(data){ Object.keys(data).forEach((key)=>content=content.replace(newRegExp(`%${key}%`,'g'),data[key])); } returncontent; } returnpath; } private_getObjectPath(obj:object,path:string):string|object{ letres=obj; constpaths=path.split('.'); constdepth=paths.length; letindex=0; while(res&&index这样,只需要在Pipe中调用Service的translate方法即可
***NzTranslateLocalePipe*** import{Pipe,PipeTransform}from'@angular/core'; import{TranslateService}from'../services/translate.service'; @Pipe({ name:'nzTranslateLocale' }) exportclassNzTranslateLocalePipeimplementsPipeTransform{ constructor(private_locale:TranslateService){ } transform(path:string,keyValue?:object):string{ returnthis._locale.translate(path,keyValue); } }好了,现在我们处理逻辑已经完全结束了,下面介绍一下如何使用
***NG-ZORRO控件***//无动态参数 ...//有动态参数 ***ts文件*** exportclassAppComponentimplementsOnInit{ demoTitle=''; users=['Jack','Johnson','Lucy']; constructor(privetetranslateService:TranslateService){ } ngOnInit(){ this.demoTitle=this.translateService.translate('app.base.hello'); } }以上流程基本上能满足大部分angular项目的国际化需求,如果需要更加复杂的国际化情况,欢迎讨论。
总结
Angular到5.0的国际化已经相对来说简便了很多,我们只需要在合适的地方打上i18n的tag即可方便快速地提取需要翻译文案,具体如何处理翻译后的文件因人而异,多种方法可帮助我们转换(如本文通过nodejs)。
复杂一点的是无法通过打i18n标签来翻译的文本,NG-ZORRO的国际化方案弥补了这方面的不足,结合起来可以很方便地完成项目的国际化。国际化如果没有专门的团队支持,翻译难度很大,需要考虑的东西很多,比如繁体还有澳门繁体、台湾繁体等,语法也不尽相同。
参考目录
Angular的国际化(i18n)在线例子
NG-ZORROLocale国际化以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。