JSP 標準標記庫(JSP Standard Tag Library,JSTL)fmt 庫通過(guò)一組頗受關(guān)注的定制標記提供了用于訪(fǎng)問(wèn)所有 Java 編程語(yǔ)言國際化功能的便利方式。Mark Kolb 研究了用于對數據進(jìn)行格式化和國際化的 fmt 標記。 在本系列的前幾篇文章中,我們討論了 JSTL 及其表達式語(yǔ)言(EL)。我們還研究了由 core 庫定義的定制標記。具體而言,在“ JSTL 入門(mén):表達式語(yǔ)言”中我們指出 EL 提供了一種簡(jiǎn)化語(yǔ)言,用于在 JSP 應用程序中訪(fǎng)問(wèn)和操作數據并使該數據可被 JSTL 定制標記用作動(dòng)態(tài)屬性值。 core 庫包含了一些定制標記,用于管理限定了作用域的變量、顯示 EL 值、實(shí)現迭代內容和條件內容以及與 URL 進(jìn)行交互,這是“ JSTL 入門(mén):探討 core”的主題。 我們接下來(lái)將討論的 JSTL 庫是 fmt 庫。 fmt 庫中的定制標記支持通過(guò)資源束對文本內容進(jìn)行本地化,并支持對數字和日期的顯示和解析。這些標記利用在 java.util 和 java.text 包中實(shí)現的 Java 語(yǔ)言的國際化 API,因此如果您已經(jīng)很熟悉諸如 ResourceBundle 、 Locale 、 MessageFormat 和 DateFormat 這樣的類(lèi),那么您將發(fā)現 fmt 庫中有很多方面值得稱(chēng)道。如果您不熟悉這些類(lèi),那么 fmt 庫的標記用直觀(guān)的方式來(lái)封裝國際化 API,這種方式使您能夠很容易將本地化功能合并到 JSP 應用程序中。 本地化 在 Java 語(yǔ)言國際化 API 中,影響數據本地化方式的因素主要有兩個(gè)。一個(gè)是用戶(hù)的 語(yǔ)言環(huán)境,另一個(gè)是用戶(hù)的 時(shí)區。語(yǔ)言環(huán)境表示某一特定區域或文化的語(yǔ)言習慣,包括日期、數字和貨幣金額的格式。一個(gè)語(yǔ)言環(huán)境始終會(huì )有一種相關(guān)聯(lián)的語(yǔ)言,在許多情況下這種語(yǔ)言是由多個(gè)語(yǔ)言環(huán)境共享的某種語(yǔ)言的方言。例如,美國英語(yǔ)、英國英語(yǔ)、澳大利亞英語(yǔ)和加拿大英語(yǔ)都具有不同的英語(yǔ)語(yǔ)言環(huán)境,而法國、比利時(shí)、瑞士和加拿大所用的法語(yǔ)方言則都具有不同的法語(yǔ)語(yǔ)言環(huán)境。 時(shí)區是數據本地化中的第二個(gè)因素,這僅僅是因為一些語(yǔ)言環(huán)境分布的地理區域很廣。當您顯示有關(guān)跨洲語(yǔ)言環(huán)境(比如澳大利亞英語(yǔ))的時(shí)間信息時(shí),針對用戶(hù)時(shí)區定制數據與對其進(jìn)行正確格式化一樣重要。 但是這就有了一個(gè)問(wèn)題:應用程序如何確定用戶(hù)的語(yǔ)言環(huán)境和時(shí)區?在 Java 應用程序的情況下,JVM 能夠通過(guò)與本地操作系統進(jìn)行交互來(lái)設置缺省語(yǔ)言環(huán)境和時(shí)區。雖然這種方法對于桌面應用程序而言可以正常工作,但是它實(shí)際上并不適合于服務(wù)器端的 Java 應用程序,因為這種應用程序所處理的請求,可能來(lái)自于距離該應用程序所駐留的服務(wù)器萬(wàn)里之遙的地方。 幸運的是,HTTP 協(xié)議通過(guò) Accept-Language 請求頭將本地化信息從瀏覽器傳遞至服務(wù)器。許多 Web 瀏覽器允許用戶(hù)定制他們的語(yǔ)言首選項,如圖 1 所示。通常,那些沒(méi)有為一種或多種首選語(yǔ)言環(huán)境提供顯式設置的瀏覽器會(huì )詢(xún)問(wèn)操作系統以確定在 Accept-Language 頭中發(fā)送哪個(gè)值(或哪些值)。servlet 規范通過(guò) javax.servlet.ServletRequest 類(lèi)的 getLocale() 和 getLocales() 方法自動(dòng)地利用 HTTP 協(xié)議的這一功能。JSTL fmt 庫中的定制標記又會(huì )利用這些方法來(lái)自動(dòng)地確定用戶(hù)的語(yǔ)言環(huán)境,從而相應地調整它們的輸出。 圖 1. 通過(guò)設置瀏覽器的語(yǔ)言首選項來(lái)選擇語(yǔ)言環(huán)境 但遺憾的是,不存在將用戶(hù)的時(shí)區從瀏覽器傳輸到服務(wù)器的標準 HTTP 請求頭。因此,那些希望自己的 Web 應用程序對時(shí)間數據進(jìn)行本地化的用戶(hù),將需要實(shí)現他們自己的機制,用來(lái)確定和跟蹤特定于用戶(hù)的時(shí)區。例如,在本系列文章第 2 部分“ JSTL 入門(mén):探討 core”中所介紹的 Weblog 應用程序包含了一種將用戶(hù)的時(shí)區首選項存儲在 cookie 中的方式。 fmt 庫 JSTL fmt 庫中的定制標記主要分成四組。第一組允許您設置本地化上下文,其它標記將在其中進(jìn)行操作。換句話(huà)說(shuō),這組標記允許頁(yè)面作者顯式地設置其它 fmt 標記在格式化數據時(shí)將要使用的語(yǔ)言環(huán)境和時(shí)區。第二組和第三組標記分別支持對日期和數字進(jìn)行格式化和解析。最后一組標記側重于對文本消息進(jìn)行本地化。 既然我們已經(jīng)有了些基本了解,那就讓我們集中精力逐個(gè)研究這四組標記,并演示其用法。 本地化上下文標記 正如我們已經(jīng)討論過(guò)的那樣,JSTL 標記在格式化數據時(shí)所使用的語(yǔ)言環(huán)境往往是通過(guò)查看用戶(hù)瀏覽器發(fā)送的每個(gè) HTTP 請求所包含的 Accept-Language 頭來(lái)確定的。如果沒(méi)有提供這樣的頭,那么 JSTL 提供一組 JSP 配置變量,您可以設置這些變量以指定缺省的語(yǔ)言環(huán)境。如果尚未設置這些配置變量,那么就使用 JVM 的缺省語(yǔ)言環(huán)境,該缺省語(yǔ)言環(huán)境是從 JSP 容器所運行的操作系統中獲取的。 fmt 庫提供了其自身的定制標記,以覆蓋這個(gè)確定用戶(hù)語(yǔ)言環(huán)境的過(guò)程: <fmt:setLocale> 。正如下面的代碼片段所示, <fmt:setLocale> 操作支持三個(gè)屬性: <fmt:setLocale value=" expression" scope=" scope" variant=" expression"/> | 其中只有一個(gè)屬性是必需的: value 屬性。該屬性的值應當是命名該語(yǔ)言環(huán)境的一個(gè)字符串或者是 java.util.Locale 類(lèi)的一個(gè)實(shí)例。語(yǔ)言環(huán)境名稱(chēng)是這樣組成的:小寫(xiě)的兩字母 ISO 語(yǔ)言代碼,可選地,后面可以跟下劃線(xiàn)或連字符以及大寫(xiě)的兩字母 ISO 國家或地區代碼。 例如, en 是英語(yǔ)的語(yǔ)言代碼, US 是美國的國家或地區代碼,因此 en_US (或 en-US )將是美式英語(yǔ)的語(yǔ)言環(huán)境名稱(chēng)。類(lèi)似的, fr 是法語(yǔ)的語(yǔ)言代碼, CA 是加拿大的國家或地區代碼,因此 fr_CA (或 fr-CA )是加拿大法語(yǔ)的語(yǔ)言環(huán)境名稱(chēng)(請參閱 參考資料以獲取所有有效的 ISO 語(yǔ)言和國家或地區代碼的鏈接)。當然,由于國家或地區代碼是可選的,因此 en 和 fr 本身就是有效的語(yǔ)言環(huán)境名稱(chēng),適用于不區別這些相應語(yǔ)言特定方言的應用程序。 <fmt:setLocale> 的可選屬性 scope 用來(lái)指定語(yǔ)言環(huán)境的作用域。 page 作用域指出這項設置只適用于當前頁(yè),而 request 作用域將它應用于請求期間訪(fǎng)問(wèn)的所有 JSP 頁(yè)面。如果將 scope 屬性設置成 session ,那么指定的語(yǔ)言環(huán)境被用于用戶(hù)會(huì )話(huà)期間訪(fǎng)問(wèn)的所有 JSP 頁(yè)面。值 application 指出該語(yǔ)言環(huán)境適用于該 Web 應用程序所有 JSP 頁(yè)面的全部請求和該應用程序所有用戶(hù)的全部請求。 variant 屬性(也是可選的)允許您進(jìn)一步針對特定的 Web 瀏覽器平臺或供應商定制語(yǔ)言環(huán)境。例如, MAC 和 WIN 分別是 Apple Macintosh 和 Microsoft Windows 平臺的變體名。 下面的代碼片段說(shuō)明了如何使用 <fmt:setLocale> 標記來(lái)顯式指定用戶(hù)會(huì )話(huà)的語(yǔ)言環(huán)境設置: <fmt:setLocale value="fr_CA" scope="session"/> | JSP 容器處理完該 JSP 代碼段之后,將忽略用戶(hù)瀏覽器設置中所指定的語(yǔ)言首選項。 <fmt:setTimeZone> 操作像 <fmt:setLocale> 一樣,可以用來(lái)設置其它 fmt 定制標記所使用的缺省時(shí)區值。它的語(yǔ)法如下所示: <fmt:setTimeZone value=" expression" var=" name" scope=" scope"/> | 和 <fmt:setLocale> 一樣,只有 value 屬性是必需的,但是在本例中它應當是時(shí)區名或 java.util.TimeZone 類(lèi)的實(shí)例。 遺憾的是,對于時(shí)區命名目前還沒(méi)有任何被廣泛接受的標準。因此您可以用于 <fmt:setTimezone> 標記的 value 屬性的時(shí)區名是特定于 Java 平臺的。您可以通過(guò)調用 java.util.TimeZone 類(lèi)的 getAvailableIDs() 靜態(tài)方法來(lái)檢索有效的時(shí)區名列表。示例包括 US/Eastern 、 GMT+8 和 Pacific/Guam 。 和 <fmt:setLocale> 的情況一樣,您可以使用可選的 scope 屬性來(lái)指出時(shí)區設置的作用域。下面的代碼演示了 <fmt:setTimeZone> 的用法,它用來(lái)指定適用于單個(gè)用戶(hù)會(huì )話(huà)的時(shí)區: <fmt:setTimeZone value="Australia/Brisbane" scope="session"/> | 您還可以使用 <fmt:setTimeZone> 操作將 TimeZone 實(shí)例的值存儲在限定了作用域的變量中。在本例中,您可以使用 var 屬性來(lái)命名限定了作用域的變量,用 scope 屬性來(lái)指定該變量的作用域(例如,就象這兩個(gè)屬性用在 <c:set> 和 <c:if> 操作中)。請注意,當您以這種方式使用 <fmt:setTimeZone> 操作時(shí),它唯一的副作用就是設置指定的變量。當指定 var 屬性時(shí),對于任何其它 JSTL 標記使用什么時(shí)區,不會(huì )對 JSP 環(huán)境作任何更改。 這組中的最后一個(gè)標記是 <fmt:timeZone> 操作: <fmt:timeZone value=" expression"> body content </fmt:timeZone> | 和 <fmt:setTimeZone> 一樣,您可以使用該標記來(lái)指定將由其它 JSTL 標記使用的時(shí)區。但是, <fmt:timeZone> 操作的作用域僅限于其標記體內容。在 <fmt:timeZone> 標記體中,由標記的 value 屬性指定的時(shí)區覆蓋了 JSP 環(huán)境中現有的任何其它時(shí)區設置。 和 <fmt:setTimeZone> 的情況一樣, <fmt:timeZone> 標記的 value 屬性應當是時(shí)區名或者是 java.util.TimeZone 實(shí)例。后面的 清單 1 中提供了一個(gè)如何使用 <fmt:timeZone> 的示例。 日期標記 fmt 庫包含了用來(lái)與日期和時(shí)間進(jìn)行交互的兩個(gè)標記: <fmt:formatDate> 和 <fmt:parseDate> 。顧名思義, <fmt:formatDate> 用來(lái)格式化和顯示日期和時(shí)間(數據 輸出),而 <fmt:parseDate> 用來(lái)解析日期和時(shí)間值(數據 輸入)。 <fmt:formatDate> 的語(yǔ)法如下所示: <fmt:formatDate value=" expression" timeZone=" expression" type=" field" dateStyle=" style" timeStyle=" style" pattern=" expression" var=" name" scope=" scope"/> | 只有 value 屬性才是必需的。其值應當是 java.util.Date 類(lèi)的實(shí)例,指定要進(jìn)行格式化和顯示的日期和/或時(shí)間數據。 可選的 timeZone 屬性指出將要顯示哪個(gè)時(shí)區的日期和/或時(shí)間。如果沒(méi)有顯式地指定 timeZone 屬性,那么就使用周?chē)魏?<fmt:timeZone> 標記所指定的時(shí)區。如果 <fmt:timeZone> 標記的主體部分沒(méi)有包含 <fmt:formatDate> 操作,那么就使用任何適用的 <fmt:setTimeZone> 操作所設置的時(shí)區。如果沒(méi)有相關(guān)的 <fmt:setTimeZone> 操作,那么就使用 JVM 的缺省時(shí)區(也就是,專(zhuān)為本地操作系統而設置的時(shí)區)。 type 屬性指出要顯示指定的 Date 實(shí)例的哪些字段,應當是 time 、 date 或 both 。該屬性的缺省值是 date ,因此如果沒(méi)有給出 type 屬性,那么 <fmt:formatDate> 標記(名符其實(shí))將只顯示與 Date 實(shí)例相關(guān)的日期信息,這個(gè)信息用該標記的 value 屬性指定。 dateStyle 和 timeStyle 屬性分別指出應當如何格式化日期和時(shí)間信息。有效的樣式有 default 、 short 、 medium 、 long 和 full 。缺省值自然是 default ,指出應當使用特定于語(yǔ)言環(huán)境的樣式。其它四個(gè)樣式值的語(yǔ)義與 java.text.DateFormat 類(lèi)定義的一樣。 可以使用 pattern 屬性來(lái)指定定制樣式,而不必依賴(lài)于內置樣式。給出定制樣式后,該模式屬性的值應當是符合 java.text.SimpleDateFormat 類(lèi)約定的模式字符串。這些模式基于用對應的日期和時(shí)間字段代替模式內指定的字符。例如,模式 MM/dd/yyyy 表明應當顯示用正斜杠分隔的兩位數的月份和日期值以及四位數的年份值。 如果指定了 var 屬性,那就把包含格式化日期的 String 值指派給指定的變量。否則, <fmt:formatDate> 標記將寫(xiě)出格式化結果。當指定了 var 屬性后, scope 屬性指定所生成變量的作用域。 清單 1(它是本系列 第 2 部分清單 8 的擴展)包含了 <fmt:formatDate> 標記的兩種用法。在第一種用法中, <fmt:formatDate> 只用來(lái)顯示第一個(gè) weblog 項的創(chuàng )建時(shí)間戳記的日期部分。此外,為 dateStyle 屬性指定了一個(gè) full 值,這樣一來(lái)所有的日期字段就將用一種特定于語(yǔ)言環(huán)境的格式進(jìn)行顯示。 清單 1. 使用 <fmt:formatDate> 標記來(lái)顯示日期和時(shí)間值 <table> <fmt:timeZone value="US/Eastern"> <c:forEach items="${entryList}" var="blogEntry" varStatus="status"> <c:if test="${status.first}"> <tr><td align="left" class="blogDate"> <fmt:formatDate value= "${blogEntry.created}" dateStyle="full"/> </td></tr> </c:if> <tr><td align="left" class="blogTitle"> <c:out value="${blogEntry.title}" escapeXml="false"/> </td></tr> <tr><td align="left" class="blogText"> <c:out value="${blogEntry.text}" escapeXml="false"/> <font class="blogPosted"> [Posted <fmt:formatDate value="${blogEntry.created}" pattern="h:mm a zz"/>] </font> </td></tr> </c:forEach> </fmt:timeZone> </table> | 在 <c:forEach> 循環(huán)體中,第二個(gè) <fmt:formatDate> 操作只用來(lái)顯示每個(gè)項的創(chuàng )建日期的時(shí)間部分。在本例中, pattern 屬性用來(lái)控制時(shí)間值的格式化、并控制指定一位數的小時(shí)顯示(如果可能的話(huà))、12 小時(shí)的時(shí)鐘和縮寫(xiě)時(shí)區的輸出。輸出如圖 2 所示: 圖 2. 清單 1 中 en_US 語(yǔ)言環(huán)境的輸出 更準確地說(shuō),用戶(hù)瀏覽器設置指定首選項是英語(yǔ)時(shí),就會(huì )產(chǎn)生圖 2 中所示的輸出。但是由于 <fmt:formatDate> 對用戶(hù)語(yǔ)言環(huán)境敏感,所以瀏覽器首選項的改變將導致生成不同的內容。例如,當給定的首選項是法語(yǔ)語(yǔ)言環(huán)境時(shí),則結果會(huì )如圖 3 所示: 圖 3. 清單 1 中 fr_CA 語(yǔ)言環(huán)境的輸出 <fmt:formatDate> 生成了 java.util.Date 實(shí)例的本地化字符串表示,而 <fmt:parseDate> 操作執行相反的操作:給定一個(gè)表示日期和/或時(shí)間的字符串,它將生成相應的 Date 對象。 <fmt:parseDate> 操作有兩種格式,如下所示: <fmt:parseDate value=" expression" type=" field" dateStyle=" style" timeStyle=" style" pattern=" expression" timeZone=" expression" parseLocale=" expression" var=" name" scope=" scope"/> <fmt:parseDate type=" field" dateStyle=" style" timeStyle=" style" pattern=" expression" timeZone=" expression" parseLocale=" expression" var=" name" scope=" scope"> body content </fmt:parseDate> | 對于第一種格式,只有 value 屬性才是必需的,它的值應當是指定日期、時(shí)間或這兩者組合的字符串。對于第二種格式,沒(méi)有必需的屬性,表示要解析的值的字符串被指定為 <fmt:parseDate> 標記必需的標記體內容。 type 、 dateStyle 、 timeStyle 、 pattern 和 timeZone 屬性對 <fmt:parseDate> 和對 <fmt:formatDate> 起一樣的作用,不同之處僅在于對于前者,它們控制日期值的解析而非顯示。 parseLocale 屬性用來(lái)指定一種語(yǔ)言環(huán)境,將根據這種語(yǔ)言環(huán)境來(lái)解析該標記的值,它應當是語(yǔ)言環(huán)境的名稱(chēng)或 Locale 類(lèi)的實(shí)例。 var 和 scope 屬性用來(lái)指定限定了作用域的變量(作為 <fmt:parseDate> 的結果),將把 Date 對象賦給該變量。如果沒(méi)有給出 var 屬性,則使用 Date 類(lèi)的 toString() 方法將結果寫(xiě)到 JSP 頁(yè)面中。清單 2 顯示了 <fmt:parseDate> 操作的一個(gè)示例: 清單 2. 使用 <fmt:parseDate> 標記來(lái)解析日期和時(shí)間 <c:set var="usDateString">4/1/03 7:03 PM</c:set> <fmt:parseDate value="${usDateString}" parseLocale="en_US" type="both" dateStyle="short" timeStyle="short" var="usDate"/> <c:set var="gbDateString">4/1/03 19:03</c:set> <fmt:parseDate value="${gbDateString}" parseLocale="en_GB" type="both" dateStyle="short" timeStyle="short" var="gbDate"/> <ul> <li> Parsing <c:out value="${usDateString}"/> against the U.S. English locale yields a date of <c:out value="${usDate}"/>.</li> <li> Parsing <c:out value="${gbDateString}"/> against the British English locale yields a date of <c:out value="${gbDate}"/>.</li> </ul> | 清單 2 的輸出如圖 4 所示。 圖 4. 清單 2 的輸出 由 <fmt:parseDate> 所執行的解析非常嚴格,注意這一點(diǎn)很重要。正如清單 2 所暗示的那樣,要解析的值必須嚴格符合特定(特定于語(yǔ)言環(huán)境)的樣式或模式。這當然更加受限制。另一方面,數據的解析并不是一個(gè)非常適合于表示層的任務(wù)。對于生產(chǎn)代碼,文本輸入的驗證和轉換最好由后端代碼(比如 servlet)來(lái)處理,而不是通過(guò) JSP 定制標記來(lái)處理。 數字標記 就象 <fmt:formatDate> 和 <fmt:parseDate> 標記用于格式化和解析日期一樣, <fmt:formatNumber> 和 <fmt:parseNumber> 標記對數字數據執行類(lèi)似的功能。 <fmt:formatNumber> 標記用來(lái)以特定于語(yǔ)言環(huán)境的方式顯示數字數據,包括貨幣和百分數。 <fmt:formatNumber> 操作由語(yǔ)言環(huán)境確定,例如,使用句點(diǎn)還是使用逗號來(lái)定界數字的整數和小數部分。下面是它的語(yǔ)法: <fmt:formatNumber value=" expression" type=" type" pattern=" expression" currencyCode=" expression" currencySymbol=" expression" maxIntegerDigits=" expression" minIntegerDigits=" expression" maxFractionDigits=" expression" minFractionDigits=" expression" groupingUsed=" expression" var=" name" scope=" scope"/> | 如 <fmt:formatDate> 的情況一樣,只有 value 屬性才是必需的。它用來(lái)指定將被格式化的數值。 var 和 scope 屬性對 <fmt:formatNumber> 操作所起的作用,如它們在 <fmt:formatDate> 中所起的作用一樣。 type 屬性的值應當是 number 、 currency 或 percentage ,并指明要對哪種類(lèi)型的數值進(jìn)行格式化。該屬性的缺省值是 number 。 pattern 屬性?xún)?yōu)先于 type 屬性,允許對遵循 java.text.DecimalFormat 類(lèi)模式約定的數值進(jìn)行更精確的格式化。 當 type 屬性的值為 currency 時(shí), currencyCode 屬性可以用來(lái)顯式地指定所顯示的數值的貨幣單位。與語(yǔ)言和國家或地區代碼一樣,貨幣代碼也是由 ISO 標準管理的(請參閱 參考資料以獲取所有有效的 ISO 貨幣符號代碼的鏈接)。該代碼用來(lái)確定作為已格式化值的一部分顯示的貨幣符號。 另外,您可以使用 currencySymbol 屬性來(lái)顯式地指定貨幣符號。請注意,由于 JDK 1.4 和相關(guān)的 java.util.Currency 類(lèi)的引入, <fmt:formatNumber> 操作的 currencyCode 屬性?xún)?yōu)先權超過(guò) currencySymbol 屬性。但是對于較老版本的 JDK 而言, currencySymbol 屬性具有優(yōu)先權。 maxIntegerDigits 、 minIntegerDigits 、 maxFractionDigits 和 minFractionDigits 屬性用來(lái)控制小數點(diǎn)前后所顯示的有效數字的個(gè)數。這些屬性要求是整數值。 groupingUsed 屬性帶有布爾值并控制是否要對小數點(diǎn)前面的數字分組。例如,在英語(yǔ)語(yǔ)言環(huán)境中,將較大數的每三個(gè)數字分為一組,每組用逗號定界。其它語(yǔ)言環(huán)境用句點(diǎn)或空格來(lái)定界這樣的分組。該屬性的缺省值為 true 。 清單 3 顯示了一個(gè)簡(jiǎn)單的貨幣示例,它本身是 清單 1 的擴展。在本例中,不指定 currencyCode 或 currencySymbol 屬性。而貨幣是由語(yǔ)言環(huán)境設置確定的。 清單 3. 使用 <fmt:formatNumber> 標記顯示貨幣值 <table> <fmt:timeZone value="US/Eastern"> <c:forEach items="${entryList}" var="blogEntry" varStatus="status"> <c:if test="${status.first}"> <tr><td align="left" class="blogDate"> <fmt:formatDate value= "${blogEntry.created}" dateStyle="full"/> </td></tr> </c:if> <tr><td align="left" class="blogTitle"> <c:out value="${blogEntry.title}" escapeXml="false"/> </td></tr> <tr><td align="left" class="blogText"> <c:out value="${blogEntry.text}" escapeXml="false"/> <font class="blogPosted"> [My <fmt:formatNumber value="0.02" type="currency"/> posted at <fmt:formatDate value="${blogEntry.created}" pattern="h:mm a zz"/>] </font> </td></tr> </c:forEach> </fmt:timeZone> </table> | en_US 語(yǔ)言環(huán)境的輸出如圖 5 所示: 圖 5. 清單 3 的 en_US 語(yǔ)言環(huán)境的輸出 fr_CA 語(yǔ)言環(huán)境的輸出如圖 6 所示: 圖 6. 清單 3 的 fr_CA 語(yǔ)言環(huán)境的輸出 如下所示, <fmt:parseNumber> 操作解析了一個(gè)數值,該數值是通過(guò) value 屬性或該操作的標記體內容以特定于語(yǔ)言環(huán)境的方式提供的,將結果作為 java.lang.Number 類(lèi)的實(shí)例返回。 type 和 pattern 屬性對 <fmt:parseNumber> 和對 <fmt:formatNumber> 起一樣的作用。同樣, parseLocale 、 var 和 scope 屬性對 <fmt:parseNumber> 起與 <fmt:parseDate> 一樣的作用。 <fmt:parseNumber value=" expression" type=" type" pattern=" expression" parseLocale=" expression" integerOnly=" expression" var=" name" scope=" scope"/> <fmt:parseNumber type=" type" pattern=" expression" parseLocale=" expression" integerOnly=" expression" var=" name" scope=" scope"> body content </fmt:parseNumber> | 先前有關(guān) <fmt:parseDate> 的說(shuō)明同樣適用于 <fmt:parseNumber> :解析數據并不是一項非常適合于表示層的任務(wù)。如果解析和驗證數據作為應用程序業(yè)務(wù)邏輯的一部分實(shí)現,那么軟件維護將會(huì )得到簡(jiǎn)化。由于這個(gè)原因,通常建議大家在產(chǎn)品 JSP 頁(yè)面中避免同時(shí)使用 <fmt:parseDate> 和 <fmt:parseNumber> 。 只有 integerOnly 屬性才是 <fmt:parseNumber> 所獨有的。該屬性獲取一個(gè)布爾值,指出是否應當只解析所給值的整數部分。如果該屬性的值為 true ,那么就忽略要被解析的字符串中跟在小數點(diǎn)后面的任何數字。該屬性的缺省值為 false 。 消息標記 在 JSTL 中用 <fmt:message> 標記實(shí)現文本的本地化。該標記允許您從特定于語(yǔ)言環(huán)境的資源束中檢索文本消息并顯示在 JSP 頁(yè)面上。而且,由于該操作利用 java.text.MessageFormat 類(lèi)所提供的功能,所以可以將參數化的值替換進(jìn)這樣的文本消息,以便動(dòng)態(tài)地定制本地化內容。 用于存儲特定于語(yǔ)言環(huán)境消息的資源束采用類(lèi)或特性文件的形式,這些類(lèi)或特性文件符合標準命名約定,在這種命名約定中基名和語(yǔ)言環(huán)境名組合在一起。例如,請研究名為 Greeting.properties 的特性文件,它駐留在我們的 weblog 應用程序的類(lèi)路徑中,該類(lèi)路徑位于與 com.taglib.weblog 包相對應的子目錄中。您可以通過(guò)在同一目錄下指定兩個(gè)新的特性文件,從而將該特性文件所描述的資源束本地化為英語(yǔ)和法語(yǔ),通過(guò)追加相應的語(yǔ)言代碼來(lái)命名。具體而言,這兩個(gè)文件應當分別命名為 Greeting_en.properties 和 Greeting_fr.properties 。如果希望另一個(gè)本地化為加拿大法語(yǔ),您可以引入第三個(gè)特性文件,在其名稱(chēng)中包含了相應的國家或地區代碼(比如 Greeting_fr_CA.properties )。 這些文件都可以定義相同的特性,但是應當將這些特性的值定制成對應的語(yǔ)言或方言。這種方法如清單 4 和清單 5 所示,它們給出了 Greeting_en.properties 和 Greeting_fr.properties 文件的樣本內容。在這些示例中,定義了兩個(gè)已本地化的消息。它們可以通過(guò) com.taglib.weblog.Greeting.greeting 和 com.taglib.weblog.Greeting.return 鍵識別。但是已經(jīng)將與這些鍵相關(guān)聯(lián)的值本地化為文件名中所確定的語(yǔ)言。請注意,出現在 com.taglib.weblog.Greeting.greeting 消息的兩個(gè)值中的 {0} 模式使已參數化的值能夠在內容生成期間動(dòng)態(tài)地插入到消息中。 清單 4. Greeting_en.properties 本地化資源束的內容 com.taglib.weblog.Greeting.greeting=Hello {0}, and welcome to the JSTL Blog. com.taglib.weblog.Greeting.return=Return |
清單 5. Greeting_fr.properties 本地化資源束的內容 com.taglib.weblog.Greeting.greeting=Bonjour {0}, et bienvenue au JSTL Blog. com.taglib.weblog.Greeting.return=Retournez | 用 JSTL 顯示這樣的本地化內容,第一步就是指定資源束。 fmt 庫為完成這一任務(wù)提供了兩個(gè)定制標記: <fmt:setBundle> 和 <fmt:bundle> ,它們的行為和前面介紹的 <fmt:setTimeZone> 和 <fmt:timeZone> 標記相似。 <fmt:setBundle> 操作設置了一個(gè)缺省資源束,供 <fmt:message> 標記在特定作用域內使用,而 <fmt:bundle> 指定了為嵌套在其標記體內容中的全部和任意 <fmt:message> 操作所用的資源束。 下面的代碼片段顯示了 <fmt:setBundle> 標記的語(yǔ)法。 basename 屬性是必需的,它標識了設為缺省值的資源束。請注意, basename 屬性的值不應當包含任何本地化后綴或文件擴展名。清單 4 和清單 5 中給出的示例資源束的基名為 com.taglib.weblog.Greeting 。 <fmt:setBundle basename=" expression" var=" name" scope=" scope"/> | 可選的 scope 屬性指明缺省資源束設置所應用的 JSP 作用域。如果沒(méi)有顯式地指定該屬性,就假定為 page 作用域。 如果指定了可選的 var 屬性,那么將把由 basename 屬性所標識的資源束賦給該屬性值所命名的變量。在這種情況下, scope 屬性指定變量的作用域;沒(méi)有將缺省資源束賦給相應的 JSP 作用域。 您使用 <fmt:bundle> 標記(其語(yǔ)法如下所示)在其標記體內容的作用域內設置缺省資源束。和 <fmt:setBundle> 一樣,只有 basename 屬性才是必需的。您可以使用可選的 prefix 屬性來(lái)為任何嵌套的 <fmt:message> 操作的 key 值指定缺省前綴。 <fmt:bundle basename=" expression" prefix=" expression"> body content </fmt:bundle> | 一旦設置了資源束,真正起到顯示本地化消息作用的是 <fmt:message> 標記。該操作支持兩種不同的語(yǔ)法,這取決于是否需要任何嵌套的 <fmt:param> 標記: <fmt:message key=" expression" bundle=" expression" var=" name" scope=" scope"/> <fmt:message key=" expression" bundle=" expression" var=" name" scope=" scope"> <fmt:param value=" expression"/> ... </fmt:message> | 對于 <fmt:message> ,只有 key 屬性才是必需的。 key 屬性的值用來(lái)確定要顯示在資源束中定義的哪些消息。 您可以使用 bundle 屬性來(lái)指定一個(gè)顯式的資源束,用來(lái)查找由 key 屬性標識的消息。請注意,該屬性的值必須是實(shí)際的資源束,比如當指定 <fmt:setBundle> 操作的 var 屬性時(shí)由該操作所賦予的資源束。 <fmt:message> 的 bundle 屬性不支持字符串值(比如 <fmt:bundle> 和 <fmt:setBundle> 的 basename 屬性)。 如果指定了 <fmt:message> 的 var 屬性,那么將由該標記所生成的文本消息賦給指定的變量,而不是寫(xiě)到 JSP 頁(yè)面。通常,可選的 scope 屬性用來(lái)指定由 var 屬性指定的變量的作用域。 需要的時(shí)候您可以通過(guò)使用 <fmt:param> 標記的 value 屬性來(lái)提供文本消息的參數化值?;蛘?,可以將該值指定為 <fmt:param> 標記體內容,在這種情況下省略該屬性。無(wú)論參數化值模式出現在消息文本中的什么地方,由 <fmt:param> 標記指定的值都將合并到從資源束檢索的消息,這與 java.text.MessageFormat 類(lèi)的行為一致。因為參數化值可以通過(guò)其下標進(jìn)行標識,因此嵌套的 <fmt:param> 標記的順序很重要。 <fmt:bundle> 、 <fmt:message> 和 <fmt:param> 標記的交互作用如清單 6 所示。此處, <fmt:bundle> 標記通過(guò)兩個(gè)嵌套的 <fmt:message> 標記指定了要在其中檢索本地化消息的資源束。這兩個(gè) <fmt:message> 標記的第一個(gè)對應于帶有一個(gè)參數化值的消息,還出現了對應的用于該值的 <fmt:param> 標記。 清單 6. 使用 <fmt:message> 標記顯示本地化消息 <fmt:bundle basename="com.taglib.weblog.Greeting"> <fmt:message key="com.taglib.weblog.Greeting.greeting"> <fmt:param value="${user.fullName}"/> </fmt:message> <br> <br> <center> <a href= "<c:url value=‘/index.jsp‘/>"><fmt:message key="com.taglib.weblog.Greeting.return"/></a> </center> </fmt:bundle> | 清單 7 演示了 <fmt:bundle> 的 prefix 屬性的用法;為 prefix 屬性提供的值在嵌套的 <fmt:message> 操作中自動(dòng)地預先添加到所有 key 值上。因此清單 7 相當于清單 6,只是清單 7 利用了這一便利的特性,使得能夠在兩個(gè) <fmt:message> 標記中使用縮略的 key 值。 清單 7. <fmt:bundle> 的 prefix 屬性對 <fmt:message> 標記的影響 <fmt:bundle basename="com.taglib.weblog.Greeting" prefix="com.taglib.weblog.Greeting."> <fmt:message key="greeting"> <fmt:param value="${user.fullName}"/> </fmt:message> <br> <br> <center> <a href="<c:url value=‘/index.jsp‘/>"><fmt:message key="return"/></a> </center> </fmt:bundle> | 圖 7 和圖 8 演示了正在工作的 fmt 庫與消息相關(guān)的標記,顯示了由清單 7 中代碼所產(chǎn)生的輸出,以及 清單 4和 清單 5中的本地化資源束。圖 7 顯示了當瀏覽器首選項為英語(yǔ)語(yǔ)言環(huán)境時(shí)的結果。 圖 7. 清單 7 中 en_US 語(yǔ)言環(huán)境的輸出 圖 8 顯示了指定法語(yǔ)的語(yǔ)言環(huán)境的輸出。 圖 8. 清單 7 的 fr_CA 語(yǔ)言環(huán)境的輸出
結束語(yǔ) JSTL fmt 庫的定制標記為 JSP 開(kāi)發(fā)人員提供了一種對 Java 平臺的國際化 API 的簡(jiǎn)單訪(fǎng)問(wèn)。文本消息、數值和日期都可以用對語(yǔ)言環(huán)境敏感的方式進(jìn)行顯示,還可以將時(shí)間調整到特定的時(shí)區??梢詮挠脩?hù)的瀏覽器設置自動(dòng)確定特定用戶(hù)的語(yǔ)言環(huán)境,或者由頁(yè)面作者顯式指定特定用戶(hù)的語(yǔ)言環(huán)境。最后,除了提供用于生成和顯示格式化數據的操作之外, fmt 庫還包含了用于解析面向數字和時(shí)間數據的定制標記。
參考資料
關(guān)于作者
|