《Techporters架构搭建》-Day06 国际化

news2024/12/28 10:35:02

什么是国际化?

国际化,也叫i18n,为什么叫i18n呢?

"i18n"是国际化(internationalization)的缩写,数字18代表了国际化这个单词中间的字母数量。类似这样的缩写还有k8s(kubernetes)等

为什么使用国际化?

简单理解就是开发的软件需要能同时应对不同国家和地区的用户访问,并根据用户地区和语言习惯,提供相应的、符合用具阅读习惯的页面和数据,例如,为中国用户提供汉语界面显示,为美国用户提供提供英语界面显示。

国际化细分

国际化需要分成两个部分:
前端国际化

前端国际化主要关注页面的显示和用户界面的本地化。它涉及将应用程序的界面元素,如文本、标签、按钮等,根据用户的语言和地区进行翻译和适配。前端国际化通常使用资源文件、语言包或翻译服务来存储和管理不同语言的文本。前端开发人员可以通过使用国际化框架或库,如React Intl、Vue I18n或Angular i18n等,来实现前端国际化功能

后端国际化

后端国际化主要关注处理与业务逻辑和数据相关的国际化问题。这包括但不限于日期和时间格式、货币符号、数字格式、排序规则、接口提示信息等。后端国际化的目标是确保应用程序能够适应不同的语言和地区,并提供正确的本地化数据。后端国际化可以通过使用国际化库或框架,如SpringBoot I18n,来实现后端国际化功能

总之,前端国际化主要关注页面显示和用户界面的本地化,而后端国际化则处理与业务逻辑和数据相关的国际化问题。两者通常需要协同工作,以实现完整的国际化功能

国际化相关知识

Locale对象

需要支持国际化,得先知道选择的是哪种地区的哪种语言,java中使用java.util.Locale来表示地区语言,这个对象内部包含了国家和语言的信息。
Locale中最常用的构造方法:

public Locale(String language, String country) {
    this(language, country, "");
}

构造方法有两个参数:language:语言、country:国家

这两个参数的值不是乱写的,国际上有统一的标准,如:zh-CN表示中国大陆地区的中文,zh-TW表示中国台湾地区的中文,en-US表示美国地区的英文,en-GB表示英国地区的英文等等。通过语言和国家构造Locale对象,比如Locale locale = new Locale(“zh”, “CN”);,表示中国大陆地区的中文。

MessageSource接口

这是 Spring 国际化的核心接口,其定义如下:

public interface MessageSource {
    /**
     * 获取国际化信息
     */
    @Nullable
    String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

    /**
     * 与上面的方法类似,只不过在找不到资源中对应的属性名时,直接抛出NoSuchMessageException异常
     */
    String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;

    /**
     * @param MessageSourceResolvable 将属性名、参数数组以及默认信息封装起来,它的功能和第一个方法相同
     */
    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}

MessageSource接口提供了三个获取国际化消息的方法,其主要是根据 Locale 信息获取对应的国际化消息的集合,然后根据 code 获取对应的消息,并且通过提供的参数 args 还可以对获取后的消息进行格式化。
具体参数含义如下:

参数名含义
code表示国际化资源中的属性名
args为消息中的参数填充的值
defaultMessage默认的消息,如果没有找到将返回默认消息
resolvable消息参数,封装了 code、args、defaultMessage
locale表示本地化对象

类图结构
在这里插入图片描述
常见3个实现类:

类名含义
ResourceBundleMessageSource这个是基于Java的ResourceBundle基础类实现,允许仅通过资源名加载国际化文件
ReloadableResourceBundleMessageSource这个功能和第一个类的功能类似,多了定时刷新功能,允许在不重启系统的情况下,更新国际化文件的信息
StaticMessageSource它允许通过编程的方式提供国际化信息。

重点:我们在项目中会创建 MessageSource接口,但不管使用哪个实现类或者我们自定义的类,都要将Bean名称设置为messageSource

加载ApplicationContext时,自动搜索上下文中定义的MessagesSource Bean(名称必须为messageSource)。
如果无法找到消息的任何源,则实例化一个空的messageSource。

LocaleResolver接口

这个接口是用来设置当前会话默认的国际化语言的,其定义如下:

public interface LocaleResolver {
   /**
    * 根据当前请求解析当前请求的本地化信息
    */
    Locale resolveLocale(HttpServletRequest request);

   /**
    * 设置当前请求、响应的本地化信息
    */
    void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}

