场景
在应用交互时,可能需要根据客户端得语言来返回不同的语言数据。前端通过参数、请求头等往后端传入locale相关得参数,后端获取参数,根据不同得locale来获取不同得语言得文本信息返回给前端。
此时一般有两种常见会用到文本的国际化:
-
代码里响应,手动获取
使用MessageSource的getMessage方法即可,也就是spring容器中的getMessage()
-
校验参数框架校验message
注解校验多个参数使自动响应提示语,此时使用校验框架的占位符实现
Spring+Validate融合
Validation为主
以校验框架Validation的默认国际化文件ValidationMessages为来源,让Spring的MessageSource去读取ValidationMessages以达到融合的目的。具体参考下面的默认Validation
处理方式。
Spring为主
不使用校验框架Validation的默认文件,使用任意自定义路径的国际化文件。让Validation去读取MessageSource中的文件。具体参考Spring自定义
处理。
前置
设置IDEA
将项目编码配置成UTF-8,避免中文乱码
Boundle插件
安装Boundle插件可以将一个包下的配置文件的一个字段进行同步编辑
Validation依赖
引入支持hibernate得validation功能进行参数校验。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
复制
默认Validation
默认路径
spring的配置文件默认路径是resource目录下的message,可以通过spring.message
配置进行指定。
Validate的默认配置文件路径是resource目录下的ValidationMessages,正常情况下可以通过自定义Validator对象来自定义文件路径,但是可能会出现读取不到占位符值得问题,可以优先使用默认值。
ValidationMessages
在Validate使用默认文件时,创建ValidationMessages得boundle。
配置spring.message
让spring得message读取路径读取ValidationMessages
spring:
messages:
basename: ValidationMessages
encoding: utf-8
复制
获取国际化文本
Spring工具类
工具类API一般是在抛出异常,手动根据配置的code来响应不同语言的信息。通过ApplicationContext调取messageSource即可通过请求上线文得locale来获取不同语言版本得信息。getMessage
中code就bundle文件中的配置文件的key,key对应不同语言的value,args就占位符传参,底层通过messageFormat来填充数据。
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static String getMessage(String code, Object... args) {
return applicationContext.getMessage(code, args, LocaleContextHolder.getLocale());
}
}
复制
Locale获取
默认情况下spring注册的messageSource对象为ResourceBundleMessageSource,会读取spring.message
配置。
请求中Locale的获取是通过LocaleResolver
进行处理,默认是AcceptHeaderLocaleResolver
,通过WebMvcAutoConfiguration
注入,从Accept-Language
请求头中获取locale信息。
此时前端可以在不同语言环境时传入不同的请求头Accept-Language即可达到切换语言的效果
Accept-Language: en-Us Accept-Language: zh-CN
复制
validation文本
在校验请求体参数合法性时我们会通过注解形式来指定特定的校验非法的message返回值,这个返回值需要Validation的国际化支持,{dictName}
就是一个占位符语句,代表配置文件中的dictName值进行国际化。
@NotNull(message = "{dictName}{notNull}")
@Length(max = 128,message = "{dictName}{lengthMax}")
private String dictName;
复制
Spring自定义
自定义ValidationMessages需要替换Validator的用户配置信息路径。
配置Validator
路径从ValidationMessages改为i18n/messages
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import javax.validation.Validator;
import java.util.Properties;
@Configuration
public class ValidateConfig {
@Bean
public Validator validator(MessageSource messageSource){
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
// 国际化
factoryBean.setValidationMessageSource(messageSource);
// 设置使用 HibernateValidator 校验器
factoryBean.setProviderClass(HibernateValidator.class);
Properties properties = new Properties();
// 设置 快速异常返回
properties.setProperty("hibernate.validator.fail_fast", "true");
factoryBean.setValidationProperties(properties);
// 加载配置
factoryBean.afterPropertiesSet();
return factoryBean.getValidator();
}
}
复制
Spring配置
spring:
messages:
basename: i18n/messages
encoding: utf-8
复制
自定义Locale获取
使用自定义的I18nLocaleResolver替换默认的AcceptHeaderLocaleResolver,重写resolveLocale
方法就可以自定义Locale的解析逻辑。
自定义后使用content-language传Locale信息,使用
_
划分语言个地区。content-language: en_US content-language: zh_CN
复制
import cn.hutool.core.util.StrUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
@Configuration
public class I18nConfig {
@Bean
public LocaleResolver localeResolver() {
return new I18nLocaleResolver();
}
/**
* 获取请求头国际化信息
*/
static class I18nLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String language = httpServletRequest.getHeader("content-language");
Locale locale = Locale.getDefault();
if (StrUtil.isNotBlank(language)) {
String[] split = language.split("_");
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
}
复制
捕捉自定义校验异常
自定义的这种校验异常会抛出MethodArgumentNotValidException异常,异常msg如下:
org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in xxx.xxx(xxx): [Field error in object 'xxx' on field 'xxx': rejected value [null]; codes [NotBlank.xxx.xxx,NotBlank.xxx,NotBlank.java.lang.String,NotBlank]; arguments
复制
这样响应就代表自定义校验生效了,需要将异常信息转成正常的提示信息。在全局异常处理器中处理MethodArgumentNotValidException异常:
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error(e.getMessage(), e);
// 提取异常消息提示明文
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return new ResponseUtil<>().setErrorMsg(message);
}
复制