SpringBoot国际化配置流程(超详细)

前言

最新第一次在做SpringBoot的国际化,网上搜了很多相关的资料,都是一些简单的使用例子,达不到在实际项目中使用的要求,因此本次将结合查询的资料以及自己的实践整理出SpringBoot配置国际化的流程。

SpringBoot国际化

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

Spring Boot国际化是指在Spring Boot应用中实现多语言支持的功能。通过国际化,应用可以根据用户的语言偏好自动切换显示的语言版本,从而提供贴近用户的界面和文本信息。

SpringBoot官方国际化支持文档:7.5. Internationalization(请点击我)

由于SpringBoot默认集成国际化,因此本次实践也是基于SpringBoot的自动配置来进行。

准备环境

本次实践我使用的环境或工具版本如下:

SpringBoot 3.0.6IDEA社区版2023.1OpenJDK 17

引入依赖

<!-- web中提供国际化支持 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <version>3.0.6</version>
</dependency>
<!-- 参数校验 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>3.0.6</version>
</dependency>

自定义配置

@AutoConfigureBefore(WebMvcAutoConfiguration.class)
public class LocaleConfig {

    /**
     * 国际化消息源
     */
    @Resource
    private MessageSource messageSource;


    /**
     * 区域解析器,供消息源@MessageSource根据不同的区域@java.util.Locale读取不同的properties文件
     *
     * @return {@code LocaleResolver}
     */
    @Bean
    public LocaleResolver localeResolver() {
        AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
        // 设置默认区域:简体中文
        localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
        return localeResolver;
    }


    /**
     * 使用自定义LocalValidatorFactoryBean,
     * 设置Spring国际化消息源,用户jsr303验证信息实现自定义国际化
     *
     */
    @Bean
    public Validator getValidator() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(messageSource);
        return bean;
    }
}

java.util.Locale: Locale对象表示特定的地理、政治或文化区域,用以区分区域。

org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver:通过请求头Accept-Language的值(zh-CN、en-US等)来改变当前的区域设置

org.springframework.context.MessageSource:用于解析消息的策略接口,支持此类消息的参数化和国际化。根据Locale区域读取不同的properties国际化文件

@AutoConfigureBefore(WebMvcAutoConfiguration.class):由于WebMvcAutoConfiguration会注入一个默认的LocaleResolver,因此自定义的LocaleConfig要在WebMvcAutoConfiguration之前先执行,且beanName是localeResolver,目的就是用我们配置的LocaleConfig替换掉WebMvcAutoConfiguration自动注入的LocaleConfig

以下代码片段是WebMvcAutoConfiguration自动注入LocaleResolver 的方法

/**
* DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME的值就是"localeResolver"
*/
@Override
@Bean
@ConditionalOnMissingBean(name = DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME)
public LocaleResolver localeResolver() {
	if (this.webProperties.getLocaleResolver() == WebProperties.LocaleResolver.FIXED) {
		return new FixedLocaleResolver(this.webProperties.getLocale());
	}
	AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
	localeResolver.setDefaultLocale(this.webProperties.getLocale());
	return localeResolver;
}

国际化文件

在工程resources目录下新建目录i18n,在i18n目录下新建三个国际化文件:
messages.properties
messages_en_US.properties
messages_zh_CN.properties

zh-CN:简体中文
en-US:英语

在这里插入图片描述

在不配置默认的区域情况下,当没有找到匹配的语言文件时,会读取默认的messages.properties

如何快速编辑资源包.properties文件

配置application.yml

spring:
  ## note: ---------------- 国际化配置 ----------------
  messages:
    basename: i18n/messages
    fallback-to-system-locale: false

basename:基名,多个基名以逗号分隔(实质上是完全限定的类路径位置),每个基名都遵循 ResourceBundle 约定,对基于斜杠的位置提供了宽松的支持。如果它不包含包限定符(例如“org.mypackage”),则将从类路径根目录解析它。

基名可以理解为前缀,默认是从classpath根路径下找,配置的路径可以用斜杠/,也可以用.,即i18n/messagesi18n.messages,然后messages就是国际化文件的前缀,messages.properties就是默认国际化文件。

