跳到主要内容

国际化

Spring 提供了强大的国际化(i18n)支持,可以轻松实现多语言应用。本章介绍 Spring 国际化的配置和使用方法。

国际化概述

国际化(Internationalization,简称 i18n)是指应用程序能够根据用户的语言和地区显示相应的内容。Spring 的国际化支持基于 MessageSource 接口。

MessageSource 配置

基于 Java 配置

@Configuration
public class I18nConfig {

@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("i18n/messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(3600);
messageSource.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return messageSource;
}

@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver resolver = new SessionLocaleResolver();
resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return resolver;
}
}

消息资源文件

src/main/resources/i18n/ 目录下创建消息资源文件:

messages_zh_CN.properties(简体中文):

welcome=欢迎
login=登录
logout=退出
user.name=用户名
user.password=密码
user.email=邮箱
error.required={0}不能为空
error.length={0}长度必须在{1}到{2}之间
order.created=订单创建成功,订单号:{0}
greeting=你好,{0}!今天是{1,date,full}。

messages_en_US.properties(美式英语):

welcome=Welcome
login=Login
logout=Logout
user.name=Username
user.password=Password
user.email=Email
error.required={0} is required
error.length={0} length must be between {1} and {2}
order.created=Order created successfully, order number: {0}
greeting=Hello, {0}! Today is {1,date,full}.

messages_ja_JP.properties(日语):

welcome=ようこそ
login=ログイン
logout=ログアウト
user.name=ユーザー名
user.password=パスワード
user.email=メールアドレス
error.required={0}は必須です
error.length={0}の長さは{1}から{2}の間でなければなりません

使用 MessageSource

在 Service 中使用

@Service
public class MessageService {

@Autowired
private MessageSource messageSource;

public String getMessage(String code, Object[] args, Locale locale) {
return messageSource.getMessage(code, args, locale);
}

public String getMessage(String code, Locale locale) {
return messageSource.getMessage(code, null, locale);
}

public String getWelcomeMessage(Locale locale) {
return messageSource.getMessage("welcome", null, locale);
}

public String getGreeting(String name, Locale locale) {
return messageSource.getMessage("greeting", new Object[]{name, new Date()}, locale);
}

public String getRequiredError(String fieldName, Locale locale) {
return messageSource.getMessage("error.required", new Object[]{fieldName}, locale);
}
}

在 Controller 中使用

@Controller
public class UserController {

@Autowired
private MessageSource messageSource;

@GetMapping("/login")
public String loginPage(Locale locale, Model model) {
model.addAttribute("title", messageSource.getMessage("login", null, locale));
model.addAttribute("usernameLabel", messageSource.getMessage("user.name", null, locale));
model.addAttribute("passwordLabel", messageSource.getMessage("user.password", null, locale));
return "login";
}

@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password,
Locale locale, RedirectAttributes redirectAttributes) {
if (username.isEmpty()) {
String error = messageSource.getMessage("error.required",
new Object[]{messageSource.getMessage("user.name", null, locale)}, locale);
redirectAttributes.addFlashAttribute("error", error);
return "redirect:/login";
}
return "redirect:/home";
}
}

LocaleResolver

Spring 提供了多种 LocaleResolver 实现:

LocaleResolver说明
FixedLocaleResolver固定语言,不可更改
SessionLocaleResolver从 Session 获取语言设置
CookieLocaleResolver从 Cookie 获取语言设置
AcceptHeaderLocaleResolver从 HTTP 请求头获取语言设置

SessionLocaleResolver

@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver resolver = new SessionLocaleResolver();
resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return resolver;
}

CookieLocaleResolver

@Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
resolver.setCookieName("lang");
resolver.setCookieMaxAge(365 * 24 * 60 * 60);
return resolver;
}

AcceptHeaderLocaleResolver

@Bean
public LocaleResolver localeResolver() {
return new AcceptHeaderLocaleResolver();
}

LocaleChangeInterceptor

