在Spring中,官方文档中提供了一种国际化
配置。
什么是国际化配置,我的感受是不同国家不同语言,针对不同国家(中国和外国),分别使用不同语言(汉语和英语)进行提示操作。
在SpringBoot中,针对国际化
配置的实现,官方主要使用了一个类org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.class
来实现。
源码中,对其有充分的说明。
在application.properties
配置文件中,以spring.messages
开头的配置信息。
这个bean
中的逻辑流程是什么呢?
默认返回一个org.springframework.boot.autoconfigure.context.MessageSourceProperties
的实例化
对象信息,其中包含有basename
、encoding
等系列配置默认信息。
如何实现自动化的修改请求的语言种类
,接下来采取修改本地org.springframework.web.servlet.LocaleResolver.resolveLocale(HttpServletRequest)
的方式测试!
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.14.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.44version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.6version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
server.port=80
spring.messages.basename=i18n/test/test
配置文件为了简化,暂时只配置了端口
和索引文件地址
。
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping("/test1")
public String test1() {
return "66666";
}
}
服务器是如何能主动区分出,客户端请求时,需要的语言信息的?
请求上述自定义接口,观察浏览器中的请求消息头信息。
测试使用的浏览器,默认的请求头语言为:Accept-Language: zh-CN,zh;q=0.9
。
由此可见,服务器通过请求头
中的此项参数信息,获取客户端需要的国际化
显示信息。
此项方法以定义数据获取
处理工具类
的形式。
修改测试请求接口,接受一个状态信息值,用来变更当前临时语言环境信息。
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
private Logger log = LoggerFactory.getLogger(TestController.class);
private static final String PATH_PARAMETER_SPLIT = "_";
@Autowired
private MessageSource messageSource;
public String getMessage(String key,HttpServletRequest request,String... strings ) {
String language = request.getParameter("language");
log.info("language:{}",language);
//获取请求头默认的local对象
Locale locale = request.getLocale();
log.info("Add default.");
if(!StringUtils.isEmpty(language)) {
//按照指定的正则表达式,解析出相关的数据信息
String[] split = language.split(PATH_PARAMETER_SPLIT);
//解析出数据后,修改local对象
locale = new Locale(split[0], split[1]);
log.info("Add custom.");
}
return this.messageSource.getMessage(key, strings,locale);
}
/**
* en_US
* zh_CN
* @param request
* @return
*/
@RequestMapping("/test1")
public String test1(HttpServletRequest request) {
//log.info("local:{}",Locale.getDefault().getLanguage());
return getMessage("test.name",request,null);
}
}
test.name=bunana
test.properties(其实和test_zh_CN.properties
是一样的)
test.name=香蕉
《码云代码下载地址》
采取
配置文件
的形式获取
编写一个配置文件,用户解析请求。
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
@Configuration
public class LocaleResolverConfig {
private static Logger log = LoggerFactory.getLogger(LocaleResolverConfig.class);
/**
* 解析器(只做数据的解析操作,但需要主动调用)
* @return
*/
@Bean
public LocaleResolver localeResolver() {
log.info("LocaleResolver success");
return new LanguageLocaleResolverConfig();
}
/**
* 拦截器,针对请求过来时,
* @return
*/
@Bean
public WebMvcConfigurer localeInterceptor() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
localeInterceptor.setParamName("l");
//针对指定的请求做过滤操作、增加某些拦截操作
registry.addInterceptor(localeInterceptor);
}
};
}
}
class LanguageLocaleResolverConfig implements LocaleResolver {
private static Logger log = LoggerFactory.getLogger(LanguageLocaleResolverConfig.class);
private static final String PATH_PARAMETER_SPLIT = "_";
@Override
public Locale resolveLocale(HttpServletRequest request) {
// 获取请求来的语言方式
String language = request.getParameter("l");
log.info("language l:{}", language);
// 获取请求头默认的local对象
Locale locale = request.getLocale();
log.info("Add default.");
if (!StringUtils.isEmpty(language)) {
// 按照指定的正则表达式,解析出相关的数据信息
String[] split = language.split(PATH_PARAMETER_SPLIT);
// 解析出数据后,修改local对象
locale = new Locale(split[0], split[1]);
log.info("Add custom.");
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
// TODO Auto-generated method stub
}
}
思想:
增加请求拦截器。达到每次请求时,将指定属性值设置至上下文中。
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
private Logger log = LoggerFactory.getLogger(TestController.class);
private static final String PATH_PARAMETER_SPLIT = "_";
@Autowired
private MessageSource messageSource;
public String getMessage(String key,HttpServletRequest request,String... strings ) {
// String language = request.getParameter("language");
// log.info("language:{}",language);
// //获取请求头默认的local对象
// Locale locale = request.getLocale();
// //log.info("Add default.");
// if(!StringUtils.isEmpty(language)) {
// //按照指定的正则表达式,解析出相关的数据信息
// String[] split = language.split(PATH_PARAMETER_SPLIT);
// //解析出数据后,修改local对象
// locale = new Locale(split[0], split[1]);
// //log.info("Add custom.");
// }
// return this.messageSource.getMessage(key, strings,locale);
return this.messageSource.getMessage(key, strings, LocaleContextHolder.getLocale());
}
/**
* en_US
* zh_CN
* @param request
* @return
*/
@RequestMapping("/test1")
public String test1(HttpServletRequest request) {
//log.info("local:{}",Locale.getDefault().getLanguage());
return getMessage("test.name",request,null);
}
}
首先观察拦截器
做了什么操作。
在配置文件中,增加了一项拦截器。此项拦截器中的思路是什么?继续看源码
在org.springframework.web.servlet.i18n.LocaleChangeInterceptor.LocaleChangeInterceptor()
中,此项类的结构为:
和增加对指定请求拦截过滤
操作方式类似!
paramName
的默认值:
设置 paranName
值:
这个类中还存在一个很重要的方法:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws ServletException {
//获取请求对象中,指定 paranName 信息
String newLocale = request.getParameter(getParamName());
//当存在请求参数时
if (newLocale != null) {
if (checkHttpMethod(request.getMethod())) {
//获取request请求域中的指定key对应的数据
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException(
"No LocaleResolver found: not in a DispatcherServlet request?");
}
try {
//将请求中指定的 paranName 值设置于 org.springframework.web.servlet.LocaleResolver 对象中。
localeResolver.setLocale(request, response, parseLocaleValue(newLocale));
}
catch (IllegalArgumentException ex) {
if (isIgnoreInvalidLocale()) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring invalid locale value [" + newLocale + "]: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
// Proceed in any case.
return true;
}
以至于在使用时,可以通过LocaleContextHolder.getLocale()
获取到对应的Local信息。
《配置方式实现代码》