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

news2024/9/20 22:56:11

Springboot国际化

  • 什么是国际化?
  • 为什么使用国际化?
  • 国际化细分
  • 国际化相关知识
    • Locale对象
    • MessageSource接口
    • LocaleResolver接口
    • 国际化文件
  • 国际化一般实现
  • 国际化改进版
  • 框架中国际化

源码地址:请看day06

什么是国际化?

国际化,也叫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/2058873.html

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

相关文章

Ubuntu24.04用C++ Connector连接MySQL数据库

首先安装MySQL官方提供的C Connector库。 sudo apt-get install libmysqlcppconn-dev 然后找一个目录&#xff0c;建立一个main.cpp文件。 #include <iostream> #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/statemen…

vscode提升:JSON 中不允许有注释

解决方案 &#xff1a; 运行&#xff1a; json with comment 参考链接&#xff1a; https://blog.csdn.net/eqizhihui/article/details/134014010 人工智能学习网站 https://chat.xutongbao.top

逆变器的防孤岛测试基础知识

防孤岛测试是逆变器并网前的重要测试项目&#xff0c;主要用于验证并网发电系统在电网断电的情况下&#xff0c;能否快速、准确地检测到电网的失电状态&#xff0c;并在规定的时间内停止向电网输送电能&#xff0c;以防止电力系统出现孤岛效应。 孤岛效应是指当电网因故障或停电…

Chainlit接入FastGpt接口快速实现自定义用户聊天界面

前言 由于fastgpt只提供了一个分享用的网页应用&#xff0c;网页访问地址没法自定义&#xff0c;虽然可以接入NextWeb/ChatGPT web等开源应用。但是如果我们想直接给客户应用&#xff0c;还需要客户去设置配置&#xff0c;里面还有很多我们不想展示给客户的东西怎么办&#xf…

数论(二)——博弈论与组合计数