resolveLocale方法用于从当前request中解析对应出对应的Locale对象,场景如:
常见4个实现类:

类名含义
AcceptHeaderLocalResovler通过请求头里面的Accept-Language:zh,en;q=0.9,zh-HK;q=0.8来决定使用具体的Locale实例,其实AcceptHeaderLocalResovler就是使用HttpServletRequest实例中的Locale实例,在进入DispatcherServlet的时候HttpServletRequest实例里面就已经有Locale实例了,可以通过request.getLocale();来获取Locale实例,HttpServletRequest里面的Locale实例就是使用请求头里面的Accept-Language:zh,en;q=0.9,zh-HK;q=0.8来构建的,比如请求头里面的Accept-Language:zh,en;q=0.9,zh-HK;q=0.8 就表示zh的权重是1,en;q=0.9表示en的权重是0.9,zh-HK;q=0.8就表示zh-HK的权重是0.8,那么我么通过request.getLocale();获取到的就是权重最高的zh,然后就是构建一个zh的Locale实例,那么AcceptHeaderLocalResovler在解析Locale的时候就会返回zh的Locale实例
CookieLocaleResovler根据用户在Cookie中设置的某参数来进行确定具体的本地化Locale实例
SessionLocaleResovler根据用户在HttpSession中设置某参数来进行确定具体的本地化Locale实例
FixedLocalResovler使用jdk自带的默认的Locale实例

国际化文件

项目中,在resources目录下创建名为i18n的文件目录,然后我们在i18n目录创建国际化文件
格式为:名称_语言_地区.properties
我们先来创建两种语言,如:
message.properties(这个文件名称没有指定Local信息,当系统找不到的时候会使用这个默认的)

name=您的姓名
text=默认文本

message_cn_ZH.properties:中文【中国】

name=姓名
text=文本

messages_en_US.properties 英文【美国】

name=name
text=text

我们通过MessageSource接口的getMessage方法传入对应的key(如name、text),便可以从国际化文件中取值。同时我们还可以指定Locale对象,便能找到对应的国际化文件然后取值。

国际化一般实现

一般让前端在请求头中, 添加 { “Accept-Language”: “zh” }来标识,用户使用的语言
然后我们添加拦截器, 将这个值取出来, 这一步springboot已经帮我们做了(默认配置)
所以一般的单体springboot项目中, 直接在配置一下国际化资源文件即可

@Configuration
public class I18nConfig implements WebMvcConfigurer {
    @Bean
    public MessageSource messageSource() {
        // 多语言文件地址
        Locale.setDefault(Locale.CHINA);
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        //设置国际化文件存储路径   resources目录下 可以设置多个
        messageSource.addBasenames("i18n/common/messages","i18n/system/messages","i18n/device/messages");
        //设置根据key如果没有获取到对应的文本信息,则返回key作为信息
        messageSource.setUseCodeAsDefaultMessage(true);
        //设置字符编码
        messageSource.setDefaultEncoding(StandardCharsets.UTF_8.toString());
        return messageSource;
    }
}

然后在对应的目录文件(/i18n/common/)下定义国际化资源文件
美式英语 messages_en_US.properties

user.login.username=User name
user.login.password=Password
user.login.code=Security code
user.login.remember=Remember me
user.login.submit=Sign In

中文简体 messages_zh_CN.properties

user.login.username=用户名
user.login.password=密码
user.login.code=验证码
user.login.remember=记住我
user.login.submit=登录

MessageUtils工具类

public class MessageUtils
{
    /**
     * 根据消息键和参数 获取消息 委托给spring messageSource
     *
     * @param code 消息键
     * @param args 参数
     * @return 获取国际化翻译值
     */
    public static String message(String code, Object... args)
    {
        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
        return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
    }
}

然后直接这样使用就行

MessageUtils.message("user.login.username")
MessageUtils.message("user.login.password")

国际化改进版

下面看网上的两种国际化
通过数据库实现国际化
基于若依-cloud的国际化方案

框架中国际化

目前我们国际化应用场景1.接口抛出的异常国际化 2.参数校验实现国际化
① 因为每个服务都有国际化配置文件,所以关于加载国际化配置的配置类我们放在common包中,首选创建加载MessageSource实现类的I18nConfig 配置类,这个配置类有以下作用:

  1. 实例化ReloadableResourceBundleMessageSource
  2. MessageSourceAccessor是对MessageSource的封装,提供了更便捷的方法来获取消息,免去我们封装MessageUtils类。
