Struts应用的国际化

来源:互联网 发布:俄罗斯方块c语言 编辑:程序博客网 时间:2024/05/21 17:54

Struts应用的国际化

1.1 本地化与国际化的概念

       国际化(简称I18N指的是在软件设计阶段,就应该使软件具有支持多种语言和地区的功能。

       本地化意味着针对不同语言的客户,开发出不同的软件版本;国际化意味着同一个软件可以面向使用各种不同语言的客户。

如果一个应用支持国际化,它应该具备以下特征:

l         当应用需要支持一种新的语言时,无需修改应用程序代码。

l         文本、消息和图片从源程序代码中抽取出来,存储在外部。

l         应该根据用户的语言和地理位置,对与特定文化相关数据,如日期、时间和货币,进行正确的格式化。

l         支持非标准的字符集。

l         可以方便快捷地对应用作出调整,使它适应新的语言和地区。

    I18NInternationalization的简称,因为该单词的首字母I与尾字母N中间隔着18个字符,由此得名。

1.2 Web应用的中文本地化

       无论是对Web应用的本地化还是国际化,都会涉及字符编码转换问题。Web应用的各种可能的输入和输出流如图,当数据流的目的地使用不同的字符编码时,就需要对字符编码进行正确的转换。

1.2.1 处理HTTP请求数据编码

       默认情况下,IE浏览器发送请求时采用”ISO-8859-1”字符编码,如果Web应用程序要正确地读取用户发送的中文数据,则需要进行编码转换。

       两种方法:

l         在处理请求前,先设置HttpServletRequest对象的字符编码:

 request.setCharacterEncoding(“gb2312”);

l         对用户输入的请求数据进行编码转换:

 String clientData = request.getParameter(“clientData”);

 if ( clientData != null )

    clientData = new String( clientData.getBytes(“ISO-8859-1”), ”GB2312” );

1.2.2 处理数据库数据编码

 Connection con = DbUtil.connectToDb();

 PreparedStatement pstmt = null;

 ResultSet rs = null;

 pStmt = con.prepareStatement(“select FILED1 from MYTABLE”);

 rs = pStmt.executeQuery();

 while ( rs.next() ) {

   String field1 = rs.getString(“FILED1”);

   String field1_ch = new String(field1.getBytes(“ISO-8859-1”), “GB2312” );

   //process data

 }

如果数据库系统字符编码使用的”ISO-8859-1”的话,那么需要如上的操作转换为GB2312


1.2.3 处理XML配置文件编码

       如果在XML文件中包含中文,可以将XML文件的字符编码设为”GB2312”,这样,当Java程序加载和解析XML文件时无需再进行编码转换:

 <?xml version=’1.0’ encoding=”GB2312” ?>

1.2.4 处理响应结果的编码

       可以通过以下方式来设置响应结果的编码:

l         Servlet

 response.setContentType(“text/html;charset=GB2312”);

l         JSP

         <%@ page contentType=”text/html;charset=GB2312” %>

l         HTML

        <head>

          <META HTTP-EQUIV=”Content-Type” CONTENT=”text/html;charset=GB2312”>

        </head>

1.3 JavaI18N的支持

       Java在其核心库中提供了支持I18N的类和接口。

1.3.1 Locale

       java.util.Locale类是最重要的Java I18N类。

       Locale类的实例代表一种特定的语言和地区。如果Java类库中的某个类在运行时需要根据Locale对象来调整其功能,那么称这个类是本地敏感(Locale-Sensitive)。例如,java.text.DateFormat类就是本地敏感的,因为它需要依照特定的Locale对象来对日期进行相应的格式化。

       创建Locale对象时,需要明确地指定其语言和国家代码:

  Locale usLocale = new Locale(“en”, “US”);  //美国的

  Locale chLocale = new Locale(“ch”, “CH”);  //中国的

       第一个参数是语言代码,由两个小写字母组成,遵从ISO-639规范,可以从http://www.unicode.org/unicode/onlinedat/languages.html中获得完整的语言代码列表。

       第二个参数是国家代码,由两个大写字母组成,遵从ISO-3166规范,可以从http://www.unicode.org/unicode/onlinedat/countries.html中获得完整的国家代码列表。

       Locale类提供了几个静态常量,代表一些常用的Locale实例,如日本,可以使用:

              Locale locale1 = Locale.JAPAN

            Locale locale2 = new Locale(“ja”, “JP”);

1.      Web容器中Locale对象的来源

Java虚拟机在启动时会查询操作系统,为运行环境设置默认的LocaleJava程序可以调用java.util.Locale类的静态方法getLocale()来获得默认的Locale

                 Locale defaultLocale = Locale.getDefautl(); 

2.      Web应用中访问Locale对象

对于Web应用程序,通常不必创建自己的Locale实例,因为Web容器会负责创建所需的Locale实例。在应用程序中,可以调用HttpServletRequest对象的以下两个方法,来取得包含Web客户的Locale信息的Locale实例:

public java.util.Locale getLocale();

public java.util.Enumeration getLocales();

这两个方法都会访问HTTP请求中的Accept-Language头信息,getLocale()方法返回客户优先使用的Locale,而getLocales()方法返回一个Enumeration集合对象,它包含了按优先级降序排列的所有Locale对象。如果客户没有配置任何LocalegetLocale()方法会返回默认的Locale

大多数浏览器允许用户配置Locale,如IE,在Internet选项的语言中,可以进行配置:

3.      Struts应用中访问Locale对象

由于Web服务器并不和客户浏览器保持长期的连接,因此每个发送到Web容器的HTTP请求中都包含了Locale信息,Struts配置文件的<controller>元素的Locale属性指定是否把Locale对象保存在session范围内,默认是true。在处理每一个用户请求时,RequestProcessor类都会调用它的processLocale()方法:

protected void processLocale(HttpServletRequest request,

                            HttpServletResponse response)  {

//Are we configured to select the Locale automatically?

if ( !moduleConfig.getControllerConfig().getLocale() )  {

  return;

}

 

//Has a Locale already been selected?

HttpSession session = request.getSession();

if ( session.getAttribute(Globals.LOCALE_KEY) != null )  {

  return;

}

 

//Use the Locale returned by the servlet container(if any)

Locale locale = request.getLocale();

if ( locale != null )  {

  session.setAttribute(Globals.LOCALE_KEY, locale);

}

}

尽管每次发送的HTTP请求都包含Locale信息,processLocale()方法把Locale对象存储在session范围内,需要满足两个条件:

l         Struts配置文件的<controller>元素的locale属性为true

l         session范围内Locale对象不存在

       Struts应用中可以很方便地读取Locale信息,在Action类中,可以调用Struts Action基类中定义的getLocale()方法。return RequestUtils.getUserLocale(request, null);

  public static Locale getUserLocale(HttpServletRequest request, String Locale)  {

Locale userLocale = null;

HttpSession session = request.getSession(false);

 

if ( locale == null )  {

  locale = Globals.LOCALE_KEY;

}

 

//Only check session if sessions are enabled

if ( session != null )  {

  userLocale = (Locale)session.getAttribute(locale);

}

 

if ( userLocale == null )  {

  //Returns Locale based on Accept-Language header or the server default

  userLocale = request.getLocale();

}

 

return userLocale;

  }

 

       getUserLocale()方法先通过HttpServletRequest参数获得HttpSession对象,然后再通过HttpSession对象来读取Locale对象,如果存在HttpSession对象并且其中存储了Locale对象,就返回该Locale对象,否则就直接调用HttpServletRequestgetLocale()方法获取Locale对象,并返回。

1.3.2 ResourceBundle

       java.util.ResourceBundle类提供存放和管理与Locale相关的资源的功能。这些资源包括文本域或按钮的Label、状态信息、图片名、错误信息和网页标题等。

       Struts框架没有直接使用Java语言提供的ResourceBundle类,而是提供了:

l         org.apache.struts.util.MessageResource

l         org.apache.struts.util.PropertyMessageResource

1.3.3 MessageFormat类和复合消息

       JavaResourceBundle类和StrutsMessageResource类都允许使用静态和动态的文本。静态的就是在ResourceBundle中添加固定的信息:

       error.requriedfield.name=The Name field is requried to save.

error.requriedfield.phone=The Phone field is requried to save.

但是如果表单有100个域,就需要写100个,就使得我们的ResourceBundle很庞大并且难于维护,这就可以使用动态方式:

error.requriedfield=The {0} field is requried to save.

lable.phone=Phone

lable.name=Name

       以此类推,还可以使用{1}{2}来表示,在运行时,MessageFormat类的format()方法可以把参数{0}替换成真正的动态文本内容。

import java.util.ResourceBundle;

import java.util.Locale;

import java.text.MessageFormat;

 

public class FormatExample  {

  public static void main(String[] args)  {

//Load the resource bundle

ResourceBundle bundle = ResourceBundle.getBundle(“ApplicationResources”);

//Get the message template

String requriedFieldMessage = bundle.getString(“error.requriedfield”);

//Create a String array of size one to hold the arguments

String[] messageArgs = new String[1];

//Get the “Name” field from the bundle and load it in as an argument

messageArgs[0] = bundle.getString(“lable.name”);

//Format the message using the message and the arguments

String formattedNameMessage =

           MessageFormat.format(requriedFieldMessage, messageArgs);

System.out.println(formattedNameMessage);

//Get the “Phone” field from the bundle and load it in as an argument

messageArgs[0] = bundel.getString(“lable.phone”);

//Format

String formattedPhoneMessage =

           MessageFormat.format(requriedFieldMessage, messageArgs);

System.out.println(formattedPhoneMessage);

  }

}

通常把包含可变数据的消息成为复合消息,复合消息允许在程序运行时把动态数据加入到消息文本中,这能够减少Resource Bundle中的静态消息数量,从而减少把静态消息文本翻译成其他Locale版本所花费的时间。

1.4 Struts框架对国际化的支持

       Struts框架对国际化的支持体现在能够输出和用户Locale相符合的文本和图片上。当Struts配置文件的<controller>元素的locale属性为true时,Struts框架把用户的Locale实例保存在session范围内,这样,Struts框架能自动根据这一Locale实例来从Resource Bundle中选择合适的资源文件。如图,当用户的Locale为英文时,Struts框架就会向用户返回来自于application_en.properties文件的文本内容;当用户的Locale为中文时,Struts框架就会向用户返回来自于application_ch.properties文件的内容。

1.4.1 创建StrutsResource Bundle

       对于多应用模块的Struts应用,可以为每个子应用配置一个或多个Resource Bundle,饮用模块中的ActionActionForm BeanJSP页和客户化标签都可以访问这些BundleStruts配置文件中的每个<message-resources>元素定义一个Resource Bundle。当应用中包含多个Resource Bundle时,它们通过<message-resources>元素的key属性来区别。

  <message-resources parameter=”application” />

  <message-resources key=”IMAGE_RESOURCE_KEY” parameter=”imageresources” />

       Resource Bundle的持久化消息文本存储在资源文件中,其扩展名为”.properties”,默认资源文件应该取名为application.properties,如果应用程序需要支持中文用户,可以再创建一个包含中文消息的资源文件,文件名为:application_ch_CN.propertiesapplication_ch.properties

       Struts框架处理Locale为中文的用户请求时,它会依次搜索如下资源文件:

l         application_ch_CN.properties

l         application_ch.properties

l         application.properties

       Struts框架在/WEB-INF/classes/目录下依次寻找这些资源文件。如果在配置Resource Bundle时还给定了包名,那么properties文件应该在classes/对应的包名目录下。

1.4.2 访问Resource Bundle

       Struts应用的每个Resource Bundleorg.apache.struts.util.MessageResources(实际上是其子类PropertyMessageResources)的一个实例对应。MessageResources对象中存放了来自资源文件的文本。当应用初始化时,这些MessageResources实例被存储在ServletContext(application范围内),因此任何一个Web组件都可以访问它们。

Struts应用、子应用模块、ResourceBundle和资源文件之间存在以下关系:

l         一个Struts应用可以有多个子应用模块,必须有且只有一个默认子应用模块

l         一个子应用模块可以有多个Resource Bundle,必须有且只有一个默认Resource Bundle

l         一个Resource Bundle可以有多个资源文件,必须有且只有一个默认资源文件

Struts应用中访问Resource Bundle的途径:

1.      通过编程来访问Resource Bundle

Action基类中定义了getResources(request)方法,它可以返回默认的MessageResources对象,代表当前应用模块使用的默认Resource Bundle,如果要获得特定的MessageResources对象,可以调用Action基类的getResources(request, key)方法,key对应<message-resources>元素的key属性。

org.apache.struts.util.MessageResourcesgetMessage()方法有几种重载形式:

l         根据参数指定的Locale检索对应的资源文件,然后返回和参数key对应的消息文本:

  getMessage(java.util.Locale locale, java.lang.String key);

l         根据参数指定的Locale检索对应的资源文件,然后返回和参数key对应的消息文本,args参数用于替换复合消息文本中的参数:

  getMessage(java.util.Locale locale, java.lang.String key, java.lang.Object[] args);

l         根据默认的Locale检索对应的资源文件,然后返回和参数key对应的消息文本:

  getMessage(java.lang.String key);

2.      使用和Resource Bundle绑定的Struts组件

Struts框架中的许多内在组件和Resource Bundle是绑定在一起的,如:

l         ActionMessage类和<html:errors>标签

每个ActionMessage实例代表Resource Bundle中的一条消息。调用ActionMessage的构造方法时,需要传递消息key。一般在ActionForm Beanvalidate()方法中:

      errors.add(“username”, new ActionMessage(“hello.no.username.error”);

对于复合消息,在创建ActionMessage对象时,调用带两个参数的构造方法:ActionMessage(java.lang.String key, java.lang.Object[] values)values用于替换复合消息中的参数。

JSP页面中,使用<html:errors>标签,就能读取显示ActionErrors集合众所有的ActionMessage对象包含的消息文本。

l         Struts Bean标签库的<bean:message>标签

<bean:message>标签从应用的Resource Bundle中获取消息字符串。如:

     <head><title><bean:message key=”hello.jsp.title” /></title></head>

<bean:message>标签根据存储在session范围内的Locale实例,从默认的Resource Bundle中检索和Locale对应的资源文件,再从资源文件中读取和”hello.jsp.title”对应的消息字符串。<bean:message>标签还有一个属性bundle,它和<message-resources>元素的key属性对应,如果没有,将访问默认的Resource  Bundle

l         Validator验证框架中访问Resource Bundle

l         在声明型异常处理中访问Resource Bundle

1.5 helloapp应用实现国际化

1.5.1 JSP文件的文件、 图片和按钮进行国际化

(1)   设置字符串编码。

可以将所有的JSP页面的字符编码统一设为UTF-8

  <%@ page contentType=”text/html;charset=UTF-8” language=”java” %>

(2)   对文本国际化。

JSP文件中不应该直接包含本地化的消息文本,而应该通过<bean:message>标签从Resource Bundle中获得。

(3)   对按钮国际化

<html:submit property=”submit”>

<bean:message key=”hello.jsp.page.submit”/>

</html:submit>

(4)   对图片国际化

 <html:img pageKey=”hello.jsp.page.strutsimage” altKey=”hello.jsp.page.struts” />

1.5.2 创建临时中文资源文件

       因为中文资源文件还需要进行转换,所以创建一个临时的,下一步进行转换,命名为application_temp.properties

1.5.3 对临时资源文件进行编码转换

       JDK提供了native2ascii命令,它能够实现字符编码转换。转换上一步的临时中文资源文件:native2ascii –encoding gb2312 application_temp.properties application_zh_CN.properties  

将会把temp中的中文转换成”/u7b2c/u4e00/u4e2a….”形式。需要验证,可能转换会错误

1.5.4 创建英文资源文件

       application.properties相同,只是文件名叫application_en.properties

1.5.5 采用Servlet过滤器设置请求数据的字符编码

       调用HttpServletRequestsetCharacterEncoding(“UTF-8”)方法,能够把用户的请求数据的字符编码也设为UTF-8,这样,Wen应用的输入和输出都采用同一种字符编码,就无需在程序中进行编码转换了。

编写SetCharacterEncodingFilter.java类,并在web.xml中加入

<filter>

  <filter-name>Set Character Encoding</filter-name>

  <filter-class>SetCharacterEncodingFilter</filter-class>

</filter>

<filter-mapping>

  <filter-name>Set Character Encoding</filter-name>

  <url-pattern>/*</url-pattern>

</filter-mapping>

1.5.6 运行国际化的helloapp应用

       通过修改IE选项的语言信息,可以改变使用的Locale信息。

    在同一个会话中,Struts框架仅读取HTTP请求的Locale信息一次,然后就把Locale对象保存在session范围内,因此在同一个会话中,Locale对象保持不变,选用的资源文件也不会变化。

1.6 异常处理的国际化

1.7 小结

       与国际化密切相关的两个组件是LocaleResource Bundle

l         Locale:包含了用户的本地化信息,如语言和国家

l         Resource Bundle:包含了多个消息资源文件,每个消息资源文件存放和一种Locale相对应的本地化消息文本。

       Struts框架在初始化时,把Resource Bundle(MessageResources对象)存储在application范围内,在响应用户请求时,把包含用户Locale信息的Locale实例存储在session范围内。Struts框架能自动根据这一Locale实例,从Resource Bundle中检索相应的资源文件,再从资源文件中读取本地化的消息文本。

       Struts应用实现国际化应该遵循以下原则:

l         尽量不在Servlet中使用含非英文字符的常量字符串。

l         对于JSP文件,应该对page指令中的charset属性进行相应的设置。

l         不要在JSP文件中直接包含本地化的消息资源,而应该把消息资源存放在Resource Bundle的资源文件中。

l         不必在每个JSPServlet中设置HTTP请求的字符编码,可以在Servlet过滤器中设置:HttpServletRequest.setCharacterEncoding(String encoding);

l         尽量使用”UTF-8”作为HTTP请求和响应的字符编码,而不是”GBK””GB2312”

l         充分考虑底层数据库所使用的编码,它可能会给应用程序的移植带来麻烦。

 
原创粉丝点击