通过拦截器动态切换语言:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("lang");
return interceptor;
}

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

访问时通过参数切换语言:

/home?lang=zh_CN
/home?lang=en_US
/home?lang=ja_JP

在 Thymeleaf 中使用

如果使用 Thymeleaf 模板引擎,可以直接使用 #{} 语法:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{login}">Login</title>
</head>
<body>
<h1 th:text="#{welcome}">Welcome</h1>

<form th:action="@{/login}" method="post">
<label th:text="#{user.name}">Username</label>
<input type="text" name="username" th:placeholder="#{user.name}"/>

<label th:text="#{user.password}">Password</label>
<input type="password" name="password" th:placeholder="#{user.password}"/>

<button type="submit" th:text="#{login}">Login</button>
</form>

<p th:text="#{greeting(${user.name}, ${#dates.createNow()})}">Hello!</p>
</body>
</html>

在 JSP 中使用

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<spring:message code="welcome"/>
<spring:message code="error.required" arguments="用户名"/>
<spring:message code="greeting" arguments="${user.name},${now}"/>

获取当前 Locale

@Component
public class LocaleHelper {

public Locale getCurrentLocale() {
return LocaleContextHolder.getLocale();
}

public String getCurrentLanguage() {
return LocaleContextHolder.getLocale().getLanguage();
}

public String getCurrentCountry() {
return LocaleContextHolder.getLocale().getCountry();
}
}

实际应用示例

用户注册验证消息

@Service
public class UserService {

@Autowired
private MessageSource messageSource;

@Autowired
private UserDao userDao;

public ValidationResult validateUser(User user, Locale locale) {
ValidationResult result = new ValidationResult();

if (user.getName() == null || user.getName().isEmpty()) {
String fieldName = messageSource.getMessage("user.name", null, locale);
result.addError(messageSource.getMessage("error.required", new Object[]{fieldName}, locale));
}

if (user.getName() != null && (user.getName().length() < 3 || user.getName().length() > 20)) {
String fieldName = messageSource.getMessage("user.name", null, locale);
result.addError(messageSource.getMessage("error.length",
new Object[]{fieldName, 3, 20}, locale));
}

return result;
}
}

异常消息国际化

public class BusinessException extends RuntimeException {
private final String code;
private final Object[] args;

public BusinessException(String code, Object... args) {
this.code = code;
this.args = args;
}

public String getCode() {
return code;
}

public Object[] getArgs() {
return args;
}
}

@ControllerAdvice
public class GlobalExceptionHandler {

@Autowired
private MessageSource messageSource;

@ExceptionHandler(BusinessException.class)
public ResponseEntity<Map<String, String>> handleBusinessException(
BusinessException ex, Locale locale) {
String message = messageSource.getMessage(ex.getCode(), ex.getArgs(), locale);
Map<String, String> body = new HashMap<>();
body.put("error", message);
return ResponseEntity.badRequest().body(body);
}
}

@Service
public class OrderService {

public void cancelOrder(Long orderId) {
Order order = orderDao.findById(orderId);
if (order == null) {
throw new BusinessException("error.order.not.found", orderId);
}
if (order.getStatus() == OrderStatus.DELIVERED) {
throw new BusinessException("error.order.delivered");
}
order.setStatus(OrderStatus.CANCELLED);
orderDao.save(order);
}
}

小结

本章介绍了 Spring 国际化支持:

  1. MessageSource 配置:ResourceBundleMessageSource
  2. 消息资源文件:不同语言的 properties 文件
  3. 使用 MessageSource:在 Service 和 Controller 中获取消息
  4. LocaleResolver:Session、Cookie、AcceptHeader 等策略
  5. LocaleChangeInterceptor:动态切换语言
  6. 模板引擎集成:Thymeleaf 和 JSP 中的使用
  7. 实际应用:验证消息、异常消息国际化

Spring 的国际化支持使得构建多语言应用变得简单高效。