程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Spring Boot

    • Spring Boot - 自动配置
    • Spring Boot - 自定义starter
    • Spring Boot - 配置文件
    • Spring Boot - 自定义SpringApplication
    • Spring Boot - 生命周期与事件
    • Spring Boot - 事件驱动
    • Spring Boot - Bean 加载方式
    • Spring Boot - 容器资源感知与获取
    • Spring Boot - 定时任务
    • Spring Boot - 异步任务
    • Spring Boot - 内置日志
    • Spring Boot - 函数式 Web
    • Spring Boot - 响应式远程调用
    • Spring Boot - 接口文档
    • Spring Boot - 单元测试
    • Spring Boot - 内容协商
    • Spring Boot - 参数校验
    • Spring Boot - HTTP客户端工具
    • Spring Boot - 控制器请求映射
    • Spring Boot - 请求参数接收
    • Spring Boot - 通用响应类
    • Spring Boot - 全局异常处理
    • Spring Boot - 整合Druid
    • Spring Boot - 整合Thymeleaf
    • Spring Boot - 国际化实现
      • 1. 国际化基础概念与工作原理
      • 2. 创建国际化资源文件
        • 2.1 资源文件的命名规则与位置
        • 2.2 资源文件内容示例
        • 2.3 资源文件编码注意事项
      • 3. 配置Spring Boot国际化
        • 3.1 基本MessageSource配置
        • 3.2 在application.properties中配置
        • 3.3 支持多个资源文件
      • 4. 使用MessageSource获取本地化消息
        • 4.1 在控制器中使用MessageSource
        • 4.2 在服务层使用MessageSource
      • 5. 在视图层使用国际化消息
        • 5.1 在Thymeleaf模板中使用国际化
        • 5.2 在JSP中使用国际化
        • 5.3 在JavaScript代码中使用国际化
      • 6. 语言切换实现方案
        • 6.1 配置LocaleResolver
        • 6.2 实现自定义LocaleResolver
        • 6.3 实现语言切换功能
      • 7. 高级国际化功能
        • 7.1 处理复数形式和条件消息
        • 7.2 时间和日期的国际化
        • 7.3 货币和数字的国际化
    • Spring Boot - 自定义注解
  • Spring高级
  • Spring Boot
scholar
2024-03-30
目录

Spring Boot - 国际化实现

# 1. 国际化基础概念与工作原理

国际化(Internationalization,通常缩写为i18n)指的是设计和开发应用程序时,使其能够适应不同语言和地区而无需进行工程上的修改。在Spring Boot中,国际化功能通过MessageSource接口实现,它允许应用程序从不同的资源文件中检索本地化消息。

国际化的主要优势:

  • 提升用户体验,使用户能以母语使用应用
  • 扩大应用的市场覆盖范围
  • 符合多国法规要求
  • 无需修改代码即可支持新语言

Spring Boot国际化的核心工作原理:

  1. 通过Locale对象识别用户的语言和地区偏好
  2. 根据Locale从对应的资源文件中检索本地化消息
  3. 提供默认语言作为回退机制

# 2. 创建国际化资源文件

国际化资源文件是包含键值对的属性文件,用于存储不同语言的消息文本。这些文件必须遵循特定的命名约定:basename_language_country.properties。

# 2.1 资源文件的命名规则与位置

Spring Boot默认在src/main/resources目录下查找国际化资源文件:

src/main/resources/
  ├── messages.properties        # 默认资源文件
  ├── messages_zh_CN.properties  # 中文(中国)资源文件
  ├── messages_en_US.properties  # 英文(美国)资源文件
  └── messages_fr_FR.properties  # 法文(法国)资源文件
1
2
3
4
5

命名规则说明:

  • messages:基本名称(basename),可自定义
  • zh:语言代码(ISO 639标准)
  • CN:国家/地区代码(ISO 3166标准)

# 2.2 资源文件内容示例

messages.properties(默认资源文件)

# 默认语言资源文件
# 当找不到匹配用户语言的资源文件时,使用此文件中的消息

# 通用页面元素
app.title=My Application
app.welcome=Welcome to our application
app.language=Language

# 用户相关消息
user.login=Login
user.register=Register
user.email=Email Address
user.password=Password
user.username=Username

# 验证消息
validation.required=This field is required
validation.email=Please enter a valid email address
validation.password.length=Password must be at least 8 characters
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

messages_zh_CN.properties(中文资源文件)

# 中文(简体)资源文件
# 编码必须是UTF-8以正确显示中文字符

# 通用页面元素
app.title=我的应用程序
app.welcome=欢迎使用我们的应用程序
app.language=语言选择

