Angular使用管道和指令进行多语言切换

news2025/1/20 1:51:40

        工作中经常遇到需要进行多种语言切换的项目。本文记录了一种在Angular页面中通过使用管道和自定义指令实现的语言切换方案。

1、实现效果

        页面显示文字根据选择的语言自动进行翻译切换,如下图所示:

        此时,页面模板的字符串全部按照管道格式书写: 

<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)中的翻译方法查找对应翻译。

2、语言服务类

        语言服务类(LanService)中主要包含加载翻译文件、同步翻译、异步翻译、设置语言方法。

2.1、加载翻译文件方法

        通过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.lanChange.next(languageCode);
        this.isLoading = false;
        resolve();
      }, err => {
        reject(`load translation json error: ${err}`);
      })
    });
  }

        文件加载完成后会发出一个语言切换的通知(this.lanChange.next(languageCode);),订阅了该通知的方法将执行对应订阅逻辑。

2.2、同步翻译方法

        通过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"));
  }

2.3、异步翻译方法

        通过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);
    }
  }

2.4、设置语言方法

        方法调用时,根据传入的语言重新加载翻译文件,加载后更新当前语言:

  /**
   * 设置页面语言
   * @param languageCode 语言代码 zh-CN | en-US
   */
  setLanguage(languageCode: string) {
    if (this.curLanguage != languageCode) {
      this.loadTranslation(languageCode).then(() => {
        this.curLanguage = languageCode;
      });
    }
  }

3、页面启动加载翻译文件

        页面启动时需要加载翻译文件,可以在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();
  }

4、管道方法

        管道方法中采用异步获取翻译的方式进行文本转换,会将传入的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{}

5、自定义指令实现

        使用上述管道就可以完全满足语言切换的需求,但是使用管道的时候,模板HTML页面代码中只能看到各字符串对应的key,如果是团队合作的代码或者长时间之前写的代码,就不能马上理解到key对应的翻译文本。这时候如果采用指令的形式实现,将key通过参数传入指令,通过指令修改元素innnerText。此时模板HTML页面中就可以保留任意语言对应的翻译,当然汉语最熟悉:

<h1 [lan]="'app.title'">多语言切换演示</h1>
<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.lanChange$ = this.lanService.lanChange.subscribe((lan) => {
      this.changeDetectorRef.detectChanges();
      if (this.key.length > 0) {
        this.setText(this.key);
      }
   });

        但是像placeholder这种还是只能使用管道。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44126.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年IB生物有什么变化?

今天我们详细了解下2023年IBDP生物新课程内容、考试评估、内容、技能发展、科学本质等详细内容。IBDP生物新课程概述 IBDP新生物课程将于2023年2月开课&#xff0c;2023年8月首次授课&#xff0c;2025年5月进行首次考试。新课程反映社会变革&#xff0c;更加关注技能和概念、背…

【iMessage苹果推群发】苹果相册推archive到appstore开发证书(Development certificate)

推荐内容IMESSGAE相关 作者推荐内容iMessage苹果推软件 *** 点击即可查看作者要求内容信息作者推荐内容1.家庭推内容 *** 点击即可查看作者要求内容信息作者推荐内容2.相册推 *** 点击即可查看作者要求内容信息作者推荐内容3.日历推 *** 点击即可查看作者要求内容信息作者推荐…

DCHP通讯协议

从通讯TCP/IP的构成传输&#xff0c;到IP地址的分类&#xff0c;再到局域网与广域网的设置。 通讯协议的构成概述IPDHCP-设定动态IPDNS数据的流动广域网 网关局域网 交换机NAT桥接模式实操概述 前景知识&#xff1a;虚拟机与工艺库管理 扩展&#xff1a;NAT模式的实操。 1、T…

yolov5量化注意事项

&#xff08;1&#xff09;使用onnxsim 0.4.1版本、ort版本1.13.1 否则使用--dynamic True时所生成的onnx就会乱掉。 &#xff08;2&#xff09;利用trtexec生成engine 所使用的命令如下&#xff1a; trtexec.exe --onnxyolov5n_ptq_detect_dynamic.onnx --saveEngineptq_int…

小白学流程引擎-FLowable(二) — 从零搭建自己的FLowable服务 — 搭建流程服务-FLowable的新手指南

一、介绍 纵览Gitee搜索Flowable开源项目&#xff0c;大多都是已开发好的项目&#xff0c;而笔者从零开始搭建属于自己的Flowable引擎&#xff0c;并且是可以拿到生产上使用的。 二、软件架构 Springboot Flowable modeler idm Mysql SrpingBoot version&#xff1a;2.7…

牛客网verilog刷题知识点盘点(75道题的版本)

牛客网verilog刷题知识点盘点(75道题的版本) 还有几个坑没填 任务和函数 1.任务和函数必须在模块内定义&#xff0c;其作用范围仅适用于该模块&#xff0c;可以在模块内多次调用。 2.任务和函数中可以声明局部变量&#xff0c;如寄存器&#xff0c;时间&#xff0c;整数&…

企业云工如何高效居家协同办公?试试这个方法

