1 LocaleResolver
LocaleResolver 是指用什么策略来检测请求是哪一种Local, Spring 提供以下几种策略。
1.1 AcceptHeaderLocaleResolver
根据浏览器Http Header中的accept-language域判定(accept-language域中一般包含了当前操作系统的语言设定,可通过HttpServletRequest.getLocale方法获得此域的内容)。 改变Local 是不支持的,即不能调用LocaleResolver接口的 setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale);
方法设置Local。
1.1.1 案列
LocaleConfiguration.class
package com.falsec.pom.configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; import java.util.Locale; @Configuration public class LocaleConfiguration { //根据浏览器Http Header中的Accept-Language域判定,如:Accept-Language:en-US、Accept-Language:zh-CH @Bean public AcceptHeaderLocaleResolver AcceptHeaderLocaleResolver() { AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver(); //如果请求头没有携带 Accept-Language:en-US 等信息就使用默认的 resolver.setDefaultLocale(Locale.CHINA); return resolver; } }
PosMessageSource.class
@Component public class PosMessageSource { private static final String SEPARATOR = "."; private static final String BUNDLE_VENDOR = "BUNDLE.VENDOR"; @Autowired private MessageSource messageSource; public String getBundleVendorName(String vendorId) { return getResource(BUNDLE_VENDOR, vendorId.toUpperCase()); } private String getResource(String resourceBaseName, String name) { name = resourceBaseName + SEPARATOR + name; return messageSource.getMessage(name, null, LocaleContextHolder.getLocale()); } }
BundleController.class
@RequestMapping({ "/bundle" }) @Controller public class BundleController { @Autowired private PosMessageSource messageSource; @Autowired private VendorBundleService vendorBundleService; @RequiresPermissions(value = {WildcardPermissionConstant.BUNDLE_VIEW}) @RequestMapping(value = {"/vendor"}, method = {RequestMethod.GET}, produces = {"application/json"}) @ResponseBody public JsonWrapper<VendorBundleData> getVendorList() { List<VendorBundleData> vendorBundleDataList = this.vendorBundleService.getAllVendorBundleData(); List<VendorBundleDataDto> vendorBundleDataDtoList = vendorBundleDataList.stream().map(c -> new VendorBundleDataDto(c)).collect(Collectors.toList()); for (VendorBundleDataDto item : vendorBundleDataDtoList) { Vendor vendor = item.getVendor(); String vendorId = vendor.getVendorId(); //国际化 String vendorDisplayName = messageSource.getBundleVendorName(vendorId); item.setVendorDisplayName(vendorDisplayName); //若有中文,则转成汉语拼音简写,并存储到排序字段上,此字段不显示,仅用于排序 List<String> pinyinOrEnglish = Pinyin4jUtil.converterToFirstSpell(vendorDisplayName); item.setSortName(pinyinOrEnglish.get(0)); } List<VendorBundleDataDto> sortedList = vendorBundleDataDtoList.stream().sorted(Comparator.comparing(c -> c.getSortName().toLowerCase())).collect(Collectors.toList()); return new JsonWrapper(sortedList); } }
message.properties 部分
BUNDLE.VENDOR.NEUSOFT = Neusoft BUNDLE.VENDOR.DPTECH = DPTech BUNDLE.VENDOR.VENUSTECH = VenusTech BUNDLE.VENDOR.RUIJIE = Ruijie BUNDLE.VENDOR.ARUBA = Aruba BUNDLE.VENDOR.CISCO = Cisco BUNDLE.VENDOR.RADWARE = Radware BUNDLE.VENDOR.LEGENDSEC = Legendsec BUNDLE.VENDOR.LEADSEC = LeadSec BUNDLE.VENDOR.HILLSTONE = Hillstone BUNDLE.VENDOR.ZTE = ZTE BUNDLE.VENDOR.SHENXINFU = ShenXinFu BUNDLE.VENDOR.HUAWEI = Huawei BUNDLE.VENDOR.SHENWEI = ShenWei BUNDLE.VENDOR.LINUX = linux BUNDLE.VENDOR.JUNIPER = Juniper
message_zh_CN.properties 部分
BUNDLE.VENDOR.NEUSOFT = 东软 BUNDLE.VENDOR.DPTECH = 迪普 BUNDLE.VENDOR.VENUSTECH = 启明星辰 BUNDLE.VENDOR.RUIJIE = 锐捷 BUNDLE.VENDOR.ARUBA = 安移通 BUNDLE.VENDOR.CISCO = 思科 BUNDLE.VENDOR.RADWARE = Radware BUNDLE.VENDOR.LEGENDSEC = 网神 BUNDLE.VENDOR.LEADSEC = 网御 BUNDLE.VENDOR.ZTE = 中兴 BUNDLE.VENDOR.SHENXINFU = 深信服 BUNDLE.VENDOR.HUAWEI = 华为 BUNDLE.VENDOR.SHENWEI = 神威 BUNDLE.VENDOR.LINUX = linux BUNDLE.VENDOR.JUNIPER = Juniper
接下来到了测试环境,首先在chrome浏览器上安装 ModHeader 插件,然后添加如下请求头:
改用英文:
显示结果:
1.2 SessionLocaleResolver
根据用户本次会话过程中的语言设定决定语言种类(如:用户登录时选择语言种类,则此次登录周期内统一使用此语言设定)。
1.3 CookieLocaleResolver
根据Cookie判定用户的语言设定(Cookie中保存着用户前一次的语言设定参数)。
1.4 FixedLocaleResolver
一直使用固定的Local, 改变Local 是不支持的。
2 Controller如何得到请求的 Local
DispatchServlet 将在初始化的时候, 会调用initLocaleResolver(context)
方法去配置文件中找名字为 localeResolver 的 bean. 如果有就用配置文件配置的localResolver. 如果没有配置将用默认的 AcceptHeaderLocaleResolver。
DispatchServlet 会在 processRequest(HttpServletRequest request, HttpServletResponse response)
方法中设置LocaleContext, 把LocalContext 和当前线程关联起来. 代码如下:LocaleContextHolder.setLocaleContext (buildLocaleContext(request), this. threadContextInheritable );
DispatchServlet 中buildLocalContext代码如下:
protected LocaleContext buildLocaleContext( final HttpServletRequest request) { return new LocaleContext() { public Locale getLocale() { return localeResolver .resolveLocale(request); } @Override public String toString() { return getLocale().toString(); } }; }
这里的Local通过localResolver 解析得到, localResolver 即是从Spring 配置文件配置的localResolver, 默认是”AcceptHeaderLocaleResolver”.如果你想要在 controller 中得到当前请求的Local, 代码可以如下写:Locale locale = LocaleContextHolder.getLocale();
或者你可以用RequestContextUtils.getLocale(HttpServletRequest request)
这种方式是先得到 request 中保存的localResolver, 并用localResolver 解析得到Local。localResolver 会在DispatcherServlet的doService 方法中,将localResolver保存到request 属性中 代码如下:request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
3 LocaleChangeInterceptor 的使用
如果想要用户能改变Local, 我们需要配置 LocaleChangeInterceptor, 这个拦截器将检查传入的请求,如果请求中有“local” 的参数(参数可以配置),如http://localhost:8080/test?local=zh_CN. 该Interceptor将使用localResolver改变当前用户的Local, 代码如下:
String newLocale = request.getParameter( this . paramName ); if (newLocale != null ) { LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver (request); if (localeResolver == null ) { throw new IllegalStateException( "No LocaleResolver found: not in a ..." ); } //改变当前的Local localeResolver.setLocale (request, response, StringUtils.parseLocaleString (newLocale)); }
提示:要使得LocaleChangeInterceptor 有效果,需要在Spring Boot配置中使用 @Bean。