目录
2.1 新建国际化文件夹(i18n)到resources目录下,分别添加对应的国际化文件配置
2.1.2 message_en_US.properties
2.2 将前面博客中的自定义响应体、响应码枚举类、统一异常码中的message替换成引用格式, 而非具体的中文消息
2.3 实现LocaleResolver实现自定义区域解析器
2.4 实现WebMvcConfigure,把自定义区域解析器添加到容器中
2.5 通过消息工具类MessageUtils,根据前端language的参数实现消息语言切换
4. 更新预告--->通过自定义注解根据请求头注入用户会话信息
1. 引言
前面我们讲到了
SpringBoot项目自定义统一响应体设计_附上完整测试代码-CSDN博客
和SpringBoot项目企业级全局异常处理-CSDN博客。
今天我们来讲讲国际化。
Spring Boot 项目里谈“国际化”(Internationalization,简称 i18n),并不是再写一堆 if-else 判断语言,而是用 Spring 自带的 MessageSource + LocaleResolver 把“可变文案”全部抽离到外部资源文件,运行时根据请求自动切换语言。下面从“是什么、怎么做、好在哪”三个维度做一个系统的介绍。
先来示例:
根据前一篇文章中代码示例, 当订单id为空的时候会抛出异常:
中文显示为

请求头设置语言为英文时, 抛出异常为对应的英文:

2. 步骤
注:其余代码在引言提到的前两篇博客么么哒!
2.1 新建国际化文件夹(i18n)到resources目录下,分别添加对应的国际化文件配置
注意文件夹名不要错误必须是i18n,这个文件夹可以自动被springboot识别的,里面文件是K=V形式,点击Resource Bundle进行添加三语言消息配置,如果没有这个选项File -> Settings -> Plugins 搜索 Resource Bundle Editor安装如图所示:

分别为默认配置、英文配置、中文配置
2.1.1 messages.properties
message.success=成功
message.fail=失败
message.request.bad=请求错误
message.token.unauthorized=未授权
message.method.not.allow=请求方法不允许
message.enum.constant.systemError=系统错误
message.enum.invalidRequestMethod=无效的请求方法
message.enum.accessDenied=访问未授权
message.enum.userSessionNotFound=用户会话信息未找到
message.enum.common.error.code.order.id.is.empty = 订单id为空
2.1.2 message_en_US.properties
message.success=Success
message.fail=Fail
message.request.bad=The request is bad
message.token.unauthorized=Unauthorized
message.method.not.allow= The request method is not allowed
message.enum.constant.systemError=systemError
message.enum.invalidRequestMethod=invalidRequestMethod
message.enum.accessDenied=accessDenied
message.enum.userSessionNotFound=userSessionNotFound
message.enum.common.error.code.order.id.is.empty = The orderId is empty
2.1.3 yml配置文件新增
spring:
messages:
# 国际化资源文件前缀
basename: i18n.messages
2.2 将前面博客中的自定义响应体、响应码枚举类、统一异常码中的message替换成引用格式, 而非具体的中文消息
注:里面有新增的消息工具类用于解析消息, 见后面章节:
2.2.1 国际化后的自定义响应体
package com.rehse.common;
import com.rehse.util.SpringBeanUtils;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import java.util.Locale;
/**
* @author: Rehse
* @description: Result
* @date: 2025/8/14
*/
@Data
@Builder
@AllArgsConstructor
public class Result<T> {
private String code;
private String message;
private T data;
public Result(){
this.code = ResultCodeEnum.SUCCESS.getCode();
this.message = getMessage(ResultCodeEnum.SUCCESS);
}
public Result(ResultCodeEnum codeEnum) {
this.code = codeEnum.getCode();
this.message = getMessage(codeEnum);
}
public Result(ResultCodeEnum codeEnum, T data) {
this.code = codeEnum.getCode();
this.message = getMessage(codeEnum);
this.data = data;
}
public static <T> Result<T> success() {
Result<T> result = new Result<>();
result.setCode(ResultCodeEnum.SUCCESS.getCode());
result.setMessage(getMessage(ResultCodeEnum.SUCCESS));
return result;
}
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(ResultCodeEnum.SUCCESS.getCode());
result.setMessage(getMessage(ResultCodeEnum.SUCCESS));
result.setData(data);
return result;
}
public static <T> Result<T> fail(String msg) {
Result<T> result = new Result<>();
result.setCode(ResultCodeEnum.FAIL.getCode());
result.setMessage(msg);
return result;
}
public static <T> Result<T> fail(String code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
return result;
}
// 重点
public static String getMessage(ResultCodeEnum code) {
// 获取bean
MessageSource messageSource = SpringBeanUtils.getBean(MessageSource.class);
String messageHolder = code.getMessage();
Locale locale = LocaleContextHolder.getLocale();
return messageSource.getMessage(messageHolder, null, locale);
}
}
2.2.2 国际化后的响应码枚举类
package com.rehse.common;
import java.util.Objects;
import java.util.stream.Stream;
/**
* @author: Rehse
* @description: ResultCodeEnum
* @date: 2025/8/13
*/
public enum ResultCodeEnum {
SUCCESS("10000", "message.success"),
FAIL("100001", "message.fail"),
BAD_REQUEST("400", "message.request.bad"),
UNAUTHORIZED("401", "message.token.unauthorized"),
METHOD_NOT_ALLOWED("405", "message.method.not.allow");
private String code; // 状态码
private String message; // 消息
ResultCodeEnum(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return this.code;
}
public String getMessage() {
return this.message;
}
public static ResultCodeEnum valueof(String code) {
return Stream.of(values())
.filter(option -> Objects.equals(option.code, code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No matching enum value for [" + code + "]"));
}
}
2.2.3 国际化后的统一异常码
package com.rehse.exception;
import com.rehse.util.MessageUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum CommonErrorCode implements ErrorCode {
SUCCESS("A10001", "message.success"),
ORDER_ID_IS_EMPTY("A20001", "message.enum.common.error.code.order.id.is.empty");
private final String code;
private final String message;
// 重点
public String getMessage() {
return MessageUtils.getMessage(this.message);
}
}
2.3 实现LocaleResolver实现自定义区域解析器
继承LocaleResolver实现自定义区域解析器,如下代码:
package com.rehse.config;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
/**
* @author: Rehse
* @description: 自定义区域信息解析器
* @date: 2025/8/13
*/
@Slf4j
public class MyLocalResolver implements LocaleResolver {
//进行配置区域解析器
@Override
public Locale resolveLocale(HttpServletRequest request) {
//从请求头取出当前的语言
String language = request.getHeader("lang");
//获取默认的区域语言
Locale locale = Locale.getDefault();
if (StrUtil.isNotEmpty(language)) {
String[] str = language.split("_");
locale = new Locale(str[0], str[1]);
}
log.info("当前的系统语言是:" + language);
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
2.4 实现WebMvcConfigure,把自定义区域解析器添加到容器中
package com.rehse.config;
import com.rehse.interceptor.BusinessOperatorArgumentResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
/**
* @author: Rehse
* @description: WebMvcConfig
* @date: 2025/8/13
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* 解析请求中的区域设置:从HTTP请求中提取用户的区域设置信息。
* 设置响应中的区域设置:在响应中设置适当的区域设置,以便返回用户偏好语言的内容。
* @return LocaleResolver
*/
@Bean
public LocaleResolver localeResolver() {
return new MyLocalResolver();
}
}
2.5 通过消息工具类MessageUtils,根据前端language的参数实现消息语言切换
其中的SpringBeanUtils工具类在下方
package com.rehse.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import java.util.Locale;
/**
* @author: Rehse
* @description: MessageUtils
* @date: 2025/8/13
*/
@Slf4j
public class MessageUtils {
/**
* 获取国际化数据
*
* @param holder 占位符key
* @return 返回国际化数据
*/
public static String getMessage(String holder) {
return getMessage(holder, null);
}
/**
* 根据key获取国际化数据
*
* @param messageHolder 占位符key
* @param defaultMsg 默认
* @return 返回国际化数据
*/
public static String getMessage(String messageHolder, String defaultMsg) {
MessageSource messageSource = SpringBeanUtils.getBean(MessageSource.class);
if (null == messageSource) {
log.info("MessageSource is empty, please config confirm i18n configuration.");
return defaultMsg;
}
Locale locale = LocaleContextHolder.getLocale();
return messageSource.getMessage(messageHolder, null, locale);
}
}
其中根据bean的类型获取消息源对象的工具类代码为:
package com.rehse.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
/**
* @author: Rehse
* @description: SpringBeanUtils
* @date: 2025/8/13
*/
@Component
public class SpringBeanUtils implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
private static ApplicationContext applicationContext;
/**
* @param applicationContext the ApplicationContext object to be used by this object
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringBeanUtils.applicationContext = applicationContext;
}
/**
* 刷新SpringContext上下文
*
* @param event the event to respond to
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
SpringBeanUtils.applicationContext = context;
}
/**
* 获取Bean
*
* @param clazz 类类型
* @param <T>
* @return Bean对象
* @throws BeansException
*/
public static <T> T getBean(Class<T> clazz) throws BeansException {
T bean = applicationContext.getBean(clazz);
return bean;
}
/**
* 获取Bean
*
* @param name Bean名称
* @return Bean对象
* @throws BeansException
*/
public static Object getBean(String name) throws BeansException {
return applicationContext.getBean(name);
}
}
3. 测试
根据请求头传入的语言为中文还是英文, 返回不同的响应消息。
示例:
设置请求头中的语言为英文, 当传入订单id为空时, 返回异常为相应的英文。

当响应成功时:

4. 更新预告--->通过自定义注解根据请求头注入用户会话信息
感谢宝宝们看完这篇博客, 感觉有帮助的话一键三连么么哒!
25届Java小登已入职, 入职后也要继续提升哇,猪咪将会不定期分享学习笔记!
重铸Java荣光, 我辈义不容辞!
强烈建议这几篇博客一起看, 因为我是一天写完的,
为了保持CSDN活跃度, 我打算一天一发, 下一篇将会于2025年8月20日08:30:00自动更新。
接下来将会更新自动根据请求头注入用户会话信息, 会用上建造者设计模式, 请关注跟进吧么么哒!
在某个用户在平台上面进行操作的时候, 会存在很多用户会话信息:包括用户id, 用户姓名, 用户邮箱等。
后续将会更新通过一个注解@UserSession, 一步注入用户会话信息。
示例:
4.1 Controller
在接口里面添加了注解@UserSession
@PostMapping("/saveOrder")
public Result<Boolean> saveOrder(@UserSession UserSessionInfo userSessionInfo, @RequestBody Order order){
return Result.success(orderService.saveOrder(order));
}
4.2 ServiceImpl
@Override
public Boolean saveOrder(Order order) {
UserSessionInfo userSessionInfo = SessionThreadLocalUtil.getCurrentUserInfo();
System.out.println("当前的UserSessionInfo为 : " + userSessionInfo);
Order orderNew = new Order();
BeanUtils.copyProperties(order, orderNew);
orderNew.setUserId(String.valueOf(userSessionInfo.getUserId()));
orderNew.setOrderId(String.valueOf(UUID.randomUUID()).replace("-", ""));
orderNew.setCreateTime(new Date());
orderNew.setUpdateTime(new Date());
orderNew.setStatus(0);
return save(orderNew);
}
4.3 控制台



2990

被折叠的 条评论
为什么被折叠?