fallback-to-system-locale:如果未找到特定区域设置的文件,是否回退到系统区域设置。如果关闭此功能,则唯一的回退将是默认文件。就是我想要的区域语言文件没有的时候,就从系统中解析系统的区域,以系统的区域再找一次文件,找到就返回系统区域文件,如果找不到,就返回默认的文件。

国际化消息工具类

@Slf4j
@Component
public class LocaleUtil implements ApplicationContextAware {

    private static MessageSource messageSource;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        LocaleUtil.messageSource = applicationContext.getBean(MessageSource.class);
    }

    /**
     * 获取国际化message
     *
     * @param code           code
     * @param args           占位参数
     * @param defaultMessage 默认值
     * @return 国际化文本
     */
    public static String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage) {
        return messageSource.getMessage(code, args, defaultMessage, LocaleContextHolder.getLocale());
    }

    /**
     * 获取国际化message
     *
     * @param code           code
     * @param args           占位参数
     * @param defaultMessage 默认值
     * @param locale         地区
     * @return 国际化文本
     */
    public static String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
        return messageSource.getMessage(code, args, defaultMessage, locale);
    }

    /**
     * 获取国际化message
     *
     * @param code           code
     * @param defaultMessage 默认值
     * @return 国际化文本
     */
    public static String getMessage(String code, @Nullable String defaultMessage) {
        return messageSource.getMessage(code, null, defaultMessage, LocaleContextHolder.getLocale());
    }

封装国际化文本的读取接口,主要方便在代码中使用,不需要每次@Autowired注入MessageSource来使用。

国际化使用

异常提示国际化

在业务代码中,业务异常我们通常是抛出异常,由统一异常处理来根据异常codemessage封装成统一的返回对象,国际化这里我们也是一样,code不变,message改成国际化消息的key

  • 定义业务异常对象
public class BussinessException extends RuntimeException {

    private String code;

    public BussinessException (String code, String message) {
        super(message);
        this.code= code;
    }
}
  • 定义异常类型枚举
public enum ErrorCodeEnum {

    DEMO_ERROR(99999, "i18n.user.check.username.err");

    /**
     * 提示
     */
    private final String message;
    /**
     * 状态吗
     */
    private final String code;

    ErrorCodeEnum (String code, String message) {
        this.code = code;
        this.message = message;
    }

    /**
     * 获取国际化错误提示
     *
     * @return 错误提示文本
     */
    public String getMessage() {
        return LocaleUtil.getMessage(message, message);
    }

    /**
     * 获取国际化错误提示
     *
     * @param args 错误提示参数
     * @return 错误提示文本
     */
    public String getMessage(Object[] args) {
        return LocaleUtil.getMessage(message, args, message);
    }

    /**
     * 获取错误code
     *
     * @return 错误code
     */
    public Integer getCode() {
        return code;
    }
}
  • 国际化文件配置

在国际化文件都配置上i18n.user.check.username.err不同语言的翻译

## messages.properties
i18n.user.check.username.err=用户名校验不通过

## messages_en_US.properties
i18n.user.check.username.err=The username does not comply with regulations

## messages_zh_CN.properties
i18n.user.check.username.err=用户名不符合规范

在这里插入图片描述

  • 业务逻辑校验(使用场景之一)
   /**
     * 校验用户名是否符合规范
     *
     * @param userName 用户名
     * @return {@code String}
     */
    public String checkUserName(String userName) {
        if (StringUtils.isBlank(userName)) {
            throw new BussinessException(ErrorCodeEnum.DEMO_ERROR.getCode(), ErrorCodeEnum.DEMO_ERROR.getMessage());
        }
        // check logic code ...
        return userName;
    }

参数校验国际化

SpringBoot项目我们在做参数校验通常会使用JSR303、jakarta.validation参数校验快速失败。

import jakarta.validation.constraints.NotEmpty;
import lombok.Data;

@Data
public class TestReq {

    @NotEmpty
    private String userName;
}

我这里是导入开头的spring-boot-starter-validation依赖就可以使用hibernate-validator给我们提供的常用校验注解的国际化。
在这里插入图片描述

查看@NotEmpty注解源码,message默认就读国际化文件里jakarta.validation.constraints.NotEmpty.message

@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
public @interface NotEmpty {

	String message() default "{jakarta.validation.constraints.NotEmpty.message}";

	Class<?>[] groups() default { };

	Class<? extends Payload>[] payload() default { };

