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 {/*** 国际化消息源*/@Resourceprivate MessageSource messageSource;/*** 区域解析器,供消息源@MessageSource根据不同的区域@java.util.Locale读取不同的properties文件** @return {@code LocaleResolver}*/@Beanpublic LocaleResolver localeResolver() {AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();// 设置默认区域:简体中文localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);return localeResolver;}/*** 使用自定义LocalValidatorFactoryBean,* 设置Spring国际化消息源,用户jsr303验证信息实现自定义国际化**/@Beanpublic 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/messagesfallback-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;@Overridepublic 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 {@NotEmptyprivate 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)@Documentedpublic @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,后续有时间再完善,大家也期待下一篇的源码解读吧。

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用系统观念打造智慧公厕,引领智慧城市的发展

智慧公厕&#xff0c;作为智慧城市建设的一部分&#xff0c;具有重要意义。在高度发达的科技条件下&#xff0c;如何打造高质量的智慧公厕是一个值得思考的问题。本文将以智慧公厕源头实力厂家广州中期科技有限公司&#xff0c;大量精品案例项目现场实景实图实例&#xff0c;探…

Rust控制台输出跑马灯效果,实现刷新不换行,实现loading效果

要在 Rust 中实现控制台刷新而不换行&#xff0c;以实现类似 "loading" 状态的效果&#xff0c;你可以使用 \r&#xff08;回车符&#xff09;来覆盖上一行的内容。 use std::io::{self, Write}; use std::thread; use std::time::Duration;fn main() {let loading_…

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

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…

动态内存操作函数使用过程中会遇见的问题

越界访问 首先我们上一个代码&#xff0c;看看这个的代码的问题 这个代码的问题显而易见 &#xff0c;就是在循环里面&#xff0c;产生了越界访问的问题&#xff0c;这里你开辟了10个整形空间&#xff0c;但是从0-10一共是11个整形空间。导致访问不合法的空间&#xff0c;从而…

卷积神经网络-卷积层

卷积神经网络-卷积层 1多层感知机&#xff08;MLP&#xff09;2卷积神经网络&#xff08;CNN&#xff09;3MLP和CNN关系与区别4仍然有人使用MLP的原因&#xff1a;5MLP的局限性&#xff1a;MLP的应用领域&#xff1a;总结&#xff1a;6全连接到卷积全连接层 vs 卷积层结构差异应…

一文教你学会用群晖NAS配置WebDAV服务结合内网穿透实现公网同步Zotero文献库

文章目录 前言1. Docker 部署 Trfɪk2. 本地访问traefik测试3. Linux 安装cpolar4. 配置Traefik公网访问地址5. 公网远程访问Traefik6. 固定Traefik公网地址 前言 Trfɪk 是一个云原生的新型的 HTTP 反向代理、负载均衡软件&#xff0c;能轻易的部署微服务。它支持多种后端 (D…

LLLM并发加速部署方案(llama.cpp、vllm、lightLLM、fastLLM)

大模型并发加速部署 解析当前应用较广的几种并发加速部署方案&#xff01; llama.cpp vllm lightLLM fastLLM

使用yolov9来实现人体姿态识别估计(定位图像或视频中人体的关键部位)教程+代码

yolov9人体姿态识别&#xff1a; 相较于之前的YOLO版本&#xff0c;YOLOv9可能会进一步提升处理速度和精度&#xff0c;特别是在姿态估计场景中&#xff0c;通过改进网络结构、利用更高效的特征提取器以及优化损失函数等手段来提升对复杂人体姿态变化的捕捉能力。由于YOLOv9的…

Java SPI 机制

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

ubuntu 中安装docker

1 资源地址 进入ubuntu官网下载Ubuntu23.04的版本的镜像 2 安装ubuntu 这里选择再Vmware上安装Ubuntu23.04.6 创建一个虚拟机&#xff0c;下一步下一步 注意虚拟机配置网络桥接&#xff0c;CD/DVD选择本地的镜像地址 开启此虚拟机&#xff0c;下一步下一步等待镜像安装。 3…

Idea2023.3.6版本无法启动设置界面-settings界面打不开无反应---IntelliJ Idea工作笔记013

先说一下网上有,把某个文件删除的 有说是因为汉化问题的 可以看到,其实都不是,这样弄就好了,很简单 Please report thisjava.lang.ClassCastException: class [Lcom.intellij.execution.filters.CompositeInputFilter$InputFilterWrapper; cannot be cast to class java.uti…

Java多线程实战-从零手搓一个简易线程池(二)线程池与拒绝策略实现

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️本系列源码仓库&#xff1a;多线程并发编程学习的多个代码片段(github) &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正…

文件操作(下)(想要了解如何操作文件,那么看这一片就足够了!)

前言&#xff1a;在文件操作&#xff08;上&#xff09;中&#xff0c;我们讲到了基础的文件操作&#xff0c;包括文件的打开&#xff0c;文件的关闭&#xff0c;以及文件的基础读写&#xff0c;那么除了之前学习的读写之外&#xff0c;还有什么其他的方式对文件进行读写操作吗…

Python提示‘ModuleNotFoundError: No module named ‘numpy.core._multiarray_umath‘

一、问题背景 在学习Python编程使用matplotlib时&#xff0c;总是提示: ModuleNotFoundError: No module named numpy.core._multiarray_umath 问题大致描述如下&#xff1a; D:\WorkSpace\PythonWorkSpace\Python编程-从入门到实践\venv\Scripts\python.exe D:\WorkSpace\Pyt…

Jenkins用户角色权限管理

Jenkins作为一款强大的自动化构建与持续集成工具&#xff0c;用户角色权限管理是其功能体系中不可或缺的一环。有效的权限管理能确保项目的安全稳定&#xff0c;避免敏感信息泄露。 1、安装插件&#xff1a;Role-based Authorization Strategy 系统管理 > 插件管理 > 可…

ES面试题

1、如何同步索引库 同步调用 在完成数据库操作后&#xff0c;直接调用搜索服务提供的接口 异步通知 在完成数据库操作后&#xff0c;发送MQ消息 搜索服务监听MQ&#xff0c;接收到消息后完成数据修改 监听binlog 2、分词器 ik分词器 ik_smart ik_max_word 自定义分词器 以拼…

安静:内向性格的竞争力 - 三余书屋 3ysw.net

精读文稿 这期我们介绍的这本书叫做《安静》&#xff0c;副标题是《内向性格的竞争力》。本书共有267页&#xff0c;我会用大约25分钟的时间为你讲述书中的精髓。内向性格具备什么样的竞争力&#xff1f;内向性格的人在人际交往和日常生活中似乎总是吃亏&#xff0c;因为他们不…

Postman传对象失败解决

文章目录 情景复现解决方案总结 情景复现 postman中调用 debug发现pId传入失败 分析解释&#xff1a; 实体类中存在pId、uid和num字段 controller层将GoodsCar作为请求体传入 解决方案 当时觉得很奇怪&#xff0c;因为uid和num可以被接收&#xff0c;而pId和num的数据类型相…

安卓Activity上滑关闭效果实现

最近在做一个屏保功能&#xff0c;需要支持如图的上滑关闭功能。 因为屏保是可以左右滑动切换的&#xff0c;内部是一个viewpager 做这个效果的时候&#xff0c;关键就是要注意外层拦截触摸事件时&#xff0c;需要有条件的拦截&#xff0c;不能影响到内部viewpager的滑动处理…

学习Fast-LIO系列代码中相关概念理解

目录 一、流形和流形空间&#xff08;姿态&#xff09; 1.1 定义 1.2 为什么要有流形? 1.3 流形要满足什么性质&#xff1f; (1) 拓扑同胚 (2) 可微结构 1.4 欧式空间和流形空间的区别和联系? (1) 区别&#xff1a; (2) 联系&#xff1a; 1.5 将姿态定义在流形上比…