概述
国际化(Internationalization),通途的讲,就是让软件实现对多种语言的支持。可以通过简单的设置就可以从一种语言切换到另一种语言。用的最多的地方就是在应用程序的界面表示上。我们经常接触到一些软件,他们支持多种语言,并且可以随用户的切换。比如我们常用的eclipse就是。通常在实现多语言支持时尽量保持亮点:
(1)不给软件开发带来过多的额外负担。
(2)实现语言切换或者添加新语言支持不需要修改已经完工的软件(代码)
Struts2也提供了对国际化的支持,它的国际化功能建立在Java自带的国际化的基础之上,并且对它做了更好的扩展,使得我们在Struts2中使用国际化功能变得更加轻松。
Java内置的国际化支持
在java中与国际化相关的有两个最重要的类:ResourceBoundle和Local。其中Local用于提供地区和语言信息,而ResourceBoundle就负责加载和解析我们编写的国际化信息文档。要实现对不同语言的支持就需要我们在加载资源文件的时候指定一个Local对象,那么ResourceBoundle就会根据我们提供的Local对象来查找相应的资源文件。
下面我们就利用Java内置的国际化支持来做一个练习:
public class Test {
public static void main(String[] args) {
ResourceBundle b = ResourceBundle.getBundle("message");
String msg = b.getString("hello");
System.out.println(msg);
}
}
我们在Src(classpath)目录下新建一个message.properties文件,内容如下:
msg = hello world
运行程序,正确输出hello world。
我们将程序稍加修改:
ResourceBundle b =ResourceBundle.getBundle("message",Locale.SIMPLIFIED_CHINESE);
在src(classpath)目录下再新建一个message_zh_CN.properties文件:
msg=u4F60u597DuFF01 (你好!)
再次运行程序,控制台将输出”你好!”而不再是”helloworld”了,这就是因为我们指定了Local对象。一般在web引用程序中,这个参数都是通过浏览器来传递。
这里要说明一下,刚才我们新建的message_zh_CN.properties文件的名字不是随便起的。要想让Java能够自动识别这些资源文件,那么我们的文件命名具有一定的规则:
basename_languageCode_countryCode.properties
如果只是有basename而没有语言代码和国家代码,那么就是我们在程序中使用系统默认Local对象时会读取的文件,另外就是我们指定了Local但是找不到相应的资源文件时也会读取这个文件。
这些语言代码和国家代码,我们都可以从网上查到,一般不需要我们死记硬背。下面是一些常见的语言代码和国家代码:
提醒:如果使用中文,那么这些中文必须保存为unicode编码。
向国际化信息传参数
有时候我们在消息输出时才能确定消息的具体内容,那么我们可以使用一些方法来实现国际化信息的参数传递(实际是对字符串的处理)(此例摘自网上):
消息文件:
abela = your order({0})is confirmed,can not be modified
测试类:
1. publicclassTest{
2. publicstaticvoidmain(String[]args){
3. //选择中文
4. Localelocale1=Locale.SIMPLIFIED_CHINESE;
5. //选择message这组文件
6. ResourceBundlebundle1=ResourceBundle.getBundle("message",locale1);
7. //选择其中的labela这一项
8. Stringmessage=bundle1.getString("labela");
9. //将参数合成进读出的国际化信息
10. Stringinfo=MessageFormat.format(message,"201009290001");
11. System.out.println(info);
12. }
13. }
输出:yourorder(201009290001)isconfirmed,cannotbemodified
除了使用properties文件之外,Java还支持通过类文件来提供这些消息信息。这个可以在网上找些参考资料。另外就是Java自带的国际化功能,对资源文件的位置有些限制,如果我们将上面的message.propterties移动到Test类所在的目录,运行程序就会报错,找不到资源文件。Struts2中对资源文件的位置更加灵活,我们可以在多个地方配置这些顺序,框架会按照一定的优先级来寻找。
Struts2中的国际化支持
我们在学习验证框架的时候就已经开始接触到Struts2的国际化功能了。那时我们只是将错误信息放置在ActionClass.properties文件中。并且可以通过从ActionSupport类中继承来的getText方法来获取对应的信息。后来我们学习输入校验时也用到了这些知识。现在我们已经知道ActionSupport类实现了TextProvider接口,实际上是通过TextProvider的实现类TextProviderSupport类来完成一些功能的,底层还是使用了Java内建的国际化支持功能。不过做了一些优化,使用起来更加方便了。
资源文件查找顺序:
(1)Action类属性文件。该文件的名字(basename)和相关的Action类名字相同并且存放在相同的目录下。
(2)Action类实现的接口的属性文件(包括父类实现的接口)。
(3)Action类的各个父类的属性文件。
(4)如果Action实现了ModelDriven接口,那么Struts2将调用getModel方法并从域对象的类开始沿着继承关系进行(1)(2)(3)顺序的搜索。
(5)默认的包文件属性。(package.properties)
(6)集成关系中下一个父包中包属性文件
(7)全局资源。
全局资源需要我们在struts.xml中使用常量指定:
<constantname="struts.custom.i18n.resources"value="message"/>
如果有多组,用逗号隔开。这些属性文件必须放在Classpath下。
注:上面说的这些顺序都是覆盖关系,即只要一找到合适的资源,就不再继续搜索!
下面我们动手做一个实例,首先定义一个空的接口ITest,并且编写ITest.properties文件:
msg= I'm from ITest
接下来写一个Test动作实现Itest接口:
- public class Test extends ActionSupport implements ITest {
- private String msg;
- public String getMsg() {
- return msg;
- }
- public void setMsg(String msg) {
- this.msg = msg;
- }
- @Override
- public String execute() throws Exception {
- msg = getText("msg");
- return SUCCESS;
- }
- }
Success.jsp中输出Test的msg属性。
在浏览器中访问test动作(配置略):
下面我们再为Test类编写对应的属性文件:
msg = I'm from Test
再次在浏览器中访问测试:
由此可以看出Struts在寻找资源文件时的顺序。
使用标签完成国际化信息显示
在Action中我们可以通过使用getText方法来获取国际化信息,但是在视图页面,我们就需要使用另一种方式了。一种方式是使用OGNL表达式来调用getText方法。另外Struts2还提供了两个用于国际化的标签。这里我们只做大致的了解,关于标签的使用,需要的时候可以查看手册。
text标签
这个标签有两个属性:
name:指定要在属性文件中检索的key
var:指定了属性,相当于将检索出来的值放入该变量中(该变量压入valueStack),而不是输出,在后面的标签中可以通过引用该变量名来获取对应的值。
<s:text name="msg" var="v"></s:text>
<s:property value="#v"/>
使用该标签进行国际化信息输出时就相当于在<s:property>标签中调用getText方法。
通过这个标签还可以实现我们前面说的国际化消息参数化的功能。通过在其中嵌套<s:param>标签来实现。
资源文件:msg = I'm from {0}
调用:
<s:text name="msg">
<s:param>struts2</s:param>
</s:text>
输出:I'm from {0}
没有正确解析,修改了下,去掉资源文件中 I’m中的单引号,解析正常。
i18n标签
i18n标签将加载一个自定义的ResourceBopundle。可能需要使用定制ResourceBoundle的原因:
(1)你想利用ListResourceBoundle把一些键与一些非String对象关联起来
(2)你希望对某个键进行某种预处理
(3)消息来自一个比较特殊或少间的资源,需要进行某种转换才能输出
如果给定的定制ResourceBoundle没有找到,那么他会去搜索默认的资源文件。
我们使用i18n来指定消息文件的来源,但是还是要使用text标签来进行显示(处理)。I18n标签有一个name属性,它是用来指定我们要加载的资源的。
(1)全局级国际化信息资源:直接用文件前缀名即可—message。
(2)包级国际化信息资源:用这个包的全限定名+package—cn.javass.i18n.package
(3)类级国际化信息资源:用这个类的全限定名—cn.javass. i18n.I18NAction。
我们将刚才做的实例稍加修改,我们只需要在success.jsp中做如下修改:
<body>
<s:text name="msg"></s:text><br>
<s:i18n name="action.ITest">
<s:text name="msg"></s:text>
</s:i18n>
</body>
输出:
第一个没有使用i18n标签,因此按照普通顺序搜索到Test.properties文件中的消息。第二个由于我们使用了i18n标签,那么就会从我们指定的ITest.properties开始寻找,找到了那就输出。
如果使用类来作为ResourceBoundle,那么它的优先级高于属性文件。
手动选择语言
默认情况下,服务器根据浏览器提供的地理位置信息来决定使用的Local。但是某些情况下可能我们需要能够手动选择页面使用的语言。当然,使用在浏览器中修改语言设置的方法可以实现,不过Struts2还提供了其他的选择。
(1) 常量设置
通过在struts.xml中设置<constantname="struts.locale"value="zh_CN"/>常量来设置语言。
(2) 参数方式
用户只要在提交请求的时候加上request_locale这个参数,并提供对应的值就可以自由选择语言了。
这得力于Struts2的i18n内建拦截器,defaultStack拦截器栈引用了i18n拦截器,因此,能很方便的由用户来选择语言。
i18n拦截器在Action运行之前会检查请求中是否包含了一个叫做“request_locale”的参数,如果存在此参数,则会用它的值建立一个Locale对象,并用这个Locale对象去覆盖Struts2的常量设置和浏览器的语言设置。除此之外,i18n拦截器还会把这个Locale对象放到名为WW_TRANS_I18N_LOCALE的Session属性中去,而这个属性也会覆盖Struts2的常量设置和浏览器的语言设置。因此,只需要传入一次request_locale参数,session就会记住用户的选择,整个网站就会都变成用户选择的语言了。
我们将刚才的实例再修改一下,分别新建一个Test_zh_CN.properties文件和一个Test_en_US.properties文件。
Test_zh_CN.properties
msg = u4F60u597DuFF01 (你好!)
Test_en_US.properties
msg = I'm from Test
success.jsp中使用text标签显示msg消息。
我们在浏览器中访问test.action。由于我们没有做任何设置,那么页面肯定是显示的中文。
我们在请求中加上request_local=en_US参数再访问:
可以看到页面显示出了英文,并且即使我们去掉请求参数再次访问,页面还是显示英文。
注:三种方式的优先级别:请求参数 >struts.xml中配置常量 > 浏览器设置