springMVC项目进行国际化改造
1. 概述
1.1名词定义
i18n(其来源是英文单词 internationalization的首末字符i和n,18为中间的字符数)是“国际化”的简称。在资讯领域,国际化(i18n)指让产品(出版物,软件,硬件等)无需做大的改变就能够适应不同的语言和地区的需要。对程序来说,在不修改内部代码的情况下,能根据不同语言及地区显示相应的界面。 在全球化的时代,国际化尤为重要,因为产品的潜在用户可能来自世界的各个角落。通常与i18n相关的还有L10n(“本地化”的简称)。
1.2 多语言的原则
笔者认为,在对我们已有系统进行多语言改造,应当遵循几个原则:
1)尽可能不改动内部代码逻辑,仅仅是在vm模板中进行更改。这样可以避免在上层改动造成逻辑上的错误;
2)对于不可预知的、没有正确配置翻译文案的字段,在进行翻译函数处理时,直接显示原语言,确保不会出错。
1.3 对今后需要多语言的系统的建议
1)禁止文案硬编码在java代码中
例如:如果一定要写入静态文本,也建议做成枚举,在Controller放入错误提示信息时也应当说明可能会出现哪些错误提示。
2)各个系统使用枚举时,一些公用的枚举,应当保持一致
2. 多语言的流程
3.多语言的配置
3.1 资源文件绑定器
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
p:basenames="file:${prod_ui_template}/config/i18n/icfmng"
p:useCodeAsDefaultMessage="true"
p:defaultEncoding="UTF-8"
p:cacheSeconds="600"/>
3.2 拦截器的配置
<bean id="localeChangeInterceptor" class="XXXXXX.XXXX.IcfmngLocaleChangeInterceptor">
<property name="paramName" value="locale"/>
<property name="enableLocales">
<list>
<value>en_US</value>
<value>zh_CN</value>
</list>
</property>
</bean>
public class IcfmngLocaleChangeInterceptor extends LocaleChangeInterceptor {
/** 参数名称 */
private String paramName = DEFAULT_PARAM_NAME;
/** 默认语言 */
private String defaultLocale = "zh_CN";
/** 支持的语言*/
private List<String> enableLocales = new ArrayList<String>();
/**
*
* @see org.springframework.web.servlet.i18n.LocaleChangeInterceptor#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws ServletException {
String newLocale = request.getParameter(paramName);
if (newLocale != null) {
if (!enableLocales.contains(newLocale)) {
newLocale = defaultLocale;
}
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;
}
/**
*
* @param enableLocales
*/
public void setEnableLocales(List<String> enableLocales) {
this.enableLocales = enableLocales;
}
}
用户切换语言的时候客户端发送get请求,local=en_US 或者 local=zh_CN 跟在url后,拦截器拦截到用户请求后,作出相应的相应。
3.3 LocaleResolver的配置
localResolver 为区域解析器,用户的区域是通过区域解析器来识别的,它必须实现localResolver接口。Spring MVC提供了几个LocaleResolver实现,让你可以按照不同的条件来解析区域。除此之外,你还可以实现这个接口,创建自己的区域解析器。
要定义一个区域解析器,只需在web应用程序上下文中注册一个LocaleResolver类型的Bean就可以了。你必须将区域解析器的Bean名称设置为localeResolver,这样DispatcherServlet才能自动侦测到它。请注意,每DispatcherServlet只能注册一个区域解析器。
一般来说可以选择基于 cookie级别的国际化(locale存cookie)或者基于session级别的国际化(locale存session),即可以从以下两个类继承:
org.springframework.web.servlet.i18n.CookieLocaleResolver
org.springframework.web.servlet.i18n.SessionLocaleResolver
这里我们以基于cookie的国际化为例。
1) 增加区域解析器的配置
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="cookieName" value="locale"/>
<property name="defaultLocale" value="zh_CN"/>
</bean>
在这里可以配置 cookieName 和默认的语言。
3.4 资源文件的规则
资源文件绑定器指明资源文件的路径和文件名统一的前缀,并且设定了所有的语言选项,至此,资源文件的命名就确定下来。在进行多语言的时候,框架会根据当前的语言选项,自动帮我们找到对应的资源文件进行多语言“翻译”。
如果需要多语言的字段较多,建议在excel 整理好,再用脚本语言去生成配置文件,这样不容易出错。
要注意的是,不要在eclipse下修改资源文件,eclipse下打开资源文件是乱码,修改会出错。建议使用其它编辑软件。
4. 多语言的使用
完成了多语言的配置后,就可以开始对页面进行多语言啦!在进行多语言的时候,只需要调用getMessage 即将多语言的key映射到对应的多语言翻译的字段。
4.1 Controller层进行多语言
在Controller层进行多语言需要先获得web容器上下文 WebApplicationContext,代码示例如下:
/**
* 根据key获取多语言value
* @param key
* @param arry 填充翻译后的参数
* @param defult 默认值
* @return
*/
public static String getValueByKey(String key, Object[] arry,
String defult) {
try {
WebApplicationContext ctx = RequestContextUtils.getWebApplicationContext(request);
LocaleResolver localeResolver = ctx.getBean("localeResolver", LocaleResolver.class);
return ctx.getMessage(key, arry, defult, localeResolver.resolveLocale(request));
} catch (Exception e) {
ExceptionUtil.caught(e, "根据key获取多语言value值出错|key=", key);
}
return defult;
}
在这里获得上下文以后,取出localeResolver这个Bean,解析当前request的语言选项参数,即可调用 ctx.getMessage(key, new Object[] {}, "", locale); 对key值进行多语言的映射,返回对应的多语言字段。
getMessage方法中,key对应多语言资源文件的键值,第二个参数是用于填充到翻译后的字段的,比如 若翻译后为“当前第{0}页,总共{1}页”
第二个参数的两个String就可以填充进去。 第三个参数为默认的返回值,即翻译失败时返回这个。第四个参数为当前的语言选项。
4.2 vm模板中进行多语言
在vm模板中进行多语言会简单很多,这里建议使用宏来调用getMessage方法,代码如下:
## 根据用户的语言选择对 $key进行翻译
## #macro(t $key, $args , $defaultMessage)
## #if($stringUtil.isBlank($args))
## #set($args = [])
## #end
## #if($stringUtil.isBlank($defaultMessage))
## #set($defaultMessage = $key)
## #end
## #SLITERAL($springMacroRequestContext.getMessage($key, $args , $defaultMessage))
## #end
#macro(t $key, $args , $defaultMessage)#if($stringUtil.isBlank($args))#set($args = [])#end#if($stringUtil.isBlank($defaultMessage))#set($defaultMessage = $key)#end#SLITERAL($springMacroRequestContext.getMessage($key, $args , $defaultMessage))#end
调用t这个宏,第二个参数用于填充,第三个参数为默认返回值,如果第三个参数不填,如果在资源文件中找不到时会返回key。
在页面vm模板中调用宏:
#t("COMMON_ORDER_NO") 或者 #t('COMMON_ORDER_NO')
---------------------------------分隔线---------------------------------------------------
至此,多语言的实现原理已经介绍完了,如果你是在新做一个系统,那么一个比较合理的做法应该是:
Ⅰ 对于静态的字段的多语言,直接在页面vm模板上调用getMessage方法进行多语言映射;
Ⅱ 对于动态的字段,在Controller层进行多语言。这就需要在写代码的过程中特别注意,任何你需要在前端展示的字段,都不要硬编码写死在程序中,而是使用枚举。
比如在Controller层中调用了某一方法,这一方法里面可能会抛出异常,而这一异常的错误信息又需要在前端展示,那么切忌将错误信息写死在代码,这会使后期多语言改造非常困难。如果将错误信息写成枚举,那么在Controller中即可根据该枚举取得key值(比方说,我们的做法是把枚举的classname和name拼在一起作为key值),这样就可以很方便地在Controller层进行多语言。