• 1. 第六章:Struts 2国际化 主讲:刘雷
  • 2. 本章内容Struts2国际化的实现原理 国际化资源文件 Locale ResourceBundle I18nInterceptor 简单的struts2国际化实现过程的讲解 国际化资源文件的优先级 手动选择显示语言
  • 3. 国际化Internationalization国际化Internationalization是使程序在不做任何修改的情况下,就可以在不同的国家或地区和不同的语言环境下,按照当地的语言和格式习惯显示字符。例如,一个数字123456.78,在法国它的书写格式是123 456,78,在德国是123.456,78,而在美国则是123,456.78. 国际化又被称为I18N,因为国际化的英文是Internationalization,它以I开头,以N结尾,中间有18个字母。 一个国际化的程序,当它运行在本地机器时,需要根据本地机器的语言和地区设置显示相应的字符,这个过程就叫做本地化(Localization),通常简称为:L10N。 在JAVA中编写国际化程序主要通过两个类来完成: java.util.Locale类和java.util.ResourceBundle抽象类。 Locale类用天提供本地信息,通常称它为语言环境。不同的语言,不同的国家和地区采用不同的Locale对象来表示。 ResourceBundle类称为资源包,包含了特定于语言环境的资源对象。当程序需要一个特定于语言环境的资源时(如字符串资源),程序可以从适合当前用户语言环境的资源包中加载它。采用这种方式,可以编写独立于用户语言环境的程序代码,而与特定语言环境相关的信息则通过资源包来提供。
  • 4. Locale类Java.util.Locale类的常用构造方法如下: public Locale(String language) public Locale(String language,String country) 其中language表示语言,它的取值是由ISO-639定义的小写的、两个字母组成的语言代码。 其中country表示国家或地区,它的取值由ISO-639定义的大写的、两个字母组成的代码 左表列出了常用的ISO-639语言代码。右表列出了常用的ISO-3166国家和地区代码
  • 5. 举例例如:应用于中国的Locale为: Locale locale = new Locale(“zh”,”CN”); 例如:应用于美国的Locale为: Locale locale = new Locale(“en”,”US”); 例如:应用于英国的Locale为: Locale locale = new Locale(“en”,”GB”);
  • 6. 资源包(一)最理想的实现国际化的方法是将要显示的字符内容从程序中分离,然后统一存储到一个资源包中,当显示时,从资源包中取出和平Locale对象相一致的字符内容。在Java中, 这种资源包是由类来实现的,这个类必须要扩展java.util.ResourceBundle. 在编写国际化程序时, 要为不同的国家地区和语言编写不同的资源类,这些资源类同属一个资源系列,共享同一个基名(base name).不同语言所对应的资源类的名称为基名加上ISO-639标准的语言代码,而应用于某个特定国家或地区的资源类的名称,则是在基名和语言代码后加上ISO-3166标准的同家或地区代码。 例如:有一个资源包系列的基名是MyResource,那么说中文的所有国家或地区共享的资源属于MyResource_zh类,中国台湾的特定资源则属于MyResource_zh_TW类。一个资源包系列可以有一个默认的资源包, 它的名字就是基名,当请求的资源包不存在时,将使用默认的资源包。 要获取某个资源包,可以调用java.util.ResourceBundle类中的静态方法getBundle(),如下: public static final ResourceBundle getBundle(String baseName) 根据基名得到资源包,使用系统缺省的Locale对象。 public static final ResourceBundle getBundle(String baseName,Locale locale) 根据基名和Locale对象得到资源名。
  • 7. 资源包(二)利用getBundle()方法可以得到对应于某个Locale对象的资源包,然后就可以利用ResourceBundle类的getString()方法得到相应语言版本的字符串。 public final String getString(String key) 从资源包中根据关键字得到字符串。 例如: Locale locale = new Locale(“zh”,”CN”); bundle = ResourceBundle. getBundle(“MyResource”,locale); String name = bundle.getString(“name”); 利用ResourceBundle类的getObject()方法,还可以从资源包中得到任意的对象。。 public final Object getObject(String key) 从资源包中根据关键字得到对象。
  • 8. 资源包(三)假设Locale为使用中文的中国大陆地区,资源包基名为MyResource , ResourceBundle类的静态方法getBundle(String baseName)按照下列的顺序查找资源包: MyResource_zh_CN.class MyResource_zh_CN.properties MyResource_zh.class MyResource_zh.properties MyResource.class MyResource.properties
  • 9. 消息格式化在资源文件中的消息文本可以带有参数,例如: greeting={0},欢迎学习struts2. 花括号中的数字是一个占位符,可以被动态的数据替换。在消息文本中的占位符可以使用0到9的数字,也就是说,消息文本的参数最多可以有10个。例如: greeting={0},欢迎学习struts2,今天是星期{1}. 要替换消息文本中的占位符,可以使用java.text.MessageFormat类,该类提供了一个静态方法format(),用来格式化带参数的文本,format() 方法定义如下: public static String format(String pattern,Object …arguments) 示例 greeting={0},欢迎学习struts2,今天是 {1}. 我们编写代码: Locale loc = Locale.getDefault(); ResourceBundle bundle = ResourceBundle.getBundle(“MyResource”,loc); String greeting = bundle.getString(“greeting”); Object[] obj = {"张三",new java.util.Date()}; String msg = MessageFormat.format(greeting,obj); 消息文本中的数字占位符将按照MessageFormat.format()方法参数的顺序(从第二个参数开始)而被替换,在上例中,占位符{0}被“张三”替换,{1}被new java.util.Date()所替换。此外format()方法参数的顺序是与占位符的数字顺序对应的,而不是与占位符出现在消息文本中的顺序对应
  • 10. Struts2资源包的组织和加载方式Struts2提供了灵活的资源包组织和加载方式,你可以为某个类提供一个资源包,也可以为某一个接口提供一个资源包,还可以为包中所有的类提供一个资源包,不同级别的资源包适用的范围的加载的顺序是不一样的。 在查找字符串时,Struts2将按照下面的顺序搜索资源包: 1)查找与调用的Action类同名的资源文件(与Action在同一个包中)。例如,Action类名为RegAction,当访问RegAction时,将首先查找RegAction.properties. 2)查找所有与Action类的基类同名的资源文件,直到Object.properties.例如,RegAction的基类为ActionSupport,那么将查找ActionSupport.properties,如果没有找到消息字符串,则继续查找ActionSupport基类,直到object.properties. 3)查找所有与Action类实现的接口同名的资源文件,例如,RegAction实现了Action, Validateable接口,则依次搜索Action.properties和Validateable.properties. 4)如果Action类实现了ModelDriven接口,则struts2会调用getModel()方法获得模型对象,然后以模型对象所属的类进行了类层次的查找,这将从步骤1)开始重复。 5)查找类所在的包和父包中的package.properties,直到最顶层包。例如:RegAction在com.ibm.action中,则依次查找action目录、ibm目录、com目录中的package.properties资源文件。 6)查找I18N消息key本身的层次关系。例如:某个消息key是user.label.username,如果user是Action类的某个属性,则以该属性所属的类,在它的类层次中去查找key为label.username的消息字符串,这与前面的步骤是一样的。 7)查找默认的资源包。
  • 11. 查找资源文件的优先级你可以在struts.properties或struts.xml文件中,通过struts.custom.i18n.resources属性设置默认的资源包。例如: struts.custom.i18n.resources=com.ibm.action.ApplicationResources 这表明ApplicationResources是默认的资源包,它可以在com.ibm.action包中找到。 除了上述加载资源包的方式外,我们还可以使用struts2和i18n标签来加载特定的资源包。如果消息字符串没有在i18n标签指定的资源包中找到,则将按照上述的7个步骤进行查找。
  • 12. 在消息文本中使用参数Struts2提供了两种在消息文本中设置参数的方式。一种是沿袭了Java中设置文本参数的方式,即使用从{0}到{9}的占位符。当使用MessageFormat类的format方法格式化消息字符串时,参数被传进来,用来替换消息文本中的占位符。另一种方式是在消息文本中使用OGNL表达式,不同于在标签的属性中使用OGNL表达式,在消息文本中使用的OGNL表达式以“${”开始,并以“}”结束,其语法格式为:${expr}. 例如:当用户登录后, 我们要向用户显示如下的欢迎信息: 张三,你好,欢迎你的访问! 那么我们可以在资源文件中编写如下的消息文本: Greeting=${username},你好,欢迎你的访问! 在登录成功页面中,使用text标签输出资源文件中的消息文本,如下所示: 在获取键为greeting的消息文本时,“${“和“}”中的表达式username将根据值栈自动进行计算,最终action类的username属性值将被用于替换消息文本中的”${username}”. 在消息文本中使用数字占位符可以看成是被动地接受值,而使用OGNL表达式则可以看成主动的去获取值。
  • 13. 访问国际化消息Struts2提供了多种方式来访问资源文件中的本地化消息,以适应不同的应用场景,主要分为 在action中访问本地化消息 在JSP页面中访问本地化消息 在表单标签的属性中访问本地消息 在资源文件中访问本地消息。
  • 14. 在action中访问本地消息Struts2在com.opensymphony.xwork2.TextProvider接口中定义了访问本地化消息的方法,ActionSupport类实现了这个接口,如果我们编写的action类继承自ActionSupport类,那么在action中就可以直接使用这些方法。这也是为什么我们在大多数的开发中,我们应该首选让action类继承ActionSupport,而不是实现Action接口。 public String getText(String aTextName) 获取以参数aTextName为键的消息字符串,如果没有找到,则返回null. public String getText(String aTextName, String defaultValue) 获取以参数aTextName为键的消息字符串,如果没有找到,则返回defaultValue. public String getText(String aTextName, List args) 获取以参数aTextName为键的消息字符串,参数args用于替换消息字符串中的占位符,列表中的第一个元素替换占位符{0},第二个元素替换占位符{1}….,依次类推。 public String getText(String aTextName, String[] args) 获取以参数aTextName为键的消息字符串,参数args用于替换消息字符串中的占位符,数组中的第一个元素替换占位符{0},第二个元素替换占位符{1}….,依次类推。
  • 15. 在action中访问本地消息在action中访问本地消息示例 greeting={0},欢迎学习struts2,今天是 {1}. 在action中可以使用getText()方法按照如下调用方式获取键为greeting的消息字符串: String msg=getText(“greeting”,new String[ ]{“张三”, new java.util.Date().toString()});
  • 16. 在JSP页面中访问本地化消息Struts2提供了text标签,用于在JSP页面中访问本地化消息。例如:对于下面的消息文本: title=用户注册 在JSP页面中可以使用text标签访问键为title的消息字符串,如下所示: 如果消息文本中有参数,那么可以使用嵌套的param标签来设置参数。例如,对于下面的消息文本: greeting={0},欢迎学习struts2,今天是 {1}. 在JSP页面中可以按照如下方式使用text标签: Param标签的顺序对应了消息文本中的数字占位符,第一个param标签传递的参数替换占位符{0},第二个元素替换占位符{1}….,依次类推,最多可以使用10个param标签。
  • 17. 在表单标签的属性中访问本地化消息由于struts2标签的属性可以使用OGNL表达式,而OGNL表达式又支持对类中的方法进行调用,所以在需要访问本地化消息时,如果值栈中存在着从ActionSupport类继承的action类实例,那么也可以使用getText()方法获取资源文件中的消息字符串。 在使用表单标签时,label属性的值通常都是从资源文件中获取的。在label属性中,我们可以使用getText()方法来获取消息字符串,如下所示: 除了使用getText()方法外,我们还可以使用表单标签的key属性来指定消息字符串的key,该属性可以使用消息字符串来自动生成label属性的值,如下所示:
  • 18. 在资源文件中访问本地化消息在消息文本中可以使用OGNL表达式,当然也可以利用OGNL表达式在一个消息文本中去访问另一个本地化消息。 在资源文件中使用OGNL表达式${getText(“key”)}来访问本地化消息。有时候,一个消息文本可能需要由另一个消息文本来组成,使用OGNL表达式将给我们提供极大的便利。例如,有如下的消息文本: email=邮件地址 error.email.invalid=${getText(“email”)} 格式错误 在程序运行过程中,当访问键为error.email.invalid的消息时,表达式getText(“email”)将被计算,消息文本中的“${getText(“email”)} ”将被“邮件地址”所替换。当我们需要修改键为email的消息文本时(例如,改为“邮箱地址”),键为error.email.invalid的消息文本不需要做任何改动。
  • 19. 实例演示一步骤: 一:建立工程myI18N,导入struts2相就的JAR包 二:配置web.xml加入struts2过滤器 三步:src建立language_en_US.properties文件加入内容:olympic=one world,one dream! 四步:src建立language_zh_CN.properties文件加入内容:olympic=同一个世界同一个梦想! 五步:配置struts.xml文件 六步:建立i18n.jsp页面 国际化基本名
  • 20. 实例演示二
  • 21. 切换国际化
  • 22. 工程层次图
  • 23. 切换国际化global__en_US.properties与global__zh_CN.properties是全局范围资源文件,应放在src下。
  • 24. 切换国际化package__en_US.properties与package__zh_CN.properties是包范围资源文件应放在首包下
  • 25. 切换国际化Login__en_US.properties与Login__zh_CN.properties是Action范围资源文件,应放在Login.java同一目录下
  • 26. 验证消息国际化文件LoginValidate__en_US.properties与LoginValidate __zh_CN.properties是Action范围资源文件,应放在LoginValidate.java同一目录下
  • 27. 切换国际化ChangeLocale__en_US.properties与ChangeLocale __zh_CN.properties文件
  • 28. login.jsp页面
  • 29. firstPage.jsp
  • 30. Login.java
  • 31. LoginValidate.java
  • 32. ChangeLocale.java
  • 33. ChangeLanguage.java
  • 34. struts.xml
  • 35. LoginValidate-validation.xml配置文件LoginValidate-validation.xml验证文件
  • 36. 谢谢!