使用 JavaServer Pages 技术开发多语言 Web 应用程序

JavaServer Pages (JSP) 技术现已成为深受 Web 应用程序开发者欢迎的工具。 使用 JSP 技术,开发者不需要其他的编程知识就可以设计出动态的 web 网页。 同时,Web 开发者可以使用一种可扩展的标记机制来管理基础软件组件的功能。

通过 Java 标准制定组织(Java Community Process)开发的一个扩展功能可为多语言应用程序的开发提供更有力的支持。 JavaServer Pages 标准标记库除了其他一些功能,还定义了一套可实现本地化和地区敏感(locale-sensitive)格式化的标记。

行文方面,本文首先对 JavaServer Pages 技术进行了简要介绍,以使您能够更好地理解如何使用它们解决国际化的问题。 然后,我会针对多语言 web 应用程序的开发讨论几个核心问题,并介绍如何使用 JavaServer Pages 技术解决它们:这些问题包括地区确定和本地化、字符编码、格式化以及解析。

JavaServer Pages 技术

JavaServer Pages(和几种相关技术)构成了 web 应用程序的表示层。 使用 JSP 技术,开发者可以创建动态的 web 页面,这些页面可以与商业逻辑(business logic)、数据库以及其他可从网络上获取的服务形成互动关系。

JavaServer Pages

使用 JSP 技术开发的网页结合了 HTML、XML 或其他含有类似 XML 标记(这些标记与基础软件库连接)的静态内容,通常这些软件库使用 Java 编程语言编写。 在这种环境中,非常重要的 Java 技术有 JavaBeans 组件架构(作为 JSP 和 Java 类之间的常规用途接口)、用于访问 SQL 数据库的 Java 数据库连接(JDBC)API 以及各种用于 XML 处理的库。

JSP 页面本身按照 servlet 格式被编译为 Java 代码,以便执行。 Servlet 是 web 服务器的扩展,它被编译并关联至服务器,从而可以获得比脚本语言更快的执行速度。 Servlet 直接以 Java 编程语言编写,并经常与 JSP 网页一起使用,其中 servlet 作为控制部分而 JSP 页面作为应用程序的视图部分。

JavaServer Pages 和底层的 servlet 技术为处理 HTTP 请求和回应信息,以及使用 Cookies 或 URL 重写进行会话维护都提供了广泛的支持。

使用 JSP 技术的一个很重要的原因在于它可以将网页作者和应用程序开发者的工作进行分离。 尽管可以将 Java 语句直接嵌入 JSP 网页,但是,开发者们已经认识到最好避免如此,而现在更倾向于使用自定义标记。

JavaServer Pages 标准标记库

JavaServer Pages 标准标记库(JSTL)包含了一系列涵盖数个功能领域的自定义操作,这些功能在 JSP 网页中经常被使用。 该库建立在许多参与者开发自己的库时所获得的经验基础之上,它提供了一种应用程序可以依靠的标准接口,并且可以独立于他们运行的服务器之外。

除了自定义标记,JSTL 还引入了一种表达语言,这种语言运进一步地减少了在 JSP 网页中使用脚本语言的需求,同时还引进了标记库验证程序以限制在 JSP 网页上对脚本和标记库的使用。 这种改进版本的表达语言,以及限制脚本的功能已在随后被集成到 JSP 2.0 规范之中,所以只有使用 JSP 1.2 时才要求 JSTL。

自定义操作包括的主要内容是:

  • 变量操作: “核心”库中的一些操作,可在不同的领域(网页、请求、会话和应用程序)中定义、删除变量或者将变量植入生成的页面中。
  • 控制流: “核心”库中的几种操作,提供了基于标记的控制流结构(例如条件和迭代器),以减少对嵌入脚本语言代码的需求。
  • URL 相关操作: “核心”库中的一些操作,可让 JSP 网页导入由 URL 定义的内容,可将内部形式的 URL 重写为外部形式(这可能包括收集会话跟踪信息)或重新定向至一个不同的网页。
  • XML 处理: “xml”库中的操作,包括解析 XML 文档并使用 XPath 表达式来解压缩内容,基于 XPath 表达式的控制流以及使用 XSLT 样式表进行的转换。
  • 关系数据库访问: “sql”库中的操作,允许 web 应用程序执行简单的 SQL 查询和更新。
  • 国际化和格式化(本文的中心内容):“fmt”库中的操作,支持地区确定、本地化、字符编码决定和地区敏感格式化与解析。

