Web應(yīng)用程序中Resource Bundle技術(shù)概述
背景概述
世界經(jīng)濟(jì)日益全球化的同時(shí),一個(gè)應(yīng)用程序需要在全球范圍內(nèi)使用勢(shì)在必然。傳統(tǒng)的程序設(shè)計(jì)方法將可翻譯信息如菜單按鈕的標(biāo)簽,提示信息,幫助文檔等文字信息硬編碼在程序代碼中,不能很好的適應(yīng)全球化發(fā)展,程序的擴(kuò)展性差,維護(hù)成本高。一個(gè)能支持全球化的應(yīng)用程序,必須實(shí)現(xiàn)單一可執(zhí)行的程序,動(dòng)態(tài)地使用資源(Single Source Single Executable),圖1 是兩種模型的對(duì)比圖。
圖 1. 傳統(tǒng)的程序模型和支持全球化程序模型
對(duì)于一個(gè)能支持全球化的應(yīng)用程序來(lái)說(shuō):
一方面需要考慮的是用戶的語(yǔ)言環(huán)境(我們稱作 Language Locale):這里主要就是指翻譯,在不同的國(guó)家用他們自己的語(yǔ)言正確運(yùn)作軟件,讓客戶感覺(jué)這個(gè)產(chǎn)品是為他們而設(shè)計(jì)的;
另一方面就是用戶的文化環(huán)境(我們稱作 Culture Locale):主要處理的是多元文化的支持,包括貨幣、日歷、時(shí)間、日期、排序、界面方向性(Bi-directional) 等符合各個(gè)國(guó)家自己習(xí)慣的顯示方式。
圖2 概述了如何使得一個(gè)應(yīng)用程序(C/S 或者 B/S)支持全球化和本地化。
圖 2. 全球化應(yīng)用程序
本文結(jié)合項(xiàng)目實(shí)踐,總結(jié)了 web 應(yīng)用程序中 Java,JSP,Dojo 和 HTML 四種不同語(yǔ)言是如何管理 Resource Bundle 的,實(shí)現(xiàn)單一可執(zhí)行程序動(dòng)態(tài)讀取資源文件,從而支持全球化和本地化。主要從三個(gè)方面來(lái)闡述:資源文件存儲(chǔ)和命名規(guī)則;用戶語(yǔ)言區(qū)域信息的讀取;如何取得對(duì)應(yīng)語(yǔ)言的資源文件中的鍵值。
Java 程序中的 Resource Bundle 管理
ResourceBundle 是一個(gè)機(jī)制,主要用來(lái)根據(jù)用戶的語(yǔ)言環(huán)境展示不同的界面文字給用戶,讓用戶感覺(jué)這個(gè)應(yīng)用程序?yàn)槲叶ㄖ啤?/p>
然而 Java 中的 ResourceBundle 是一個(gè)類,包含在標(biāo)準(zhǔn)的 Java 發(fā)行版中。圖3總結(jié)了 Java 程序中 ResourceBundle 的管理機(jī)制。
圖 3. Java 程序 Resource Bundle 管理流程
Java 程序中資源文件的存儲(chǔ)和命名
在一個(gè)多模塊的 Java 應(yīng)用程序中,一般每個(gè)模塊都有自己獨(dú)立的資源文件(也叫 Resource Bundles),Resource Bundle 一般存儲(chǔ)在對(duì)應(yīng)模塊的 src/resources/bundles/java/ 目錄下面。通常的命名規(guī)則是:模塊名_語(yǔ)言_國(guó)家 .properties({moduleName}_{language}_{country}.properties)。對(duì)應(yīng)資源文件中的每一個(gè) key,一般都是小寫字母開(kāi)頭,用下劃線表示這個(gè) key 在程序中的層級(jí)結(jié)構(gòu),并且按照字母順序排序,便于管理和查找,如清單1所示的例子。
清單 1. Java properties 文件示例
- 英文Properties 文件:
 - helloKey=Hello!
 - goodMorningKey=Good Morning!
 - goodEveningKey=Good Evening!
 - 日語(yǔ)Properties 文件:
 - helloKey=\u3053\u3093\u306b\u3061\u306f!
 - goodMorningKey=\u304a\u306f\u3088\u3046!
 - goodEveningKey=\u3053\u3093\u3070\u3093\u306f!
 
