SpringMVC国际化

By | 2021年12月31日

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。

发表评论

您的电子邮箱地址不会被公开。