近日的疫情反扑让全国各地的防疫压力增加&#xff0c;并且甚至很多地方不知道的啥时候就要被居家隔离&#xff0c;所以在新的防疫背景下&#xff0c;居家就顺理成章地成为一种常态化的学习和工作方式。 现在越来越多公司也将日常业务搬到了线上&#xff0c;以保证疫情期间公司的…

基于ssm的旅游网站的设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

vue——路由

目录 一、介绍路由 1、路由是什么 Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成&#xff0c;让用 Vue.js 构建单页应用变得轻而易举。 2、为什么要使用路由 3、主要用途 二、安装路由 1、安装命令 2、配置文件 3、在main.js 中进行挂载 三、案例展示 …

Linux服务器配置与管理(基于Centos7.2)任务目标(五)

文章目录一、知识目标二、能力目标二、任务实施任务一&#xff1a;安装Apache任务二&#xff1a;配置简单Web站点任务三&#xff1a;配置基于主机名的虚抛主机任务四&#xff1a;安装vsftpd任务五&#xff1a;匿名用户访问FTP服务器任务六&#xff1a;本地用户配置一、知识目标…

HummerRisk 使用场景-混合云安全治理(2)--阿里云安全最佳实践

背景&#xff1a; 阿里云安全最佳实践&#xff0c;是基于众多客户上云的成功案例萃取而成的最优化企业上云指导。每个最佳实践包括使用场景、多产品部署架构及部署手册。 最佳实践目前覆盖23种常用场景&#xff0c;目前有200篇最佳实践&#xff0c;涉及100款以上阿里云产品的…

20221128-1Spring_day02(资料来自黑马程序)

Spring_day02 今日目标 掌握IOC/DI配置管理第三方bean掌握IOC/DI的注解开发掌握IOC/DI注解管理第三方bean完成Spring与Mybatis及Junit的整合开发 1&#xff0c;IOC/DI配置管理第三方bean 前面所讲的知识点都是基于我们自己写的类&#xff0c;现在如果有需求让我们去管理第三方…

安卓讲课笔记5.11 菜单

文章目录零、本讲学习目标一、导入新课二、新课讲解&#xff08;一&#xff09;菜单概述1、选项菜单2、上下文菜单3、子菜单&#xff08;二&#xff09;选项菜单案例演示1、创建安卓应用2、准备图片素材3、字符串资源文件4 、主布局资源文件5、主界面类实现功能6、启动应用&…

2. Vue3 Composition API

Composition API 1.Composition API 接下来我们来介绍一下Vue3中新增的Composition API如何使用。注意Composition API仅仅是Vue3中新增的API&#xff0c;我们依然可以使用Options API。先来实现一下之前演示的获取鼠标位置的案例。做这个案例之前&#xff0c;需要先介绍一下…

MybatisPlus简单使用与自定义sql以及通过自定义sql实现多表联查的分页查询

MybatisPlus简单使用与自定义sql以及通过自定义sql实现多表联查的分页查询前言1. mybatis的简单使用2. MybatisPlus 的简单使用2.1 入门2.1.1 简单配置2.1.2 入门例子2.1.3 测试2.2 MybatisPlus自带封装的增删改查2.2.1 傻瓜式使用2.2.2 批量添加数据3 MybatisPlus 动态查询sql…

计算机网络---数据链路层扩展的以太网

&#xff08;一&#xff09;在物理层扩展以太网 使用光纤扩展&#xff1a;主机使用光纤和一对光纤调制解调器连接到集线器 使用集线器扩展&#xff1a;主机使用光纤和一对光纤调制解调器连接到集线器 使用集线器扩展的优缺点 优点 &#xff1a;使原来属于不同碰撞域&#xff0…

Nodejs -- Express中间件的概念及基本使用

文章目录1 中间件的概念1.1 什么是中间件1.2 现实生活中的例子1.3 Express中间件的调用流程1.4 Express的中间件的格式1.5 next函数的作用2 Express中间件初体验2.1 定义中间件函数2.2 全局生效的中间件2.3 定义全局中间件的简化形式2.4 中间件的作用2.5 定义多个全局中间件2.6…

机器学习-(手推)线性回归3-正则化-岭回归(Ridge)-频率角度贝叶斯角度

一、正则化-岭回归-频率角度 回顾&#xff1a; Loss Function&#xff1a; 过拟合的解决方法&#xff1a; ①最直接&#xff1a;加数据 ②降维&#xff08;特征选择/特征提取&#xff08;PCA&#xff09;&#xff09; ③正则化&#xff08;对参数空间&#xff0c;例如w的约…

预处理,编译,汇编,链接,全过程。

编译&#xff0c;链接&#xff0c;全过程。背景知识预处理&#xff1a;1.宏定义指令&#xff0c;如#define MAX 1&#xff1b;2.条件编译指令&#xff0c;如#ifdef、 #ifndef、#else、#elif、#endif等。3.头文件包含指令&#xff0c;如#include等。4.特殊符号&#xff0c;预编译…

SQL explain解析器

EXPLAIN 参数前言字段参数id 查询编号select_type 关联类型SIMPLEPRIMARYUNION & UNION RESULTDERIVEDSUBQUERYDEPENDENTUNCACHEABLEMATERIALIZEDtable 表名partitions 数据的分区信息type 关联类型system & consteq_refreffulltextref_or_nullindex_mergeunique_subqu…