SpringBoot i18n 国际化实现案例
国际化(i18n)是使应用程序能够支持多种语言和地区的过程。SpringBoot提供了强大的国际化支持,本文将详细介绍如何在SpringBoot项目中实现国际化功能。
一、基本实现步骤
1. 创建国际化资源文件
在 src/main/resources 目录下创建 i18n 文件夹,然后创建以下资源文件:
messages.properties:默认语言配置文件messages_zh_CN.properties:中文配置文件messages_en_US.properties:英文配置文件
示例内容:
# messages.properties (默认)
welcome.message=Welcome to our application!
user.greeting=Hello, {0}
page.title=Home Page
# messages_zh_CN.properties (中文)
welcome.message=欢迎使用我们的应用程序!
user.greeting=你好,{0}
page.title=首页
# messages_en_US.properties (英文)
welcome.message=Welcome to our application!
user.greeting=Hello, {0}
page.title=Home Page
2. 配置application.yml
在配置文件中设置国际化相关属性:
spring:
messages:
encoding: UTF-8
basename: i18n/messages
fallback-to-system-locale: false
3. 创建国际化配置类
@Configuration
public class I18nConfig implements WebMvcConfigurer {
/**
* 配置区域解析器
*/
@Bean
public LocaleResolver localeResolver() {
// 使用SessionLocaleResolver可以基于Session存储用户选择的语言
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
// 设置默认语言为中文
localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return localeResolver;
}
/**
* 配置语言切换拦截器
* 通过URL参数lang来切换语言,例如:?lang=zh_CN 或 ?lang=en_US
*/
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
4. 在Controller中使用国际化
@RestController
@RequestMapping("/api/i18n")
public class I18nController {
@Autowired
private MessageSource messageSource;
/**
* 获取国际化消息
*/
@GetMapping("/message")
public String getMessage(@RequestParam(defaultValue = "welcome.message") String key) {
// 使用当前线程上下文的区域设置
return messageSource.getMessage(key, null, LocaleContextHolder.getLocale());
}
/**
* 获取带参数的国际化消息
*/
@GetMapping("/message/params")
public String getMessageWithParams(@RequestParam(defaultValue = "user.greeting") String key,
@RequestParam(defaultValue = "Guest") String username) {
return messageSource.getMessage(key, new Object[]{username}, LocaleContextHolder.getLocale());
}
/**
* 手动切换语言(可选,如果不使用拦截器)
*/
@GetMapping("/change-language")
public String changeLanguage(@RequestParam String lang, HttpSession session) {
Locale locale;
if ("en_US".equals(lang)) {
locale = Locale.US;
} else {
locale = Locale.SIMPLIFIED_CHINESE;
}
// 如果使用SessionLocaleResolver
session.setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, locale);
return "Language changed to: " + locale;
}
}
二、支持表单验证的国际化
1. 增强配置类以支持验证国际化
@Configuration
public class I18nConfig implements WebMvcConfigurer {
@Autowired
private MessageSource messageSource;
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return localeResolver;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("lang");
return interceptor;
}
/**
* 配置验证国际化工厂
*/
@Bean
public LocalValidatorFactoryBean validatorFactoryBean() {
LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
validatorFactoryBean.setValidationMessageSource(messageSource);
return validatorFactoryBean;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
@Override
public Validator getValidator() {
return validatorFactoryBean();
}
}
2. 在实体类中使用国际化验证信息
@Data
public class User {
@NotBlank(message = "{user.name.not.blank}")
private String name;
@Email(message = "{user.email.invalid}")
private String email;
@Min(value = 18, message = "{user.age.too.young}")
private int age;
}
3. 在资源文件中添加验证消息
# messages.properties
user.name.not.blank=Name cannot be blank
user.email.invalid=Invalid email format
user.age.too.young=Age must be at least 18
# messages_zh_CN.properties
user.name.not.blank=用户名不能为空
user.email.invalid=邮箱格式不正确
user.age.too.young=年龄必须年满18岁
4. 在Controller中使用验证
@RestController
@RequestMapping("/api/user")
public class UserController {
@PostMapping("/register")
public ResponseEntity<?> register(@Valid @RequestBody User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<String> errors = bindingResult.getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(errors);
}
// 处理注册逻辑
return ResponseEntity.ok("User registered successfully");
}
}
三、自定义注解实现国际化验证
1. 创建自定义注解
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = I18nNotNullValidator.class)
public @interface I18nNotNull {
String message() default "{validation.not.null}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2. 创建验证器
@Component
public class I18nNotNullValidator implements ConstraintValidator<I18nNotNull, Object> {
private final MessageSource messageSource;
private String messageKey;
public I18nNotNullValidator(MessageSource messageSource) {
this.messageSource = messageSource;
}
@Override
public void initialize(I18nNotNull constraintAnnotation) {
this.messageKey = constraintAnnotation.message();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
Locale locale = LocaleContextHolder.getLocale();
String message = messageSource.getMessage(messageKey, null, locale);
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
return false;
}
return true;
}
}
四、使用Profile实现不同语言环境下的业务逻辑
1. 创建配置类
@Configuration
@Data
public class I18NConfiguration {
@Value("${spring.profiles.active}")
private String locale;
@Profile("zh_CN")
@Bean
public IProductService zhCNProductService() {
return new ZhCNProductService();
}
@Profile("en_US")
@Bean
public IProductService enUSProductService() {
return new EnUSProductService();
}
}
2. 创建服务接口和实现
public interface IProductService {
Map<String, String> getProduct();
}
@Service
public class ZhCNProductService implements IProductService {
@Override
public Map<String, String> getProduct() {
Map<String, String> product = new HashMap<>();
product.put("name", "产品名称");
product.put("description", "产品描述");
return product;
}
}
@Service
public class EnUSProductService implements IProductService {
@Override
public Map<String, String> getProduct() {
Map<String, String> product = new HashMap<>();
product.put("name", "Product Name");
product.put("description", "Product Description");
return product;
}
}
五、其他实用技巧
1. 使用AcceptHeaderLocaleResolver
如果希望通过HTTP请求头中的Accept-Language来自动检测用户语言偏好:
@Bean
public LocaleResolver localeResolver() {
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
// 设置支持的语言
List<Locale> locales = new ArrayList<>();
locales.add(Locale.SIMPLIFIED_CHINESE);
locales.add(Locale.US);
localeResolver.setSupportedLocales(locales);
// 设置默认语言
localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return localeResolver;
}
2. 在Thymeleaf模板中使用国际化
如果使用Thymeleaf模板引擎,可以这样使用国际化:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{page.title}"></title>
</head>
<body>
<h1 th:text="#{welcome.message}"></h1>
<p th:text="#{user.greeting(${username})}"></p>
<!-- 语言切换链接 -->
<div>
<a th:href="@{/(lang=zh_CN)}">中文</a>
<a th:href="@{/(lang=en_US)}">English</a>
</div>
</body>
</html>
3. 在自定义异常中使用国际化
@Service
public class UserService {
@Autowired
private MessageSource messageSource;
public void validateUser(User user) {
if (user == null) {
String errorMessage = messageSource.getMessage("user.null", null, LocaleContextHolder.getLocale());
throw new BusinessException(errorMessage);
}
// 其他验证逻辑
}
}
六、完整项目结构
src/main/java/
└── com/example/demo/
├── DemoApplication.java
├── config/
│ └── I18nConfig.java
├── controller/
│ ├── I18nController.java
│ └── UserController.java
├── service/
│ ├── IProductService.java
│ ├── ZhCNProductService.java
│ └── EnUSProductService.java
├── model/
│ └── User.java
├── exception/
│ └── BusinessException.java
└── annotation/
└── I18nNotNull.java
src/main/resources/
├── i18n/
│ ├── messages.properties
│ ├── messages_zh_CN.properties
│ └── messages_en_US.properties
└── application.yml
通过以上步骤,您可以在SpringBoot项目中完整实现国际化功能,支持多语言切换、验证消息国际化以及根据不同语言环境提供不同的业务逻辑。这种方式既灵活又易于维护,适用于各种需要国际化的应用场景。