# 用户相关消息
user.login=登录
user.register=注册
user.email=电子邮箱
user.password=密码
user.username=用户名

# 验证消息
validation.required=此字段为必填项
validation.email=请输入有效的电子邮箱地址
validation.password.length=密码长度必须至少为8个字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

messages_en_US.properties(美国英语资源文件)

# 英文(美国)资源文件

# 通用页面元素
app.title=My Application
app.welcome=Welcome to our application
app.language=Language

# 用户相关消息
user.login=Sign In
user.register=Sign Up
user.email=Email Address
user.password=Password
user.username=Username

# 验证消息
validation.required=This field is required
validation.email=Please enter a valid email address
validation.password.length=Password must be at least 8 characters
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2.3 资源文件编码注意事项

确保所有的资源文件都使用UTF-8编码,特别是包含非ASCII字符的文件,如中文、俄文等。在大多数IDE中,可以在文件属性或保存选项中设置文件编码。

# 3. 配置Spring Boot国际化

# 3.1 基本MessageSource配置

在Spring Boot配置类中定义MessageSource Bean,指定资源文件的位置和默认编码。

/**
 * 国际化配置类
 * 定义消息源和相关国际化配置
 */
@Configuration
public class InternationalizationConfig {

    /**
     * 配置消息源Bean
     * 用于加载和解析国际化资源文件
     * 
     * @return 配置好的消息源
     */
    @Bean
    public MessageSource messageSource() {
        // 创建基于资源包的消息源
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        
        // 设置资源文件的基本名称(不含语言和国家后缀)
        // Spring将查找classpath下的messages.properties, messages_zh_CN.properties等文件
        messageSource.setBasename("messages");
        
        // 设置资源文件的默认编码,确保正确读取非ASCII字符
        messageSource.setDefaultEncoding("UTF-8");
        
        // 设置是否使用消息格式的缓存(提高性能)
        messageSource.setCacheMillis(3600000); // 1小时缓存
        
        // 当找不到特定消息代码的翻译时是否抛出异常
        // false表示返回消息代码本身而不是抛出异常
        messageSource.setUseCodeAsDefaultMessage(true);
        
        return messageSource;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 3.2 在application.properties中配置

Spring Boot提供了更简单的方式,可以直接在application.properties或application.yml中配置国际化:

# 消息源配置
spring.messages.basename=messages
spring.messages.encoding=UTF-8
spring.messages.cache-duration=3600s
spring.messages.fallback-to-system-locale=true
1
2
3
4
5

YAML格式:

spring:
  messages:
    # 指定消息资源文件的基本名称
    basename: messages
    # 指定资源文件的编码
    encoding: UTF-8
    # 设置缓存过期时间
    cache-duration: 3600s
    # 是否回退到系统Locale
    fallback-to-system-locale: true
1
2
3
4
5
6
7
8
9
10

# 3.3 支持多个资源文件

如果需要将消息分散到多个资源文件中,可以指定多个基本名称:

/**
 * 配置多个基本名称的消息源
 */
@Bean
public MessageSource messageSource() {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    
    // 通过逗号分隔多个基本名称
    // Spring将查找classpath下的messages、validation、errors等前缀的资源文件
    messageSource.setBasenames("messages", "validation", "errors");
    
    messageSource.setDefaultEncoding("UTF-8");
    return messageSource;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

或在属性文件中:

# 通过逗号分隔指定多个基本名称
spring.messages.basename=messages,validation,errors
1
2

# 4. 使用MessageSource获取本地化消息

# 4.1 在控制器中使用MessageSource

Spring Boot会自动注入配置好的MessageSource Bean,可以在控制器或服务中使用它:

/**
 * 演示如何在控制器中使用国际化消息
 */
@RestController
@RequestMapping("/api")
public class WelcomeController {

    /**
     * 自动注入配置好的消息源
     */
    @Autowired
    private MessageSource messageSource;

    /**
     * 获取欢迎消息的API端点
     * 根据请求中的Locale返回相应语言的欢迎消息
     * 
     * @param request HTTP请求对象,用于获取客户端的Locale
     * @return 本地化的欢迎消息
     */
    @GetMapping("/welcome")
    public String welcome(HttpServletRequest request) {
        // 从请求中获取客户端的Locale(语言和地区)
        Locale locale = request.getLocale();
        
        // 记录当前请求的Locale信息(可选)
        System.out.println("Client locale: " + locale.toString());
        
        // 使用MessageSource获取本地化消息
        // 参数1: 消息代码,对应资源文件中的key
        // 参数2: 消息参数,用于替换消息中的占位符(如{0}, {1}),此处不需要所以传null
        // 参数3: 客户端的Locale
        String welcomeMessage = messageSource.getMessage("app.welcome", null, locale);
        
        return welcomeMessage;
    }
    
    /**
     * 演示如何使用带参数的消息
     * 
     * @param username 用户名参数
     * @param request HTTP请求对象
     * @return 带用户名的欢迎消息
     */
    @GetMapping("/hello")
    public String hello(@RequestParam String username, HttpServletRequest request) {
        Locale locale = request.getLocale();
        
        // 假设资源文件中有: user.greeting=Hello, {0}! Welcome to our application.
        // 第二个参数传入对象数组,用于替换消息中的占位符
        String greeting = messageSource.getMessage("user.greeting", new Object[]{username}, locale);
        
        return greeting;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

# 4.2 在服务层使用MessageSource

/**
 * 演示如何在服务层使用国际化消息
 */
@Service
public class UserService {

    @Autowired
    private MessageSource messageSource;
    
    /**
     * 生成用户注册确认消息
     * 
     * @param user 用户对象
     * @param locale 用户的语言区域
     * @return 本地化的确认消息
     */
    public String generateRegistrationConfirmation(User user, Locale locale) {
        // 假设资源文件包含以下条目:
        // registration.confirmation=Dear {0}, your registration was successful. Your ID is {1}.
        
        // 使用用户信息作为消息参数
        Object[] args = {user.getName(), user.getId()};
        
        // 获取本地化消息
        return messageSource.getMessage("registration.confirmation", args, locale);
    }
    
    /**
     * 获取本地化的验证错误消息
     * 
     * @param errorCode 错误代码
     * @param locale 用户的语言区域
     * @return 本地化的错误消息
     */
    public String getValidationErrorMessage(String errorCode, Locale locale) {
        // 尝试获取自定义错误消息,如果不存在则使用默认消息
        try {
            return messageSource.getMessage("validation." + errorCode, null, locale);
        } catch (NoSuchMessageException e) {
            // 消息代码不存在时返回通用错误消息
            return messageSource.getMessage("validation.general.error", null, "Validation failed", locale);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# 5. 在视图层使用国际化消息

# 5.1 在Thymeleaf模板中使用国际化

Thymeleaf与Spring Boot无缝集成,提供了简单的语法来访问国际化消息:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- 使用国际化消息作为页面标题 -->
    <title th:text="#{app.title}">Application Title</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <!-- 使用基本的国际化消息 -->
    <h1 th:text="#{app.welcome}">Welcome</h1>
    
    <!-- 使用带参数的国际化消息 -->
    <!-- 假设资源文件中有: user.greeting=Hello, {0}! -->
    <p th:text="#{user.greeting(${username})}">Hello, User!</p>
    
    <!-- 创建一个登录表单 -->
    <form th:action="@{/login}" method="post">
        <div>
            <label th:text="#{user.email}">Email:</label>
            <input type="email" name="email" required>
            <small th:text="#{validation.email}">Please enter a valid email</small>
        </div>
        
        <div>
            <label th:text="#{user.password}">Password:</label>
            <input type="password" name="password" required>
            <small th:text="#{validation.password.length}">Password must be at least 8 characters</small>
        </div>
        
        <!-- 登录按钮使用国际化文本 -->
        <button type="submit" th:text="#{user.login}">Login</button>
    </form>
    
    <!-- 添加语言切换链接 -->
    <div>
        <span th:text="#{app.language}">Language:</span>
        <a href="?lang=en_US">English</a> |
        <a href="?lang=zh_CN">中文</a>
    </div>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 5.2 在JSP中使用国际化

对于使用JSP的项目,需要使用JSTL标签库来访问国际化消息:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>

<!DOCTYPE html>
<html>
<head>
    <!-- 使用spring:message标签访问国际化消息 -->
    <title><spring:message code="app.title" /></title>
    <meta charset="UTF-8">
</head>
<body>
    <h1><spring:message code="app.welcome" /></h1>
    
    <!-- 使用带参数的国际化消息 -->
    <p><spring:message code="user.greeting" arguments="${username}" /></p>
    
    <form action="<c:url value='/login'/>" method="post">
        <div>
            <label><spring:message code="user.email" /></label>
            <input type="email" name="email" required>
        </div>
        
        <div>
            <label><spring:message code="user.password" /></label>
            <input type="password" name="password" required>
        </div>
        
        <button type="submit">
            <spring:message code="user.login" />
        </button>
    </form>
    
    <!-- 语言切换链接 -->
    <div>
        <spring:message code="app.language" />:
        <a href="?lang=en_US">English</a> |
        <a href="?lang=zh_CN">中文</a>
    </div>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 5.3 在JavaScript代码中使用国际化

在前端JavaScript中使用国际化消息需要先将消息传递给前端:

<!-- 在Thymeleaf模板中 -->
<script th:inline="javascript">
    /*<![CDATA[*/
    // 创建一个全局消息对象,包含所有需要在JS中使用的国际化消息
    var messages = {
        welcome: /*[[#{app.welcome}]]*/ 'Welcome',
        login: /*[[#{user.login}]]*/ 'Login',
        required: /*[[#{validation.required}]]*/ 'This field is required',
        emailInvalid: /*[[#{validation.email}]]*/ 'Invalid email'
    };
    
    // 使用这些消息
    function validateForm() {
        var email = document.getElementById('email').value;
        if (!email) {
            alert(messages.required);
            return false;
        }
        
        if (!email.includes('@')) {
            alert(messages.emailInvalid);
            return false;
        }
        
        return true;
    }
    /*]]>*/
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 6. 语言切换实现方案

# 6.1 配置LocaleResolver

LocaleResolver用于确定当前用户的Locale(语言和地区)。Spring Boot提供了几种实现:

/**
 * Web国际化配置类
 * 配置Locale解析器和语言切换拦截器
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    /**
     * 配置Locale解析器
     * 决定如何确定用户的语言偏好
     * 
     * @return 配置好的LocaleResolver
     */
    @Bean
    public LocaleResolver localeResolver() {
        // 1. 基于Session的Locale解析器
        // 将用户选择的语言存储在会话中
        SessionLocaleResolver localeResolver = new SessionLocaleResolver();
        
        // 设置默认区域,当用户没有明确选择语言时使用
        localeResolver.setDefaultLocale(Locale.US);
        
        // 2. 基于Cookie的Locale解析器(替代方案)
        /*
        CookieLocaleResolver localeResolver = new CookieLocaleResolver();
        localeResolver.setDefaultLocale(Locale.US);
        localeResolver.setCookieName("language");
        localeResolver.setCookieMaxAge(3600); // Cookie有效期1小时
        */
        
        // 3. 基于Accept-Language头的Locale解析器(替代方案)
        /*
        AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
        localeResolver.setDefaultLocale(Locale.US);
        */
        
        return localeResolver;
    }

    /**
     * 配置语言切换拦截器
     * 允许通过请求参数切换语言
     * 
     * @param registry 拦截器注册表
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 创建语言切换拦截器
        LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
        
        // 设置语言参数名,例如:?lang=zh_CN
        localeInterceptor.setParamName("lang");
        
        // 注册拦截器
        registry.addInterceptor(localeInterceptor);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

# 6.2 实现自定义LocaleResolver

如果内置的LocaleResolver无法满足需求,可以实现自定义的解析器:

/**
 * 自定义Locale解析器
 * 同时支持URL参数、Cookie和请求头来确定用户语言
 */
public class CustomLocaleResolver implements LocaleResolver {

    // 语言参数名
    private static final String LANGUAGE_PARAM = "lang";
    // Cookie名称
    private static final String LANGUAGE_COOKIE = "language";
    // 默认语言
    private final Locale defaultLocale = Locale.US;

    /**
     * 解析请求中的Locale
     * 
     * @param request HTTP请求
     * @return 解析到的Locale
     */
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        // 1. 首先尝试从URL参数中获取语言
        String language = request.getParameter(LANGUAGE_PARAM);
        if (language != null && !language.isEmpty()) {
            return parseLocale(language);
        }
        
        // 2. 如果URL参数中没有语言设置,尝试从Cookie中获取
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (LANGUAGE_COOKIE.equals(cookie.getName())) {
                    return parseLocale(cookie.getValue());
                }
            }
        }
        
        // 3. 如果Cookie中也没有,使用Accept-Language头
        Locale locale = request.getLocale();
        return locale != null ? locale : defaultLocale;
    }

    /**
     * 设置Locale
     * 
     * @param request HTTP请求
     * @param response HTTP响应
     * @param locale 要设置的Locale
     */
    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        // 将选择的语言保存到Cookie中
        Cookie cookie = new Cookie(LANGUAGE_COOKIE, locale.toString());
        cookie.setPath("/");
        cookie.setMaxAge(60 * 60 * 24 * 30); // 30天
        response.addCookie(cookie);
    }
    
    /**
     * 解析语言字符串为Locale对象
     * 
     * @param language 语言字符串,例如"zh_CN"或"en"
     * @return 对应的Locale对象
     */
    private Locale parseLocale(String language) {
        String[] parts = language.split("_");
        if (parts.length == 1) {
            // 只有语言代码
            return new Locale(parts[0]);
        } else if (parts.length == 2) {
            // 语言和国家/地区代码
            return new Locale(parts[0], parts[1]);
        } else if (parts.length == 3) {
            // 语言、国家/地区和变体
            return new Locale(parts[0], parts[1], parts[2]);
        }
        return defaultLocale;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

注册自定义解析器:

@Bean
public LocaleResolver localeResolver() {
    return new CustomLocaleResolver();
}
1
2
3
4

# 6.3 实现语言切换功能

在页面上添加语言切换选项:

<!-- Thymeleaf模板中的语言切换 -->
<div class="language-switcher">
    <span th:text="#{app.language}">Language</span>: 
    <a href="?lang=en_US" th:text="English">English</a> | 
    <a href="?lang=zh_CN" th:text="中文">中文</a> | 
    <a href="?lang=fr_FR" th:text="Français">Français</a>
</div>

<!-- 或者使用下拉菜单 -->
<div class="language-dropdown">
    <label for="language-select" th:text="#{app.language}">Language</label>
    <select id="language-select" onchange="changeLanguage(this.value)">
        <option value="en_US">English</option>
        <option value="zh_CN">中文</option>
        <option value="fr_FR">Français</option>
    </select>
    
    <script>
        function changeLanguage(lang) {
            window.location.href = '?lang=' + lang;
        }
        
        // 根据当前URL设置下拉框选中项
        document.addEventListener('DOMContentLoaded', function() {
            var urlParams = new URLSearchParams(window.location.search);
            var currentLang = urlParams.get('lang');
            if (currentLang) {
                document.getElementById('language-select').value = currentLang;
            }
        });
    </script>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 7. 高级国际化功能

# 7.1 处理复数形式和条件消息

不同语言对复数的处理方式不同,可以使用MessageFormat来处理复数形式:

messages.properties:

cart.items={0,choice,0#Your cart is empty|1#Your cart has 1 item|1<Your cart has {0} items}
1

messages_zh_CN.properties:

cart.items={0,choice,0#您的购物车是空的|1#您的购物车有1件商品|1<您的购物车有{0}件商品}
1

使用示例:

@GetMapping("/cart")
public String getCartMessage(@RequestParam(defaultValue = "0") int itemCount, HttpServletRequest request) {
    Locale locale = request.getLocale();
    return messageSource.getMessage("cart.items", new Object[]{itemCount}, locale);
}
1
2
3
4
5

# 7.2 时间和日期的国际化

时间和日期格式在不同国家也有差异,可以使用Java的DateTimeFormatter结合Locale进行格式化:

/**
 * 日期时间国际化示例
 */
@GetMapping("/datetime")
public Map<String, String> getLocalizedDateTime(HttpServletRequest request) {
    Locale locale = request.getLocale();
    ZonedDateTime now = ZonedDateTime.now();
    
    // 使用本地化的格式化器
    DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(locale);
    DateTimeFormatter timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM).withLocale(locale);
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(locale);
    
    // 创建结果Map
    Map<String, String> result = new HashMap<>();
    result.put("locale", locale.toString());
    result.put("date", now.format(dateFormatter));
    result.put("time", now.format(timeFormatter));
    result.put("dateTime", now.format(dateTimeFormatter));
    
    return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 7.3 货币和数字的国际化

不同国家/地区使用不同的货币符号和数字格式:

/**
 * 货币和数字国际化示例
 */
@GetMapping("/format")
public Map<String, String> getLocalizedFormats(HttpServletRequest request) {
    Locale locale = request.getLocale();
    double amount = 1234567.89;
    
    // 获取本地化的格式化器
    NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
    NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale);
    NumberFormat percentFormat = NumberFormat.getPercentInstance(locale);
    
    // 创建结果Map
    Map<String, String> result = new HashMap<>();
    result.put("locale", locale.toString());
    result.put("number", numberFormat.format(amount));
    result.put("currency", currencyFormat.format(amount));
    result.put("percent", percentFormat.format(amount / 100));
    
    return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
编辑此页 (opens new window)
上次更新: 2025/04/05, 20:16:54
Spring Boot - 整合Thymeleaf
Spring Boot - 自定义注解

← Spring Boot - 整合Thymeleaf Spring Boot - 自定义注解→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式