文章目录 4题单A - [有趣的数](https://www.acwing.com/problem/content/description/3198/)B - [取数游戏 II](https://www.luogu.com.cn/problem/P1288)C - [acwing-nim](https://www.acwing.com/problem/content/submission/code_detail/29453954/)D - [排列计数](https://w…

C语言小tip之整型提升

今天让我们来学习一下C语言中的一个小知识点-----整型提升 什么叫整型提升呢&#xff1f; C语言中整型算术运算总是至少以缺省&#xff08;默认&#xff09;整型类型的精度来进行的。​为了获得这个精度&#xff0c;表达式中的字符和短整型操作数在使用之前被转换为普通整型&a…

从大模型到通用AI《智能计算系统》带你揭秘大模型背后的智能计算革命

“只要你想把大模型做得更好、做得更大、做得更快、做得更省电&#xff0c;你就必须要有系统的人才跟思维。 . 我们这个领域变化得特别快,教的内容,教的方式,都要不断地变化。如果我们中国的研究者和教育者做得更好一点如果我们在具身智能的大模型上,或者是说在未来终极的通用人…

AI预测福彩3D采取888=3策略+和值012路或胆码测试8月21日新模型预测第63弹

经过62多期的测试&#xff0c;当然有很多彩友也一直在观察我每天发的预测结果&#xff0c;得到了一个非常有价值的信息&#xff0c;那就是9码定位的命中率非常高&#xff0c;62多期一共只错了6次&#xff0c;这给喜欢打私房菜的朋友提供了极高价值的预测结果~当然了&#xff0c…

【JVM】JVM解析字节码文件过程(二)

JVM解析字节码文件过程 字节码中的数据结构 field_info 在Java字节码中&#xff0c;field_info结构是用来描述类或接口中的字段(成员变量的)。每个field_info结构对应类文件中的一个字段。其中它的组成部分包括如下&#xff1a; 1.access_flags:访问标志,表示字段的访问级别(如…

哪款骨传导耳机好?2024不同价位骨传导耳机推荐!

对于我个人而言&#xff0c;蓝牙耳机早已成为了我形影不离的“私人音乐盒”&#xff0c;满足了我日常各式各样的需求&#xff0c;仿佛是身体的一部分&#xff0c;不可或缺。然而&#xff0c;我也曾面临一个不小的挑战——由于耳孔小巧且敏感&#xff0c;长时间佩戴传统的入耳式…

使用钉群发送告警通知

创建钉群&#xff0c;添加机器人 创建群 添加机器人并设置信息 需要注意的是设置“安全设置”时如果使用自定义关键词方式&#xff0c;那设置的内容必须要包含告警消息的内容 代码 模拟http请求发送通知 /*** param content 消息内容* param webhook 设置告警通知的群中机器…

基于vue框架的搬家网平台iz216(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,服务项目,订单信息,工人,商家,项目类型,指派信息,进度情况,服务评价 开题报告内容 基于Vue框架的搬家网平台开题报告 一、引言 随着城市化进程的加快和人们生活水平的提高&#xff0c;搬家服务需求日益增长。传统的搬家服务往往…

pygame开发课程系列(8):进阶开发

第八章 进阶学习 在本章中&#xff0c;我们将深入探讨一些高级的游戏开发技巧。这些技术可以使你的游戏更具吸引力和互动性&#xff0c;从动画效果到复杂的碰撞检测&#xff0c;再到多人网络功能。掌握这些技巧将帮助你提升游戏的质量&#xff0c;并为玩家带来更丰富的体验。 …

QWidget加border-color,一定要使用确定的名字。要不然整个qwidget内部的子控件都会添加边框

QWidget加border-color&#xff0c;一定要使用确定的名字。要不然整个qwidget内部的子控件都会添加边框

【学术会议征稿】第二届物联网与云计算技术国际学术会议 (IoTCCT 2024)

第二届物联网与云计算技术国际学术会议 (IoTCCT 2024) 2024 2nd International Conference on Internet of Things and Cloud Computing Technology (IoTCCT 2024) 第二届物联网与云计算技术国际学术会议(IoTCCT 2024)将于2024年9月27日至29日于广西桂林召开。本次会议将围绕…

zabbix常见架构及组件

Zabbix作为一个开源的、功能全面的监控解决方案&#xff0c;广泛应用于各类组织中&#xff0c;以实现对网络、服务器、云服务及应用程序性能的全方位监控。部署架构灵活性高&#xff0c;可支持从小型单一服务器环境到大型分布式系统的多种场景。基本架构通常包括监控端&#xf…

点灯案例优化(二) 利用位运算修改特定位

前面&#xff0c;我们对点灯代码进行了第一次优化&#xff0c;效果如下 尽管第一次优化以后代码可读性确实高了不少&#xff0c;也看起来更加简洁&#xff0c;但是&#xff0c;这里仍旧存在一个很严重的问题&#xff1a;就在每一个表达式右边&#xff0c;我们给寄存器的数据赋值…

[数据集][目标检测]绳子检测数据集VOC+YOLO格式322张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;322 标注数量(xml文件个数)&#xff1a;322 标注数量(txt文件个数)&#xff1a;322 标注类别…

虹科波形实验室 | 识别振动/敲击方向的方法

熟悉虹科Pico的朋友们都知道&#xff0c;我们的NVH诊断设备可以精确采集三个方向上的振动幅值&#xff0c;并通过软件的自动计算&#xff0c;定位振动来源。但最近收到用户的咨询&#xff0c;问我们NVH设备的振动传感器是否能识别振动的方向&#xff1f;比如Y轴的振动&#xff…

uipath发送邮件给多人:配置教程步骤解答!

uipath发送邮件给多人如何设置&#xff1f;使用uipath发信技巧&#xff1f; 自动化流程能够极大提高工作效率&#xff0c;其中邮件发送作为日常操作的重要组成部分&#xff0c;尤为关键。AokSend将详细介绍如何通过uipath发送邮件给多人&#xff0c;帮助您在自动化流程中更好地…