本博客介绍一下SpringBoot集成i18n,实现系统语言国际化处理,ok,先创建一个SpringBoot项目,具体的参考我的博客专栏:
SpringBoot系列之i18n国际化多语言支持教程
@[toc]
1、环境搭建
本博客介绍一下SpringBoot集成i18n,实现系统语言国际化处理,ok,先创建一个SpringBoot项目,具体的参考我的博客专栏:SpringBoot系列博客专栏链接
环境准备:
- IntelliJ IDEA
- Maven
项目集成:
- Thymeleaf(模板引擎,也可以选jsp或者freemark)
- SpringBoot2.2.1.RELEASE
2、resource bundle资源配置
ok,要实现国际化语言,先要创建resource bundle文件:
在resources文件夹下面创建一个i18n的文件夹,其中:
- messages.properties是默认的配置
- messages_zh_CN.properties是(中文/中国)
- messages_en_US.properties是(英文/美国)
- etc.
IDEA工具就提供了很简便的自动配置功能,如图,只要点击新增按钮,手动输入,各配置文件都会自动生成属性
messages.properties:
|
|
messages_zh_CN.properties:
|
|
messages_en_US.properties:
|
|
在项目的application.properties修改默认配置,让SpringBoot的自动配置能读取到resource bundle资源文件
|
|
注意要点:
spring.messages.basename必须配置,否则SpringBoot的自动配置将失效
MessageSourceAutoConfiguration.ResourceBundleCondition 源码:12345678910111213141516171819202122232425262728293031323334353637383940protected static class ResourceBundleCondition extends SpringBootCondition {//定义一个map缓存池private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>();public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");ConditionOutcome outcome = cache.get(basename);//缓存拿得到,直接从缓存池读取if (outcome == null) {//缓存拿不到,重新读取outcome = getMatchOutcomeForBasename(context, basename);cache.put(basename, outcome);}return outcome;}private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, String basename) {ConditionMessage.Builder message = ConditionMessage.forCondition("ResourceBundle");for (String name : StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(basename))) {for (Resource resource : getResources(context.getClassLoader(), name)) {if (resource.exists()) {//匹配resource bundle资源return ConditionOutcome.match(message.found("bundle").items(resource));}}}return ConditionOutcome.noMatch(message.didNotFind("bundle with basename " + basename).atAll());}//解析资源文件private Resource[] getResources(ClassLoader classLoader, String name) {String target = name.replace('.', '/');//spring.messages.basename参数值的点号换成斜杆try {return new PathMatchingResourcePatternResolver(classLoader).getResources("classpath*:" + target + ".properties");}catch (Exception ex) {return NO_RESOURCES;}}}cache-duration在2.2.1版本,指定的是s为单位,找到SpringBoot的MessageSourceAutoConfiguration自动配置类
3、LocaleResolver类
SpringBoot默认采用AcceptHeaderLocaleResolver类作为默认LocaleResolver,LocaleResolver类的作用就是作为i18n的分析器,获取对应的i18n配置,当然也可以自定义LocaleResolver类
|
|
4、I18n配置类
I18n还是要继承WebMvcConfigurer,注意,2.2.1版本才是实现接口就可以,之前1.+版本是要实现WebMvcConfigurerAdapter适配器类的
|
|
注意要点:
- 旧版代码可以不加LocaleChangeInterceptor 拦截器,2.2.1版本必须通过拦截器
- 如下代码,bean的方法名必须为localeResolver,否则会报错
|
|
原理:
跟一下源码,点进LocaleChangeInterceptor类
DispatcherServlet是Spring一个很重要的分发器类,在DispatcherServlet的一个init方法里找到这个LocaleResolver的init方法
这个IOC获取的bean类名固定为localeResolver,写例子的时候,我就因为改了bean类名,导致一直报错,跟了源码才知道Bean类名要固定为localeResolver
抛异常的时候,也是会获取默认的LocaleResolver的
找到资源文件,确认,还是默认为AcceptHeaderLocaleResolver
配置了locale属性的时候,还是选用AcceptHeaderLocaleResolver作为默认的LocaleResolver
WebMvcAutoConfiguration.localeResolver方法源码,ConditionalOnMissingBean主键的意思是LocaleResolver没有自定义的时候,才作用,ConditionalOnProperty的意思,有配了属性才走这里的逻辑
- 拦截器拦截的请求参数默认为locale,要使用其它参数,必须通过拦截器设置 ,eg:
localeChangeInterceptor.setParamName("lang");
- LocalResolver种类有:CookieLocaleResolver(Cookie)、SessionLocaleResolver(会话)、FixedLocaleResolver、AcceptHeaderLocaleResolver(默认)、.etc
5、Thymeleaf集成
本博客的模板引擎采用Thymeleaf的,所以新增项目时候就要加上maven相关依赖,没有的话,自己加上:
|
|
ok,然后去找个bootstrap的登录页面,本博客已尚硅谷老师的例子为例,进行拓展,引入静态资源文件:
Thymeleaf的i18n支持是采用#符号的
切换中文网页:
切换英文网页:
当然不点链接传locale的方式也是可以自动切换的,浏览器设置语言:
原理localeResolver类会获取Accept language参数
附录:
logging manual:SpringBoot官方手册
example source:例子代码下载链接