使用 Ajax 实现本地化后的客户端消息验证
?來源:http://www.ibm.com/developerworks/cn/web/wa-aj-local/?S_TACT=105AGX52&S_CMP=tec-csdn#download
?
級別: 中級
Ritesh Prasad, Senior Staff Software Engineer, IBM
2009 年 3 月 19 日
您 在創(chuàng)建一個適合全球用戶的 Web 應(yīng)用程序時,有兩點需要考慮:國際化/本地化了的頁面內(nèi)容及用戶輸入驗證和消息顯示。用資源包(特定于本地語言環(huán)境的屬性文件)在服務(wù)器端構(gòu)建一個頁面的 國際化版本很容易,但如果驗證是在客戶端進行的,那么顯示國際化了的驗證消息就非常困難了。Asynchronous JavaScript + XML (Ajax) 是可以簡化此項工作的一個不錯選擇。本文將介紹如何聯(lián)合使用 Ajax 與資源包來使國際化/本地化了的客戶端驗證消息處理的過程變得較為簡單。簡介
在構(gòu)建一個能影響全球用戶的 Web 應(yīng)用程序時,有兩點需要考慮。第一點是需要呈現(xiàn)本地化后的頁面內(nèi)容,第二點是驗證用戶輸入以及本地化后的驗證消息顯示。
使用資源包(特定于本地語言環(huán)境的屬性文件)在服務(wù)器端構(gòu)建此頁面的本地化版本很容易。同樣地,也可以使用服務(wù)器端驗證來顯示本地化后的驗證消息。對于國際化而言,有很多具備良好支持的現(xiàn)成框架可用,比如 Jakarta Struts、Spring、Tapestry 和 Freemarker。不過,在幾乎所有的這類框架內(nèi),都缺少對在客戶端驗證本地化消息的現(xiàn)成支持。
如 果驗證是在客戶端進行的,將很難顯示本地化后的驗證消息。通過在構(gòu)建頁面時提前處理整個頁面(包括靜態(tài)內(nèi)容和必要的 JavaScript 驗證消息)或從特定于本地語言環(huán)境的資源包解析出消息鍵,可以顯示這些消息。不過,上述方式具有一個暗含的限制:整個 JavaScript 驗證邏輯都應(yīng)在 JavaServer Page (JSP) 本身內(nèi)編寫以便基于 Java? 的消息鍵解析邏輯可被重用。不要忘記,JavaScript 通常都是由頁面設(shè)計人員編寫的,而這些設(shè)計人員并不一定同時也掌握 Java 的開發(fā)技術(shù)。混合 Java 代碼和 JavaScript 可能會讓 Web 應(yīng)用程序的開發(fā)和維護復(fù)雜化。
聯(lián)合使用 Ajax 和資源包是另一種可以簡化工作的方式。它讓您能將此驗證 JavaScript 移到另一個文件,而不是 JSP。并且,只對需要的消息鍵進行解析,而不是像使用預(yù)先構(gòu)造的本地化版本方法一樣,對所有消息鍵進行解析。
本文描述了如何聯(lián)合使用 Ajax 和資源包來簡化本地化后的客戶端驗證消息處理。我將側(cè)重于使用 Ajax 的強大功能,而不會涉及現(xiàn)成框架的復(fù)雜性。本文所介紹的方式非常適合于需要快速響應(yīng)的 Web 2.0 應(yīng)用程序,比如動態(tài)跟蹤用戶動作。
在本文中,我不會過多涉及 JSP 頁面內(nèi)靜態(tài) HTML 內(nèi)容的本地化。本文所側(cè)重的是聯(lián)合使用 Ajax 和資源包來實現(xiàn)本地化后的客戶端驗證消息處理。不過,用來在服務(wù)器端解析消息鍵的 Java 實用工具也可用于本地化 JSP 頁面內(nèi)的靜態(tài) HTML 內(nèi)容。
?
|
關(guān)鍵字
國際化(Internationalization) — 指一種應(yīng)用程序設(shè)計過程,使應(yīng)用程序不需要進行重大修改就可適用于各種語言和地區(qū)。有時,國際化這一術(shù)語常被縮寫為 i18n,因為在首字母 “i” 和最后一個字母 “n” 之間共有 18 個字母。
本地化(Localization) — 針對某個特定地區(qū)或語言調(diào)整軟件的過程,添加特定于本地語言環(huán)境的組件和經(jīng)過翻譯的文本。本地化這一術(shù)語常被縮寫為 l10n,這是因為在字母 “l(fā)” 和 “n” 之間共有 10 個字母。本地化的首要任務(wù)是翻譯用戶界面元素和文檔。本地化所涉及的不僅是互換語言,而且還會涉及更改相關(guān)元素,比如數(shù)字、日期、貨幣等的顯示。其他類型 的數(shù)據(jù)(比如聲音和圖像)若對文化敏感,可能也都需要進行本地化。應(yīng)用程序的國際化越好,越容易針對特定的語言和字符編碼模式對其進行本地化。
Ajax 是一種技術(shù),用來創(chuàng)建更好、更快且更為交互的 Web 應(yīng)用程序。借助 Ajax,JavaScript 代碼可使用 XMLHttpRequest 對象與服務(wù)器直接通信。有了此對象,JavaScript 代碼可以在不重新裝載頁面的情況下與 Web 服務(wù)器進行數(shù)據(jù)交換。
?
|
開發(fā)一個國際化的 Web 應(yīng)用程序
只要是針對全球(地理位置分散)用戶開發(fā) Web 應(yīng)用程序,就要尊重和考慮用戶對本地語言和文化的偏好。在 Web 應(yīng)用程序內(nèi)提供對國際化的支持時,需要考慮以下幾點。
- 識別出文化敏感數(shù)據(jù)
- 在資源包中分離出可翻譯文本
- 處理混合消息
- 格式化數(shù)字及貨幣
- 格式化日期及時間
- 使用 Unicode 字符屬性
- 正確地對比字符串
- 將非 Unicode 文本轉(zhuǎn)換為 Unicode
?
|
支持本地化了的客戶端消息處理的高級步驟
在構(gòu)建具有國際化支持和本地化了的客戶端消息處理機制的 Web 應(yīng)用程序時,下列幾點是必須要滿足的。
- 所有本地化了的頁面都應(yīng)支持 UTF-8 字符集(頁面編碼)。
- 所有客戶端消息都應(yīng)使用特定于客戶機本地語言環(huán)境的資源包從服務(wù)器端獲取。
- 資源包應(yīng)存儲鍵值對。數(shù)值要使用 Unicode 字符。
- 使用 Ajax 將來自客戶端 JavaScript 的請求發(fā)送給一個服務(wù)器端資源,該資源會解析此請求以針對此鍵獲得特定于客戶機本地語言環(huán)境的消息。
- 解析所獲取的消息并正確顯示。
?
|
使用 Ajax 實現(xiàn)本地化后的客戶端消息處理的一種實用方式
在本節(jié)中,我們會帶您親歷構(gòu)建基本結(jié)構(gòu)所需執(zhí)行的各種操作,并會用一個示例頁面對它進測試,然后會在您的 Web 應(yīng)用程序中反復(fù)使用。
首先,要準備資源包屬性文件。這些 *.properties 文件保存在該項目的類路徑中,文件內(nèi)包含鍵-值對,并可用作資源包來獲得特定于本地語言環(huán)境的運行時解析的驗證消息。所有這些 *.properties 文件都應(yīng)符合 Java 國際化標準命名約定。
清單 1. 包屬性文件
| # org/rpd/i18n/bundles/Messages.properties - Resource Bundle for default locale # The sample message key-value pairs... error.loginid.required = User Name is Mandatory. error.useremail.required = Email Id is Mandatory. error.password.required = Password is required. error.password.length = Password Length should not be less than six(6)character. error.confirmpassword.required = Confirm Password is required. error.passwordconfirm.match = Password and Confirm Password does not match. error.firstName.required = First Name is required. error.lastName.required = Last Name is required. |
?
接下來,創(chuàng)建一個 Java 類來管理這些資源包。這個類(比如說 ResourceManager.java)提供了將特定于本地語言環(huán)境的資源包加載到緩存的函數(shù)。借助它,還能根據(jù)給定消息鍵和本地語言環(huán)境檢索消息值。
清單 2. ResourceManager.java
| package org.rpd.i18n.common; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; public class ResourceManager { // The name and location of the resource bundle. private final String mMessageBundle = "org/rpd/i18n/bundles/Messages"; // The loaded message cache... private Map<Locale, ResourceBundle> mResourceCache = null; // Private instance variable. private static ResourceManager mManager = null; // default private constructor. private ResourceManager(){ mResourceCache = new HashMap<Locale, ResourceBundle>(); } // Get the locale-specific bundle from cache. // First load to the cache if not already loaded public ResourceBundle getBundle(Locale locale){ if(locale == null){ locale = Locale.getDefault(); } if(mResourceCache.containsKey(locale) == false){ ResourceBundle bundle = ResourceBundle.getBundle(mMessageBundle, locale); mResourceCache.put(locale, bundle); } return mResourceCache.get(locale); } // Thread safe Singleton pattern implementation... private static ResourceManager getInstance(){ synchronized (ResourceManager.class) { if(mManager == null){ mManager = new ResourceManager(); } } return mManager; } // Get the message for the key using default locale. public static String getMessage(String key){ return getMessage(key, null); } // Get the message for the key and specified locale. public static String getMessage(String key, Locale locale){ try{ return getInstance().getBundle(locale).getString(key); }catch(Exception e){ return ""; } } } |
?
創(chuàng)建一個 JSP 文件來處理 Ajax 請求。這個 JSP 文件(例如,MessageResolver.jsp)負責處理 Ajax 請求以解析這些消息鍵(對這個 JSP 而言,就是請求參數(shù),message-key)。它使用由 ResourceManager 類提供的消息檢索特性來解析作為請求參數(shù)進入這個 JSP 的每個消息鍵。
清單 3. MessageResolver.jsp
| <%@page import="org.rpd.i18n.common.ResourceManager"%> <% // The name of the request parameter representing the input // "HTML Element - Message Key" combination. final String REQ_ID = "message-key"; // Message prefix to be used in output string. final String MSG_PREFIX = "begin::"; // Message suffix to be used in output string. final String MSG_SUFFIX = "::end"; // The standard "HTML Element - Message Key" delimiter. final String ELEMENT_KEY_DELIM = ","; // The "HTML Element - Localize Message" delimiter to be used in // output string. final String KEY_VAL_DELIM = "=="; // Find the request parameter containing the "HTML Element - Message Key" // combination String. String keysArr = request.getParameter(REQ_ID); // If the desired request parameter doesn't exist, it means request is invalid. if(keysArr == null){ out.println("Invalid message key"); } else { // Split the input using the element - key delimiter (ELEMENT_KEY_DELIM). String keys[] = keysArr.split(ELEMENT_KEY_DELIM); // Check if the number of tuples is even. If not, it means the input is incorrect. if((keys.length%2) != 0){ out.println("Improper elem-key combination: " + keysArr); } else { // Iterate through each elem-key combination. for(int i=0; i < keys.length; i = i + 2){ // Retrieve the localized message against the key. Prepare the result string // as follows: // Message Prefix (followed by) HTML Element key (followed by) Key-value // delimiter // (followed by) Localized value (followed by) Message suffix. out.println(MSG_PREFIX + keys[i].trim() + KEY_VAL_DELIM + ResourceManager.getMessage(keys[i+1].trim(), request.getLocale()) + MSG_SUFFIX); } } } %> |
?
準 備 JavaScript 實用程序來管理 Ajax 調(diào)用。應(yīng)該同時編寫一組 JavaScript 例程(例如,MessageDisplay.js),這有助于為 MessageResolver.jsp 的 Ajax 調(diào)用創(chuàng)建正確的輸入。JSP 需要的是一個請求參數(shù),這個請求參數(shù)必須是一個由逗號分隔的字符串組。字符串由 HTML Element(占位符)Identifier 和消息鍵對組成,并且這個消息鍵將在解析之后顯示在該 HTML 元素內(nèi)。這里也會涉及到一個例程(displayMessage() 方法),用它來將解析后的消息正確地顯示給相關(guān)的 HTML 元素。
清單 4. MessageDisplay.js
| var xmlHttp = null; var msgKeys = new Array(); var msgPrefix = "begin::"; var msgSuffix = "::end"; var msgDelimiter = "=="; var jspURL = "MessageResolver.jsp"; // reset msgKeys array. function resetMsgKeys(){ msgKeys = new Array(); } // Adding to the array of keys function addMsgKey(elemId, msgKey) { msgKeys[msgKeys.length] = new Array(); msgKeys[msgKeys.length - 1][0] = elemId; msgKeys[msgKeys.length - 1][1] = msgKey; document.getElementById(elemId).innerText = ""; } //Different browsers use different methods to create XMLHttpRequest objects. function getXmlHttpObject() { xmlHttp = null; try { // Firefox, Opera 8.0+, Safari xmlHttp = new XMLHttpRequest(); } catch (e) { // Internet Explorer try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { alert("Your browser does not support Ajax!"); return false; } } } return xmlHttp; } //To pass on the key to the JSP function getMessage(key) { // checking for browser support xmlHttp = getXmlHttpObject(); if (xmlHttp == null) { alert("Browser does not support Ajax"); return false; } var url = jspURL + "?message-key=" + key; xmlHttp.onreadystatechange = displayMessage; xmlHttp.open("GET", url, true); xmlHttp.send(null); } //Response generated against the request function displayMessage() { if (xmlHttp.readyState == 4 || xmlHttp.readyState == "complete") { var localizedMsg = xmlHttp.responseText; while (true) { var begInd = localizedMsg.indexOf(msgPrefix); var endInd = localizedMsg.indexOf(msgSuffix); if (begInd < 0 || endInd < 0) { break; } var msg = localizedMsg.substring((begInd + msgPrefix.length), endInd); var elemVal = msg.split(msgDelimiter); document.getElementById(elemVal[0]).innerText = document.getElementById(elemVal[0]).innerText + "**" + elemVal[1]; localizedMsg = localizedMsg.substring(endInd + msgSuffix.length); } } } |
?
創(chuàng) 建一個用于測試的客戶機 JSP/HTML。這個頁面(例如,NewUserRegistration.js)有一些輸入字段,這些字段的數(shù)據(jù)需要用 JavaScript 在客戶端進行驗證。但是由于用戶很有可能在輸入數(shù)據(jù)時出錯,因此,驗證邏輯應(yīng)能立即用恰當?shù)南⑻崾居脩簟S捎诖蓑炞C消息應(yīng)該被本地化,因而驗證邏輯需要 使用之前準備好的 Ajax 實用程序來針對各類有效性問題顯示特定于本地語言環(huán)境的消息。
清單 5. NewUserRegistration.jsp
| <%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>New User Registration</title> <script type="text/javascript" src="ValidateNewUser.js"> </script> <script type="text/javascript" src="MessageDisplay.js"> </script> </head> <body> <form οnsubmit="return validateNewUser();" name="frmRegistration" id="frmRegistration" action="#" method="post"> <table border="0" width="100%"> <tbody> <tr> <td align="right" nowrap="nowrap"> Email ID</td> <td align="center">:</td> <td><input type="text" name="emailId" id="emailId" size="20"></td> <td width="100%"><p id="emailErrMsg"></p></td> </tr> <tr> <td align="right" nowrap="nowrap">User Name</td> <td align="center">:</td> <td><input type="text" name="loginId" id="loginId" size="20"></td> <td width="100%"><p id="loginErrMsg"></p></td> </tr> <tr> <td align="right" nowrap="nowrap">Password</td> <td align="center">:</td> <td><input type="password" name="password" id="password" size="20" ></td> <td width="100%"><p id="passwordErrMsg"></p></td> </tr> <tr> <td align="right" nowrap="nowrap">Confirm Password</td> <td align="center">:</td> <td><input type="password" name="confirmPassword" id="confirmPassword" size="20"></td> <td width="100%"><p id="confirmpassErrMsg"></p></td> </tr> <tr> <td align="right" nowrap="nowrap">First Name</td> <td align="center">:</td> <td><input type="text" name="firstName" id="firstName" size="20"></td> <td width="100%"><p id="firstNameErrMsg"></p></td> </tr> <tr> <td align="right" nowrap="nowrap">Last Name</td> <td align="center">:</td> <td><input type="text" name="lastName" id="lastName" size="20"></td> <td width="100%"><p id="lastNameErrMsg"></p></td> </tr> <tr> <td colspan="4" align="center"> <input type="submit" name="cmdSubmit" value="Submit"> </td> </tr> </tbody> </table> </form> </body> </html> |
?
在表單字段上應(yīng)用驗證條件。這個示例 JSP 具有一個表單,需要在此表單進行必要的輸入以便完成用戶的系統(tǒng)注冊過程。此外,還需要滿足一些驗證條件才能對它進行提交:
/
創(chuàng)建 JavaScript 例程來驗證表單。負責對各字段執(zhí)行驗證的 JavaScript 例程(例如,ValidateNewUser.js)要在 HTML 表單發(fā)生 onsubmit 事件時調(diào)用。它還會準備恰當?shù)南㈡I和占位符 HTML Element ID 對,如需要,它們將被用作此 Ajax 調(diào)用內(nèi)的輸入以便解析和顯示本地化了的消息。如果此表單通過了驗證測試,它就會被提交給目標操作。
清單 6. ValidateNewUser.js
| function validateNewUser() { resetMsgKeys(); document.getElementById("loginErrMsg").innerText = ""; document.getElementById("emailErrMsg").innerText = ""; document.getElementById("passwordErrMsg").innerText = ""; document.getElementById("confirmpassErrMsg").innerText = ""; document.getElementById("firstNameErrMsg").innerText = ""; document.getElementById("lastNameErrMsg").innerText = ""; if (document.frmRegistration.loginId.value == "") { addMsgKey("loginErrMsg", "error.loginid.required"); } if (document.frmRegistration.emailId.value == "") { addMsgKey("emailErrMsg", "error.useremail.required"); if (document.frmRegistration.password.value == "") { addMsgKey("passwordErrMsg", "error.password.required"); } else { if (document.frmRegistration.password.value.length < "6") { addMsgKey("passwordErrMsg", "error.password.length"); } } if (document.frmRegistration.confirmPassword.value == "") { addMsgKey("confirmpassErrMsg", "error.confirmPassword.required"); } else { if (document.frmRegistration.confirmPassword.value != document.frmRegistration.password.value) { addMsgKey("confirmpassErrMsg", "error.passwordconfirm.match"); } } if (document.frmRegistration.firstName.value == "") { addMsgKey("firstNameErrMsg", "error.firstName.required"); } if (document.frmRegistration.lastName.value == "") { addMsgKey("lastNameErrMsg", "error.lastName.required"); } if (msgKeys.length > 0) { getMessage(msgKeys); return false; } return true; } |
?
部署與測試
將這個應(yīng)用程序部署到任意一個 Web 服務(wù)器(例如,Tomcat)上,并啟動此服務(wù)器實例。然后,打開一個瀏覽器實例(例如,Internet Explorer),找到客戶機內(nèi)設(shè)置的首選語言。它應(yīng)該是 English,如圖 1 所示(單擊 這里 可以查看放大圖)。
圖 1. 語言首選
瀏覽此 URL,尋找用戶注冊頁面(NewUserRegistration.jsp),如圖 2 所示(單擊 這里 可以查看放大圖)。
圖 2. 用戶注冊
在除 User Name 字段外的所有字段輸入值。User Name 字段空著。按下 Submit。應(yīng)該會看到如圖 3 所示的 User Name 行旁邊的驗證消息(英語)(單擊 這里 可以查看放大圖)。
圖 3. 輸入用于驗證的值
更改瀏覽器(Internet Explorer)中的語言。刪除 English 并加入 Hindi。
圖 4. 將首選語言更改為 Hindi
關(guān)閉瀏覽器的這個實例并打開一個新實例。
再次瀏覽同一個(用戶注冊)頁面。正確地輸入所有字段,“User Name” 字段仍舊留空。應(yīng)該會看到 “User Name” 行旁邊顯示的驗證消息(印度語),如圖 5 所示(單擊 這里 可以查看放大圖)。
圖 5. 更新后的驗證消息
|
步驟總結(jié)
?
|
結(jié)束語
現(xiàn)在,您應(yīng)該了解了用 Ajax 顯示本地化后的客戶端驗證消息是多么簡單。盡管我已經(jīng)盡可能簡化了設(shè)置,盡可能實現(xiàn)開箱即用,但本方式還是可以通過一些現(xiàn)成的系統(tǒng)和技術(shù)得到進一步改進。 例如,如果您的 Web 應(yīng)用程序使用 Jakarta Struts 或其他類似框架,而這些框架均能提供現(xiàn)成的、更完善的資源包的使用方法,那么您盡可以使用這些框架,而不是自己創(chuàng)建的 ResourceManager.java 實用程序類。類似地,JavaScript(和 Ajax)例程也可以編寫得更為完善和全面,從而滿足您自己的 Web 應(yīng)用程序的標準和要求。
|
下載
| LocalizationExample.zip | 14KB | HTTP |
| 關(guān)于下載方法的信息 | ||||
參考資料
- 閱讀文章 “XML 國際化簡介”了解用 XML 進行國際化的各種技術(shù)。
- 閱讀 “Unicode and software internationalization”了解 Unicode 技術(shù)。
- 這個 Ajax 教程 給出了 Ajax 技術(shù)的基本概念。
- 了解 JavaScript 是如何支持 Web 內(nèi)容的國際化的。
- 這是有關(guān) 嘗試 Struts 國際化 的優(yōu)秀教程。
- Dojo and Internationalization 文檔會給您打下一個很好的基礎(chǔ)。
- 了解有關(guān) Java Internationalization and Localization with Resource Bundles 的信息。
關(guān)于作者
| Ritesh Prasad 是位于印度 Bangalore 的 Rational Application Developer 開發(fā)團隊的一員。他具有 Indian Institute of Technology 的 Industrial Engineering and Management 專業(yè)的技術(shù)碩士學位并曾先后在幾個 Web 應(yīng)用程序開發(fā)項目中擔任過 Java/J2EE 程序員。Ritesh 還從事過基于 J2EE 的 CRM Framework 方面的工作,比如 Chordiant 和 Eiphany,并有幸成為了 IBM India 接觸 Epiphany 的第一人。在工作之外,Ritesh 喜歡與家人在一起和欣賞音樂。 | ||
總結(jié)
以上是生活随笔為你收集整理的使用 Ajax 实现本地化后的客户端消息验证的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mac OS X怎么进行优化
- 下一篇: Ext---CheckBoxGroup的