	/**
	 * Defines several {@code @NotEmpty} constraints on the same element.
	 *
	 * @see NotEmpty
	 */
	@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
	@Retention(RUNTIME)
	@Documented
	public @interface List {
		NotEmpty[] value();
	}
}

这里就贴ValidationMessages.propertiesjakarta.validation.constraints.NotEmpty.message,其他文件大家自行查看。

jakarta.validation.constraints.NotEmpty.message        = must not be empty

自定义校验错误提示信息

当我们不想使用hibernate-validator给我们提供的默认提示信息时,我们可以自定义自己的错误提示信息。这里就复用前面配置的i18n.user.check.username.err

@Data
public class TestReq {

    @NotEmpty(message = "{i18n.user.check.username.err}")
    private String userName;
}

如此,在Accept-Language=en-US即我想返回的信息是英文,参数校验不通过时,提示的不是must not be empty,而是我们自定义的The username does not comply with regulations

结语

时间有限,国际化的实践流程截图没提供,大家根据文中的操作步骤也可以完成国际化的demo,后续有时间再完善,大家也期待下一篇的源码解读吧。

文中如有描述不清楚、读者不理解意思的地方,大家评论区打出,我来完善哈。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智慧公厕四大核心能力,赋能城市公共厕所智能化升级

公共厕所是城市基础设施中不可或缺的一部分&#xff0c;但由于传统的公共厕所在建设与规划上&#xff0c;存在一定的局限性&#xff0c;导致环境卫生差、管理难度大、使用体验不佳等问题&#xff0c;给市民带来了很多不便。而智慧公厕作为城市智能化建设的重要组成部分&#xf…

谈谈变压器中的位置编码

变压器中的位置编码 一、说明 在上一期的“Transformers for Everyone”系列中&#xff0c;我们介绍了 Transformer 的概念&#xff0c;并深入研究了第一个关键架构元素&#xff1a;输入嵌入。如果你错过了第一集&#xff0c;你可以通过阅读来赶上&#xff1a;适合所有人的变形…

【Linux】 gcc(linux下的编译器)程序的编译和链接详解

目录 前言&#xff1a;快速认识gcc 1. 程序的翻译环境和执行环境 2.编译和链接 2.1翻译环境 2.2编译环境 1. 预处理 gcc -E指令 test.c&#xff08;源文件&#xff09; -o test.i&#xff08;生成在一个文件中&#xff0c;可以自己指定&#xff09; 预处理完成之后就停下来&am…

状态压缩的三种模型

第一种类型&#xff08;摆放方块&#xff09;&#xff1a; 代码如下&#xff1a; #include<iostream> #include<climits> #include<algorithm> #include<cstring> #include<cstdio> #include<cmath> #include<queue> #include<s…

vue3+ts+element home页面侧边栏+头部组件+路由组件组合页面教程

文章目录 效果展示template代码script代码样式代码 效果展示 template代码 <template><el-container class"home"><el-aside class"flex" :style"{ width: asideDisplay ? 70px : 290px }"><div class"aside-left&q…

项目中自动引入神器 - unplugin-auto-import/unplugin-vue-components

前端 项目中 自动引入 神器 前言 在开发中&#xff0c;我们总喜欢站在巨人的肩膀上开发&#xff0c;比如用一些 框架&#xff1a;vue,react, 组件库&#xff1a;element&#xff0c;ant。 工具函数&#xff1a;axios&#xff0c;lodash 现在是模块化时代&#xff0c;我们…

基于SpringBoot后端实现连接MySQL数据库并存贮数据

目录 一、什么是MySQL数据库 二、基于SpringBoot框架连接MySQL数据库 1、首先添加MySQL依赖&#xff1a; 2、配置数据库连接&#xff1a; 3、创建实体类&#xff1a; 4、创建Repository接口&#xff1a; 5、使用Repository&#xff1a; 三、编写业务SQL语句 1、使用Spring Data…

浅模仿小米商城布局(有微调)

CSS文件 *{margin: 0;padding: 0;box-sizing: border-box; }div[class^"h"]{height: 40px; } div[class^"s"]{height: 100px; } .h1{width: 1528px;background-color: green; } .h11{background-color:rgb(8, 220, 8); } .h111{width: 683px;background-c…

异或和之和【蓝桥杯】/拆位+贡献法

异或和之和 拆位贡献法 思路&#xff1a;刚开始考虑遍历L和R&#xff0c;同时可以用一个二维数组存储算过的值&#xff0c;但是时间复杂度还是O(n^2)&#xff0c;所以这种还是要拆位和利用贡献法 可以对于每个位&#xff0c;一定区间内&#xff0c;如果有奇数个1则异或值为1&…

阿里云ECS选型推荐配置

本文介绍构建Kubernetes集群时该如何选择ECS类型以及选型的注意事项。 集群规格规划 目前在创建Kubernetes集群时&#xff0c;存在着使用很多小规格ECS的现象&#xff0c;这样做有以下弊端&#xff1a; 网络问题&#xff1a;小规格Worker ECS的网络资源受限。 容量问题&…

Cubase 8.0 下载地址及安装教程

Cubase是一款流行的音乐制作和音频录制软件&#xff0c;由德国公司Steinberg开发。它是一款专业级的数字音频工作站&#xff08;DAW&#xff09;&#xff0c;广泛应用于音乐制作、音频录制、混音和制作等领域。 Cubase提供了丰富的功能和工具&#xff0c;用于录制、编辑、混音…

ubuntu22.04物理机双系统手动分区

ubuntu22.04物理机双系统手动分区 文章目录 ubuntu22.04物理机双系统手动分区1. EFI系统分区2. 交换分区3. /根分区4. /home分区分区后的信息 手动分区顺序&#xff1a;EFI系统分区(/boot/efi)、交换分区(/swap)、/根分区、/home分区。 具体参数设置&#xff1a; 1. EFI系统分…

Java SPI 机制

SPI 机制的定义 在Java中&#xff0c;SPI&#xff08;Service Provider Interface&#xff09;机制是一种用于实现软件组件之间松耦合的方式。它允许在应用程序中定义服务接口&#xff0c;并通过在类路径中发现和加载提供该服务的实现来扩展应用程序功能。 SPI 机制通常涉及三…

霉霉说地道中文,口型、卡点几乎完美,网友:配音时代结束了?

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站 每天给大家更新可用的国内可用chatGPT资源 更多资源欢迎关注 「给电影配音的时代即将结束了。」 AI 的发展让很多人直呼饭碗被抢了&#xff0c;以前是艺术家、程序员…… 现在配音员也要失业了&a…

Docker部署一个SpringBoot项目(超级详细)

注意&#xff1a;下面的教程主要是针对 Centos7 的&#xff0c;如果使用的其他发行版会有细微的差别&#xff0c;请查看官方文档。 Docker部署一个SpringBoot项目&#xff08;超级详细&#xff09; 一、安装Docker1.卸载旧版2.配置Docker的yum库3.安装Docker4.设置开机自启动5.…

超级充电测试基础认识

超级充电技术是电动汽车&#xff08;EV&#xff09;充电技术的一种&#xff0c;它的主要目标是在最短的时间内为电动汽车充满电。这种技术的出现&#xff0c;对于推动电动汽车的普及和发展具有重要的意义。 超级充电测试是对超级充电设备和系统进行全面、深入的检查和评估&…

SQL107 将两个 SELECT 语句结合起来(二)(不用union,在where里用or)

select prod_id,quantity from OrderItems where quantity 100 or prod_id like BNBG% order by prod_id;在where子句里使用or

Windows 7 静态 IP 地址

Windows 7 静态 IP 地址 References 静态 IP 地址设置为 192.168.1.198 控制面板 -> 查看网络状态和任务 更改适配器设置 网络连接 -> 属性 TCP / IPv4 警告 - 已计划将多个默认网关用于提供单一网络 (例如 intranet 或者 Internet) 的冗余。 6.1. 关闭 redundancy VMw…

Day23 代码随想录(1刷) 二叉树

669. 修剪二叉搜索树 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没有被移除&#xff0c;原有的父代…

Web系统开发之——文章管理

原文地址&#xff1a;Web系统开发之——文章管理 - Pleasure的博客 下面是正文内容&#xff1a; 前言 经过一番考量&#xff0c;关于Web应用系统功能部分的开发&#xff0c;决定采取基础的文字文章管理为核心功能。 不再采取前后端分阶段完成的方式&#xff0c;而是以一个一个…