内容


创建国际化的 JSP 应用程序

了解服务器端应用程序所面临的独特挑战

Comments

世界经济日益全球化推动了人们对基于 Web 的软件的需求,因为许多国家的用户都能访问 Web 软件。这些用户使用的语言、显示、数据录入、表示和文化需求等都可能存在很大的差异。国际化(缩写为 i18n)是一种创建为分散用户群工作的应用程序的艺术。

也许有点让人感到惊讶,当不作任何定制修改就在服务器端使用时,J2SE 对国际化的内建支持会表现出一些不足。一般来说,服务器端的国际化仍然是一门艺术,而不是一项科学,它常常涉及一些专用的或用户自主开发的解决方案。

本文把服务器端基于 JSP 的应用程序的国际化需求与 J2SE 应用程序的国际化需求区别开来。本文将介绍导致服务器端需求明显不同的各种客户机 / 服务器技术。然后,还将查看实际代码,这些代码展示了用来解决基本问题的、广泛采用的两个解决方案。

超越 J2SE 地区

J2SE 用 地区(locale)的概念(请参阅 Notation for a locale)进行国际化。在一台机器上,地区代表用户选择的显示语言(例如,英语或西班牙语),以及日期、时间、货币等方面的格式化约定。通常,由底层操作系统管理地区选项设置,并在运行的时候把它传递给 J2SE。

如果运行的服务器在本地机器上,或者是在局域网上,那么特定于机器的地区的概念就很好用。包含的所有客户机和服务器都在同一地区,所以它们都使用相同的显示语言、日期约定等。这些场景都不会带来麻烦的国际化问题。但是如果想用同一台服务器为多个国际位置上的用户提供服务的话,那么情况就变得很复杂。

服务器端国际化问题

当为了进行国际化访问而部署服务器应用程序时,该应用程序必须同时为不同的地区提供支持。图 1 显示了一种可能的场景。每台机器 —— 服务器以及可在任何时间访问服务器的客户机 —— 都可能有自己的地区设置,而这些地区是不同的。

图 1. 为不同地区的用户提供服务的服务器
为不同地区的用户提供服务的服务器
为不同地区的用户提供服务的服务器

在图 1 中,服务器机器位于 San Francisco,特定于机器的地区是 en_US(美国英语)。来自纽约和达拉斯的用户都使用 en_US 地区,所以没有额外的国际化需求。但是,来自汉城的用户期望看到用韩语表示的应用程序提示,他们的地区是 ko_KR。同时,来自上海的用户想看到中文表示的应用程序文本,他们的地区是 zh_CN。来自东京的用户期望用日语显示应用程序,他们的地区是 ja_JP。所有这些用户的需求都必须通过运行在 San Francisco 服务器上的 JSP 应用程序来得到满足。

在服务器端,您对于服务器机器自己的地区拥有全部控制权,但您无法改变客户机的地区或强行将它转换成某个特定地区。相反,应用程序必须识别出用户的地区,并保证 JSP 页面以正确的本地化形式出现(请参阅 检测客户机地区)。

更多地区判断的复杂情况

就在您以为可以安全地判断客户机地区,并据此呈现 JSP 时,新的问题出现了。请考虑以下这些非常现实的场景。

来自东京的一位访问人员工正在使用位于中国上海的销售办公室的机器,机器的地区是 zh_CN。因为不熟悉书面中文,所以她想用日语访问 Web 应用程序。关于这种情况的说明,参见图 2(a)。

图 2. 用户期望的地区与客户端机器上的地区不同
用户期望的地区与客户端机器上的地区不同
用户期望的地区与客户端机器上的地区不同

在图 2(a),San Francisco 服务器的地区是 en_US。客户端机器在上海,地区是 zh_CN。但是 JSP 应用程序需要用 ja_JP 地区显示,对用户才有用。

