SpringBoot国际化支持实例开发
国际化的支持,对于app开发的小伙伴来说应该比价常见了;作为java后端的小伙伴,一般来讲接触国际化的机会不太多,毕竟业务开展到海外的企业并没有太多
SpringBoot提供了国际化的支持,网上也有相关的教程,然而实际体验的时候,发现并没有预期的那么顺利;本文将介绍一下SpringBoot如何支持国家化,以及在支持的过程中,一些注意事项
I.项目环境1.项目依赖本项目借助SpringBoot2.2.1.RELEASE+maven3.5.3+IDEA进行开发
开一个web服务用于测试
dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-thymeleaf/artifactId/dependency/dependencies2.配置文件
配置文件中,指定国际化的参数,thmeleaf的配置信息
application.yml
spring:messages:basename:i18n/messages/messagesencoding:UTF-8fallbackToSystemLocale:falsethymeleaf:mode:HTMLencoding:UTF-8servlet:content-type:text/htmlcache:false3.国际化信息文件
上面的配置spring.messages.basename指定国际化配置文件的目录与前缀,取值为i18n/messages/messages
所以在资源目录下,新建文件i18n/messages,国际化文件名为messages-xxx.properties,项目结果如
对应的信息如简体中文messages_zh_CN.properties
=成功=內部异常name=用户名pwd=密码
英文messages_en_US.properties
=success=unexpectedexceptionname=usernamepwd=password
繁体messages_zh_TW.properties
=成功=內部異常name=用戶名pwd=密碼
说明
注意spring.messages.basename这个配置的取值为国际化文件的目录+文件名前缀,比如上面若少了最后一层的messages,会提示取不到配置
其次在IDEA中,选中国家化文件之后,点击下方的ResourceBundle,可以进入如上图中更友好的编辑框,支持一次修改多个语言的信息
II.国际化支持前面是国际化的基本配置,那么如何根据前面配置中的key,获取不同语言的value呢?
1.MessageSource在SpringBoot中主要借助MessageSource来获取不同语言的value信息
如一个最基本的封装
publicclassMsgUtil{privatestaticMessageSourcemessageSource;publicstaticvoidinti(MessageSourcemessageSource){MsgUtil.messageSource=messageSource;}/***获取单个国际化翻译值*/publicstaticStringget(StringmsgKey){try{returnmessageSource.getMessage(msgKey,null,LocaleContextHolder.getLocale());}catch(Exceptione){returnmsgKey;}}}2.测试demo
接下来写一个基础的测试demo,根据传参来修改LocalContextHolder中的值,从而实现不同语言的切换
ControllerSpringBootApplicationpublicclassApplication{publicApplication(MessageSourcemessageSource){MsgUtil.inti(messageSource);}publicstaticvoidmain(String[]args){SpringApplication.run(Application.class);}DataAccessors(chain=true)publicstaticclassRspWrapperT{privateintcode;privateStringmsg;privateTdata;}GetMapping(path="change")ResponseBodypublicStringchangeLocal(Stringlanguage){String[]s=language.split("_");LocaleContextHolder.setLocale(newLocale(s[0],s[1]));RspWrapperres=newRspWrapper().setCode().setMsg(MsgUtil.get("")).setData(true);returnJSON.toJSONString(res);}}演示如下
3.子线程支持上面虽然可以根据请求参数来切换语言,但是有个问题,如果在子线程中进行国际化支持,则会不生效
GetMapping(path="change2")ResponseBodypublicStringchangeLocal(Stringlanguage){String[]s=language.split("_");LocaleContextHolder.setLocale(newLocale(s[0],s[1]));RspWrapperres=newRspWrapper().setCode().setMsg(MsgUtil.get("")).setData(true);returnJSON.toJSONString(res);}如下图,即便修改了language,返回都是默认的中文
针对这种解决办法是在设置Locale时,指定第二个可继承参数为true
GetMapping(path="change3")ResponseBodypublicStringchangeLocal(Stringlanguage){String[]s=language.split("_");LocaleContextHolder.setLocale(newLocale(s[0],s[1]));RspWrapperres=newRspWrapper().setCode().setMsg(MsgUtil.get("")).setData(true);returnJSON.toJSONString(res);}4.Cookies方式缓存国际化信息上面虽说支持了根据传参来设置国际化,但是需要每次传参都带上这个参数language=zh_CN,还需要我们自己来解析这个请求参数,我们可以考虑借助拦截器来实现统一的Local设置
这个拦截器可以自己按照上面的方式写,当然更推荐的是直接使用已封装好的
ConfigurationpublicclassAutoConfigimplementsWebMvcConfigurer{/***这个如果不存在,则会抛异常:nestedexceptionisjava.lang.UnsupportedOperationException:CannotchangeHTTPacceptheader-useadifferentlocaleresolutionstrategy**return*/BeanpublicLocaleResolverlocaleResolver(){//也可以换成SessionLocalResolver,区别在于国际化的应用范围CookieLocaleResolverlocaleResolver=newCookieLocaleResolver();localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);returnlocaleResolver;}/***根据请求参数,来设置本地化**return*/BeanpublicLocaleChangeInterceptorlocaleChangeInterceptor(){LocaleChangeInterceptorlocaleChangeInterceptor=newLocaleChangeInterceptor();//Defaultsto"locale"ifnotsetlocaleChangeInterceptor.setParamName("language");returnlocaleChangeInterceptor;}OverridepublicvoidaddInterceptors(InterceptorRegistryinterceptorRegistry){interceptorRegistry.addInterceptor(localeChangeInterceptor());}}请注意上面的localResolver,当我们不注册这个bean的时候,运行则会抛出异常nestedexceptionisjava.lang.UnsupportedOperationException:CannotchangeHTTPacceptheader-useadifferentlocaleresolution
上面的实例中,采用的是CookieLocaleResolver,因此会在cookie中缓存语言信息,一次修改,后续都会生效
测试如下
GetMapping(path="say")ResponseBodypublicStringsay(Stringname){RspWrapperres=newRspWrapper().setCode().setMsg(MsgUtil.get("")).setData(MsgUtil.get("name")+":"+name);returnJSON.toJSONString(res);}GetMapping(path="say2")ResponseBodypublicStringsay2(Stringname){RspWrapperres=newRspWrapper().setCode().setMsg(MsgUtil.get("")).setData(MsgUtil.get("name")+":"+name);returnJSON.toJSONString(res);}主要一个地方设置了语言,后续的访问不带语言参数时,都会复用之前设置的语言,这样使用来说就更简洁了
5.页面元素国际化上面介绍的是返回的json串支持国际化,另外一个场景就是我们返回的页面,希望渲染的数据也可以实现国际化支持
在上文的基础上实现这个也没什么难度了
在资源目录下,新建目录templates,新建模板文件index.html
!DOCTYPEhtmlhtmlxmlns:th="