【附录】Spring 国际化支持 基础及应用

此文是 【Spring 容器详解】-> 【ApplicationContext 做了哪些企业化的增强?】的支节点。

Spring国际化支持是Spring框架提供的重要功能,允许应用程序根据用户的语言和地区偏好来显示不同的文本内容。这对于开发多语言应用程序至关重要。

0.核心概念 及 整体流程

  • 国际化(i18n):Internationalization的缩写,指应用程序能够适应不同语言和地区
  • 本地化(L10n):Localization的缩写,指为特定地区定制应用程序
  • Locale:表示语言和地区的组合,如zh_CN(简体中文)、en_US(美式英语)

一、完整配置及使用示例

1. Spring Boot配置

java 复制代码
@Configuration
public class I18nConfig implements WebMvcConfigurer {
    
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:messages");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setCacheSeconds(3600);
        return messageSource;
    }
    
    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver resolver = new SessionLocaleResolver();
        resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
        return resolver;
    }
    
    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        interceptor.setParamName("lang");
        return interceptor;
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
}

2. application.properties配置

properties 复制代码
# 国际化配置
spring.messages.basename=messages
spring.messages.encoding=UTF-8
spring.messages.cache-duration=3600
spring.messages.fallback-to-system-locale=false

3. 消息文件命名规则

消息文件应按照以下规则命名:

  • messages.properties - 默认语言
  • messages_zh_CN.properties - 简体中文
  • messages_en_US.properties - 美式英语
  • messages_ja_JP.properties - 日语

4. 消息文件示例

messages.properties (默认)

properties 复制代码
welcome.message=Welcome to our application
user.login=Login
user.register=Register
error.notfound=Resource not found

messages_zh_CN.properties (简体中文)

properties 复制代码
welcome.message=欢迎使用我们的应用程序
user.login=登录
user.register=注册
error.notfound=资源未找到

messages_en_US.properties (美式英语)

properties 复制代码
welcome.message=Welcome to our application
user.login=Login
user.register=Register
error.notfound=Resource not found

5. 在Controller中使用

java 复制代码
@RestController
public class UserController {
    
    @Autowired
    private MessageSource messageSource;
    
    @GetMapping("/welcome")
    public String welcome(Locale locale) {
        return messageSource.getMessage("welcome.message", null, locale);
    }
    
    @GetMapping("/user/login")
    public String login(Locale locale) {
        return messageSource.getMessage("user.login", null, locale);
    }
    
    @GetMapping("/error/404")
    public String notFound(Locale locale) {
        return messageSource.getMessage("error.notfound", null, locale);
    }
}

6. 在Service中使用

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private MessageSource messageSource;
    
    public String getWelcomeMessage(Locale locale) {
        return messageSource.getMessage("welcome.message", null, locale);
    }
    
    public String getErrorMessage(String errorCode, Object[] args, Locale locale) {
        return messageSource.getMessage(errorCode, args, locale);
    }
}

7. 在JSP/Thymeleaf中使用

JSP示例:

jsp 复制代码
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
    <title><spring:message code="welcome.title"/></title>
</head>
<body>
    <h1><spring:message code="welcome.message"/></h1>
    <a href="?lang=zh_CN">中文</a>
    <a href="?lang=en_US">English</a>
</body>
</html>

Thymeleaf示例:

html 复制代码
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title th:text="#{welcome.title}">Welcome</title>
</head>
<body>
    <h1 th:text="#{welcome.message}">Welcome</h1>
    <a href="?lang=zh_CN">中文</a>
    <a href="?lang=en_US">English</a>
</body>
</html>

二、配置信息中相关接口解释

1. 定义MessageSource接口获取国际化消息的方法

MessageSource是Spring i18n的核心接口,定义了获取国际化消息的方法:

java 复制代码
public interface MessageSource {
    String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
    String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}

主要实现类:

ResourceBundleMessageSource

基于Java ResourceBundle的实现,适用于大多数场景:

java 复制代码
@Bean
public MessageSource messageSource() {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setBasename("messages");
    messageSource.setDefaultEncoding("UTF-8");
    return messageSource;
}

ReloadableResourceBundleMessageSource

支持热重载的实现,适用于开发环境:

java 复制代码
@Bean
public MessageSource messageSource() {
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setBasename("classpath:messages");
    messageSource.setDefaultEncoding("UTF-8");
    messageSource.setCacheSeconds(3600); // 缓存1小时
    return messageSource;
}

2. 使用LocaleResolver解析用户的语言偏好

使用LocaleResolver可以基于不同的方案进行解析,分别是,基于Session、基于Cookie、基于HTTP Accept-Language头。这三种都可以。

SessionLocaleResolver

基于Session的Locale解析器:

java 复制代码
@Bean
public LocaleResolver localeResolver() {
    SessionLocaleResolver resolver = new SessionLocaleResolver();
    resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
    return resolver;
}

CookieLocaleResolver

基于Cookie的Locale解析器:

java 复制代码
@Bean
public LocaleResolver localeResolver() {
    CookieLocaleResolver resolver = new CookieLocaleResolver();
    resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
    resolver.setCookieName("locale");
    resolver.setCookieMaxAge(3600 * 24 * 365); // 1年
    return resolver;
}

AcceptHeaderLocaleResolver

基于HTTP Accept-Language头的解析器:

java 复制代码
@Bean
public LocaleResolver localeResolver() {
    AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
    resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
    return resolver;
}

3. 配置拦截器LocaleChangeInterceptor用于动态切换语言

java 复制代码
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
    LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
    interceptor.setParamName("lang");
    return interceptor;
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(localeChangeInterceptor());
}

三、高级特性

1. 参数化消息

消息文件支持参数化:

properties 复制代码
# messages.properties
greeting.message=Hello {0}, welcome to {1}!

# messages_zh_CN.properties
greeting.message=你好 {0},欢迎来到 {1}!

使用示例:

java 复制代码
String message = messageSource.getMessage("greeting.message", 
    new Object[]{"张三", "我们的网站"}, locale);

2. 嵌套消息

支持消息的嵌套引用:

properties 复制代码
# messages.properties
app.name=My Application
welcome.message=Welcome to {app.name}

# messages_zh_CN.properties
app.name=我的应用程序
welcome.message=欢迎使用 {app.name}

3. 默认消息

当找不到消息时,可以提供默认值:

java 复制代码
String message = messageSource.getMessage("unknown.key", 
    null, "默认消息", locale);

4. 单元测试

java 复制代码
@SpringBootTest
class I18nTest {
    
    @Autowired
    private MessageSource messageSource;
    
    @Test
    void testChineseMessage() {
        String message = messageSource.getMessage("welcome.message", 
            null, Locale.SIMPLIFIED_CHINESE);
        assertEquals("欢迎使用我们的应用程序", message);
    }
    
    @Test
    void testEnglishMessage() {
        String message = messageSource.getMessage("welcome.message", 
            null, Locale.US);
        assertEquals("Welcome to our application", message);
    }
}

总结

Spring的国际化支持提供了完整的多语言解决方案,通过合理配置和使用,可以轻松实现应用程序的国际化需求。关键点包括:

  1. 正确配置MessageSource和LocaleResolver
  2. 合理组织消息文件
  3. 在代码中正确使用国际化功能
  4. 注意性能优化和错误处理
  5. 遵循最佳实践
相关推荐
库库林_沙琪马15 分钟前
REST接口幂等设计深度解析
spring boot·后端
IT_陈寒18 分钟前
Redis性能提升50%的7个关键优化策略,90%开发者都不知道第5点!
前端·人工智能·后端
智商偏低23 分钟前
ASP.NET Core 身份验证概述
后端·asp.net
冷冷的菜哥23 分钟前
ASP.NET Core使用MailKit发送邮件
后端·c#·asp.net·发送邮件·mailkit
canonical_entropy41 分钟前
XDef:一种面向演化的元模型及其构造哲学
后端
小林coding1 小时前
再也不怕面试了!程序员 AI 面试练习神器终于上线了
前端·后端·面试
lypzcgf1 小时前
Coze源码分析-资源库-删除插件-后端源码-错误处理与总结
人工智能·后端·go·coze·coze源码分析·ai应用平台·agent平台
文心快码BaiduComate1 小时前
WAVE SUMMIT深度学习开发者大会2025举行 文心大模型X1.1发布
前端·后端·程序员
SamDeepThinking1 小时前
在Windows 11上配置Cursor IDE进行Java开发
后端·ai编程·cursor
知其然亦知其所以然1 小时前
面试官微笑发问:第100万页怎么查?我差点当场沉默…
后端·mysql·面试