Super detailed Angular internationalization solution

Time:2022-11-25

It is the first time to implement an internationalization solution on the Angular framework, and share the experience of Eoapi with everyone~

1. Project background

  1. Need to support web and Electron desktop
  2. technology stack
  • Angular 14
  • Electron 19
  • NG-ZORR 13.0.6

2. Possible difficulties

  • The styles in different languages ​​are not uniform, for example, Arabic text is displayed from right to left, English is longer than Chinese, etc.
  • Variable localization, such as initializing units such as API data, logs, timestamps, and amounts
  • Translation costs are high
  • Collaboration process is complicated
  • Formulation and implementation of technical standards

3. Research

3.1 Conclusion

First go to the table to see the conclusion, with a full score of three stars, and finally select the I18n solution.

Super detailed Angular internationalization solution

I have been wandering between the runtime language pack and the compilation method, and finally chose to use the I18n solution. There are the following considerations:

  • Low intrusion to code, does not affect the existing development model
  • The solution is mature, verified by the angular team/community, and various conditions have been considered in advance
  • The generated file xlf follows the international specification XLIFF, which is relatively common. Even if the technology changes, the language pack is still common

Although it cannot support the same URL to dynamically switch language packs, multiple sets of code schemes are not routinely implemented on the desktop (you may need to type in the installation packages of various languages), but considering the needs, switching language packs is an ultra-low-frequency operation and use The compilation method can improve a good development experience, and I am still impressed by the i18n solution.

In fact, whether it is a runtime solution or a compilation method, as long as the official support is available, mutual conversion can be realized. I look forward to the subsequent support of Angular to reverse compile the language pack into code and then support dynamic switching of the language pack. That is the perfect solution in my ideal.

3.2 Analysis

3.2.1 Online translation

When the API switches language packs, call OpenAPI to translate the current page, similar to Google Translate.

The difficulty of implementation is average, as long as this tool is implemented, it can be compatible with various languages.

The translation API is not necessarily stable. At the same time, there is a limit on the number of free translations. It is suitable for static pages that do not require high expression accuracy and need to support multiple languages.

3.2.2 Self-developed language compiler

Use compilation means to extract, for example, recognize the Chinese in the code, extract it into a language pack, and package it

If you are interested, you can read this article, the principle is similar:https://juejin.cn/post/684490…

The original development mode is not changed, and there is no learning cost.

It is somewhat intrusive to the code, and has different language matching rules for codes in different native languages.

The implementation cost of self-development is relatively high, and it may not be able to cooperate with the popular translation platforms on the market to get through the process. It takes time for the tool ecosystem to mature.

3.2.3 Angular I18n

Programhttps://angular.cn/guide/i18n…

Similar to the self-developed compilation and extraction of language text, some identifiers are used to mark which fields need to be translated, and some additional additional rules are provided, such as marking the same word with different meanings.

It is more mature, the code is less intrusive, and there is a certain development and learning cost.

The practice recommended by the official document is that different language packs are packaged into different codes, and then mapped to different codes through nginx. This is suitable for the web, but it is cumbersome to implement on the desktop, and files cannot be dynamically loaded.

Ideally, the angular solution supports a set of code-loading language pack internationalization solutions (one bundle for all languages). The official discussion on dynamically switching language packs:

https://github.com/angular/an…
https://github.com/angular/an…

As of the completion of this solution, Angular only supports packaging into multiple language pack files, and the dynamic switching of language packs is currently in the proposal approval stage, and the future can be expected.

Super detailed Angular internationalization solution

3.2.4 Runtime Language Packs

The solutions that support Angular are:

I think the biggest problem with this solution is the poor development experience. Although many tools can use VSCode plug-ins to display [default language text] to smooth out differences, the source code is still variable.

Super detailed Angular internationalization solution

Secondly, switching language packs is a low-frequency operation. Generally, users will only choose one language pack for a long time, and the runtime solution will also increase unnecessary memory usage (of course, if you want to say that it can be ignored, I will not argue with you, it’s up to you. win).

3.2.5 Multiple sets of codes

There are no technical difficulties, high synchronization costs, and high flexibility.

4. Technical implementation

The source code of the following code configuration is in this warehouse, if necessary, you can pull it down and deploy it to see the specific effect.

https://github.com/eolinker/e…

