springboot 国际化处理
国际化处理的关键类
Locale 语言设置,由language 和 country 组成
LocaleContextHolder 语言上下文,实际为ThreadLocal,
其中包含两个ThreadLocal:
一个为当前线程的语言环境localeContextHolder
一个为inheritableLocaleContextHolder
关键方法:
setLocale():设置当前语言环境
getLocale():获取当前语言环境
resetLocaleContext():重置语言环境
MessageSource 语言包
关键方法:
getMessage(key, null, locale):根据key和语言获取消息
LocaleResolver 默认使用的语言包
当未找到相应语言时,使用的语言环境,需注册为bean
接入流程:
概述:
spring 环境下 spring-context-5.2.14.RELEASE.jar 中,已有i18n相关依赖
在请求到来时,获取用户设置的语言环境,并将该环境设置进上下文
后续需要国际化的地方,使用MessageSource,根据key进行信息获取
请求结束后,对上下文环境进行reset
接入关键点
-
在resources下,新建i18n文件夹,并在其中创建
message.properties:当未找到相应的语言环境时,会使用此语言包的内容 message_zh_CN.properties:中文:中国 message_en_US.properties:英文:美国 内容为key=value形式
-
配置:
spring: messages: basename: i18n/messages # 资源存放位置 encoding: UTF-8 use-code-as-default-message: true # 当找不到code对应的消息时,是否使用code作为消息
-
注册LocaleResolver,设置默认语言环境。
-
在相应的位置进行语言设置:
前端设置:在header或cookies中进行语言设置,请求时传递,由后台解析 后台方案一:注册interceptor,获取语言环境,并使用LocaleContextHolder.setLocale()进行设置 后台方案二:注册filter,获取语言环境,并使用LocaleContextHolder.setLocale()进行设置 注意事项:interceptor在filter之后执行,如果在filter中有做预处理或者请求拦截,则需要在filter中进行语言设置,不然走不到interceptor中,也就不生效
-
在需要返回国际化消息的类中,注入 MessageSource,根据key和并使用LocaleContextHolder.getLocale()调用 getMessage方法
可考虑的优化处理:
新建常量类,保存所有的messageKey,避免在代码中使用魔法值
新建MessageSourceUtil,静态注入MessageSource,在静态方法中进行消息获取,在需要使用国际化消息的地方直接调用
新建ResponseBodyAdvice对返回结果进行处理,不用在所有返回结果的地方进行message的获取
关键代码:
SpringBoot 配置:
spring:
messages:
basename: i18n/messages # 资源存放位置
encoding: UTF-8
use-code-as-default-message: true # 当找不到code对应的消息时,是否使用code作为消息
默认语言设置为简体中文
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return localeResolver;
}
setLocale方法:
考虑写在filter中,还是写在interceptor中,
此处使用cookies进行语言设置:
Cookie[] cookies = request.getCookies();
Cookie langCookie = null;
Cookie countryCookie = null;
if (ArrayUtils.isNotEmpty(cookies)) {
for (Cookie cookie : cookies) {
if (langCookie != null && countryCookie != null) break;
if (cookie.getName().equals("lang")) {
langCookie = cookie;
continue;
}
if (cookie.getName().equals("country")) {
countryCookie = cookie;
continue;
}
}
}
if (langCookie == null) {
langCookie = new Cookie("lang", Locale.SIMPLIFIED_CHINESE.getLanguage());
langCookie.setMaxAge(604800);
countryCookie = new Cookie("country", Locale.SIMPLIFIED_CHINESE.getCountry());
countryCookie.setMaxAge(604800);
response.addCookie(langCookie);
response.addCookie(countryCookie);
}
Locale locale;
if (null == countryCookie) {
locale = new Locale(langCookie.getValue());
} else {
locale = new Locale(langCookie.getValue(), countryCookie.getValue());
}
LocaleContextHolder.setLocale(locale);
return true;
MessageSourceUtil
@Component
public class MessageSourceUtil {
private static MessageSource messageSource;
@Autowired
public void setMessageSource(MessageSource messageSource) {
MessageSourceUtil.messageSource = messageSource;
}
public static String getMessage(String code) {
Locale locale = LocaleContextHolder.getLocale();
return getMessageByLocale( locale,code);
}
public static String getMessageByLocale(Locale locale, String key) {
return messageSource.getMessage(key, null, locale);
}
}
ResponseBodyAdvice
@RestControllerAdvice
public class ResponseBodyConfig implements ResponseBodyAdvice<封装的返回类型||Object> {
// 对所有内容进行支持
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
/** 真正的处理方法,
* 如果在filter 中进行直接res.write,不会走到此方法,所以filter中的返回要单独处理
*/
@Override
public 封装的返回类型||Object beforeBodyWrite(封装的返回类型||Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
// 根据业务代码中返回的内容,从语言包进行获取
body.setMessgae(MessageSourceUtil.getMessage(body.getMessgae()));
return body;
}
}