再看另外一个更加怪异但并非不可能的场景。国际化 JSP 应用程序的开发人员正在一项调试。在一个地区为 en_US 的客户端系统上,打开了三个运行 JSP 应用程序的浏览器实例。服务器机器在局域网上,地区也是 en_US。但是,现在要为中文和日语用户测试应用程序的处理能力。所以在一台 en_US 客户端机器上,一个浏览器实例用的是英文(en_US),一个用的是日语(ja_JP),还有一个用的是中文(zh_CN)。图 2(b)演示了这种情况。

目前为止,基本的国际化问题应当很清楚了:在处理显示国际化应用程序的任意特殊实例所需的实际地区时,客户机地区和服务器地区都有些力不从心。只有用户才能说清要用哪个地区显示页面。

但是谢天谢地,通常可以肯定地假设在使用应用程序期间,用户不会改变显示语言。所以通常可以把地区与会话关联起来。

解决特定于地区的语言的显示问题

对于 JSP 应用程序,至少有两种处理不同语言的显示问题的普遍接受方法可以使用:

  • 存储多组 JSP,每组 JSP 都用不同的语言编码,然后根据用户的地区选择在这些 JSP 之间切换。
  • 分离所有使用的字符串,代之以从资源绑定获得一个特定于地区的字符串。(这种方式使用了 J2SE 特定于地区的资源绑定处理方式。)

本文提供了示例代码的两个版本,分别对应上面的两种方法。示例应用程序是一个叫做 developerWorks Email 的虚构的电子邮件服务的登录屏幕。首先,会用语言选择屏幕提示电子邮件系统的用户,让他确定所需的当前会话地区。可以进行的选择包括英语、韩语、日语和中文,如图 3 所示。 如果想试试代码的效果,请用 http://<server address>/dwi18n/multdir/index.jsp 这个 URL 访问该页面。

图 3. 进行显式地区选择的语言选择屏幕
四种不同的地区在同一台机器上
四种不同的地区在同一台机器上

在图 3 中,语言选择屏幕用 4 个图片表示 4 个地区选择(请参阅 Using images for initial language selection)。登录屏幕使用用户在这里选择的语言进行显示。图 4 显示了日语的登录屏幕。

图 4. 日语登录屏幕
日语登录屏幕
日语登录屏幕

使用多组冗余的特定于语言的 JSP 集

示例的第一个版本位于示例代码发行包的 webapps/dwi18n/multdir/ 目录中,它运用了多组 JSP 页面。图 5 显示了这个应用程序的目录结构。

图 5. dwi18n/multdir 的目录结构显示了特定于地区的目录
dwi18n/multdir 的目录结构显示了特定于地区的目录

在图 5 中,每个地区都有对应的子目录。与 en_EN 地区对应的用英文编码的 JSP 在 en 子目录中。与 ko_KR 地区对应的用韩语编码的 JSP 在 ko 子目录中。对于 ja_JP 地区,用日语编码的 JSP 在 ja 子目录中。zh_CN 地区则用 zh 子目录中用中文编码的 JSP 表示。每个子目录都既包含登录屏幕的 JSP(login.jsp),也包含数据确认 JSP(confirm.jsp)。

数据确认 JSP 只显示在这个简单的示例中输入的数据。例如,如果在中文登录屏幕中输入了信息,然后单击按钮,那么数据确认 JSP 就会显示输入的数据,如图 6 所示。

图 6. 中文的确认页面
中文的确认页面
中文的确认页面

编写 JSP 代码

地区选择 JSP 叫做 index.jsp,它直接链接到特定于语言的一组 JSP。这个版本的 index.jsp 的代码在清单 1 中:

