It is the first time to implement an internationalization solution on the Angular framework, and share the experience of Eoapi with you~
1. Project Background
- Requires support for web and Electron desktop
- technology stack
- Angular 14
- Electron 19
- NG-ZORR 13.0.6
2. Possible difficulties
- The styles in each language are not uniform, for example, Arabic characters are displayed from right to left, English is longer than Chinese, etc.
- Variable localization, such as initializing API data, logs, timestamps, amounts, etc.
- High cost of translation
- The collaboration process is complex
- The formulation and implementation of technical standards
3. Research
3.1 Conclusion
First look at the conclusion in the table, out of three stars, and finally choose the I18n solution.
I have been wandering about the two schemes of runtime language package and compilation method, and finally chose to use the I18n scheme. There are the following considerations:
- Low intrusion to the code, does not affect the existing development mode
- The solution is mature, verified by the angular team/community, and various conditions are considered in advance
- The generated file xlf follows the international standard XLIFF, which is more general. Even if the technology changes, the language package is still common.
Although it is impossible to support the same URL to dynamically switch language packs, and multiple code schemes are not conventionally implemented on the desktop (it may be necessary to type in the installation packs of various languages), considering the demand, switching language packs is an ultra-low frequency operation and use The compilation method can improve a good development experience, but is still convinced by the i18n solution.
In fact, whether it is the runtime solution or the compilation method, as long as it is officially supported, it is completely possible to achieve mutual conversion. I hope that Angular will support the dynamic switching of language packages after reverse compiling the language package into code. That is my ideal perfect solution.
3.2 Analysis
3.2.1 Online translation
When the API switches language packs, it calls 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, and the free version has a limited number of times. It is suitable for static pages that do not require high precision and need to support multiple languages.
3.2.2 Self-developed language compiler
Use compiling methods to extract, such as identifying Chinese in the code, extract it as a language package, and package it
If you are interested, you can read this article, the principle is similar: https://juejin.cn/post/6844904042489970695#heading-3
It does not change the original development mode, and there is no learning cost.
It is intrusive to the code, and there are different language matching rules for the code of different native languages.
The implementation cost of self-research is high, and it may not be able to cooperate with 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
Scheme https://angular.cn/guide/i18n-overview
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 the marking of different meanings of the same word.
It is relatively mature, the code is less intrusive, and there is a certain development and learning cost.
The practice recommended by the official documentation is to package different language packages into different codes, and then map them to different codes through nginx. This is suitable for the web, but it is troublesome to implement on the desktop side, and files cannot be loaded dynamically.
The ideal situation is that the angular solution supports a set of internationalization solutions for code loading language packs (one bundle for all languages). The official discussion on dynamically switching language packs:
https://github.com/angular/angular/issues/24549#issuecomment-398371120
https://github.com/angular/angular/issues/38953#issuecomment-862492065
As of the completion of this project, Angular only supports packaging into multiple language package files, and the dynamic switching of language packages is currently in the stage of proposal approval, and the future can be expected.
3.2.4 Runtime Language Packs
The solutions that support Angular are:
- http://www.ngx-translate.com/
- https://ngneat.github.io/transloco/docs/translation-in-the-template
- https://github.com/lokalise/i18n-ally
I think the biggest problem with this solution is the poor development experience. Although many tools can use the VSCode plugin to display [default language text] to smooth out differences, the source code is still various variables.
The second is that switching language packages is a low-frequency operation. Generally, users will only choose one language package 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, then 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 realization
The source code of the following code configuration is in this warehouse, you can pull it down and deploy it if necessary to see the specific effect.
https://github.com/eolinker/eoapi
Internationalization is mainly divided into several processes:
4.1 Language Packs
4.1.1 Language Pack Configuration
4.1.1.1 Command Line Configuration
Use the instruction ng extract-i18n provided by angular, and at the same time make the directory src/locale generated by the language package
ng extract-i18n --output-path src/locale
After running, a default language package message.xlf will be generated
It is recommended to use the following npm command to package, run npm run lang:gen to generate the language package 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)
Tell the build tool in angular.json what your language package is called and where it is located
{
...
"projects": {
"eoapi": {
...
"i18n": {
"sourceLocale": "zh-Hans",//中文语言包
"locales": {
"en-US": "src/locale/messages.en-US.xlf"//英文语言包
}
},
4.1.1.3 Ant-Design Configuration
https://ng.ant.design/docs/i18n/en
/** 导入需要使用的语言包 **/
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);
/** 配置 ng-zorro-antd 国际化 **/
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 Debug
Official Word: Due to the deployment complexity of i18n and the need to minimize rebuild time, the development server only supports localizing a single locale at a time
Let me translate it: your requirement is not common, and it is too troublesome to implement. If you are troublesome to debug, just troubleshoot it. How fast is the build time, 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), which doesn't matter much.
You can use the following two configurations to debug:
- You can disable localization while debugging by setting "localize": false
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"localize":false,
"aot":true,//本地化一定要开启 AOT
...
- angular.json specifies to use a certain language pack
{
...
"projects": {
"eoapi": {
...
"architect": {
"build": {
"options": {
"localize":true,//开启本地化
"aot": true,
...
},
"configurations": {
"web": {
//指定某个运行环境下的语言包,传入上面 i18n 定义的语言包名称
"localize": ["zh-Hans"],
...
}
}
},
"serve": {
...
"configurations": {
"web": {
"browserTarget": "eoapi:build:web"
}
...
Use the command to formulate 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
There is not much difference from the previous one, mainly through the <base> tag in index.html to specify the base path. The packaged code results are shown in the figure:
href="en-US" for English language packs
!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>
...
4.1.3.2 Desktop
Electron needs to specify the language to start
https://github.com/electron/electron/issues/5649
The desktop side 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 mark 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 command
In general, the Angular framework will handle it for us, but our product involves 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 I can't get some initial routing information, 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 & 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-common-deploy#configure-a-server
4.2 Research on translation process tools
- https://github.com/alibaba/kiwi
- https://github.com/mozilla/pontoon/ (open source)
- https://www.volcengine.com/product/i18ntranslate
4.2.1 Poeditor
https://poeditor.com
Free, average experience, data is not lost, collaboration process is not clear enough, automatic translation cannot be used
4.2.2 Crowdin (official use of electron)
https://en.crowdin.com/
Good experience, clear collaboration, but may pay in the future
5 Conclusion
The entire Angular internationalization plan is finished. Internationalization not only involves technical implementation, but also the translation collaboration process. The picture shows a common translation process:
And 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 all implemented in the open source project Eoapi, welcome to pay attention to the online experience function
Github: https://github.com/eolinker/eoapi
Gitee: https://gitee.com/eolink_admin/eoapi
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。