地区确定和本地化

设计多语言 web 应用程序时,您必须首先决定如何确定用户的语言和地区首选项,以及如何使这些首选项与该应用程序和基础的 Java 运行环境支持的一套地区设置相匹配。 这部分首先描述了 web 应用程序必须具有的外部环境和要求。 下一步,我们将了解相关的 Java 2 Standard Edition (J2SE) 平台提供的功能,最后我们将了解 JavaServer Pages 标准标记库的标记如何连接到环境和 J2SE 中。

确定用户首选项

web 应用程序有两种方法来确定用户的语言首选项:首先,它可以由浏览器使用 HTTP 请求报头字段 Accept-Language 传输至服务器的语言和地区首选项。 尽管标准规定了许多语言标记,但是一般使用 ISO 639 语言代码(如 ja 为日文)和 ISO 3166 国家代码(如 IT 代表意大利)。 浏览器通常让用户创建一个语言列表作为其首选项的一部分。 然而,这种方法不太可靠;用户不一定会创建该列表,而且该列表不一定会包含该应用程序支持的地区设置。 由于这些不确定性,多语言应用程序通常采用第二种方法:他们让用户直接从支持语言的列表中选择,并把选择的语言作为用户资料的一部分进行保存,或只在进行该会话的时候保存。 一个好的方法是,在对用户一无所知的情况下,首先使用 Accept-Language 信息,在应用程序的开始页面中给予用户直接选择语言的机会。

Accept-Language 地区设置主要用于语言和文化首选项是没什么意义的。 例如,它们不应该被解释为表示用户居住的国家。 同样,在很多情况下浏览器提供的地区设置只有一个语言代码,而一些地区敏感的功能(例如,日期格式)则随着国家的不同而相异。 在很多情况下,通过语言假设主要国家的规范是合理的(如果没有指定国家);例如,如果指定了日本语,则可使用日本通用的日期格式。 然而,如果是根据国家而设置的重要功能(例如货币),则必须给用户一个机会来修正该假设。

在许多情况下,web 应用程序是由若干组件组合而来的,这些组件可能已经本地化为不同的语言。 一个特别值得一提的组件是 Java 运行环境,它在一些地区敏感区域可能具备支持超过 40 种语言中的 100 种区域设置的功能(例如日期格式),远远超出了典型的 web 应用程序。 因此,应用程序开发者必须决定是否在整个应用程序中限制所支持语言的本地化功能,或者充分发挥每个组件的功能优势。 第一种方法的优势在于用户可以看到的全部页面都使用同一语言,而第二种方法可能导致页面中存在不同的语言——其中一种语言出现在绝大多数文本中,而另一种则出现在例如日期的格式中。

Java 2 Standard Edition Platform 中的本地化

为了了解 JSTL 如何确定应用程序被哪些地区设置支持,我们来看看在基础的 Java 2 Standard Edition 平台中是如何进行本地化的。 java.util 软件包的核心主要有两种类:LocaleResourceBundle

Locale 对象只是用来确定地区设置的:它们结合了 ISO 639 语言代码(例如,ja 代表日文)和 ISO 3166 国家代码(例如,IT 代表意大利),还可能包含一个(非标准化的)变量字符串。 注意,HTTP 的地区标识符使用相同的 ISO 标准,所以对比通常比较容易。

ResourceBundle 对象是本地化对象的容器,形成成键/值对。 一个基础资源束定义了一个基础束的名称、一套键以及默认值(通常是英文值,但不是必须的)。 例如,一个简单的 Messages 资源束可能定义 greeting-day 键的默认值为 Hello。 其他的特定语言和国家束可以被定义,其名称由基础名称组成(通过后缀指示其语言、国家和变量)并提供已本地化的值。 例如,德语 Messages_de 资源束可以给出 Guten Tag 值(针对 greeting-day 键),而一个奥地利 Messages_de_AT 束可能用 Servus 值来覆盖该值。 资源束可以作为 Java 类或简单的“属性”文本文件来执行。