清单 1. 地区选择 JSP 直接链接到特定于语言的页面
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
 <html> 
 <head> 
 <title>Select a language</title> 
 </head> 
 <body> 
 <table> 
 <tr> 
 <td colspan=4 bgcolor="black"> 
 <br/> 
 <center><font face="arial" size=+2 color="white"> 
    <b><i>developer</i>Works Email</b></font> 
 </center> 
 <br/> 
 </td> 
 </tr> 
 <tr><td> 
 <c:url value="en/login.jsp" var="englishURL"/> 
 <a href="${englishURL}"> 
    <img src="english.gif"/> 
 </a> 
 </td> 
 <td> 
 <c:url value="ja/login.jsp" var="japaneseURL"/> 
  <a href="${japaneseURL}"> 
       <img src="japanese.gif"/> 
  </a> 
 </td> 
 <td> 
  <c:url value="ko/login.jsp" var="koreanURL"/> 
  <a href="${koreanURL}"> 
       <img src="korean.gif"/> 
  </a> 
 </td> 
 <td> 
  <c:url value="zh/login.jsp" var="chineseURL"/> 
  <a href="${chineseURL}"> 
       <img src="chinese.gif"/> 
  </a> 
 </td> 
 </tr> 
 </table> 
 </body> 
 </html>

注意,清单 1 中使用了来自 JSP 标准标签库(JSTL)的 <c:url>标签来创建链接 URL。这可以确保会话管理得到恰当的处理。(请参阅 参考资料,以了解关于 JSTL 和 <c:url>标签的更多信息。)

每组 login.jsp 和 confirm.jsp 都用特定于地区的语言编写代码。清单 2 显示了 ja_JP 地区的 login.jsp(与 图 4对应):

清单 2. ja_JP 地区的登录页面(login.jsp)
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
 <html> 
 <head> 
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > 
 <title>developerWorks 電子メール </title> 
 </head> 
 <body> 
 <c:url value="confirm.jsp" var="actionURL"/> 
 <form action="${actionURL}" method="post"> 
 <table> 
 <tr> 
 <td colspan=2 bgcolor="black"> 
 <br/> 
 <center><font face="arial" size=+2 color="white"> 
    <b><i>developer</i>Works 電子メール </b></font> 
 </center> 
 <br/> 
 </td> 
 </tr> 
 <tr> 
 <td> ユーザ ID</td> 
 <td><input type="text" name="userid" size="40"/></td> 
 </tr> 
 <tr> 
 <td> パスワード </td> 
 <td><input type="password" name="pass" size="40"/></td> 
 </tr> 
 <tr> 
 <td colspan="2" align="center"> 
 <input type="submit" value="ログイン"/></td> 
 </tr> 
 </table> 
 </form> 
 </body> 
 </html>

与此类似,对于 zh_CN 地区,confirm.jsp(与 图 6对应)是用中文编码的,如清单 3 所示:

清单 3. zh_CN 地区的数据确认页面(confirm.jsp)
 <html> 
 <head> 
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > 
 <title>developerWorks 电邮 </title> 
 </head> 
 <body> 
 <table border="1"> 
 <tr> 
 <td colspan=2 bgcolor="black"> 
 <br/> 
 <center><font face="arial" size=+2 color="white"> 
     <b><i>developer</i>Works 电邮 </b></font> 
 </center> 
 <br/> 
 </td> 
 </tr> 
 <tr> 
 <td> 用户帐号 </td> 
 <td>${param.userid}</td> 
 </tr> 
 <tr> 
 <td> 密码 </td> 
 <td>${param.pass}</td> 
 </tr> 
 </table> 
 </body> 
 </html>

前面介绍的多冗余集(multiple-redundant-set)方法对于以下这类应用程序是一种可行的解决方案:

  • 主要用一种语言访问,偶尔从其他地区访问。
  • 底层表示层的 JSP 变化不是很频繁。

使用 J2SE 资源绑定

前面一节 Using multiple redundant sets of language-specific JSPs中的解决方案的最大不足在于:当需要更新特定于语言的 JSP 集时,所有冗余编码的 JSP 集都必须同时更新。对于一个中等规模的项目而言,这会造成冗长的、容易出错的更新。

而现在要介绍的解决方案的外观和作用都与前一个类似,但是在这个案例中,只有一套 login.jsp 和 confirm.jsp。这个解决方案利用了 J2SE 在资源绑定中对地区的支持,只在需要的时候才采用特定于地区的文本字符串(请参阅 参考资料,以了解更多关于 J2SE 资源绑定的信息)。这个解决方案的示例代码位于 webapps\dwi18n\javares 目录中。如果部署了示例代码,那么请使用 http://<server address>/dwi18n/javares/index.jsp 这个 URL。