package com.tps.cloud.config;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.MessageSource;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
public class I18nConfig {

    private static final String BASE_NAME = "classpath:i18n/messages";

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
        reloadableResourceBundleMessageSource.setBasenames(BASE_NAME);
        reloadableResourceBundleMessageSource.setCacheSeconds(5);
        reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8");
        return  reloadableResourceBundleMessageSource;
    }


    @Bean
    public MessageSourceAccessor messageSourceAccessor(MessageSource messageSource) {
        MessageSourceAccessor accessor = new MessageSourceAccessor(messageSource) ;
        return accessor ;
    }

}

ValidatorConfig配置类,此类作用:

  • 注入MessageSource,用于国际化配置。
  • 实例化工厂LocalValidatorFactoryBean,设置:
    • 设置国际化:将MessageSource设置到ValidationMessageSource
    • 设置提供者类(校验器):HibernateValidator
    • 设置属性:实例化Properties,配置Hibernate的快速异常返回,hibernate.validator.fail_fast,加入到工厂配置。(快速返回指的是遇到一个不合法的,就不继续往下校验。)
  • 加载配置:调用factoryBean的afterPropertiesSet
  • 返回工厂方法的Validator

这里可以顺便谈谈关于Spring的FactoryBean:

  • FactoryBean是接口,实现该接口的类可以自定义创建Bean。一般在框架中用来创建复杂的Bean。
  • 这里的FactoryBean,实现InitializingBean接口,在afterPropertiesSet方法中创建bean,会在bean 实例化后调用。
  • FactoryBean让Bean构建过程更灵活,可以理解为一种策略模式,我们需要生成什么样的
    bean,可以通过实现接口来自定义。
package com.tps.cloud.config;

import jakarta.validation.Validator;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

import java.util.Properties;

@AutoConfiguration
public class ValidatorConfig {

    @Autowired
    private MessageSource messageSource;

    /**
     * 配置校验框架 快速返回模式
     */
    @Bean
    public Validator validator() {
        LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
        // 国际化
        factoryBean.setValidationMessageSource(messageSource);
        // 设置使用 HibernateValidator 校验器
        factoryBean.setProviderClass(HibernateValidator.class);
        Properties properties = new Properties();
        // 设置 快速异常返回
        properties.setProperty("hibernate.validator.fail_fast", "true");
        factoryBean.setValidationProperties(properties);
        // 加载配置
        factoryBean.afterPropertiesSet();
        return factoryBean.getValidator();
    }
}

③ 在对应的服务下创建国际化资源文件
在这里插入图片描述
④ 在接口入参对象上添加占位符,然后测试保存用户接口

    @NotBlank(message = "{SystemUserDto.Username.NotEmpty}")
    @UniqueUsername
    private String username;
@PostMapping
    public Result<Long> save(@RequestBody @Validated SystemUserDto systemUserDto) {
        Long id=systemUserService.createUser(systemUserDto);
        return Result.ok(id);
    }

⑤ 创建异常处理器,用于捕获参数验证异常,并返回统一数据结构

package com.tps.cloud.handler;

import com.tps.cloud.response.Result;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;


/**
 * 全局异常处理器MethodArgumentNotValidException
 */
@RestControllerAdvice
public class GlobalExceptionHandler {


    /**
     * validation Exception (以form-data形式传参)
     * @param exception
     * @return Result
     */
    @ExceptionHandler({ BindException.class })
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result bindExceptionHandler(BindException exception) {
        List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
        return Result.failed(fieldErrors.get(0).getDefaultMessage());
    }

    /**
     * validation Exception
     * @param exception
     * @return Result
     */
    @ExceptionHandler({ MethodArgumentNotValidException.class })
    public Result handleBodyValidException(MethodArgumentNotValidException exception) {
        FieldError fieldError = exception.getBindingResult().getFieldError();
        return Result.failed(String.format("%s", fieldError.getDefaultMessage()));
    }
}

⑥通过Apifox测试,通过Header传递国际化参数Accept-Language参数(en-US,zh-CN),模拟参数username为空状态
在这里插入图片描述
在这里插入图片描述
⑦ 现在模拟异常抛出国际化,首先在保存接口添加用户账号唯一校验(记得去除掉上节添加的@UniqueUsername注解,防止被影响)

  private final MessageSourceAccessor messages;

    @PostMapping
    public Result<Long> save(@RequestBody @Validated SystemUserDto systemUserDto) {
        //验证用户唯一
        SystemUserVo systemUserVo=systemUserService.findByUsername(systemUserDto.getUsername());
        if(systemUserVo!=null){
            return Result.failed(messages.getMessage("SystemUserDto.Username.Exist"));
        }
        Long id=systemUserService.createUser(systemUserDto);
        return Result.ok(id);
    }

