国际化
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 国际化支持:
- MessageSource 配置:ResourceBundleMessageSource
- 消息资源文件:不同语言的 properties 文件
- 使用 MessageSource:在 Service 和 Controller 中获取消息
- LocaleResolver:Session、Cookie、AcceptHeader 等策略
- LocaleChangeInterceptor:动态切换语言
- 模板引擎集成:Thymeleaf 和 JSP 中的使用
- 实际应用:验证消息、异常消息国际化
Spring 的国际化支持使得构建多语言应用变得简单高效。