JavaServer Pages 应用程序的本地化方法

要对基于 JavaServer Pages 技术的应用程序进行本地化,方法通常有两个。 第一个方法是使用国际化的页面,这些页面常可以通过自定义标记从资源束获得与特定地区设置相关的内容。 如果页面需要保持复杂的结构并与所有地区设置同步,则通常会采取这种方法。 第二种方法使用单独的特定地区设置页面以及分发到适当页面的 servlet(取决于用户的地区选择)。 如果页面包含的主要是文本或者地区设置间的结构截然不同时,则通常会采取这种方法。

地区确定和 JSTL 中的本地化

JSTL 构建于 J2SE 工具之上,它可进行地区确定和本地化。 使用任何一种 JSP 本地化方法(如上所述)均可以进行地区确定,而本地化功能的目的是为国际化的页面提供支持。

JSTL 对上述两种确定用户地区首选项的方法都提供支持。 应用程序可以使用 JSTL 的 <fmt:setLocale> 操作,指定一个固定的地区(通常是用户从支持语言列表中所直接选择的)。 一旦使用了该操作,指定的地区设置将应用于所有的地区敏感操作中。 如果没有使用 <fmt:setLocale> 操作,地区敏感操作将会从地区选择列表中搜索第一种支持的地区设置,这些地区设置通常由 Accept-Language 报头提供。

下面是一些您可以用于 web 应用程序开始页面的代码片断。 这些代码片断可让用户非常轻松地选择他或她的地区设置。 假设这些代码是 locale-choice.jsp 页面中的一部分:

<%-- Interpret user's locale choice --%>
<c:if test="${param['locale'] != null}">
  <fmt:setLocale value="${param['locale']}" scope="session" />
</c:if>
 
<%-- Offer locale choice to user --%>
<a href="locale-choice.jsp?locale=en-US">USA</a> -
<a href="locale-choice.jsp?locale=de-DE">Deutschland</a> -
<a href="locale-choice.jsp?locale=ja-JP">日本</a>
 
<%-- Use URL rewriting to ensure proper session tracking --%>
<form method="get" action="<c:url value='/locale-choice.jsp' />">
  <input type=submit value="Stay in session">
</form>

第一部份(此部分必须在生成的 HTML 页面任何内容之前)表示用户的地区选择,该选择作为一个请求参数显示在 JSP 页面上。 如果定义了 locale 参数,则它将被用于进行会话的地区设置。

第二部分(此部分是生成的 HTML 页面内容的一部分)为用户提供了返回同一页面的链接,但是根据选定的国家提供了 locale 参数设置。 注意,在本地语言中已经给出国家的名称,所以即使页面的其他部分已经本地化为用户不能识别的语言,但是用户仍然可以容易地识别这些名称。 例如,“日本”是“日本”的数字字符引用,即“日本”的日语单词。 新版的浏览器如果装有日本字体,将会正确地转换这些文本;对于旧版的浏览器,使用图片则可能更合适一些。

最后一部分显示如何使用 <c:url> 标记生成 URL,此 URL 包含了一个会话 ID,如果需要对该会话进行追踪的话(如果用户启用了 cookies,则 cookies 将代替 URL 重写)。 这将确保一旦选择了地区设置,该选择将应用于该 web 应用程序中的所有页面。

如果从 web 应用程序本身的用户界面中选择了地区设置,然后使用 <fmt:setLocale> 进行设置,那么就可以假设该应用程序确实支持此地区设置。 另一方面,如果没有使用 <fmt:setLocale> 并且 JSTL 必须从 Accept-Language 报头中的地区设置列表中找到一个支持的地区设置,那么情况会变得更复杂。

要决定哪个地区设置是被支持的,JSTL 将参考该应用程序所使用的资源束。 有两种操作可用于访问资源束:<fmt:bundle><fmt:setBundle>。 它们的基本功能是相同的:它们查询一个资源束并创建一个“本地化环境”,在这个“本地化环境”中包含了对该资源束和用于请求该资源束的地区设置的引用。

