工作中经常遇到需要进行多种语言切换的项目。本文记录了一种在Angular页面中通过使用管道和自定义指令实现的语言切换方案。
页面显示文字根据选择的语言自动进行翻译切换,如下图所示:

此时,页面模板的字符串全部按照管道格式书写:
- <h1>{{ "app.title" | translate}}</h1>
- <div>
- <span>{{ "app.demo.text" | translate}}</span>
- <input type="text" placeholder="{{ 'app.demo.placeholder' | translate}}">
- <button (click)="showMessage()">{{"app.demo.btn" | translate}}</button>
- </div>
- <button *ngFor="let lan of languages" (click)="setLanguage(lan.code)">
- {{lan.icon + " " + lan.text}}
- </button>
其中 "app.title","app.demo.text",'app.demo.placeholder',"app.demo.btn" 等为各字符串的key,translate为管道选择器。translate管道通过key查找对应翻译进行翻译。用于查找翻译的字典通过加载文件方式加载到页面内存中。翻译文件格式如下:

管道通过调用语言服务类(LanService)中的翻译方法查找对应翻译。
语言服务类(LanService)中主要包含加载翻译文件、同步翻译、异步翻译、设置语言方法。
通过HttpClient.get()加载翻译文件:
- /**
- * 加载翻译文件
- * @returns
- */
- loadTranslation(languageCode: string = this.curLanguage) {
- const jsonFile = `assets/language/${languageCode}.json`;
- this.isLoading = true;
- return new Promise<void>((resolve, reject) => {
- this.http.get<any>(jsonFile).subscribe(data => {
- this.translation = data;
- this.isLoading = false;
- this.lanChange.next(languageCode);
- resolve();
- }, err => {
- reject(`load translation json error: ${err}`);
- })
- });
- }
文件加载完成后会发出一个语言切换的通知(this.lanChange.next(languageCode);),订阅了该通知的方法将执行对应订阅逻辑。
通过key获取当前字典中对应的翻译,未查找到对应key的翻译直接返回key,如果服务正在加载翻译文件也直接返回key:
- /**
- * 获取单个文本的翻译 同步方法
- * @param key 文本资源key
- * @returns 对应翻译结果字符串,未查找到返回key
- */
- translate(key: string) {
- if (this.isLoading) return key;
- else return this.translation[key] || key;
- }
对于一些在确定语言包json已加载完成的场景中可以使用同步翻译方法获取翻译。比如页面中点击按钮提示的文字就可以采用同步方法获取翻译:
- /**
- * 弹出提示文字
- */
- showMessage() {
- alert(this.lanService.translate("app.welcome"));
- }
通过key获取当前字典中对应的翻译,返回数据以Observable格式封装。如果服务正在加载翻译文件,则订阅语言切换的通知,待文件加载完成发出通知后,再查找对应翻译;相反则直接查找翻译:
- /**
- * 获取单个文本的翻译 异步方法
- * @param key 文本资源key
- * @returns 对应翻译结果字符串,未查找到返回key
- */
- get(key: string) {
- if (this.isLoading) {
- return from(new Promise<string>(resolve => {
- this.lanChange.subscribe(lan => {
- resolve(this.translation[key] || key);
- })
- }));
- } else {
- return of(this.translation[key] || key);
- }
- }
方法调用时,根据传入的语言重新加载翻译文件,加载后更新当前语言:
- /**
- * 设置页面语言
- * @param languageCode 语言代码 zh-CN | en-US
- */
- setLanguage(languageCode: string) {
- if (this.curLanguage != languageCode) {
- this.loadTranslation(languageCode).then(() => {
- this.curLanguage = languageCode;
- });
- }
- }
页面启动时需要加载翻译文件,可以在AppModule中注册页面启动的加载方法:
- export function StartUpServiceFactory(startUpService: StartUpService) {
- return () => startUpService.load();
- }
- const APP_INIT_PROVIDERS = [
- StartUpService,
- {
- provide: APP_INITIALIZER,
- useFactory: StartUpServiceFactory,
- deps: [StartUpService],
- multi: true
- }
- ];
- @NgModule({
- //...
- providers: [
- ...APP_INIT_PROVIDERS
- ],
- // ...
- })
- export class AppModule { }
在启动服务类(StartUpService)中调用语言服务类(LanService)的加载翻译文件方法:
- /**
- * 启动加载项
- */
- load() {
- this.lan.loadTranslation();
- }
管道方法中采用异步获取翻译的方式进行文本转换,会将传入的key及其翻译进行缓存,每次查询会优先查询缓存值。没有缓存才会进行异步获取翻译。管道还会订阅语言服务类(LanService)的语言切换通知,收到通知后重新获取翻译,更新翻译结果:
- transform(key: string) {
- if (this.cacheKey == key) return this.translation;
- this.cacheKey = key;
- this.updateTranslation(key);
- this.unsubscribe();
- if (!this.lanChange$) {
- this.lanChange$ = this.lanService.lanChange.subscribe(lan => {
- if (this.cacheKey) {
- this.cacheKey = "";
- this.updateTranslation(key);
- }
- });
- }
- return this.translation;
- }
其中updateTranslation()方法完成了异步获取翻译的操作:
- /**
- * 异步更新翻译
- * @param key 字符串key
- */
- updateTranslation(key: string) {
- let callback = (tran: string) => {
- this.translation = tran || key;
- this.cacheKey = key;
- this._ref.markForCheck();
- }
- this.lanService.get(key).subscribe(callback);
- }
为了使Angular状态检查能够检测到管道值的变化,管道装饰器的pure字段必须设置为false,否则页面不会渲染更新修改语言而导致的文本变化:
- @Pipe({
- name: 'translate',
- pure: false
- })
- export class TranslatePipe implements PipeTransform{}
使用上述管道就可以完全满足语言切换的需求,但是使用管道的时候,模板HTML页面代码中只能看到各字符串对应的key,如果是团队合作的代码或者长时间之前写的代码,就不能马上理解到key对应的翻译文本。这时候如果采用指令的形式实现,将key通过参数传入指令,通过指令修改元素innnerText。此时模板HTML页面中就可以保留任意语言对应的翻译,当然汉语最熟悉:
"'app.title'">多语言切换演示
- <span [lan]="'app.demo.text'">文本内容演示span>
- <button [lan]="'app.demo.btn'" (click)="showMessage()">点击提示文字button>
这样就既能满足切换语言需求,又能一眼看到中文信息,提高代码可读性。指令代码:
- /**
- * 传入的字符串key
- */
- @Input('lan')
- set lan(value: string) {
- this.key = value;
- this.setText(value);
- }
-
- /**
- * 异步获取翻译设置元素内容文本
- * @param key
- */
- setText(key: string) {
- key = key || '';
- if (key.length > 0) {
- const el = this.el.nativeElement;
- this.lanService.get(key).subscribe(tran => {
- el.innerText = tran;
- this.changeDetectorRef.markForCheck();
- });
- }
- }
-
- //订阅服务类通知更新翻译
- this.lanChange$ = this.lanService.lanChange.subscribe((lan) => {
- if (this.key.length > 0) {
- this.setText(this.key);
- }
- });
但是像placeholder这种还是只能使用管道。
完整项目代码下载:Angular使用管道和指令实现多语言切换示例,无第三方库引用-Typescript文档类资源-CSDN下载