图 7 显示了运行在同一台客户端机器上的 4 个浏览器会话,每个会话请求的都是不同的地区。

图 7. 同一台机器上的 4 个不同地区
同一台机器上的 4 个不同地区
同一台机器上的 4 个不同地区

在图 7 中,可以清楚地看到国际化的 JSP 应用程序可以同时处理多个地区。

编写 JSP 代码

在这个案例中,index.jsp 略有不同,因为它现在链接到单独的 login.jsp。清单 4 显示了这个版本的 index.jsp 的代码:

清单 4. 链接到单独 login.jsp 的地区选择页面
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
 <html> 
 <head> 
 <title>Select Language</title> 
 </head> 
 <body> 
 <table> 
 <tr> 
 <td colspan=4 bgcolor="black"> 
 <br/> 
 <center><font face="arial" size=+2 color="white"> 
    <b><i>developer</i>Works Email</b></font> 
    </center> 
 <br/> 
 </td> 
 </tr> 
 <tr><td> 
 <c:url value="login.jsp" var="englishURL"> 
   <c:param name="locale" value="en_US"/> 
 </c:url> 
 <a href="${englishURL}"> 
    <img src="english.gif"/> 
 </a> 
 </td> 
 <td> 
  <c:url value="login.jsp" var="japaneseURL"> 
     <c:param name="locale" value="ja_JP"/> 
  </c:url> 
  <a href="${japaneseURL}"> 
       <img src="japanese.gif"/> 
  </a> 
 </td> 
 <td> 
  <c:url value="login.jsp" var="koreanURL"> 
     <c:param name="locale" value="ko_KR"/> 
  </c:url> 
  <a href="${koreanURL}"> 
       <img src="korean.gif"/> 
  </a> 
 </td> 
 <td> 
  <c:url value="login.jsp" var="chineseURL"> 
     <c:param name="locale" value="zh_CN"/> 
  </c:url> 
  <a href="${chineseURL}"> 
       <img src="chinese.gif"/> 
  </a> 
 </td> 
 </tr> 
 </table> 
 </body> 
 </html>

注意,在清单 4 中,使用 JSTL 的 <c:param> 标签设置了一个叫做 locale的 URL 请求参数。当用户单击语言选择时,这个参数被传递给 login.jsp。清单 5 显示了 login.jsp 的代码:

清单 5. 使用 J2SE 资源绑定的登录页面(login.jsp)
 <%@ page pageEncoding="UTF-8" %> 
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 
 <html> 
 <c:set var="loc" value="en_US"/> 
 <c:if test="${!(empty param.locale)}"> 
  <c:set var="loc" value="${param.locale}"/> 
 </c:if> 
 <fmt:setLocale value="${loc}" /> 
 <fmt:bundle basename="app"> 
 <head> 
 <title>developerWorks <fmt:message key="email"/></title> 
 </head> 
 <body> 
 <c:url value="confirm.jsp" var="formActionURL" /> 
 <form action="${formActionURL}" method="post"> 
 <table> 
 <tr> 
 <td colspan=2 bgcolor="black"> 
 <br/> 
 <center><font face="arial" size=+2 color="white"><b> 
       <i>developer</i>Works <fmt:message key="email"/> 
         </b></font></center> 
 <br/> 
 </td> 
 </tr> 
 <tr> 
 <td><fmt:message key="userid"/></td> 
 <td> 
 <input type="hidden" name="locale" value="${loc}"/> 
 <input type="text" name="userid" size="40"/></td> 
 </tr> 
 <tr> 
 <td><fmt:message key="password"/></td> 
 <td><input type="text" name="pass" size="40"/></td> 
 </tr> 
 <tr> 
 <td colspan="2" align="center"> 
 <input type="submit" value="<fmt:message key='login'/>"/></td> 
 </tr> 
 </table> 
 </form> 
 </body> 
 </fmt:bundle> 
 </html>

