Home  >  Article  >  Web Front-end  >  How to implement internationalization using Angular (detailed tutorial)

How to implement internationalization using Angular (detailed tutorial)

亚连
亚连Original
2018-06-13 10:06:122910browse

This article mainly introduces the method of realizing internationalization of Angular projects. Now I will share it with you and give you a reference.

As the angular official website says, project internationalization is a challenging task that requires multi-faceted efforts, lasting dedication and determination.
This article will introduce the internationalization plan of the angular project, involving the internationalization of static files (html) and ts file copywriting.

Background

  1. ##Angular: 5.0

  2. Angular Cli: 1.6.1(1.5.x Also available)

  3. NG-ZORRO: 0.6.8

Angular i18n

i18n template The translation process has four stages:

  1. Mark the static text information that needs to be translated in the component template (that is, add the i18n tag).

  2. Angular's i18n tool extracts the tagged information into an industry-standard translation source file (such as an .xlf file, using ng xi18n).

  3. The translator edits the file, translates the extracted text information into the target language, and returns the file to you (translator access is required. This article uses converting xlf files to json format file output, and finally convert the json file back to xlf format file).

  4. The Angular compiler imports the translated file, replaces the original information with the translated text, and generates a new target language version of the application.

You can build and deploy separate project versions for each supported language by simply replacing the translated xlf files.

How to use it in template file?

i18n provides several ways to use it, and also provides translation methods for singular and plural numbers (I haven’t used it personally, and it feels inconvenient). Next, a separate html file will be used to introduce several usage methods.

<!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>

The above code shows several ways to use i18n:

1. Use i18n attribute tags (explanatory copy can be added, the format is such as: title|description@@id, title and description can help translators better understand the meaning of the copywriting. Whether to add it depends on the situation of your own project)

You can directly tag the i18n tag on the static tag, such as the xlf(xml generated by

<span i18n="@@agDescription"></span>

) field format is

<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. Add the i18n attribute to the title

For the html tag attribute, you can also add i18n, such as the xlf (xml) format generated by

<span class="delete" i18n-title="@@agDelete" title="删除"></span>

is the same as above

3. Translate text without creating elements

We sometimes have multiple sentence fragments in one sentence. If we add elements such as span and label every time, it may seriously affect the page. Layout, at this time we can use ng-container to wrap the copy that needs to be translated.

<p>
  <ng-container i18n=@@agLetGo>让我们现在开始吧!</ng-container>朋友!
</p>

is displayed on the page as

<p>
  <!---->
  LET&#39;S GO朋友!
</p>

* ng-container becomes a comment block, which will not affect the page layout (especially when style is applied)

After tagging, we only need to execute ng xi18n to automatically create an xlf file, usually message.xlf. If you need to customize it, you can go to the Angular CLI official website to view it.

XLF and JSON conversion

xlf to json method

I personally use the xml2js library to operate, the simple code is as follows:

const fs = require(&#39;fs&#39;);
xml2js = require(&#39;xml2js&#39;);
var parser = new xml2js.Parser();
fs.readFile(fileName, &#39;utf8&#39;, (err, data) => {
 parser.parseString(data, function (err, result) {
  // 读取新文件全部需要翻译的数据,并对比已翻译的进行取舍,具体转换成的格式结构可自行查看
  result[&#39;xliff&#39;][&#39;file&#39;][0][&#39;body&#39;][0][&#39;trans-unit&#39;].forEach((item) => {
   var itemFormat = {
    "key" : item[&#39;$&#39;][&#39;id&#39;],
    "value": item[&#39;source&#39;][0]
   };
   // 执行相关操作,key-value形式是为了统一翻译文件结构,可按需定义
  })
 });
});

json to xlf method

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[&#39;xliff&#39;][&#39;file&#39;][0][&#39;body&#39;][0][&#39;trans-unit&#39;].push(tmp);
  });
 }
 var builder = new xml2js.Builder();
 var xml = builder.buildObject(xlfFormat);
 return xml;
}

In this way, the extraction of copy information and conversion of the translated file are completed. Next, we need to apply the translated copy to the project.

Deploy translation files

Create a locale folder in the src directory, and save the translated demo.en-US.xlf file in the changed directory

Create a new i18n-providers.ts in the app folder