Java 程序?qū)Y源文件的回滾機(jī)制:當(dāng)對(duì)應(yīng)翻譯的資源文件不存在時(shí),將使用"默認(rèn)"的資源文件(通常為英文),圖 4 是 Java 資源文件的組織方式。
圖 4. Java 資源文件組織方式
Java 中用戶語(yǔ)言環(huán)境(Locale)和資源文件的讀取
根據(jù)用戶使用的區(qū)域信息來(lái)決定從哪個(gè) Resource Bundle 里面讀取對(duì)應(yīng)的 key 值。Java 語(yǔ)言通過(guò) java.util.Locale 類來(lái)表示區(qū)域,一個(gè) Locale 實(shí)例就代表了一個(gè)特定的區(qū)域。在實(shí)際的項(xiàng)目中,通常將讀取資源文件包裝成一個(gè)類,方便后續(xù)代碼的重用和管理,如清單 2 的示例所示。
清單 2. Java 中 ResourceBundleService 使用
- public class LocalizedPropertyResources extends MessageResources {
 - private static final String MODULE_NAME = "resourceBundle.module.name";
 - public String getProperty(String key, Object... parameters) {
 - Locale locale = getUserLocale();
 - String value = getProperty(key, locale, parameters);
 - Return value;
 - }
 - public String getProperty(String key, Locale locale, Object... parameters) {
 - ResourceBundle resourceBundle = ResourceBundle.getBundle(MODULE_NAME, locale);
 - String value resourceBundle.getString(key, parameters);
 - return value;
 - }
 - }
 
JSP 中的 Resource Bundle 管理
資源文件管理:JSP 是基于 Java 技術(shù),所以 Java 中的 ResourceBundle 機(jī)制也可以應(yīng)用于 JSP,同樣以 .properties 形式存儲(chǔ)資源文件,資源文件的命名規(guī)則、資源文件的存儲(chǔ)結(jié)構(gòu),key 的命名規(guī)則等都可以遵循 Java 程序中的 Resource Bundle 管理機(jī)制。
用戶語(yǔ)言環(huán)境的設(shè)定:HTTP 協(xié)議通過(guò) Accept-Language 請(qǐng)求頭將本地化信息從瀏覽器傳遞至服務(wù)器, JSTL fmt 庫(kù)中的定制標(biāo)記又會(huì)利用這些方法來(lái)自動(dòng)地確定用戶的語(yǔ)言環(huán)境,從而相應(yīng)地調(diào)整它們的輸出。同時(shí)用戶也可以通過(guò) 來(lái)設(shè)置用戶語(yǔ)言環(huán)境。
對(duì)于 JSP 中資源文件的讀取有以下三種情況:
1、使用 fmt 消息標(biāo)簽:JSP 標(biāo)準(zhǔn)標(biāo)簽庫(kù)(JSP Standard Tag Library,JSTL)中的 fmt標(biāo)簽支持通過(guò)資源文件(ResourceBundle)對(duì)文本內(nèi)容進(jìn)行本地化,該功能可以對(duì)一個(gè)特定的語(yǔ)言請(qǐng)求作出相應(yīng)的響應(yīng),它使用了 J2SE 的 ResourceBundle 來(lái)保持各種翻譯過(guò)的語(yǔ)言編碼。另外用來(lái)設(shè)置地區(qū),比如,這等于設(shè)定了語(yǔ)言和國(guó)家代碼。默認(rèn) JPS 將讀取 Accept-Language 信息。還可以指定 ResourceBundle,比如: 。一旦設(shè)定了 locale(地區(qū))或 ResourceBundle,就可以使用 來(lái)把原文進(jìn)行相應(yīng)的轉(zhuǎn)化,同時(shí)還可以使用< fmt:requestEncoding/>來(lái)設(shè)定請(qǐng)求的字符編碼。如清單3 示例所示:
清單 3. JSTL fmt 標(biāo)簽示例
- //myTest.jsp
 - <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
 - <html>
 - <head>
 - <title>JSTL fmt: Localized tag testing</title>
 - </head>
 - <body>
 - <fmt:requestEncoding value="UTF-8" />
 - <fmt:setLocale value="zh_TW"/>
 - <fmt:setTimeZone value= "GMT+8" scope="request"/>
 - <fmt:bundle basename="src.resources.bundles.java.menu ">
 - <fmt:message key="OK"/><br/>
 - <fmt:message key="CANCEL"/><br/>
 - </fmt:bundle>
 - //也可以通過(guò)下面的方式獲取資源文件
 - <fmt:setBundle basename="src.resources.bundles.java.menu" var="resources"/>
 - …
 - <fmt:message key="OK"/><br/>
 - <fmt:message key="CANCEL"/><br/>
 - …
 - </body>
 - </html>
 