Internationalization is mainly divided into several processes:

Super detailed Angular internationalization solution

4.1 Language packs

4.1.1 Language pack configuration

4.1.1.1 Command line configuration

Use the command ng extract-i18n provided by angular, and make the directory src/locale generated by the language pack

ng extract-i18n --output-path src/locale

After running, a default language pack message.xlf will be generated

Super detailed Angular internationalization solution

It is recommended to use the following npm command package, run npm run lang:gen to generate the language pack file

"scripts": {
"lang:gen": "ng extract-i18n --output-path src/locale",
....

4.1.1.2 angular.json

Configuration If you want to add other language packs, copy a file from the default language pack message.xlf, and then configure it to support multiple languages.

Please refer to the file name language ID naming (en-US, zh-Hans)

Super detailed Angular internationalization solution

Tell the build tool what your language pack is called and where it is located in angular.json

{
 ...
  "projects": {
    "eoapi": {
      ...
     "i18n": {
        "sourceLocale": "zh-Hans",//Chinese language pack
        "locales": {
          "en-US": "src/locale/messages.en-US.xlf"//English language pack
        }
      },

4.1.1.3 Ant-Design configuration

https://ng.ant.design/docs/i1…

/** Import the language packs that need to be used**/
import { LOCALE_ID } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import en from '@angular/common/locales/en';
import zh from '@angular/common/locales/zh';
registerLocaleData(en);
registerLocaleData(zh);
/** Configure ng-zorro-antd internationalization**/
import { en_US, NZ_I18N, zh_CN } from 'ng-zorro-antd/i18n';
...
{
      provide: NZ_I18N,
      useFactory: (localId: string) => {
        switch (localId) {
          case 'zh':
            return zh_CN;
          default:
            return en_US;
        }
      },
      deps: [LOCALE_ID],
    }

4.1.2 Debugging

Official words: Due to the deployment complexity of i18n and the need to minimize rebuild time, the development server only supports localization of a single locale at a time

Let me translate: Your requirement is not common, and it is too troublesome to implement. If you have trouble debugging, please troublesome. How fast the build time is, I am doing it for your own good.

All in all, Angular only supports one language pack when debugging, which means that you can’t directly test the effect of switching language packs at runtime (of course, it can be achieved indirectly by running two services), and it doesn’t matter much.

You can use the following two configurations for debugging:

  1. Localization can be disabled while debugging by setting “localize”:false
"architect": {
        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "localize":false,
            "aot": true, // localization must enable AOT
            ...
  1. angular.json specifies the use of a certain language pack
{
 ...
  "projects": {
    "eoapi": {
      ...
      "architect": {
        "build": {
          "options": {
            "localize": true, //Enable localization
            "aot": true,
             ...
          },
          "configurations": {
            "web": {
              //Specify a language pack in a certain operating environment, and pass in the name of the language pack defined by i18n above
              "localize": ["zh-Hans"],
               ...
            }
          }
        },
        "serve": {
         ...
          "configurations": {
            "web": {
              "browserTarget": "eoapi:build:web"
            }
       ...

Use the command to formulate and use –configuration web at runtime

ng serve -c web -o

4.1.3 Build

The Angular i18n solution will be packaged into two folders, so you need to pay attention to the relative addresses of resources in the code.

4.1.3.1 web

It is not much different from before, mainly through the index.html inside<base> label to specify the base path, and the result of the packaged code is shown in the figure:

Super detailed Angular internationalization solution

href=”en-US” for the English language pack

!DOCTYPE html><html lang="en-US" dir="ltr"><head>
  <meta charset="utf-8">
  <title>Eoapi</title>
  <base href="/en-US/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_12799_18.e98aaa4d03d4378a3a2c18bfd1670872.js"></script>
  ...

Super detailed Angular internationalization solution

4.1.3.2 Desktop

Electron startup needs to specify the language
https://github.com/electron/e…
The desktop uses the file protocol, you need to change the base href to a relative path ./.
Even if you use ng build –base-href ./, the language identifier will be automatically added before the href after packaging

<base href="./en-US/">

So you need to specify baseHref as an empty string in angular.json 18n

  "projects": {
    "eoapi": {
      "root": "",
      "i18n": {
        "sourceLocale": {
          "code": "zh-Hans",
          "baseHref": ""//.
        },
        "locales": {
          "en-US": {
            "baseHref": "",
            "translation": "src/locale/messages.en-US.xlf"
          }
        }
      },

4.1.3.3 Build.js commands

Under normal circumstances, the Angular framework will handle it for us, but our products involve the web and the desktop, and there are two sets of packaging logic, so we use the Node script to execute the Build command, and modify the angular.json file before executing the ng build command.

//change angular.json
const fs = require('fs');
const { execSync } = require('child_process');

class webPlatformBuilder {
  resetBuildConfig(json) {
    delete json.projects.eoapi.i18n.sourceLocale.baseHref;
    Object.keys(json.projects.eoapi.i18n.locales).forEach((val) => {
      delete json.projects.eoapi.i18n.locales[val].baseHref;
    });
    return json;
  }
  executeBuild() {
    execSync('ng build -c production', { stdio: 'inherit' });
  }
}
class appPlatformBuilder {
  resetBuildConfig(json) {
  ...
  }
  executeBuild() {
    ...
  }
}
class PlatformBuilder {
  constructor(platForm) {
    switch (platForm) {
      case 'web': {
        this.instance = new webPlatformBuilder();
        break;
      }
      case 'app': {
        this.instance = new appPlatformBuilder();
        break;
      }
    }
  }
  build() {
    const filePath = '../angular.json';
    let buildConfigJson = require(filePath);
    buildConfigJson = this.instance.resetBuildConfig(buildConfigJson);
    let that=this;
    fs.writeFile(filePath, JSON.stringify(buildConfigJson), function (err) {
      if (err) {
        console.error('build/beforeBuild.js:', err);
      }
      that.instance.executeBuild();
    });
  }
}

4.1.4 Deployment

4.1.4.1 Vercel

Vercel does not support multi-project configuration, so it can only redirect to a fixed Language instead of a user-configured Language.

{
  "rewrites": [
    {
      "source": "/:path((?!en/).*)",
      "destination": "/en/:path*"
    },
    {
      "source": "/:path((?!zh/).*)",
      "destination": "/zh/:path*"
    }
  ]
}

The performance of this solution is still not good, and some initial routing information cannot be obtained, so I manually wrote a script to produce blank HTML, and then manually redirected to the corresponding location.

  fs.writeFile(
      './dist/index.html',
      `<!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <title>Eoapi - Easy &amp; Open Source API Ecosystem</title>
        <script>
         let lang=window.location.href.includes("/en")?'en':'zh';
         try{
          lang=JSON.parse(window.localStorage.getItem("LOCAL_SETTINGS_KEY"))["eoapi-language"]=='en-US'?'en':'zh';
         }catch(e){
          
         }
         let baseDir="/"+lang+'/'
         let search={};
         if(window.location.search){
          window.location.href=baseDir+window.location.search;
         }else{
         window.location.href=baseDir;
         }
        </script>
      </head>
      <body></body>
    </html>
    `,
      () => {}
    );

4.1.4.2 Nginx

If you use Nginx to deploy, you can configure it according to the official file.

https://angular.cn/guide/i18n…

4.2 Research on Translation Process Tools

4.2.1 Poeditor

https://poeditor.com

Free, average experience, no data loss, collaboration process is not clear enough, automatic translation is not available

Super detailed Angular internationalization solution

4.2.2 Crowdin (official use of electron)

https://zh.crowdin.com/

Good experience, clear collaboration, but may pay in the future

Super detailed Angular internationalization solution

5 Conclusion

The entire Angular internationalization plan is finished. Internationalization not only involves technical implementation, but also includes the translation collaboration process. The figure shows a common translation process:

Super detailed Angular internationalization solution

As well as the international technical specifications of the entire technical team. After all, translation not only involves UI, but also data specifications such as various time data storage formats.

Thank you for reading, the above practices are implemented in the open source project Eoapi, welcome to pay attention~Online experience function

Github:https://github.com/eolinker/e…
Gitee: https://gitee.com/eolink_admi…

Super detailed Angular internationalization solution

6. Information

Angular project
Internationalization Program Design Human-centered Internationalization (i18n) Engineering Program
Internationalization and Localization
Front-end internationalization
Angular internationalization (i18n) tutorial – Localizely
https://cloud.tencent.com/developer/section/1489559
Taobao FED | Taobao front-end team