一、背景
很多网站的用户分布在世界各地,因此网站需要针对不同国家的用户展示不同语言的内容,因此就有了国际化实现的需求,大多数网站都会在网站的头部或尾部设置语言切换链接,这样就可以直接切换成相应的内容。其中有些网站是通过网站地址或参数进行区分,有些是通过设置cookie值进行进行区分。
二、解决思路
前面已经写过一篇JDK的国际化支持,讲解了JDK实现国际化的具体实现。那么网站的国际化实现具体如何做呢?
其实网站的国际化实现与前面介绍的JDK实现思路类似,只是本地化信息的获取需要从页面得到而已。得到了页面信息,再获取对应的数据并进行格式化处理,最后渲染到页面即可。这里主要说明后端的处理思路,前端的处理思路其实也类似,只是实现方式有区别而已。
那如何从页面获取本地化信息呢?这个是所有处理的首要环节,常用的几种方式有:
(1)直接根据Request.getLocale()方法得到本地化信息,实际就是从Http Request Headers里面取“accept-language”对应的值,该值拥有浏览器端的语言信息;
(2)在浏览器端保存一个自定义名字的cookie,默认情况下指定一个值,对应的切换通过语言切换链接的点击修改对应的值;
(3)在请求URL上面添加带本地化信息的参数或者地址里面包含本地化信息。
通过上面几种方式,在web程序中就可以直接从request中得到了本地化信息,然后根据本地化信息从相应的properties文件中获取数据(比如可以通过JDK的ResourceBundle类),得到数据后如果需要的化再对数据进行格式化处理(比如可以通过JDK的MessageFormat类),最后将处理过的数据展示到前台即完成了整个国际化操作。
思路已经有了,那么具体如何实现呢?下面以Spring MVC的实现为例,因为该框架做了很好的抽象和封装,是个非常好的参考例子。
三、Spring MVC实现及原理
3.1 本地化信息获取
3.1.1 概述
Spring MVC的DispatcherServlet类会在initLocaleResolver方法中查找一个locale resolver,如果没有找到就会用默认的AcceptHeaderLocaleResolver类。locale resolver会去根据请求Request设置当前的locale信息。
除了resolver类,还可以定义拦截器去设置locale信息,比如通过请求参数去设置,具体下面细讲。
Spring MVC相关的处理类都在org.springframework.web.servlet.i18n包下。而本地化信息的获取可以通过RequestContext.getLocale()方法得到。另外,RequestContext.getTimeZone()方法还可以得到时区信息。
3.1.2 AcceptHeaderLocaleResolver
这个从名字也能看出大概来,这个类是解析request的header中的accept-language值,这个值通常包含客户端支持的本地化信息,所以通过这个值可以获取本地化信息。不过这个类拿不到时区信息。这个类是默认配置的,所以使用的话不用额外配置。
3.1.3 CookieLocaleResolver
这个类是通过cookie去存取本地化信息,客户端可以在cookie中存储一个指定名字的值代表本地化信息,然后这个类获取后做相应的解析即可。具体的配置如下:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> <property name="cookieName" value="clientlanguage"/> <property name="cookieMaxAge" value="100000"/> <property name="cookiePath" value="/"/> </bean>
这里对几个配置的属性做下说明:
cookieName | 默认值:classname + LOCALE | cookie名字 |
cookieMaxAge | 默认值:Servlet容器默认值 | 这个值为cookie在客户端保留的时间,如果值为-1,则不保留;这个值会在关闭浏览器后无效。 |
cookiePath | 默认值:/ | 这个值设置cookie的适用路径,如果这个值设置了,那么就表示cookie只对当前目录及其子目录可见。 |
3.1.4 SessionLocaleResolver
这个类是通过request获取本地化信息的,然后存在HttpSession中,所以本地化信息存取依赖于session的生命周期。具体配置如下:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
</bean>
3.1.5 LocaleChangeInterceptor
这个拦截器会拦截请求中的参数,然后根据参数去调用LocaleResolver的setLocale()方法,改变当前的locale值。下面举个例子,有这个地址http://www.sf.net/home.view?siteLanguage=nl,参数siteLanguage代表locale信息,配置拦截修改locale值:
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="siteLanguage"/></bean> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>
这里配置CookieLocaleResolver是因为LocaleChangeInterceptor需要调用LocaleResolver的setLocale()方法,这个例子里面用到了CookieLocaleResolver,当然也可以用其他的LocaleResolver实现类。
3.2 数据获取与格式化
Spring MVC的数据处理定义了一个接口MessageSource,该接口定义了数据获取的方法。方法如下:
- String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
这里code为属性文件中的key值,args是文件中需要替换的参数值,defaultMessage是找不到内容时的默认内容,locale为本地化信息。 - String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
这个方法与上面的方法类似,只是没有了默认内容,而是找不到内容时抛出异常。 - String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
这里参数中有个新接口MessageSourceResolvable,对前面的参数进行了封装,locale为本地化信息。
对于MessageSource接口,Spring MVC的ApplicationContext和HierarchicalMessageSource都有继承,ApplicationContext在加载的时候,它会先去上下文里面查找bean名为messageSource的实现,找到后上面MessageSource方法的调用就用这个实现类; 如果找不到就会找包含MessageSource bean的类去使用; 再找不到就用DelegatingMessageSource去执行方法调用了。
MessageSource常见的实现主要有如下几个:
- ResourceBundleMessageSource类:这个类实际是依赖的JDK的ResourceBundle类获取数据、MessageFormat去做格式化。
- ReloadableResourceBundleMessageSource类:这个与上面的比较就多了可重新加载,即可以在不重新启动应用的情况下重新读取新的内容。具体实现方式也有区别,这个类是通过Spring的PropertiesPersister策略加载,依赖的是JDK的Properties类读取内容。
- StaticMessageSource类:这个类提供了简单的实现,内容是需要先配置好的。使用比较少,适合在内容较少较简单情况下使用。
下面以最常用的ResourceBundleMessageSource类做个简单示例:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>format</value> <value>exceptions</value> </list> </property> </bean>
format与exceptions为文件基础名,对应内容配置在具体locale相应的文件名中即可。详细示例见下面部分。
3.3 小结与示例
上面两节主要讲了本地化信息获取,数据获取与格式化两部分,这两部分其实也是整个国际化过程最核心的两个部分,至于请求的匹配与接收,返回结果的页面渲染这个就不展开讲,与国际化不直接相关,属于Spring MVC的基础内容。
这里对整个Spring MVC的国际化过程做个大概的梳理,整个过程大概是这样:接收请求——>LocaleResolver获取/设置locale信息——>MessageSource获取数据并格式化——>内容展示到页面。
讲了半天,还是有点抽象,下面直接来个详细示例:
@Controller public class I18nController { @Autowired private MessageSource messageSource; @RequestMapping("i18n") public String i18n(Model model){ //获取本地化信息,从LocaleContext中得到 Locale locale = LocaleContextHolder.getLocale(); //初始化参数,这里简便演示,真实参数可能是从数据库查询处理的。这里的参数是与i18n目录下的配置文件需要替换的内容对应的 Object [] objArr = new Object[4]; objArr[0] = new Date(); objArr[1] = messageSource.getMessage("goods", null, locale);//这个具体商品从配置中读取 objArr[2] = "taobao"; objArr[3] = new BigDecimal("39.20"); //获取格式化后的内容 String content = messageSource.getMessage("template", objArr, locale); model.addAttribute("content", content); return "/i18n/show"; } }
LocaleResolver配置,这里以Cookie为例:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> <property name="cookieName" value="clientlanguage"/> <property name="cookieMaxAge" value="100000"/> <property name="cookiePath" value="/"/> </bean>
MessageSource配置,这里以ResourceBundleMessageSource为例:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>/i18n/message</value> </list> </property> </bean>
Properties配置,这里统一放在/i18n目录下,message名字开头:
更详细的代码可以查看我的Github项目。
四、拓展介绍
4.1 LocaleResolver对应Bean是如何初始化的?
初始化工作是在DispatcherServlet类初始化时调用initLocaleResolver方法执行的。
private void initLocaleResolver(ApplicationContext context) { try { this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); if (logger.isDebugEnabled()) { logger.debug("Using LocaleResolver [" + this.localeResolver + "]"); } } catch (NoSuchBeanDefinitionException ex) { // We need to use the default. this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); if (logger.isDebugEnabled()) { logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME + "': using default [" + this.localeResolver + "]"); } } }
从代码里面可以看到处理化过程分为两步:
(1)先从当前上下文环境中取名字为localeResolver的bean;
(2)如果找不到就根据默认策略去取LocaleResolver这个Class名字的bean,即执行getDefaultStrategy方法,该方法实际是取DispatcherServlet.properties文件中的org.springframework.web.servlet.LocaleResolver对应的值,即默认的AcceptHeaderLocaleResolver类,再创建对应的bean。
所以如果上下文中自定义了LocaleResolver就用自定义的,没有定义会用默认的AcceptHeaderLocaleResolver类。这种写法在写公共逻辑且提供多种策略时很实用。
相关推荐
Spring MVC 国际化的配置方法及使用,希望能对大家的学习、工作有帮助。
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。...通过阅读本书,读者将深度把握Spring MVC的各项特性及实用技巧。
struts1工作原理 struts2工作原理 struts1和struts2比较 spring mvc工作原理 struts和spring mvc比较
本文详细介绍spring MVC的原理和开发心得体会。
采用spring技术 实现项目中英文切换 国际化
用STS(Spring Tool Suite)开发的,spring mvc + spring security 实现的最简单的登录系统,无数据库。
全书共计12章,分别从Spring框架、模型2和MVC模式、Spring MVC介绍、控制器、数据绑定和表单标签库、传唤器和格式化、验证器、表达式语言、JSTL、国际化、上传文件、下载文件多个角度介绍了Spring MVC。除此之外,...
全书共计12章,分别从Spring框架、模型2和MVC模式、Spring MVC介绍、控制器、数据绑定和表单标签库、传唤器和格式化、验证器、表达式语言、JSTL、国际化、上传文件、下载文件多个角度介绍了Spring MVC。除此之外,...
Spring MVC + Mybatis+Spring实现的个人博客系统基于SSM实现的个人博客系统.zip
Spring+Spring MVC+Spring JDBC+MySql实现简单登录注册
基于spring+spring mvc+mybatis框架整合实现超市货物管理系统(实现登入功能、mysql数据库的增删改查及分页显示)
精通Spring MVC 4 中文
spring mvc文件上传,通过监听器实现进度条
6.Spring MVC 国际化案例 7.Spring MVC 请求转发与请求重定向案例 8.Spring MVC 下载 Excel 文档的需求案例 9.Spring MVC RESTful 风格的请求方式案例 10.Spring + Spring MVC + JDBCTemplate 整合并通过 RESTful ...
spring mvc4.1.4 源代码 spring mvc4.1.4 源代码spring mvc4.1.4 源代码spring mvc4.1.4 源代码spring mvc4.1.4 源代码
首先, 我需要在你心里建立起 Spring MVC 的基本概念. 基于 Spring 的 Web 应用程序接收到 http://localhost:8080/hello.do(事实上请求路径是 /hello.do) 的请求后, Spring 将这个请求交给一个名为 helloController ...
spring MVC数据绑定 含例子 转载自疯芒毕露的专栏 刚开始用spring mvc 做web开发时 经常会不知道如何合适绑定页面数据 用惯struts2的朋友更认为spring mvc 绑定数据不如struts2方便 本人最开始也是这么认为 经过一段...
Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于...