国际化配置文件添加SystemUserDto.Username.Exist:
在这里插入图片描述
通过Apifox测试,通过Header传递国际化参数Accept-Language参数(en-US,zh-CN),模拟参数username重复存在的情况。
在这里插入图片描述

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

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

相关文章

Ubuntu离线安装库并解决依赖关系

&#xff08;1&#xff09;起因 安装插件出现库未找到的错误 configure: error: curses library is required but not found.&#xff08;2&#xff09;解决方法 手动到Ubuntu的库发布网页下载 http://packages.ubuntu.com/ 选择系统对应架构的版本下载&#xff0c;然后上传…

AI语言大模型商业价值深度解析

点击蓝字 关注我 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;特别是深度学习算法的进步&#xff0c;AI语言大模型在自然语言处理领域的表现日益突出。国内外多种语言大模型如&#xff1a;OpenAi 的 ChatGpt&#xff0c;阿里通义千问&#xff0c;百度文心…

计算机网络之TCP序号,确认序号和报文传输时间

开篇提示 本篇适合于了解基础知识&#xff0c;进行扩展提高的使用&#xff0c;附带考研习题以及解析。 TCP序号和确认序号的区别 TCP首部中有序号和确认序号&#xff0c;他们都是4个字节&#xff08;4B&#xff09;&#xff0c;且在数据传输中有很重要的意义&#xff0c;那么两…

在Net8.0中使用 MQTTnet 开源库实现 MQTT 应用程序开发(实践)

1. 介绍 MQTTnet 是一个强大的开源 MQTT 客户端库&#xff0c;适用于 C# 平台。它提供了丰富的功能和灵活的 API&#xff0c;可以轻松地构建基于 MQTT 协议的应用程序。本文将逐步学习如何使用 MQTTnet 库创建 MQTT 客户端&#xff0c;并实现基本的发布、订阅功能。 简介 什么…

无缝融入,即刻智能[二]:Dify-LLM平台(聊天智能助手、AI工作流)快速使用指南,42K+星标见证专属智能方案

无缝融入,即刻智能[二]:Dify-LLM平台(聊天智能助手、AI工作流)快速使用指南,42K+星标见证专属智能方案 1.快速创建应用 你可以通过 3 种方式在 Dify 的工作室内创建应用: 基于应用模板创建(新手推荐) 创建一个空白应用 通过 DSL 文件(本地 / 在线)创建应用 从模板创建…

六级英语翻译

大纲解读 1. 考查方式&#xff1a;段落汉译英。 2. 涉及主题&#xff1a;中国的历史、文化、经济、社会发展等。 3. 篇幅&#xff1a;六级为180-200个汉字。 4. *卷面分&#xff1a;106.5 / 710。 5. *考试时长&#xff1a;30分钟。 第一步&#xff1a;Extracting&…

Nvidia AI 发布 Llama-Minitron 3.1 4B:通过修剪和提炼 Llama 3.1 8B 构建的新语言模型

Nvidia 刚刚发布了语言模型的新版本&#xff0c;不过这次是一个小型语言模型&#xff1a;Llama-3.1-Minitron 4B 模型。这意味着它是语言模型不断发展的重要步骤之一&#xff0c;通过剪枝和知识提炼等尖端技术&#xff0c;将大型模型的效率与小型模型相结合。 Llama-3.1-Minitr…

Qt动画效果、动画曲线

Qt动画效果 QPropertyAnimation *animation new QPropertyAnimation(labelWin, "geometry",this); // 创建胜利标签动画animation->setStartValue(labelWin->geometry()); // 设置动画的起始位置animation->setEndValue(QRect(labelWin->x(), labelW…

Servbay 1.40版支持MySQL了,快升级吧。

全新的服务管理界面及MySQL支持 现在&#xff0c;你可以在ServBay中使用MySQL了。除了带来全新的服务管理界面外&#xff0c;我们还为你带来了MySQL5.1-MySQL9.0的所有版本&#xff0c;满足你对数据管理的一切需求。 全新的数据库管理功能 通过新的数据库管理功能&#xff0c…

