当前位置: 首页 > news >正文

Springboot实现国际化(MessageSource)

背景

  • 在当今全球化的数字时代,软件应用的用户群体不再局限于单一语言或地区。无论是面向全球市场的电商平台,还是提供跨国服务的企业级应用,都需要能够适应不同语言环境,以满足全球用户的个性化需求。这就引出了一个关键的技术需求——国际化(Internationalization,通常简称为 i18n)。
  • Spring Boot 作为一款强大的后端开发框架,提供了多种方式来实现国际化,而 MessageSource 是其中一种非常实用且广泛采用的实现方式。接下来,我们将以 MessageSource 为例子实现国际化。

配置文件

# 国际化配置
spring:messages:default-locale: zh_CN		# 默认中文encoding: UTF-8				# 确认国际化文件采用utf-8格式,否则乱码basename: i18n/messages		# 放在resources/i18n目录下

配置类

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;import java.util.Locale;/*** MessageSourceConfig 消息源配置** @author xxx* @since 2025/9/4*/
@Configuration
public class MessageSourceConfig {@Value("${spring.messages.default-locale:}")private String defaultLocale;@Value("${spring.messages.encoding}")private String encoding;@Value("${spring.messages.basename}")private String basename;@Beanpublic MessageSource messageSource() {ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();messageSource.setBasename(basename);messageSource.setDefaultEncoding(encoding);messageSource.setDefaultLocale(Locale.forLanguageTag(defaultLocale));return messageSource;}
}

工具类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;import java.util.Locale;/*** MessageSourceUtil 消息工具类** @author xxx* @since 2025/9/4*/
@Component
public class MessageSourceUtil {private static MessageSource messageSource;@Autowiredpublic MessageSourceUtil(MessageSource messageSource) {MessageSourceUtil.messageSource = messageSource;}public static String getMessage(String code, Object[] args) {Locale locale = LocaleContextHolder.getLocale();return messageSource.getMessage(code, args, locale);}
}

国际化文件

修改属性文件编码格式

属性文件默认编码格式

国际化文件

在 resources/i18n 目录下新建:

messages_en_US.properties

PERM_NO_PERMISSION=No permission to operate

messages_zh_CN.properties

PERM_NO_PERMISSION=没有权限执行该操作

业务错误枚举类

import cn.com.techvision.mdm.common.utils.MessageSourceUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;/*** BusinessErrorConstant 业务错误枚举值** @author xxx* @since 2025/8/16*/
@AllArgsConstructor
@Getter
public enum BusinessErrorConstant {PERM_NO_PERMISSION(403, "PERM-ERR-001"),;/*** 状态码*/private final int statusCode;/*** 业务错误码*/private final String errCode;public static String getErrMsg(BusinessErrorConstant businessErrorConstant, Object... elements) {return String.format(MessageSourceUtil.getMessage(businessErrorConstant.name(), null), elements);}
}

在响应体中使用

响应体结构

import cn.com.xxx.BusinessErrorConstant;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 响应包的基类*/
@Data
@NoArgsConstructor
public class BaseResponse<T> {private int status;private String errCode;private String msg;private T data;private BaseResponse(int status, String errCode, String msg, T data) {this.status = status;this.errCode = errCode;this.msg = msg;this.data = data;}public static <T> BaseResponse<T> error(BusinessErrorConstant businessErrorConstant, Object... args) {return new BaseResponse<>(businessErrorConstant.getStatusCode(), businessErrorConstant.getErrCode(),BusinessErrorConstant.getErrMsg(businessErrorConstant, args), null);}
}

测试

测试类

@RestController
@RequestMapping("/system/org/{orgId}/project/{projectId}/test")
@Slf4j
public class TestController {@GetMapping("/i18n-res-err")public BaseResponse<String> testI18nResErr(){return BaseResponse.error(BusinessErrorConstant.PERM_NO_PERMISSION);}
}

测试用例

Accept-Language=en-US

curl --location --request GET 'http://localhost:24433/system/org/1/project/-1/test/i18n-res-err' \
--header 'Accept-Language: en-US' \
--header 'Content-Type: application/json' \
--data-raw ''

响应结果

{"status": 403,"errCode": "PERM-ERR-001","msg": "No permission to operate","data": null
}

Accept-Language=zh-CN

curl --location --request GET 'http://localhost:24433/system/org/1/project/-1/test/i18n-res-err' \
--header 'Accept-Language: zh-CN' \
--header 'Content-Type: application/json' \
--data-raw ''

响应结果

{"status": 403,"errCode": "PERM-ERR-001","msg": "没有权限执行该操作","data": null
}

在自定义业务异常中使用

import cn.com.xxx.BusinessErrorConstant;
import cn.com.xxx.MessageSourceUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;/*** BusinessException 业务异常** @author xxx* @since 2025/9/4*/
@EqualsAndHashCode(callSuper = true)
@Data
@Getter
public class BusinessException extends RuntimeException {/*** 状态码*/private final int statusCode;/*** 业务错误码*/private final String code;/*** 错误信息*/private final String message;public BusinessException(BusinessErrorConstant constant) {super(MessageSourceUtil.getMessage(constant.name(), null));this.statusCode = constant.getStatusCode();this.code = constant.getErrCode();this.message = MessageSourceUtil.getMessage(constant.name(), null);}public BusinessException(BusinessErrorConstant constant, Object... elements) {super(String.format(MessageSourceUtil.getMessage(constant.name(), null), elements));this.statusCode = constant.getStatusCode();this.code = constant.getErrCode();this.message = String.format(MessageSourceUtil.getMessage(constant.name(), null), elements);}
}

全局异常捕获

import cn.com.xxx.BaseResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {@ExceptionHandler({BusinessException.class})public BaseResponse<String> businessExceptionHandler(BusinessException businessException){log.error(businessException.getMessage(), businessException);return BaseResponse.error(businessException.getStatusCode(), businessException.getCode(),businessException.getMessage(), null);}@ExceptionHandler({Exception.class})public void globalExceptionHandler(Exception ex, HttpServletResponse response){log.error(ex.getMessage(), ex);response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);}
}

测试

测试类

@RestController
@RequestMapping("/system/org/{orgId}/project/{projectId}/test")
@Slf4j
public class TestController {@GetMapping("/i18n-exception")public BaseResponse<String> testI18nThrowException(){throw new BusinessException(BusinessErrorConstant.PERM_NO_PERMISSION);}
}

测试用例

Accept-Language=en-US

curl --location --request GET 'http://localhost:24433/system/org/1/project/-1/test/i18n-exception' \
--header 'Accept-Language: en-US' \
--header 'Content-Type: application/json' \
--data-raw ''

响应结果

{"status": 403,"errCode": "PERM-ERR-001","msg": "No permission to operate","data": null
}

Accept-Language=zh-CN

curl --location --request GET 'http://localhost:24433/system/org/1/project/-1/test/i18n-exception' \
--header 'Accept-Language: zh-CN' \
--header 'Content-Type: application/json' \
--data-raw ''

响应结果

{"status": 403,"errCode": "PERM-ERR-001","msg": "没有权限执行该操作","data": null
}
http://www.xdnf.cn/news/1466065.html

相关文章:

  • AI Compass前沿速览:Kimi K2、InfinityHuman-AI数字人、3D-AI桌面伴侣、叠叠社–AI虚拟陪伴
  • 查询语言的进化:SQL之后,为什么是GQL?数据世界正在改变
  • 生态 | 华院计算与深至科技达成战略合作,携手推动AI+医学影像算法升级迭代
  • 代码随想录70期day3
  • 算法(keep learning)
  • 外包干了3年,技术退步太明显了。。。。。
  • 计算机网络1 第一章 概述——以寄邮件比喻整个流程
  • threeJS 实现开花的效果
  • 概率论第三讲——多维随机变量及其分布
  • 要搞清楚你为什么上班
  • 大型语言模型SEO(LLM SEO)完全手册:驾驭搜索新范式
  • 深入剖析 ThreadLocal 及其生态系统:从基础用法到源码实现,从设计思想到工程实践
  • Android14 init启动Zygote详解
  • 必知!机器人的分类与应用:RPA、人形与工业机器人
  • 大数据毕业设计选题推荐-基于大数据的高级大豆农业数据分析与可视化系统-Hadoop-Spark-数据可视化-BigData
  • solidity函数篇
  • 5分钟征服Linux:20个神级命令+系统架构解密,让命令行恐惧症瞬间治愈!
  • 智能风险评估与欺诈检测系统
  • 普通键盘在MacOS上如何使用快捷键
  • 分布式常见面试题整理
  • k8s 部署 redis
  • springboot redis 缓存入门与实战
  • [bat-cli] 输出处理 | `OutputType` 和 `OutputHandle`
  • 基于华为云平台的STM32F103C8T6工业生产线温湿度监控系统
  • 深度学习书籍推荐
  • LangChain: Models, Prompts 模型和提示词
  • UE4 Mac构建编译报错 no member named “disjunction” in namespace “std”
  • 企业为何仍困在“数据孤岛”?——从iPaaS重构信息流的实践路径
  • 一个专为地图制图和数据可视化设计的在线配色网站,可以助你制作漂亮的地图!
  • Leetcode—2749. 得到整数零需要执行的最少操作数【中等】(__builtin_popcountl)