在很多成熟的商业软件中,可以让用户*切换语言,当用户进入系统时候,可以出现一个下拉列表框,让用户选择语言,一旦用户选择了自己需要使用的语言环境,整个系统的语言环境将一直是这种语言环境。下面使用Struts2的国际化实现此功能。
实现原理: 在Struts 2中,可以通过ActionContext.getContext().setLocale(Locale arg)可以设置用户的默认语言。而设置用户默认的语言环境时,struts2提供了一个名i18n的拦截器,并且将其住注册在默认的拦截器栈中(defaultStack)。 Struts2的默认拦截器栈的配置片段如下:
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servlet-config"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="profiling"/>
<interceptor-ref name="scoped-model-driven"/>
<interceptor-ref name="model-driven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="static-params"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
i18n拦截器在执行Action方法前,自动查找请求中的一个名为request_locale的参数。如果该参数存在,拦截器就将其作为参数,并转换成Locale对象,并将其设为用户默认的Locale(代表语言/国家环境)。
除此之外,i18n拦截器还会将上面生成的Locale对象保存在用户Session的名为“WW_TRANS_I18N_LOCALE”的属性中。一旦用户Session中存在一个名为“WW_TRANS_I18N_LOCALE”的属性,则该属性指定的Locale将会作为浏览者的默认
Locale。
设计思想:为了实现让用户自行选择程序语言的功能,只需提供一个下拉列表框,让下拉列表框中列出本应用所支持的各种语言,并且,当用户选择下拉列表框中某一项时,系统将该下拉项的值作为request_locale参数提交给Struts2系统,为此,将系统所支持的语言放入一个Map中,通过在JSP页面中迭代该Map对象,通过这种方式,就可以在页面上列出系统所支持的全部语言,并让用户*选择。
首先,编写一个javabean, 这个JavaBean里保存了当前应用所支持的全部语言,代码如下所示:
package org.lw.selectlanguage;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
public class Locales {
// 因为本实例也需要实现国际化,所以使用current作为用户当前的Local
private Locale current;
private Map<String, Locale> locales;
// 取得用户当前Locale的setter方法
public void setCurrent(Locale cur) {
this.current = cur;
}
public Locale getCurrent() {
return current;
}
public void setLocales(Map<String, Locale> locales) {
this.locales = locales;
}
// 取得本系统所支持的全部语言
public Map<String, Locale> getLocales() {
// 将当前系统支持的全部语言保持在Map对象中
Map<String, Locale> locales = new Hashtable<String, Locale>();
ResourceBundle bundle = ResourceBundle.getBundle("messageResource",
current);
// 添加当前系统支持的语言,key是系统支持语言的显示名字,value是系统支持语言的Locale实例
locales.put(bundle.getString("enus"), Locale.US);
locales.put(bundle.getString("zhcn"), Locale.CHINA);
return locales;
}
}
在上面JavaBean中,使用了一个Map对象来保存所有用户支持的语言,该Map对象的key是所支持语言的显示名字,而该Map对象的value是所支持语言的Locale实例。应用支持语言的显示名字,也是通过国际化消息来生成的。一旦定义了该JavaBean之后,就可以在JSP页面中创建该JavaBean的实例,并为其传入一个current参数,决定该JavaBean中的Locale参数,就可以根据该Locale参数来决定怎样显示系统所支持语言显示名称。
定义相应的国际化资源文件:
messageResource_en_US.properties:
language=Select Language
enus=American English
zhcn=Simplified Chinese
messageResource_zh_CN.properties:
language=\u9009\u62E9\u8BED\u8A00
enus=\u7F8E\u5F0F\u82F1\u8BED
zhcn=\u7B80\u4F53\u4E2D\u6587
在struts.properties中指定全局国际化资源文件:
struts.custom.i18n.resources=messageResource
编写语言选择页面代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'selectlanguage.jsp' starting page</title>
<script type="text/javascript">
function langSelecter_onChanged() {
document.getElementById("langForm").submit();
}
</script>
</head>
<body>
<!-- 将用户Session中的“WW_TRANS_I18N_LOCALE”属性值设置成SESSION_LOCALE。 -->
<s:set name="SESSION_LOCALE" value="#session['WW_TRANS_I18N_LOCALE']" />
<!-- 使用lee.Locales创建locales实例 -->
<s:bean id="locales" name="org.lw.selectlanguage.Locales">
<!-- 为locales实例这种current参数值 -->
<s:param name="current"
value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE" />
</s:bean>
<!-- 让用户选择语言的表单 -->
<form action=" " id="langForm"
style="background-color: #bbbbbb; padding-top: 4px; padding-bottom: 4px;">
<!-- 输出国际化提示 -->
<s:text name="language" />
<!-- 使用s:select标签迭代locales实例的locales Map属性 -->
<s:select label="Language" list="#locales.locales" listKey="value"
listValue="key"
value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"
name="request_locale" id="langSelecter"
onchange="langSelecter_onChanged()" theme="simple" />
</form>
</body>
</html>
此时如果直接访问selectlanguage.jsp页面的话会出现如下异常信息,经过一番深究,发现如果jsp中含有struts2的标签,且标签使用到了OGNL Context中的值,可会解析错误。 配置Struts2拦截不到jsp页面,于是就不会有Action执行。
对此可以将JSP页面selectlanguage.jsp放在WEB-INF/jsp路径下,从而避免了直接访问JSP页面,同时也为了更好的为了更好的安全性,让所有的JSP页面都能得到Struts2的处理,在struts.xml文件中增加如下配置片段:
<!-- 使用通配符号定义Action的name -->
<action name="*">
<!-- 将请求转发该WEB-INF/jsp路径下同名的JSP页面 -->
<result>/WEB-INF/jsp/{1}.jsp</result>
</action>
此时在浏览器中请求selectlanguage.action,将看到选择程序语言的页面如下: