JSP 标准标记库(JSP Standard Tag Library,JSTL)fmt 库通过一组颇受关注的定制标记提供了用于访问所有 Java 编程语言国际化功能的便利方式。Mark Kolb 研究了用于对数据进行格式化和国际化的 fmt 标记。在本系列的前几篇文章中,我们讨论了 JSTL 及其表达式语言(EL)。我们还研究了由 core 库定义的定制标记。具体而言,在“JSTL 入门:表达式语言”中我们指出 EL 提供了一种简化语言,用于在 JSP 应用程序中访问和操作数据并使该数据可被 JSTL 定制标记用作动态属性值。core 库包含了一些定制标记,用于管理限定了作用域的变量、显示 EL 值、实现迭代内容和条件内容以及与 URL 进行交互,这是“JSTL 入门:探讨 core”的主题。我们接下来将讨论的 JSTL 库是 fmt 库。fmt 库中的定制标记支持通过资源束对文本内容进行本地化,并支持对数字和日期的显示和解析。这些标记利用在 java.util 和 java.text 包中实现的 Java 语言的国际化 API,因此如果您已经很熟悉诸如 ResourceBundle、Locale、MessageFormat 和 DateFormat 这样的类,那么您将发现 fmt 库中有很多方面值得称道。如果您不熟悉这些类,那么 fmt 库的标记用直观的方式来封装国际化 API,这种方式使您能够很容易将本地化功能合并到 JSP 应用程序中。本地化在 Java 语言国际化 API 中,影响数据本地化方式的因素主要有两个。一个是用户的语言环境,另一个是用户的时区。语言环境表示某一特定区域或文化的语言习惯,包括日期、数字和货币金额的格式。一个语言环境始终会有一种相关联的语言,在许多情况下这种语言是由多个语言环境共享的某种语言的方言。例如,美国英语、英国英语、澳大利亚英语和加拿大英语都具有不同的英语语言环境,而法国、比利时、瑞士和加拿大所用的法语方言则都具有不同的法语语言环境。时区是数据本地化中的第二个因素,这仅仅是因为一些语言环境分布的地理区域很广。当您显示有关跨洲语言环境(比如澳大利亚英语)的时间信息时,针对用户时区定制数据与对其进行正确格式化一样重要。但是这就有了一个问题:应用程序如何确定用户的语言环境和时区?在 Java 应用程序的情况下,JVM 能够通过与本地操作系统进行交互来设置缺省语言环境和时区。虽然这种方法对于桌面应用程序而言可以正常工作,但是它实际上并不适合于服务器端的 Java 应用程序,因为这种应用程序所处理的请求,可能来自于距离该应用程序所驻留的服务器万里之遥的地方。幸运的是,HTTP 协议通过 Accept-Language 请求头将本地化信息从浏览器传递至服务器。许多 Web 浏览器允许用户定制他们的语言首选项,如图 1 所示。通常,那些没有为一种或多种首选语言环境提供显式设置的浏览器会询问操作系统以确定在 Accept-Language 头中发送哪个值(或哪些值)。servlet 规范通过 javax.servlet.ServletRequest 类的 getLocale() 和 getLocales() 方法自动地利用 HTTP 协议的这一功能。JSTL fmt 库中的定制标记又会利用这些方法来自动地确定用户的语言环境,从而相应地调整它们的输出。图 1. 通过设置浏览器的语言首选项来选择语言环境
但遗憾的是,不存在将用户的时区从浏览器传输到服务器的标准 HTTP 请求头。因此,那些希望自己的 Web 应用程序对时间数据进行本地化的用户,将需要实现他们自己的机制,用来确定和跟踪特定于用户的时区。例如,在本系列文章第 2 部分“JSTL 入门:探讨 core”中所介绍的 Weblog 应用程序包含了一种将用户的时区首选项存储在 cookie 中的方式。fmt 库JSTL fmt 库中的定制标记主要分成四组。第一组允许您设置本地化上下文,其它标记将在其中进行操作。换句话说,这组标记允许页面作者显式地设置其它 fmt 标记在格式化数据时将要使用的语言环境和时区。第二组和第三组标记分别支持对日期和数字进行格式化和解析。最后一组标记侧重于对文本消息进行本地化。既然我们已经有了些基本了解,那就让我们集中精力逐个研究这四组标记,并演示其用法。本地化上下文标记正如我们已经讨论过的那样,JSTL 标记在格式化数据时所使用的语言环境往往是通过查看用户浏览器发送的每个 HTTP 请求所包含的 Accept-Language 头来确定的。如果没有提供这样的头,那么 JSTL 提供一组 JSP 配置变量,您可以设置这些变量以指定缺省的语言环境。如果尚未设置这些配置变量,那么就使用 JVM 的缺省语言环境,该缺省语言环境是从 JSP 容器所运行的操作系统中获取的。fmt 库提供了其自身的定制标记,以覆盖这个确定用户语言环境的过程:。正如下面的代码片段所示, 操作支持三个属性:其中只有一个属性是必需的:value 属性。该属性的值应当是命名该语言环境的一个字符串或者是 java.util.Locale 类的一个实例。语言环境名称是这样组成的:小写的两字母 ISO 语言代码,可选地,后面可以跟下划线或连字符以及大写的两字母 ISO 国家或地区代码。例如,en 是英语的语言代码,US 是美国的国家或地区代码,因此 en_US(或 en-US)将是美式英语的语言环境名称。类似的,fr 是法语的语言代码,CA 是加拿大的国家或地区代码,因此 fr_CA(或 fr-CA)是加拿大法语的语言环境名称(请参阅参考资料以获取所有有效的 ISO 语言和国家或地区代码的链接)。当然,由于国家或地区代码是可选的,因此 en 和 fr 本身就是有效的语言环境名称,适用于不区别这些相应语言特定方言的应用程序。 的可选属性 scope 用来指定语言环境的作用域。page 作用域指出这项设置只适用于当前页,而 request 作用域将它应用于请求期间访问的所有 JSP 页面。如果将 scope 属性设置成 session,那么指定的语言环境被用于用户会话期间访问的所有 JSP 页面。值 application 指出该语言环境适用于该 Web 应用程序所有 JSP 页面的全部请求和该应用程序所有用户的全部请求。variant 属性(也是可选的)允许您进一步针对特定的 Web 浏览器平台或供应商定制语言环境。例如,MAC 和 WIN 分别是 Apple Macintosh 和 Microsoft Windows 平台的变体名。下面的代码片段说明了如何使用 标记来显式指定用户会话的语言环境设置:JSP 容器处理完该 JSP 代码段之后,将忽略用户浏览器设置中所指定的语言首选项。 操作像 一样,可以用来设置其它 fmt 定制标记所使用的缺省时区值。它的语法如下所示:和 一样,只有 value 属性是必需的,但是在本例中它应当是时区名或 java.util.TimeZone 类的实例。遗憾的是,对于时区命名目前还没有任何被广泛接受的标准。因此您可以用于 标记的 value 属性的时区名是特定于 Java 平台的。您可以通过调用 java.util.TimeZone 类的 getAvailableIDs() 静态方法来检索有效的时区名列表。示例包括 US/Eastern、GMT+8 和 Pacific/Guam。和 的情况一样,您可以使用可选的 scope 属性来指出时区设置的作用域。下面的代码演示了 的用法,它用来指定适用于单个用户会话的时区:您还可以使用 操作将 TimeZone 实例的值存储在限定了作用域的变量中。在本例中,您可以使用 var 属性来命名限定了作用域的变量,用 scope 属性来指定该变量的作用域(例如,就象这两个属性用在 和 操作中)。请注意,当您以这种方式使用 操作时,它唯一的副作用就是设置指定的变量。当指定 var 属性时,对于任何其它 JSTL 标记使用什么时区,不会对 JSP 环境作任何更改。这组中的最后一个标记是 操作:body content和 一样,您可以使用该标记来指定将由其它 JSTL 标记使用的时区。但是, 操作的作用域仅限于其标记体内容。在 标记体中,由标记的 value 属性指定的时区覆盖了 JSP 环境中现有的任何其它时区设置。和 的情况一样, 标记的 value 属性应当是时区名或者是 java.util.TimeZone 实例。后面的清单 1 中提供了一个如何使用 的示例。日期标记fmt 库包含了用来与日期和时间进行交互的两个标记: 和 。顾名思义, 用来格式化和显示日期和时间(数据输出),而 用来解析日期和时间值(数据输入)。 的语法如下所示:只有 value 属性才是必需的。其值应当是