2、被 Spring 管理的 JSP,通常 JSP 中有這樣的 taglig"",這種情況下可以使用 Spring message 標(biāo)簽。
- <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
 - <spring:message code="common_button_ok"/>
 
3、對(duì)于由 Struts 操作類提供的JSP,可以使用 Struts bean 消息標(biāo)簽:
- <%@ taglib uri="/tags/struts-bean" prefix="bean" %>
 - <bean:message key=" common_button_ok "/>
 
Dojo 中的 Resource Bundle 管理
Dojo 是一個(gè) JavaScript 庫(kù),提供了一個(gè)有用的工具來(lái)創(chuàng)建豐富的 Web 客戶端界面。同時(shí) Dojo 也提供了 API 支持全球化功能,如翻譯資源包,函數(shù)的格式化和解析日期、數(shù)字和貨幣,Dojo 對(duì)全球化的支持主要基于 CLDR (Common Locale Data Repository) 和 ICU 的使用。 這一小節(jié)總結(jié)了 Dojo 中資源文件的管理,用戶區(qū)域信息(locale)和資源文件的讀取,如圖5所示。
圖 5. Dojo 中資源文件管理
Dojo 中資源文件的存儲(chǔ)和命名
UTF-8 是 Dojo 程序編碼的先決條件,Dojo 腳本一般將使用 HTML,JSP,Servlet,js等編寫,所以這些文件必須以 UTF-8 編碼。Dojo 用 JSON 格式來(lái)組織資源文件,首先在 /nls 目錄下有一個(gè)主資源文件(一般是英文),主資源文件中定義了支持的區(qū)域,區(qū)域的值設(shè)置為 true 或者 false,如清單4所示。
清單 4. Dojo 主資源文件的定義
- define({
 - root: {
 - OK: "Ok",
 - CANCEL: "Cancel",
 - OK_CANCEL: "${OK}, ${CANCEL}"
 - },
 - "ko": true,
 - "ja": true,
 - "ru": false,
 - "zh-cn": true
 - });
 
翻譯的資源文件應(yīng)放在/ nls / 目錄中,目錄的命名需要符合下面的規(guī)范:
- 目錄的名稱必須要全部小寫
 - 用橫線(而不是下劃線)分割語(yǔ)言和國(guó)家,比如 zh-cn,zh-tw 等,如清單5所示。
 
清單 5. 翻譯資源文件的目錄結(jié)構(gòu)
- /src/web/js/nls/menu.js ... 主資源文件,用于默認(rèn)的消息顯示
 - /src/web/js/nls/ko/menu.js ... 韓語(yǔ)的翻譯文件
 - /ja/menu.js ... 日語(yǔ)的翻譯文件
 - /ru/menu.js ...俄語(yǔ)的翻譯文件
 - /zh-cn/menu.js ...簡(jiǎn)中的翻譯文件
 - ...
 
Dojo 資源文件的回滾機(jī)制為:在 / nls 目錄的根目錄下如果檢測(cè)到區(qū)域設(shè)置,但 nls 目錄中不存在特定于區(qū)域設(shè)置的資源,則將使用主包;當(dāng)主資源文件中某個(gè)語(yǔ)言設(shè)置為 false 時(shí),即使該語(yǔ)言的資源文件存在,也將使用主包。
Dojo 中用戶語(yǔ)言環(huán)境(Locale)和資源文件的讀取
Dojo 和 Java 以相同的方式支持用戶區(qū)域信息,但 Dojo 和 Java 實(shí)現(xiàn)之間存在微小差異。
- "Dojo 和 Java 對(duì)區(qū)域設(shè)置命名約定有一些區(qū)別,Dojo 使用" - "(連字符)作為連接語(yǔ)言代碼,國(guó)家代碼和變體的分隔符,而 Java 使用"_"(下劃線)。例如,Java 中的 "zh_CN" 與 Dojo 中的 "zh-cn" 相似。
 - 像 Java 中的默認(rèn)用戶語(yǔ)言區(qū)域一樣,Dojo 有一個(gè)全局變量 dojo.locale 用來(lái)存儲(chǔ)默認(rèn) locale 值,但是我們不能直接修改 dojo.locale,可以通過(guò) dojoConfig.locale 來(lái)對(duì) dojo.locale 做初始化工作。
 - 如果 dojoConfig.locale 未定義,Dojo 使用瀏覽器的顯示語(yǔ)言作為用戶區(qū)域設(shè)置。另外 dojoConfig.locale 的設(shè)定需要在加載 dojo.js 之前,這樣設(shè)置的 dojoConfig.locale 才會(huì)起效果。如清單 6 是一個(gè)示例將用戶的 locale 信息保存在 sessionScope,然后傳遞給 dojoConfig。
 
清單 6. 將用戶 locale 保存在 sessionScope 傳遞給 dojoConfig
- var dojoConfig = {
 - async: 'sync',
 - baseUrl: '',
 - locale: '${sessionScope.userLanguageLocale}',
 - bindEncoding: "utf-8",
 - …
 - };
 
userLanguageLocale:是應(yīng)用程序中用戶所設(shè)定的 locale,每個(gè)應(yīng)用程序可能不太一樣。
Dojo 中有兩種方式讀取資源文件:
方式 1:又叫 AMD 方式,通過(guò) dojo/i18n! 這個(gè)插件加載資源文件,首先檢測(cè) dojoConifg.locale 是否設(shè)置,如果設(shè)置了就讀取 dojoConfig.locale,否則就會(huì)使用瀏覽器的顯示語(yǔ)言讀取對(duì)應(yīng)的翻譯資源文件。清單 7 是 dojo/i18n! 的使用示例。
清單 7. Dojo/i18n!使用示例
- ...
 - <script src="dojo-release-1.9.1/dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: true></script>
 - <script>
 - require(["dojo/string", "dojo/i18n!js/nls/menu"],
 - function(string, resources){
 - var strOk = resources.OK;
 - alert(strOk);
 - });
 - </script>
 - ...
 
方式 2: 使用 dojo.requireLocalization 加載資源文件,dojo.i18n.getLocalization 讀取資源文件,這個(gè)是舊的資源加載方式,新的 Dojo 版本都將采用 AMD 的方式,不過(guò)這種方式用戶可以指定 locale 信息,缺省 locale 的時(shí)候,采用和 AMD 同樣的 locale 解析方式。清單8 是一個(gè)示例。
清單 8. Dojo. requireLocalization 使用示例
- <script src="dojo-release-1.9.1/dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: true></script>
 - <script>
 - dojo.require("dojo.i18n");
 - dojo.requireLocalization("js", "menu");
 - var resources = dojo.i18n.getLocalization("js", "menu", locale);
 - //或者省去 locale 參數(shù),var resources = dojo.i18n.getLocalization("js", "menu")
 - alert(resources.OK);
 - </script>
 - ...
 