<fmt:bundle><fmt:setBundle> 操作所使用的资源束查询允许多次请求地区设置(这样它就可以处理由 Accept-Language 报头提供的列表),并使用由 web 应用程序定义的备用(fallback)地区设置。 如果使用 <fmt:setLocale> 操作设置地区,那么 <fmt:bundle><fmt:setBundle> 操作将请求用于该地区设置的束,或者,如果不成功的话,将请求用于备用地区设置的束。 如果没有使用 <fmt:setLocale>,随后的操作将请求用于由 Accept-Language 报头提供的地区设置和备用地区设置,直到请求成功。 在每种情况下,基本查询(查询束的基本名称和一个请求地区设置)将为请求地区设置本身搜索一个资源束,随后搜索更简单的地区设置(首先从请求地区设置中舍弃变量,然后舍弃国家组件)。 如果全部的请求地区设置以及备用地区设置的查询都失败,那么将使用基础束。

以下是一些例子。 让我们假设某个应用程序拥有用于 enzh_CNzh_TWjako 的束。 备用地区设置被设置为 en。 没有使用 <fmt:setLocale> 标记。 下表显示最终本地化环境的束和地区设置(用于一些请求地区设置列表):

被请求的地区设置

结果束

结果地区设置

zh_SG、zh_CN

zh_CN

zh_CN

zh、ja

ja

ja

es_MX

en

en

en_US

en

en_US

ja、zh_CN

ja

ja

ResourceBundle 类的行家会注意到,JSTL 操作所使用的查询策略与 ResourceBundle 所使用的查询策略是不同的。 ResourceBundle 使用的策略只接受一个请求地区设置,这不足以处理由 Accept-Language 报头所提供的地区设置列表,并且它会恢复 Java 运行环境的默认地区设置,此默认地区设置与 web 应用程序和其用户并不相关,使用它将导致不可移植性。

那么,为什么查询资源束时存在两种不同的操作呢? 它们的区别在于它们使用的方法:<fmt:bundle> 标记提供了一个用于嵌套标记的环境,而 <fmt:setBundle> 操作将最终本地化环境存储在一个变量中,此变量可以在相同页面中被后继操作访问,并且可以被其他页面中的操作所访问(这取决于该变量的范围)。

<fmt:message> 操作是一种利用本地化环境的 JSTL 标记。 它最简单的形式是,它从一个本地化环境的资源束中为一个指定的键获取一条信息并将该信息插入生成的页面中。 下面例子显示了它的不同用法:

<fmt:setBundle basename="Errors" var="errorBundle" />
<fmt:bundle basename="Messages">
  <%-- Localization context established by <fmt:bundle> tag --%>
  <fmt:message key="greeting" />
  <p>
  <%-- Localization context established by <fmt:setBundle> tag --%>
  <fmt:message key="emptyField" bundle="${errorBundle}" />
</fmt:bundle>

其次,为什么有一个请求地区设置与本地化环境相关联? 这个地区设置是 JSTL 将格式化标记限制到应用程序所支持的语言范围内的方法,这样展现在读者面前的页面语言将完全统一。 嵌套于 <fmt:bundle> 标记中的格式化操作使用该标记的本地化环境来确定它们应该使用的地区设置。 例如,让我们观察下面的页面片断:

<jsp:useBean id="now" class="java.util.Date" />
<fmt:formatDate value="${now}" timeStyle="long" dateStyle="long" />
<p>
<fmt:bundle basename="Messages">
  <fmt:formatDate value="${now}" timeStyle="long" dateStyle="long" />
</fmt:bundle>

如果 HTTP Accept-Language 地区设置是 fren,并且基础的 Java 运行环境对这两种语言的日期格式都支持(但 web 应用程序的 Messages 束只存在于 en),那么第一个日期采用法文格式,而第二个则采用英文格式。 因此,页面设计者可以决定是使用统一的语言还是通过选择适当的标记嵌套来利用所有现有的本地化信息。