清单 5 使用了 JSTL 国际化辅助标签库(请参阅 参考资料)。如果传入的 param.locale为空,那么在默认情况下,地区设置为 en_US。在使用资源绑定的时候,可以用 <fmt:setLocale>标签设置地区。

完成地区设置之后,<fmt:message>就会从处于绑定状态的属性文件中提取文本,该文本对应于指定的关键字。用 <fmt:bundle>JSTL 标签把绑定的基本名称设为 app。 如果查看 dwi18n/WEB-INF/classes 目录,可以看到资源绑定中的所有文件都在那里。表 1 描述了这些文件。请参阅 参考资料,以获得使用 J2SE 资源绑定的更多信息。

表 1. 资源绑定中的文件
文件名说明
app.properties默认使用的属性文件。对应 en_US 地区。
app_zh.propertieszh_CN 地区的属性文件。包含用中文编码的字符串。
app_ko.propertiesko_KR 地区的属性文件。包含用韩语编码的字符串。
app_ja.propertiesja_JP 地区的属性文件。包含用日语编码的字符串。
*.ucd创建属性文件的 Unicode 源文件。
convacii.bat将 ucd 文件转换成属性文件的批处理文件。

作为示例,清单 6 显示了 app_ko.properties文件的内容:

清单 6. 资源绑定中的 app_ko.properties 文件
 email=\uc774\uba54\uc77c 
 userid=\uc544\uc774\ub514 
 password=\ube44\ubc00\ubc88\ud638 
 login=\ub85c\uadf8\uc778

注意,在清单 6 中,所有的 Unicode 字符均被转义。必须这么做,因为 Java 的资源绑定机制只接受用 ASCII 编码的属性文件。要创建这个文件,既可以使用 IDE 中的字符串资源编辑器,也可以使用 Unicode 编辑器创建一个 Unicode 文件,然后用 JDK 的 nativetoascii工具转换它。在该例中,convascii.bat文件负责进行转换。

结束语

在设置国际化 JSP 应用程序时,需要了解它的独特需求。应用程序必须做好准备,支持具有不同地区需求的用户的多个并发访问。本文介绍了用特定于地区的语言文本显示国际化应用程序这个问题的两个解决方案。但我也仅仅是触及到创建国际化服务器端应用程序的迷人艺术的表面。其他重要的问题包括处理不同的日期和货币格式、管理 GUI 布局和使用专门的输入法编辑器(IME,输入外国字符的实用软件)。请参阅 参考资料,以获得有助于进一步了解国际化的信息。


下载资源


相关主题

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文
  • 单击本文顶部或底部的 Code(或请参阅 Downloads)下载本文讨论的代码 code.zip。
  • 地区中使用的两个字符的语言代码是 ISO-639 代码。Consult the list.
  • 地区中使用的两个字符的国家代码是 ISO-3166 代码。Consult the list.
  • 要了解所有的 JSTL 标签,请参阅 developerWorks 上的 A JSTL 入门系列。其中 表示就是一切与这些标签紧密相关,它介绍了一些国际化标签。
  • 通过阅读 java.util.ResourceBundle类的 Javadoc,发现更多关于 J2SE 资源绑定、如何创建它们和在哪儿放置它们的消息。也可以阅读 javadoc 的在线文章 here
  • Unicode Input Method Editor是一个可以协助尽早在开发周期中发现全球化问题的工具,它能提供了一个简单的机制,以便很容易地重现全球化问题。
  • International Components for Unicode是 Unicode 支持、软件国际化、全球化的一个成熟的、广泛应用的库。ICU 是一个由 IBM 赞助、支持和使用的开源开发项目。
  • 参阅 Sing Li 撰写的书籍 Beginning JavaServer Pages(John Wiley & Sons,2005),以便进一步研究国际化和本地化。
  • 可以在 developerWorks 的 Java 技术专区 发现关于 Java 编程的各个方面的文章。
  • 请参阅 Developer Bookstore,以获得技术书籍的完整清单,其中包括数百本 Java 相关主题的书籍。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Web development
ArticleID=59102
ArticleTitle=创建国际化的 JSP 应用程序
publish-date=03292005