【附录】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. 遵循最佳实践
相关推荐
笑衬人心。几秒前
缓存的三大问题分析与解决
java·spring·缓存
XiangCoder22 分钟前
🔥Java核心难点:对象引用为什么让90%的初学者栽跟头?
后端
二闹32 分钟前
LambdaQueryWrapper VS QueryWrapper:安全之选与灵活之刃
后端
得物技术33 分钟前
Rust 性能提升“最后一公里”:详解 Profiling 瓶颈定位与优化|得物技术
后端·rust
XiangCoder38 分钟前
Java编程案例:从数字翻转到成绩统计的实用技巧
后端
duration~38 分钟前
SpringAI实现Reread(Advisor)
java·人工智能·spring boot·spring
aiopencode39 分钟前
iOS 文件管理全流程实战,从开发调试到数据迁移
后端
YuforiaCode1 小时前
24SpringCloud黑马商城微服务整合Seata重启服务报错的解决办法
java·spring·微服务
Lemon程序馆1 小时前
Kafka | 集群部署和项目接入
后端·kafka
集成显卡1 小时前
Rust 实战五 | 配置 Tauri 应用图标及解决 exe 被识别为威胁的问题
后端·rust