import {
 LOCALE_ID,
 MissingTranslationStrategy,
 StaticProvider,
 TRANSLATIONS,
 TRANSLATIONS_FORMAT
} from &#39;@angular/core&#39;;
import { CompilerConfig } from &#39;@angular/compiler&#39;;
import { Observable } from &#39;rxjs/Observable&#39;;
import { LOCALE_LANGUAGE } from &#39;./app.config&#39;; // 自行定义配置位置

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 === &#39;zh-CN&#39;) {
  return Promise.resolve(noProviders);
 }

 // Ex: &#39;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: &#39;xlf&#39; },
   { 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 = &#39;&#39;;
 const fileRequest = new XMLHttpRequest();
 fileRequest.open(&#39;GET&#39;, 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 file and modify it to

import { enableProdMode } from &#39;@angular/core&#39;;
import { platformBrowserDynamic } from &#39;@angular/platform-browser-dynamic&#39;;

import { AppModule } from &#39;./app/app.module&#39;;
import { environment } from &#39;./environments/environment&#39;;
import { getTranslationProviders } from &#39;./app/i18n-providers&#39;;

if (environment.production) {
 enableProdMode();
}

getTranslationProviders().then(providers => {
 const options = { providers };
 platformBrowserDynamic().bootstrapModule(AppModule, options)
  .catch(err => console.log(err));
});

Don’t forget to add the locale directory to .angular-cli.json to separate it Pack.

In this way, our translation of static copy has basically been completed, but how should we translate some dynamic copy such as copy in ts files or third-party frame attributes? The following will introduce the solution for dynamic copy translation for ts files and the NG-ZORRO framework.

ts file copywriting and NG-ZORRO framework copywriting translation

Specific ideas

Call the Service method through Pipe and match json according to the corresponding unique id value The translation results in the object are then returned to the front-end for rendering, referring to the international implementation plan of the NG-ZORRO framework.

First we define the format of the json translation object, which is a three-layer structure. Dynamic variables need to be wrapped by %%. The reason for this is that it is related to the project structure and also facilitates the unification of the format with i18n later.

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

The format has been determined, we continue to define the Service processing method

The internationalization solution of NG-ZORRO is reused here, which can simplify our development. If you are interested, you can refer to its source code.

*** TranslateService ***
import { Injectable } from &#39;@angular/core&#39;;
// 引入语言配置和国际化文件文案对象
import { LOCALE_LANGUAGE } from &#39;../app.config&#39;;
import { enUS } from &#39;../locales/demo.en-US&#39;;
import { zhCN } from &#39;../locales/stream.zh-CN&#39;;

@Injectable()
export class TranslateService {

 private _locale = LOCALE_LANGUAGE.toString() === &#39;zh-CN&#39; ? 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 === &#39;string&#39;) {
   if (data) {
    Object.keys(data).forEach((key) => content = content.replace(new RegExp(`%${key}%`, &#39;g&#39;), data[key]));
   }
   return content;
  }
  return path;
 }

 private _getObjectPath(obj: object, path: string): string | object {
  let res = obj;
  const paths = path.split(&#39;.&#39;);
  const depth = paths.length;
  let index = 0;
  while (res && index < depth) {
   res = res[paths[index++]];
  }
  return index === depth ? res : null;
 }
}

In this way, you only need to call the translate method of Service in Pipe

*** NzTranslateLocalePipe ***
import { Pipe, PipeTransform } from &#39;@angular/core&#39;;
import { TranslateService } from &#39;../services/translate.service&#39;;

@Pipe({
 name: &#39;nzTranslateLocale&#39;
})
export class NzTranslateLocalePipe implements PipeTransform {
 constructor(private _locale: TranslateService) {
 }

 transform(path: string, keyValue?: object): string {
  return this._locale.translate(path, keyValue);
 }
}

Okay, now that our processing logic is completely over, let’s introduce how to use it

*** NG-ZORRO 控件 ***
<nz-input [nzPlaceHolder]="&#39;app.base.hello&#39;|nzTranslateLocale"></nz-input> // 无动态参数
<nz-popconfirm [nzTitle]="&#39;app.base.userCount&#39;|nzTranslateLocale: {num:users.length}" ...>
... // 有动态参数
</nz-popconfirm>

*** ts文件 ***
export class AppComponent implements OnInit {
 demoTitle=&#39;&#39;;
 users = [&#39;Jack&#39;, &#39;Johnson&#39;, &#39;Lucy&#39;];
 constructor(privete translateService: TranslateService) {
 }
 ngOnInit() {
  this.demoTitle = this.translateService.translate(&#39;app.base.hello&#39;);
 }
}

The above process can basically meet the internationalization needs of most angular projects. If more complex internationalization is needed, please feel free to discuss it.

Summary

The internationalization of Angular to 5.0 has been relatively simple. We only need to tag i18n in the appropriate place to easily and quickly extract the copy that needs to be translated. How to do it specifically? Processing translated files varies from person to person, and multiple methods can help us convert (such as this article through nodejs).

What is more complicated is the text that cannot be translated by typing i18n tags. NG-ZORRO’s internationalization solution makes up for the shortcomings in this aspect. Together, it can easily complete the internationalization of the project. If there is no dedicated team support for internationalization, translation will be very difficult, and there are many things to consider, such as Traditional Chinese, Macau Traditional Chinese, Taiwan Traditional Chinese, etc., and the grammar is also different.

Reference directory

Angular’s ​​internationalization (i18n) online example
NG-ZORRO Locale Internationalization

The above is what I compiled for everyone Yes, I hope it will be helpful to everyone in the future.

Related articles:

How to implement form validation using JQuery, what should be done specifically?

How to set multiple Classes using Vue

How to get the value of the multi-select box value in post in SpringMVC (code example)

jQuery Checkbox selection and value passing example in SpringMVC_jquery

The above is the detailed content of How to implement internationalization using Angular (detailed tutorial). For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn