前言
在全球化业务场景下,系统适配多语言已成为标配需求。SpringBoot 作为主流的 Java 开发框架,提供了完善的国际化(i18n)解决方案。本文将从实战角度出发,完整讲解两种企业级 i18n 实现方案:基于配置文件的静态实现(适配简体中文、繁体中文、英文)和基于数据库的动态实现(支持运行时修改语言配置),同时覆盖校验注解国际化、性能优化、常见问题排查等核心要点。
一、国际化基础认知
1.1 核心概念
i18n 的核心目标是让系统在不修改代码的前提下,通过配置适配不同语言和地区的使用习惯。SpringBoot 中实现 i18n 的核心依赖是:
MessageSource:消息源接口,负责加载和解析多语言消息,默认实现为ResourceBundleMessageSource(基于配置文件)。Accept-Language:语言地区标识,格式为语言代码_国家/地区代码,如:- 简体中文:
zh_CN - 繁体中文:
zh_TW - 英文(美国):
en_US
- 简体中文:
LocaleResolver:语言解析器,负责从请求中获取/设置当前 Locale。LocaleChangeInterceptor:语言切换拦截器,用于拦截请求参数实现语言动态切换。
1.2 核心原理
SpringBoot 启动时,MessageSource 会加载指定路径下的多语言配置文件;当业务代码获取国际化消息时,框架会根据当前 Accept-Language 从对应配置文件/数据源中匹配消息键(Key),返回对应的消息值(Value)。
二、方式一:基于配置文件的 i18n 实现
2.1 环境准备
2.1.1 依赖配置
新建 SpringBoot 项目(推荐 2.7.x 或 3.2.x),核心依赖仅需 spring-boot-starter-web,无需额外依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 可选:简化配置文件编写(.yml) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
2.1.2 目录结构
在 resources 目录下创建 i18n 文件夹,用于存放多语言配置文件,最终目录结构:
resources/
├── application.yml # 核心配置
└── i18n/ # 国际化配置文件目录
├── messages.properties # 默认配置(无语言标识)
├── messages_zh_CN.properties # 简体中文
├── messages_zh_TW.properties # 繁体中文
└── messages_en_US.properties # 英文
2.2 多语言配置文件编写
2.2.1 命名规则
配置文件命名必须遵循 basename_语言代码_国家代码.properties 规则:
basename:自定义前缀(如messages),需在 application.yml 中配置。- 无语言标识的
messages.properties为默认配置,当匹配不到指定 Locale 的配置时,会使用该文件内容。
2.2.2 配置文件内容
- 默认配置(messages.properties):兜底使用,建议与默认语言(简体中文)保持一致
# 通用提示
common.submit=提交
common.cancel=取消
# 用户相关
user.name=用户名
user.age=年龄
# 校验提示
validate.required.id=主键不能为空
validate.required.name=姓名不能为空
- 简体中文(messages_zh_CN.properties):
# 通用提示
common.submit=提交
common.cancel=取消
# 用户相关
user.name=用户名
user.age=年龄
# 校验提示
validate.required.id=主键不能为空
validate.required.name=姓名不能为空
- 繁体中文(messages_zh_TW.properties):
# 通用提示
common.submit=提交
common.cancel=取消
# 用户相关
user.name=使用者名稱
user.age=年齡
# 校验提示
validate.required.id=主鍵不能為空
validate.required.name=姓名不能為空
- 英文(messages_en_US.properties):
# 通用提示
common.submit=Submit
common.cancel=Cancel
# 用户相关
user.name=Username
user.age=Age
# 校验提示
validate.required.id=Primary key cannot be empty
validate.required.name=Name cannot be empty
注意:properties 文件默认编码为 ISO-8859-1,直接写中文会乱码!需将 IDE 的 properties 文件编码设置为 UTF-8(IDEA:Settings → File Encodings → Properties Files → 勾选 Transparent native-to-ascii conversion)。
2.3 SpringBoot 核心配置
在 application.yml 中配置国际化相关参数,指定配置文件路径、默认语言、编码等:
spring:
# 国际化配置
messages:
basename: i18n/messages # 配置文件路径(无需写.properties 后缀)
encoding: UTF-8 # 解决中文乱码
fallback-to-system-locale: false # 禁用系统语言回退
default-locale: zh_CN # 默认语言:简体中文
cache-duration: 3600s # 配置文件缓存时间(生产建议设置)
# Web 配置(可选,用于请求参数解析)
web:
locale: zh_CN
2.4 自定义语言解析器与拦截器
默认情况下,SpringBoot 仅支持从请求头 Accept-Language 获取 Locale,为了方便通过请求参数(如 ?Accept-Language=en-US)切换语言,需自定义 LocaleResolver 并注册拦截器。
2.4.1 自定义 LocaleResolver
创建 config/I18nConfig.java,实现 LocaleResolver 接口:
package com.example.i18n.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import org.springframework.web.servlet.handler.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
/**
* 国际化核心配置类(修改为 Header 拦截语言)
*/
@Configuration
public class I18nConfig implements WebMvcConfigurer {
/**
* 注册自定义 LocaleResolver(基于 Session 存储 Locale)
* 替代默认的 AcceptHeaderLocaleResolver
*/
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver resolver = new SessionLocaleResolver();
// 设置默认语言:简体中文(与 application.yml 中保持一致)
resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return resolver;
}
/**
* 自定义拦截器:从 Accept-Language Header 解析并设置 Locale
*/
@Bean
public HandlerInterceptor localeHeaderInterceptor(LocaleResolver localeResolver) {
return new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 从请求头中获取语言标识(自定义 Header 名:Accept-Language,可根据需求修改)
String acceptLanguage = request.getHeader("Accept-Language");
// 2. 设置默认语言为空或者抛异常使用 Locale locale = Locale.SIMPLIFIED_CHINESE; // 默认语言// 默认:zh_CN
Locale locale = Locale.SIMPLIFIED_CHINESE;
// 3. 若 Header 中有值,则解析并设置 Accept-Language;无值则使用默认 Accept-Language
if (acceptLanguage != null && !acceptLanguage.isEmpty()) {
try {
// 取第一个语言项(如 "zh-CN,en;q=0.8" → "zh-CN")
String primary = acceptLanguage.split(",")[0].trim();
// Spring 工具类能正确解析 "zh-CN"、"en" 等格式
locale = StringUtils.parseLocale(primary);
} catch (Exception e) {
// 解析失败则使用默认语言,不抛异常
}
}
// 使用容器中真实的 LocaleResolver 实例设置 Locale(存入 Session)
localeResolver.setLocale(request, response, locale);
return true;
}
};
}
/**
* 注册拦截器到 Spring MVC 拦截器链
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeHeaderInterceptor(localeResolver()))
.addPathPatterns("/**")
.order(0); // 优先级最高
}
}
关键说明:
SessionLocaleResolver:将 Locale 存储在 Session 中,一次切换后,后续请求无需重复传参。localeHeaderInterceptor:拦截请求头Header中Accept-Language参数,自动更新当前 Locale(如Accept-Language=zh-TW会切换为繁体中文)。
2.5 国际化消息使用示例
2.5.1 工具类封装(推荐)
创建 utils/I18nUtils.java,封装获取国际化消息的方法,简化业务使用:
package com.example.i18n.utils;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Locale;
/**
* 国际化工具类
*/
@Component
public class I18nUtils {
@Resource
private MessageSource messageSource;
/**
* 获取国际化消息(使用当前 Locale)
* @param key 消息键
* @return 消息值
*/
public String getMessage(String key) {
return getMessage(key, null, LocaleContextHolder.getLocale());
}
/**
* 获取国际化消息(带参数)
* @param key 消息键
* @param args 参数数组(如消息为"你好{0}",args=new Object[]{"张三"})
* @return 消息值
*/
public String getMessage(String key, Object[] args) {
return getMessage(key, args, LocaleContextHolder.getLocale());
}
/**
* 手动指定 Locale 获取消息
* @param key 消息键
* @param args 参数数组
* @param locale 语言标识
* @return 消息值
*/
public String getMessage(String key, Object[] args, Locale locale) {
try {
// 从 MessageSource 中获取消息,若未找到则返回 key 本身
return messageSource.getMessage(key, args, locale);
} catch (Exception e) {
return key;
}
}
}
核心 API 说明:
LocaleContextHolder.getLocale():获取当前线程的 Locale(由 LocaleResolver 解析)。messageSource.getMessage(key, args, locale):核心方法,参数说明:key:消息键(如user.name)。args:消息参数(用于替换消息中的占位符,如user.hello=你好{0})。locale:指定语言标识。
2.5.2 控制器使用示例
创建 controller/I18nController.java,编写接口测试国际化效果:
package com.example.i18n.controller;
import com.example.i18n.utils.I18nUtils;
import org.springframework.web.bind.annotation.

