国际化、本地化及Spring MVC 的设计

本文不讲述具体使用配置过程。。请先google了解需要配置的三个地方。。

 

一、概述:

 

各个框架对于国际化和本地化的支持方式都类似,从用户使用角度大致分为以下两步:

 

1、准备资源文件,比如:message.xml,message_zh_CN.xml,message_zh_TW.xml,当然properties 文件也是一样了,里面核心的内容是key1=value1,

2、在页面使用宏、标签等展示内容;比如:#springMessage("key");然后通过不同环境加载不同的文件获取不同value,实现本地化;

 

框架要做的事情:

 

1、判断应该使用哪一个资源文件,

2、提供宏,从对应的资源文件中获取对应的value;

 

先说怎么判断使用哪一个资源文件;判断的根本来源是什么?是客户,不同地区的客户使用不同的资源,或者客户主动要求使用哪一种资源(手动切换语言)。接下去客户怎么告诉我?

 

最简单的方式是每一次请求,客户都告诉我需要什么类型的语言,显然这是最简单的方式,也意味这是最不可取的方式;为什么呢?一是繁琐,二是一旦某一次忘了就不知所措了;

 

第二种:第一次来,你告诉我,以后除非你明确改变,我都用第一次的;明确改变以后使用改变后的;这里根据第一次适用的范围不同又分为两种,一是从浏览器打开到关闭作为一个生命周期,下一次打开作为新的第一次;二是第一次进入网站后,后面都算第二次,当然这个“永远”是相对的,比如cookie清理,永远就失效了。

 

第三种,第一次来,你也不用告诉我,我可以根据你的区域默认给你,如果不满意,你在替换;第一次怎么来默认?那就是 httpServletRequest.getLocale()这个方法了,看一下定义:

 

getLocale

Locale getLocale()
Returns the preferred Locale that the client will accept content in, based on the Accept-Language header. If the client request doesn't provide an Accept-Language header, this method returns the default locale for the server.
Returns:
the preferred Locale for the client

 

很明显这个是优先使用HTTP请求中的  Accept-Language这个头信息来设置的,chrome看一下内容如下:

Accept-Language:
zh-CN,zh;q=0.8
 

如果想进一步这个参数的意义,是怎么来的,怎么修改参考下面资源:

http://www.ietf.org/rfc/rfc1766.txt

http://man.chinaunix.net/newsoft/Apache2.2_chinese_manual/content-negotiation.html

http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/intl/nls_08tg.asp

 

能区分之后,怎么和文件关联,自然就是文件名了,zh_CN、zh_TW 这样的,为什么是这样的,其他的可不可以,请看下面:

 

二、从Locale类说起:

 

查看代码这个类做了很多事情,核心是依赖sun的BaseLocale类,我们只关注前面一长窜的常量定义:

 

 

 /** Useful constant for language.
     */
    static public final Locale ENGLISH = createConstant("en", "");

    /** Useful constant for language.
     */
    static public final Locale FRENCH = createConstant("fr", "");
    /** Useful constant for language.
     */
    static public final Locale CHINESE = createConstant("zh", "");

    /** Useful constant for language.
     */
    static public final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN");

    /** Useful constant for language.
     */
    static public final Locale TRADITIONAL_CHINESE = createConstant("zh", "TW");

    /** Useful constant for country.
     */
    static public final Locale FRANCE = createConstant("fr", "FR");

    /** Useful constant for country.
     */
    static public final Locale CHINA = SIMPLIFIED_CHINESE;

    /** Useful constant for country.
     */
    static public final Locale PRC = SIMPLIFIED_CHINESE;

    /** Useful constant for country.
     */
    static public final Locale TAIWAN = TRADITIONAL_CHINESE;

 

 看到这里应该明白了,这里定义了主要的常用的语言和国家,而zh,CN,TW 也都能一一对应;

 

纵观上面,一言蔽之,就是在请求来的时候,通过参数也好、httpServletRequest.getLocale()也好获取当次请求的Locale,其后定位文件,获取对应的文案;不同的是Locale的值怎么存放的问题;

 

三、springMVC的实现:

 

先看一下核心类:


由于大部分请求都需要设置locale,很自然想到filter实现,在springmvc中是使用LocaleChangeInterceptor 类来实现的,该类只有一个方法

 

 

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws ServletException {

		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 DispatcherServlet request?");
			}
			LocaleEditor localeEditor = new LocaleEditor();
			localeEditor.setAsText(newLocale);
			localeResolver.setLocale(request, response, (Locale) localeEditor.getValue());
		}
		// Proceed in any case.
		return true;
	}

 

 

逻辑也很简单从request中获取 locale 参数,设置到当前的LocaleResolver中。

 

在springmvc 一种提供四种LocaleResolver,分别是SessionLocaleResolver、CookieLocaleResolver、AcceptHeaderLocaleResolver、 FixedLocaleResolver ,都实现了LocaleResolver接口;

 

 

Locale resolveLocale(HttpServletRequest request);

void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale);

 

提供设置locale、和读取locale的方法,在set方法中根据策略不同设置到不同的位置,比如session,就放到session中,cookie就放到cookie中,但是AcceptHeaderLocaleResolver、FixedLocaleResolver  这两个类的set方法是直接跑出异常“Cannot change fixed locale - use a different locale resolution strategy”,如果使用他们,一定要重写该方法

 

读取的方法逻辑相对复杂一点,比如session策略,会先读取session,没有读取默认值、没有再从request中获取。cookie也类似,而AcceptHeaderLocaleResolver只从request中获取,FixedLocaleResolver 则只获取默认值。可以根据不同的应用场景使用不同的方案;

 

另外LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request); 这句可能比较困惑,这里只是获取,那里放进去的呢?在DispatcherServlet 这个类的doService()方法中可以看到 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver),同样localeResolver是在initLocaleResolver() 方法中设置的,这里如果用户没有配置,会设置默认值;

 

 涉及类图如下:


 

最后再来看页面的使用,以常用的模板语言velocity为例。事实上支持很多,ftl等,先看对应的类

 


以vm为例,在使用时,需要只需要使用spring.vm中提供的宏,以最简单的宏为例

 

#macro( springMessageText $code $text )$springMacroRequestContext.getMessage($code, $text)#end

 

springMacroRequestContext 这个变量在类 AbstractTemplateView 能找到放入content的地方  model.put(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE, new RequestContext(request, model));也就是说实际上就是RequestContext类,看这个的getMessage方法:

 

 

public String getMessage(String code, Object[] args, String defaultMessage, boolean htmlEscape) {
		String msg = this.webApplicationContext.getMessage(code, args, defaultMessage, this.locale);
		return (htmlEscape ? HtmlUtils.htmlEscape(msg) : msg);
	}

 

可以看到实际上是使用webApplicationContext去获取资源了;而我们在具体使用的时候需要配置messagesource,如:

 

  1. <!-- 资源文件绑定器 -->           
  2. <bean id="messageSource"class="org.springframework.context.support.ResourceBundleMessageSource">                   
  3. <property name="basename" value="message-info" />                 
  4. <property name="useCodeAsDefaultMessage" value="true" />         
  5. </bean>   

其中,message-info是你的properties文件的通用名。如:message-info.properties,message-info_zh_CN.properties等等。

我们知道webApplicationContext是实现了message相关接口的。至于这里面怎么去获取文件的。。待下会分解。。

 

 

本站支持 pay for your wishes

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值