最后,为什么本地化环境使用请求地区设置而不使用由资源束找到的地区设置? 答案是,这样可以避免丢失重要的信息,某些格式标记可能需要这些信息。 很多应用程序不能区分相同语言中不同变量之间的区别,而且只提供(例如)英文资源束,期望着这些文本在英国、澳大利亚和新加坡都能被同样理解。 然而对于日期格式,国家是很关键的——对于英国读者来说,“2/6/02”表示“ 2002 年 6 月 2 日”,但对于习惯美国规范的读者来说,则表示“2002 年 2 月 6 日”。 所以,在很多情况下,如果使用了请求地区设置(而不是资源束地区设置),则国家信息将会被保留。

字符编码

当前,我们使用两种截然不同的模块表示存储在计算机中或通过网络传输的文本:旧的字符编码模式专门用于较小的语言集合、国家和/或操作系统(包括如 ISO 8859 系列、 Windows 代码页和 EUC 编码);而新的基于 Unicode 编码的模式能够(至少理论上能够)表示所有的语言并可以在任何地方使用。

旧的模块具有很大的劣势:

  • 每种旧的字符编码方法通常只支持一个小的语言集合。 例如,Shift-JIS 支持日文和英文,但不支持其他的亚洲或欧洲语言。 ISO 8859-1 支持一些西欧语言但不支持东欧语言。
  • 字符转换可能会带来意料之外的信息丢失。 开发者通常选择 ISO 8859-1 作为德语、法语和其它西欧语言的编码方法,然后会很惊讶地发现“€”字符(德国、法国和许多其他欧洲国家通用货币的标志)不被这种编码所支持。 为了防止这种信息丢失,您必须使用 Windows-1252、ISO 8859-15 或其他编码方法,这取决于浏览器的基础操作系统。

当前版本的主要软件系统所包含的创建、分发和解释 web 内容都支持新的模块;它们通常将 Unicode 用于内部处理,或者至少知道怎么使用(用于 web、基于 Unicode 编码的)UTF-8。 基于 Unicode 的编码有着以下显著的优势:它们支持多语言页面并清晰区分地区设置(从字符编码处理)问题。 同样,因为编码转换而带来的信息丢失的风险也很小,同时基于 Unicode 的编码与现在的服务器和客户端系统比较吻合。

尽管如此,很多 web 开发者仍然不太愿意使用 UTF-8。其中的原因可能包括对旧版本的浏览器支持不充分,或者缺少支持它的工具。

JavaServer Pages 技术对新旧两种模块都支持。 现在我们来看看字符编码问题所涉及的各种不同领域,并了解 JSP 技术和 JSTL 如何处理它们。

处理源程序页编码

JSP 源文件的编码通常由可用的编辑工具决定,所以可能使用特定国家和操作系统的编码。 字符编码与 JSP 运行环境(“容器”)之间的通讯方法有许多种,随着时间推移其中的机制和规则已不断改进。 同时 JSP 源文件相应存在着两种语法:标准语法和基于 XML 的新语法。

在检测字符编码时,JSP 2.0 规范将在这两种语法中进行辨别。 对于采用 XML 语法的文件,编码将被检测为采用 XML 规范;这意味着 UTF-8 或 UTF-16 为默认的编码,而其他的编码必须在文件开始处的 XML 声明中予以说明。 对于采用标准语法的文件,容器将考虑两种主要的信息来源:首先它们访问应用程序的配置描述符,查询一个 page-encoding 元素,该元素位于 jsp-property-group(其 URL 格式与文件相匹配);然后在此页中查询 pageEncoding 属性。 如果两者都没有,容器也会寻找 contentType 属性中的 charset (参阅下一部分“处理 Web 页面编码”),或使用 ISO 8859-1 作为最终的备用选择。

以下是基于 JSP 2.0 的应用程序的一些简单建议:对于采用 XML 语法的文件,确保没有使用 UTF-8 或 UTF-16 编码的文件能够正确识别它们的字符编码。 对于采用标准语法的文件,如果您对所有源文件使用 UTF-8,则请在配置描述符中只使用一个元素 page-encoding 来阐述它。 如果您使用特定地区设置编码,则根据该地区设置来组织或命名您的文件,并使用 page-encoding 元素来描述它们的关系。 例如,如果全部的韩文文件以 EUC-KR 编码并保存在 /ko/KR web 的应用程序的子目录中,请使用以下语句:

<jsp-property-group>
  <url-pattern>/ko/KR/*</url-pattern>
  <page-encoding>EUC-KR</page-encoding>
</jsp-property-group>

如果应用程序中的源文件不能以这种方式组织,则为每个源文件添加 pageEncoding 属性。 不过请切记,此属性必须能够在文件的开始处找到并且只能用于标识 ASCII 扩展码的字符编码。 后一个限制考虑到了 UTF-8 和许多旧的字符编码,但没考虑 UTF-16 或基于 EBCDIC 的编码。 不建议根据 contentType 属性中的 charset 值来标识源程序页编码;这个值应只用于标识 web 页面编码(参见下一部分)。

关于源文件字符编码,JSP 1.2 规范没有清楚地区分使用标准语法的文件和使用 XML 语法的文件。 它也没有提供识别配置描述符中的字符编码的方法。 为确保正确检测字符编码,设计用于 JSP 1.2 容器的应用程序应总是识别每个使用 pageEncoding 属性的源文件中的字符编码。

JSTL 定义了一个<c:import> 操作,该操作允许包含由 URL 指定到 JSP 生成的页面的外部数据。 该操作允许字符编码规范,如果外部数据没有指定它本身的编码时会使用此规范。

处理 Web 页面编码

web 应用程序必须选择生成的 web 页中使用的字符编码(该编码被称为“反应字符编码”),它基于目标浏览器的性能、页面内容的编写系统和语言以及可能的浏览器主机的操作系统。 根据 HTTP 规范,字符编码在 Content-Type 实体报头的 charset 参数中被指定。

如果所有目标浏览器都支持 UTF-8,一般来说最好使用这种编码,这样就可以支持多语言文档并避免字符转换带来的信息丢失。

如果不能使用 UTF-8 ,必须小心谨慎地使用应用程序将字符编码与使用的语言相匹配,包括一些特殊字符。 为防止出现错误,可能需要在整个页面里使用同一种语言,如本文开始部分“地区确定和本地化”中所述。 同样,也有必要避免使用“€”字符。

Web 应用程序可以直接指定一个页面的字符编码,也可以让 JSP 技术根据地区设置信息间接决定。

  • 通过页面的 contentType 属性的显式规范最为简便,该属性可让应用程序连同生成页面的内容类型一起来指定字符编码。 如果应用程序在处理请求时需要设置字符编码,则它需要使用一个自定义操作或一些 Java 代码来调用 javax.servlet.ServletResponse.setContentType 方法或新的(在 Servlet 2.4 中)javax.servlet.ServletResponse.setCharacterEncoding 方法。
  • 间接地,每当字符编码创建一个本地化环境时,它们也是由 JSTL 格式化操作(包括 <fmt:message>)以及 <fmt:bundle><fmt:setBundle><fmt:setLocale> 操作无条件地来决定。 通过 ServletResponse.setLocale 方法,它们将本地化环境的地区设置或指定的地区设置映射到一个字符编码并根据页面的内容类型对其进行设置。 Servlet 2.4 规范通过配置描述符中的 locale-encoding-mapping-list 元素为应用程序提供了一个控制映射的方法。 如果应用程序没有提供此元素,或者当您使用基于旧的 Servlet 规范的容器时,那么从地区设置到字符编码的映射取决于该容器;典型的实现依赖于旧的字符编码。

间接决定字符编码是可行的,只要旧的字符编码可以被接受,并且整个页面使用相同的语言而且避免出现常用字符编码所不支持的特殊字符。 然而,若要利用 UTF-8 则要求使用显式规范。 因为 Servlet 2.4 规范使显式规范优先于隐式规范,所以将字符编码设置为 contentType 属性的一部分已足够——随后使用 JSTL 格式化操作不会影响字符的编码。 不过,在早期版本的 Servlet 规范中,并不保证地区设置信息中的显式规范优先于隐式决定。 如果需要与基于旧规范的容器兼容,您必须通过在显式字符编码规范和首次使用自定义操作之间调用 ServletResponse.flushBuffer 来冻结字符编码,这些自定义操作可能间接决定字符编码。

处理请求参数编码

JSP 技术不仅能够生成 web 页面,而且还可以接收和解释与 HTTP 请求一起收到的参数——通常是来自某种表格的输入,这种表格属于前面生成的 web 页面的一部分。 用于这些参数的字符编码并非在任何地方都被指定,但实际标准是浏览器使用的编码要与包含这些表格的网页使用的编码相同。

这意味着 web 应用程序需要跟踪先前生成的网页的编码。 一个常用的机制是把编码的名称存储到表格本身的一个隐藏域中,在下一个请求时解压缩为第一个参数,然后用它来解码出其他的参数。 然而,JSP 页面还可以使用会话管理来跟踪请求之间的信息。

应用程序可以使用 JSTL 自定义操作 <fmt:requestEncoding> 来指定要编码的参数的编码方法。 如果应用程序总是发送 UTF-8 编码的页面,那么可以简单指定这种编码为请求编码。 否则,如果它将直接指定生成页面的编码,它应该将该编码作为会话信息的一部分进行跟踪并直接将其传递给 <fmt:requestEncoding> 操作。 如果它依赖于字符编码的间接决定,则它会简单地使用 <fmt:requestEncoding> 操作而无需指定一种字符编码;间接决定生成页面的编码这一操作还会在会话中储存信息,<fmt:requestEncoding> 可以检索和使用这些信息。

格式化和解析

以本地化的格式表示数据(如数字和日期)是任何类型地应用程序都要完成的常见任务,就如同用户提供的输入解释。 不同语言和文化所使用的格式区别很大,所以如果开发者不依靠现有的库的话,那么这个工作就不会是一项简单的任务。

幸运的是,确实存在这样的库。 Java 2 Standard Edition (J2SE) 平台提供了在 java.text 软件包中用于格式化和解析常用数据类型的类库,并且 Sun 已将这些类库本地化为 100 多种地区设置。

JavaServer Pages 标准标记库提供了自定义操作,可将这些功能直接应用到 JSP 页面中。

用于格式化和解析操作的地区确定

您可以在预定义的本地化环境中对数字和日期使用格式化和解析的操作(例如,如果标记嵌套于一个 <fmt:bundle> 标记中),或者在这种环境以外进行操作。 如果您在预定义的本地化环境中使用操作,则它们将使用此本地化环境的地区设置。 否则,它们将决定 <fmt:bundle><fmt:setBundle> 操作(如前文所述)的,用于修改的资源束查询策略所使用的地区设置。 主要的区别在于:与查找资源束不同,算法通过使用 java.text.NumberFormat.getAvailableLocales 方法(用于数字格式化和解析操作)或者 java.text.DateFormat.getAvailableLocales 方法(用于日期和时间格式化和分解操作)来决定受支持的地区设置。

数字格式化和解析

JSTL 用于数字格式化和解析的自定义操作 <fmt:formatNumber><fmt:parseNumber> 基于 J2SE 类 java.text.NumberFormat,用于处理简单的数字以及百分比和货币值。

特别值得一提的是它们对货币格式化的支持。 传统上,许多格式化库假设货币符号可以从地区设置中得出——例如,如果地区设置是中国,那么货币符号就是人民币(RMB)。 在一个跨境交易的环境中,这并没有多大的意义。 如果某个英国公司以英镑来计算价格,而 web 应用程序将价格显示为人民币(RMB)的形式,就会出现两个问题:第一,人民币的汇率比英镑低;其次,人民币换回英镑会比较困难。 由于货币的选择属于商业上的决定,所以货币必须作为值的一部分而不是格式的一部分。

因此,<fmt:formatNumber> 操作可以让应用程序指定一个 ISO 4217 货币代码或货币符号,它将覆盖本地化数字格式使用的默认货币。 假设应用程序使用一个带有值和货币属性的价格 bean,下面的页面语句片断可以对价格进行格式化:

<fmt:formatNumber type="currency" value="${price.value}"
  currencyCode="${price.currency}" />

如果 JSP 页面指定了一个货币代码,则底层的 NumberFormat 对象会尝试对指定的货币使用一个货币符号,该指定的货币已本地化为所处本地化环境的地区设置。 例如,如果为美元指定货币代码 USD,则它可能会使用符号“$”(如果地区设置是en_US),在其他可接受该货币符号的地区设置中将使用 US$,或者如果本地化符号未知,则使用备用货币代码 USD

日期和时间的格式化和解析

用于日期和时间的格式化和解析的 JSTL 自定义操作 <fmt:formatDate><fmt:parseDate> 基于 J2SE 类 java.text.DateFormat 并用于处理不同的日期和时间表示方法。

令人感兴趣的一点是显示的日期和时间不仅仅取决于一种指定地区设置的格式,还取决于时区信息。 用户通常对服务器时区不感兴趣,但另一方面,要找出用户所在地的时区却并不简单。应用程序可以通过使用一些客户端的 JavaScript 代码来找出用户的当前时区与格林尼治标准时间的偏差,或让用户指定当前时区并将其作为用户信息的一部分。 JSTL 操作并没有解决这个问题,但它们提供了两个自定义操作,可以用来告知有关时区的日期和时间的格式化和解析:<fmt:timeZone><fmt:setTimeZone>。 与 <fmt:bundle><fmt:setBundle> 一样,<fmt:timeZone> 标记可为嵌套标记定义时区,而 <fmt:setTimeZone> 将时区储存在一个变量中以供后继操作使用。

信息格式化

<fmt:message> 操作(前面已提及)不但能从一个资源束中获取一个字符串并将其插入至生成的页面中,而且它还可以执行参数替换并根据需要格式化参数。 它基于 java.text.MessageFormat 类,因此该操作获取的字符串实际上是一个 MessageFormat 模式字符串。 <fmt:param> 操作提供了必要的自变量。

例如,如果 JSP 页面包含了以下语句:

<jsp:useBean id="now" class="java.util.Date" />
<fmt:bundle basename="Messages">
  <fmt:message key="greeting">
    <fmt:param value="${now}" />
  </fmt:message>
</fmt:bundle>

并且找到的资源束是德语,而且 为 greeting 键提供了“Willkommen! Heute ist der {0,date,long}.”值,那么生成日期为(假设)2002 年 6 月 21 日的页面内容将会是“Willkommen! Heute ist der 21. Juni 2002.”

结论

如本文所述,JavaServer Pages 技术(特别是 JavaServer Pages 标准标记库)为您提供了一个开发多语言应用程序的坚实基础。 您需要仔细考虑以下几个设计选择:如何确定用户的语言和地区设置首选项,如何构造您用于本地化的 JSP 页面,是否采用单一语言的页面或充分利用现有的地区设置支持,以及使用哪一种字符编码模块。 JSP 技术使您能够选择其中任意一种,这样您就可以将页面以最佳的方式展示给全世界的读者,而且最重要的是,以他们自己的语言来展示。

参考书目

R. Fielding et al.: Hypertext Transfer Protocol -- HTTP/1.1. RFC 2616. The Internet Society, 1999.

Dave Raggett et al. (ed.): HTML 4.01 Specification. World Wide Web Consortium, 1999.

Tim Bray et al. (ed.): Extensible Markup Language (XML) 1.0 (Second Edition). World Wide Web Consortium, 2000.

Java 2 Platform, Standard Edition, v 1.4.2 API Specification.Sun Microsystems, 2002.

Danny Coward (ed.): Java Servlet Specification. Version 2.3.Sun Microsystems, 2001.

Danny Coward, Yutaka Yoshida (ed.): Java Servlet Specification. Version 2.4.Sun Microsystems, 2003.

Eduardo Pelegrlopart (ed.): JavaServer Pages Specification. Version 1.2.Sun Microsystems, 2001.

Mark Roth, Eduardo Pelegr lopart (ed.): JavaServer Pages Specification. Version 2.0.Sun Microsystems, 2003.

Pierre Delisle (ed.): JavaServer Pages Standard Tag Library. Version 1.0.Sun Microsystems, 2002.

Pierre Delisle (ed.): JavaServer Pages Standard Tag Library. Version 1.1.Sun Microsystems, 2003.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值