HTML 中的 Resource Bundle 管理
HTML 中的硬編碼(Hard Code)通常分為兩種情況:一種情況是 HTML 頁(yè)面不涉及任何編程邏輯,這時(shí)候可以翻譯整個(gè) HTML 文件(這種方式在開(kāi)發(fā)過(guò)程中不需要特殊處理),或者將 HTML 變更為 JSP,這樣就可以利用 JSP 中 fmt 標(biāo)簽將 Hard Code 提取出來(lái),如清單 9 示例所示。
清單 9. HTML 更改為 JSP
- //Index.html
 - <html>
 - <head>
 - <script language="JavaScript">
 - window.location = "/login.do";
 - </script>
 - <title> Welcome Page </title>
 - </head>
 - <body>
 - …
 - </body>
 - </html>
 - //將 Index.html 更改為 Index.jsp,這樣就可以利用 JSP 中 fmt 標(biāo)簽
 - <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 - <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
 - <html>
 - <head>
 - <script language="JavaScript">
 - …
 - </script>
 - <title> <fmt:message key="common_welcome" /></ title>
 - </head>
 - <body>
 - …
 - </body>
 - </html>
 - //test.properties
 - Common_welcome=Welcome Page
 - …
 
//將 Index.html 更改為 Index.jsp,這樣就可以利用 JSP 中 fmt 標(biāo)簽
另外一種情況是 HTML 作為 Dojo widget 模板文件,這種情況下可以使用 dojo API 的 i18n 庫(kù)來(lái)處理硬編碼消息,其處理方式和 Dojo 很類似,模板 HTML 文件中使用的 Key 需要在 Widget 的 js 文件中定義和獲取。下面將通過(guò)示例介紹 HTML 作為 Dojo 模板文件時(shí),如何抽取當(dāng)中的 hard code 信息。通常分三個(gè)步驟完成模板 HTML 文件中的字符串抽取。
***步: 將 HTML 中的字符串放到一個(gè)資源文件如 message.js 中,資源文件的組織方式和 Dojo 是一樣的;
第二步: 在 Dojo widget 的 js 文件中使用 Dojo Resource Bundle 獲取資源文件,定義變量獲取對(duì)應(yīng)的Key值;
第三步: 在HTML模板文件中使用 Dojo .js 文件中定義的變量。如清單 10 所示。
清單 10. HTML 作為 Dojo Widget 模板
- Test.html
 - <div dojoAttachPoint="testNode" style="display:inline">
 - <table style="position: relative; margin: 20px 0 0 120px; padding: 0;">
 - <tr>
 - <td style="text-align: right;">Username:</td> //原來(lái)的 hard code
 - <td style="text-align: right;">${usernameLabel}</td> //Resource out 之后的變量引用
 - ….
 - </tr>
 - </div>
 - Test.js
 - dojo.requireLocalization("js", "message");
 - dojo.declare("Test") {
 - templatePath: dojo.moduleUrl("common", " /html/Test.html"),
 - usernameLabel: "",
 - postMixInProperties: function(){
 - this.inherited(arguments);
 - var resources = dojo.i18n.getLocalization("js", "message");
 - this.usernameLabel = resources.labels.username; //獲取資源文件
 - }
 - }
 
清單 11. HTML 中特殊硬編碼處理
- // Hard Code
 - <html>
 - <body>
 - <select id="Product">
 - <option>Is Not</option>
 - <option>Is</option>
 - </select>
 - </body>
 - </html>
 - //處理后的 HTML
 - <html>
 - <body>
 - <select id="Product">
 - <option value="Is Not"> ${ Label.IS_NOT}</option>
 - <option value="Is">${Label.IS}</option>
 - </select>
 - </body>
 - </html>
 
總結(jié)
本文總結(jié)了如何存儲(chǔ) web 應(yīng)用程序中使用不同框架時(shí)的資源文件及其命名規(guī)則,如何取得資源文件中的鍵值。希望這篇文章能為正在開(kāi)發(fā)國(guó)際化 Web 應(yīng)用程序的讀者提供一定的參考價(jià)值。
























 
 
 

 
 
 
 