Datawhale AI 夏令营 第四期 AIGC Task3

活动简介 活动链接&#xff1a;Datawhale AI 夏令营&#xff08;第四期&#xff09; 以及AIGC里面的本次任务说明&#xff1a;Task 3 进阶上分-实战优化 这次任务呢&#xff0c;主要是对知识的一个讲解&#xff0c;包括ComfyUI工具的使用啊&#xff0c;以及LoRA的原理啊&…

机器学习课程学习周报八

机器学习课程学习周报八 文章目录 机器学习课程学习周报八摘要Abstract一、机器学习部分1.1 self-attention的计算量1.2 人类理解代替自注意力计算1.2.1 Local Attention/Truncated Attention1.2.2 Stride Attention1.2.3 Global Attention1.2.4 聚类Query和Key 1.3 自动选择自…

使用哪种方式可以将 MATLAB 算法转换到FPGA中运行?

FPGA在进行相关算法计算时&#xff0c;一般都会使用高级语言进行算法验证&#xff0c;目前比较常见的就是 MATLAB &#xff0c;那么使用哪种方式可以将MATLAB中实现的算哒转换到FPGA中&#xff1f; 目前可以通过多种方式在 FPGA 中实现算法。 Simulink HDL Coder MathWorks 提供…

Keepalived学习

环境准备&#xff1a;两台服务器&#xff0c;两台客户机&#xff0c;关闭火墙和selinux 在两台主机上安装ka yum install keepalived -y 开启软件 keepalived配置 进入文件 vim /etc/keepalived/keepalived.conf 修改配置 配置slave 效果 在另一台路由配置 抢占模式和非…

UE基础 —— 项目设置

目录 访问项目设置 类别和分段 Project Game Engine Editor Platforms Plugins 通过 项目设置&#xff08;Project Settings&#xff09;&#xff0c;可以配置影响以下内容&#xff1a; 虚幻引擎项目&#xff1b;引擎在运行项目时的行为&#xff1b;项目如何在特定平台…

JavaEE 第13节 synchronized关键字基本实现原理

目录 synchronized的基本特点&#xff1a; synchronized关键字的底层实现&#xff1a; 1&#xff09;锁升级 2&#xff09;锁消除 3&#xff09;锁粗化 synchronized的基本特点&#xff1a; 以下特点只考虑&#xff08;jdk1.8&#xff09;&#xff1a; 1&#xff09;刚开始…

高可用集群keep-alive

keepalive简介 keepalive为LVS应用延伸的高可用服务。lvs的调度器无法做高可用。但keepalive不是为lvs专门集群服务的&#xff0c;也可以为其他的的代理服务器做高可用。 keepalive在lvs的高可用集群&#xff0c;主调度器和备调度器(可以有多个) 一主两备或一主一备。 VRRP: k…

Windows下枚举USB设备信息Demo

目录 1 简介 1.1 设备接口类 1.2 枚举设备信息原理 2 SetupDi系列函数介绍 2.1 SetupDiGetClassDevs 2.2 SetupDiEnumDeviceInfo 2.3 SetupDiGetDeviceRegistryProperty 2.4 SetupDiGetDeviceRegistryProperty 3 演示Demo 3.1 开发环境 3.2 功能介绍 3.3 下载地址 …

70 爬楼梯

解题思路一&#xff1a;&#xff08;动态规划&#xff09; \qquad 假设F(n)返回的是爬n阶的所有方法个数&#xff0c;由题可知&#xff0c;每次可以爬1-2级台阶&#xff0c;那么可以得到&#xff1a; \qquad \qquad \qquad \qquad \qquad F(n) F(n - 1) F(n - 2) \qquad 我…

WeTab AI桌面端的下载安装

wetab AI的使用很方便&#xff0c;收费也不高&#xff0c;专业版的最新版本的AI核心配置如下&#xff1a; 现在推出了桌面端&#xff0c;下载链接&#xff1a;桌面端下载链接 在下载页面点击windows&#xff08;Beta版&#xff09;&#xff1a; 下载并安装&#xff0c;桌面上就…

DRF组件讲解

DRF组件 1. Web应用模式 在开发Web应用中&#xff0c;有两种应用模式&#xff1a; 前后端不分离[客户端看到的内容和所有界面效果都是由服务端提供出来的。 前后端分离【把前端的界面效果(html&#xff0c;css&#xff0c;js分离到另一个服务端&#xff0c;python服务端只需…