资讯专栏INFORMATION COLUMN

Angular 项目 国际化

cncoder / 1342人阅读

摘要:正如官网所说,项目国际化是一件具有挑战性,需要多方面的努力持久的奉献和决心的任务。本文将介绍项目的国际化方案,涉及静态文件和文件文案的国际化。参考目录的国际化在线例子国际化

正如angular官网所说,项目国际化是一件具有挑战性,需要多方面的努力、持久的奉献和决心的任务。
本文将介绍angular项目的国际化方案,涉及静态文件(html)和ts文件文案的国际化。

背景

Angular: 5.0

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

NG-ZORRO: 0.6.8

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

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

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

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

Angular编译器导入完成翻译的文件,使用翻译的文本替换原始信息,并生成新的目标语言版本的应用程序。

你可以为每种支持的语言构建和部署多带带的项目版本,仅需替换翻译后的xlf文件即可。
如何在模板文件中使用?

i18n提供了几种使用方式,还专门为单复数提供了翻译方式(个人没有使用,感觉不太方便)。接下来以一个多带带的html文件来介绍几种使用方法。





Angular i18n


    

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"S GO朋友!

* 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;
}

这样提取文案信息和转换翻译后的文件就完成了,接下来我们需要把翻译好的文案应用到项目中去。
部署翻译文件

JIT模式
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 {

  // 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里,来多带带打包。

AOT模式(推荐)

对于AOT模式打包的持续来说,不需要上述复杂的配置,只需要在原有build基础上,加上相应的i18n文件即可,如:

ng build --prod --build-optimizer -bh / --i18n-format=xlf --locale=en --i18n-file=./src/locale/demo.en-US.xlf

这样打出的包会自动将翻译文件应用到项目中。

这样我们对静态文案的翻译工作基本已经完成了,但是有些动态文案如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 控件 ***
 // 无动态参数

... // 有动态参数


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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90691.html

相关文章

  • 关于angular2.0的i18n际化的解决方法,分享一下给大家,有错大家指出来互相学习哈。。。

    摘要:关于的,网上的资料也不多,刚好项目需要用到,就自己去查阅各种资料,自己整理了出来,分享下出来给大家吧。 关于angular2的i18n,网上的资料也不多,刚好项目需要用到,就自己去查阅各种资料,自己整理了出来,分享下出来给大家吧。废话不多说!直接上代码: 首先我们肯定要新建一个文件,叫aaa(网上angular-cli教程很多),cmd打开命令进入到随便一个目录底下: ng new a...

    娣辩孩 评论0 收藏0
  • 关于angular2.0的i18n际化的解决方法,分享一下给大家,有错大家指出来互相学习哈。。。

    摘要:关于的,网上的资料也不多,刚好项目需要用到,就自己去查阅各种资料,自己整理了出来,分享下出来给大家吧。 关于angular2的i18n,网上的资料也不多,刚好项目需要用到,就自己去查阅各种资料,自己整理了出来,分享下出来给大家吧。废话不多说!直接上代码: 首先我们肯定要新建一个文件,叫aaa(网上angular-cli教程很多),cmd打开命令进入到随便一个目录底下: ng new a...

    alaege 评论0 收藏0
  • Angular 5.0 来了! 有这些大变化

    摘要:以下简单介绍的重大变化。状态转交及对的支持这样更便于在服务端和客户之间共享应用状态。状态转交的相关文档几周后会发布。我们删除很多以前废弃的如,也公布了一些新的废弃项。以上指南会详细介绍这些变更。已知问题当前已知与相关的问题。 我们很高兴地宣布Angular 5.0.0——五角形甜甜圈发布啦!这又是一个主版本,包含新功能并修复了很多bug。它再次体现了我们把Angular做得更小、更快、...

    张红新 评论0 收藏0

发表评论

0条评论

cncoder

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<