公司领导让研究下Struts2国际化,在网上搜了一堆东西以后搞出了个凑合能用的东西,在此记录以备后用。
Struts2国际化是建立在Java国际化的基础上的,是通过提供不同国家/语言环境的消息资源(.properties文件),然后通过ResourceBundle加载指定Locale对应的资源文件,再取得该资源文件中指定key对应的消息--整个过程与JAVA程序的国际化完全相同,只是Struts2框架对JAVA程序国际化进行了进一步封装,可以在java代码与jsp上使用,从而简化了应用程序的国际化。
一、Struts2国际化的配置
1.默认资源文件名称与默认语言的配置
在struts.xml中添加如下语句:
<!-- 语言 --> <constantname="sturts.locale"value="zh_CN"/> <constantname="struts.custom.i18n.resources"value="pageText"/> |
第一句指定了默认local为中国大陆地区的中文,第二句则指明默认资源文件名称为pageText。
完成上述配置后Struts2国际化就已经生效了,当切换客户端语言环境后,已经国际化的文本将会自动切换至相应语言。
2.资源文件命名与使用
默认资源文件应放置在项目的SourceFolder目录下。资源文件的命名形如:basename_language_country.properties。若配置文件如上配置,则默认资源文件的basename即应为pageText。language则指定语言,country指定国家与地区。
例如在中国大陆地区简体中文的环境下,系统搜索资源文件的次序将是:
(1) pageText_zh_CN.properties
(2) pageText_zh.properties
(3) pageText.properties
资源文件里保存的是名-值(key-value)对,在使用时通过key来获取value。中文的value需要经过转码后保存。可以通过MyEclipse Properties Editor自动转码。
二、Java字符串国际化方法
在Java代码中使用国际化资源非常简单。
要使用Struts2国际化的类需要继承自ActionSupport类,然后在代码中使用getText方法。该方法接受一个字符串参数即key,返回当前local对应资源文件中的消息。
System.out.println(getText("label")); |
三、Struts2国际化在Jsp页面上的使用
要在Jsp上使用Struts2国际化,首先要在配置文件中将jsp加入struts过滤器过滤对象中。在web.xml中,添加如下:
<filter-mapping> <filter-name>struts2</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> |
然后在需要国际化的Jsp页面头引入Struts2标签。
<%@taglibprefix="s"uri="/struts-tags"%> |
最后使用Struts2标签获取资源文件中信息,展示在Jsp页面上。
<h2> <s:textname="label"/> </h2> |
四、Ext国际化与Struts2国际化的结合使用
Ext控件自带有部分国际化功能,但是比较薄弱,需要实现引入指定语言与local的js文件,且从官方提供的例子中可以看出它只对诸如第几页共几页和日期控件的一月二月以及tab页的关闭提示等做了国际化,无法对标签、窗口标题、grid列标题等大部分信息进行国际化。因此需要结合struts2国际化来弥补这些缺陷。
1. Ext国际化资源的引入
为使每个Jsp页面引入的js能跟随设置变化,要使引入ext0国际化js的标签值可变。切换语言时,将当前local对应js名称信息保存在session中,在Jsp页面上如下引入ext国际化资源即可:
<script type="text/javascript"src="../../expandExt/js/ext-lang-<s:propertyvalue="#session.jsLocal">.js"></script> |
2. Ext与Struts国际化结合使用
在js文件中无法使用struts2标签。因此,要使得窗口标题、输入框标签等也可以随着语言环境改变而改变,需要作如下处理:
1. 抽取所有需要国际化的字符串,在页面js上声明如下:
<scripttype="text/javascript"> varemailTitle = '<s:text name="email.title"/>'; varemailLabel = '<s:text name="emial.label"/>'; </script> |
2. 在js中使用这些字符串变量来取代字符串常量,如下:
/*Emailfield*/ var emailfield =new Ext.FormPanel({ renderTo: 'emailfield', labelWidth: 100, frame: true, title: emailTitle, bodyStyle: 'padding:5px 5px 0', width: 360, defaults: {width: 220}, defaultType: 'textfield',
items: [{ fieldLabel: emailLabel, name:'email', vtype: 'email' }] }); |
如上操作后,页面字符将会随语言变更而改变。
五、手工切换语言
页面上需要提供选择框供用户选择语言。因此,我们需要手工切换程序local使得应用程序能够切换其语言。
1. Struts2语言切换机制
Struts通过以下几步获取当前语言环境。
1. 从浏览器发送的请求报头里获得语言信息,创建Locale对象,保存到ActionContext中。
2. I18nInterceptor拦截器获取名为request_locale请求参数的值,如果这个值存在则创建以这个值的Locale对象,然后将这个对象作为session的属性(WW_TRANS_I18N_LOCALE)保存,并保存到ActionContext中。
3. 如果上述都没有与已有资源文件匹配的值,判断struts.locale属性(在struts.xml中配置的constant标签)是否有值,如果有值,将这个值转换为Locale对象保存到ActionContext中。
在实际使用中发现,修改session的WW_TRANS_I18N_LOCALE值和request_locale等无效之。最终发现程序始终是只会读取浏览器发送过来的区域信息。估计这是struts2.2.1国际化得BUG了。
2. 修正Struts2国际化问题
为了解决这个问题,可以用用户自定义切换的国际化区域信息来替代浏览器请求报头里的语言信息。
在web.xml中添加过滤器对所有请求进行过滤,并在过滤器中使用Session中的“WW_TRANS_I18N_LOCALE”值修改请求头即可。
web.xml添加代码如下:
<filter> <filter-name>i18nFilter</filter-name> <filter-class>com.fujitsu.action.I18nFilter</filter-class> </filter> <filter-mapping> <filter-name>i18nFilter</filter-name> <url-pattern>*</url-pattern> </filter-mapping> |
I18nFilter.java代码如下:
package com.fujitsu.action;
import java.io.IOException;
import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest;
publicclass I18nFilterimplements Filter {
publicvoid doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain)throws IOException, ServletException { HttpServletRequest r = (HttpServletRequest) req; MyRequestWrapper request =new MyRequestWrapper(r); filterChain.doFilter(request, resp); }
publicvoid destroy() { }
publicvoid init(FilterConfig arg0)throws ServletException { } } |
MyRequestWrapper.java代码如下:
package com.fujitsu.action;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpSession;
publicclassMyRequestWrapper extends HttpServletRequestWrapper {
private Localelocale = null;
public MyRequestWrapper(HttpServletRequest request) { super(request); HttpSession session = request.getSession(); locale = (Locale) session.getAttribute("WW_TRANS_I18N_LOCALE"); }
/** * struts2的BUG,如果重定向的话,国际化默认取HTTP请求头中的参数替换HTTP请求参数 **/ public String getHeader(String name) { String value = super.getHeader(name); if ("Accept-Language".equals(name) &&locale != null) { value = locale.getLanguage() + "_" +locale.getCountry() + value.substring(6, value.length()); } return value; }
public Locale getLocale() { if (locale !=null) { returnlocale; } returnsuper.getLocale(); }
} |
3. 切换语言
切换语言时只需要将要切换的语言发送至后台,保存在session的“WW_TRANS_I18N_LOCALE”值中即可。同时要将Ext引入国际化文件的“jsLocal”值修改为当前语言。这两个值的格式都是 语言_国家 ,语言代码遵循ISO-639-1标准,国家代码遵循ISO-3166-1标准。
六、使用多个资源文件
1. Java中使用多个资源文件
在Java中使用多个资源文件十分简单,只要在该Java类所在包下建立一个basename为package的资源文件,Java中getText方法默认就将优先使用该类下basename为package的资源文件中的资源,在找不到该文件或在该文件中找不到key时才会在struts配置文件中配置的默认资源文件中查找。
2. Jsp中使用多个资源文件
在Jsp中使用多个资源文件略微复杂。
要使用指定的资源文件,需要用s:i18n标签指定资源文件所在路径。代码如下:
<scripttype="text/javascript"> <s:i18n name="com/fujitsu/action/package"> varemailTitle = '<s:text name="email.title"/>'; varemailLabel = '<s:text name="emial.label"/>'; </s:i18n> </script> |
被s:i18n标签包围住的s:text标签将优先使用指定资源文件中的资源。name属性中,com/fujitsu/action/为路径,package为资源文件的basename。