• home
  • forum
  • my
  • kt
  • download
  • Ajax Localized client-side validation messaging

    Author: 2009-04-17 08:06:50 From:

    When building a Web application that caters to users across the globe, there are two points to consider: internationalized/localized page content and validation of user inputs and message displays. While you can easily build an internationalized version of the page using resource bundles (locale-specific property files) on the server side, it is very difficult to display internationalized validation messages when the validation is being done at the client side. Using Asynchronous JavaScript + XML (Ajax) is one option to make your life easier. This article discusses using Ajax and resource bundles together to make the process of internationalized/localized client-side validation messaging a little easier.

    Introduction

    There are two points to consider when building a Web application that affects users across the globe. The first point is the need to present localized page content, and the second point is the validation of user inputs and localized validation message display.

    You can easily build a localized version of the page using resource bundles (locale-specific property files) on the server side. Similarly, you can use server-side validation to display localized validation messages. For internationalization, there are many ready-made frameworks with fancy support, such as Jakarta Struts, Spring, Tapestry, and Freemarker. However, in almost all such frameworks, there is a lack of ready-to-use support for localized messaging with client-side validation.

    It is very difficult to display localized validation messages when the validation is being done at the client side. You could display the messages by prefabricating the entire page, including static contents and necessary JavaScript validation messages, when you're constructing the page or by resolving the message-keys from locale-specific resource bundles. However, these approaches have an implicit limitation: The entire JavaScript validation logic should be written in the JavaServer Page (JSP) itself so that the Java™-based message key resolution logic can be reused. Don't forget that JavaScript is usually written by page designers who may not be Java developers. Mixing Java code and JavaScript can complicate the development and maintenance of the Web application

    Using Ajax with resource bundles is another option that can make your life easier. This approach allows you to move the validation JavaScript to a file other than the JSP. And, only the needed message-keys are resolved on demand, as compared to all message-keys when using the prefabricated localized version approach.

    This article describes how to use Ajax and resource bundles together to make localized client-side validation messaging a little easier. I focus on using the power of Ajax without the added complication of a ready-made framework. The article's approach is ideal for Web 2.0 applications that require a very quick validation response, such as tracking the user activities on the go.

    I do not cover the localization of static HTML content in the JSP pages. This article focuses on the use of Ajax and resource bundles for localized client-side validation messaging. However, the same Java utility used to resolve message-keys on the server side can be used to localize the static HTML contents in the JSP pages.



    Back to top


    Keywords

    Internationalization — the process of designing an application so that it can be adapted to various languages and regions without engineering changes. Sometimes the term internationalization is abbreviated as i18n, because there are 18 letters between the first "i" and the last "n."

    Localization — the process of adapting software for a specific region or language by adding locale-specific components and translated text. The term localization is often abbreviated as l10n, because there are 10 letters between the "l" and the "n." The primary task of localization is translating user interface elements and documentation. Localization involves not only changing the language interaction, but also other relevant elements such as display of numbers, dates, currency, and so on. Other types of data, such as sounds and images, may require localization if they are culturally sensitive. The better internationalized an application is, the easier it is to localize it for a particular language and character-encoding scheme.

    Ajax — a technique for creating better, faster, and more interactive Web applications. With Ajax, the JavaScript code uses the XMLHttpRequest object to communicate directly with the server. With this object, the JavaScript code can trade data with a Web server, without reloading the page.



    Back to top


    Developing an internationalized Web application

    Whenever there is a need to develop a Web application that targets a global (geographically distributed) audience, proper respect and care is required for sensitivity toward the audience's preferred language and culture. The following points should be addressed when supporting internationalization in Web applications.

    • Identify culturally dependent data
    • Isolate translatable text in resource bundles
    • Deal with compound messages
    • Format numbers and currencies
    • Format dates and times
    • Use Unicode character properties
    • Compare strings properly
    • Convert Non-Unicode text to Unicode


    Back to top


    High-level steps to supporting localized client-side messaging

    The following points are essentially mandatory when building a Web application with internationalization support and localized client-side messaging.

    • All the localized pages should support the UTF-8 character set (page encoding).
    • All the client-side messages should be fetched from the server-side using the client-locale-specific resource bundles.
    • The resource bundle should store key-value pairs. Use Unicode characters for the values.
    • Use Ajax to send the request from the client-side JavaScript to a server-side resource, which resolves the request to get client-locale-specific messages against the key.
    • Parse the obtained message and display it properly.


    Back to top


    A practical approach to localized client-side messaging using Ajax

    Let's go through a detailed account of activities that need to be performed to make the basic structure, test it with a sample page, and then use repetitively in your Web application.

    First, prepare the resource bundle property files. The *.properties files, kept in the class path of the project, contain key-value pairs and are used as a resource bundle to get the locale-specific runtime-resolved validation messages. All such *.properties file should follow the Java internationalization standard naming convention.


    Listing 1. Bundle property file
    # 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.          

    Next, create a Java class to manage the resource bundles. This class (say, ResourceManager.java) exposes the function to load the locale-specific resource bundles into the cache. It also provides a way to retrieve the message value as per the given message key and locale.


    Listing 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 "";
              }
         }
    }             

    Create a JSP file to handle the Ajax requests. This JSP file (for example, MessageResolver.jsp) is responsible for handling the Ajax request to resolve the message keys (a request parameter, message-key, to this JSP). It uses the message retrieval features exposed by the ResourceManager class to resolve each message key coming as request parameter to this JSP.


    Listing 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);
          }
            }
       }
    %>     

    Prepare JavaScript utilities to manage the Ajax calls. A set of JavaScript routines should be written together (for example, MessageDisplay.js), which helps in creating the correct input for the Ajax call to MessageResolver.jsp. The JSP expects a request parameter that is a comma-separated set of Strings. The String is formed by pairs of HTML Element (placeholder) Identifiers and the message-key that will be displayed inside that HTML element after resolution. This also has a routine (displayMessage() method) to correctly display the resolved messages to the associated HTML elements.


    Listing 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);
       }
         }
    }        

    Create a client JSP/HTML to test. The page (for example, NewUserRegistration.jsp) has some input fields whose data needs to be validated on the client side using JavaScript. However, it is highly possible that the user will make a mistake in entering the correct data. Therefore, the validation logic should immediately prompt the user with an appropriate message. Because the validation message should be localized, the validation logic should use the Ajax utility prepared before to get locale-specific messages displayed for each type of validity issues.


    Listing 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 onsubmit="return validateNewUser();" name="frmRegistration" 
             id="frmRegistration" action="#" method="post">
             <table border="0" width="100%">
                <tbody>
                   <tr>
                      <td align="right" nowrap="nowrap">&nbsp;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>      

    Apply validation criteria on form fields. This example JSP has a form that expects the necessary inputs to complete the user's registration formalities to the system. The form expects some validation criteria to be passed for correct submission:

    1. The Email ID and User Name fields are mandatory.
    2. The Password field is mandatory. The password should be at least 6 characters.
    3. The Confirm Password field should have exactly the same value that has been entered in the "Password" field.
    4. The First Name and Last Name fields are also mandatory.

    Create JavaScript routines to validate the form. A JavaScript routine (for example, ValidateNewUser.js) should be invoked on the onsubmit event of the HTML form that performs validations on various fields. It also prepares the pairs of appropriate message key and placeholder HTML Element IDs, which will be used later as input in the Ajax call for the resolution and display of localized messages, if needed. If the form passes the validity test, the form is submitted to the target action.


    Listing 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;
    }                

    Deploy and Test

    Deploy the application on any Web server (for example, Tomcat), and start the server instance. Then, open a browser instance (for example, Internet Explorer), and find the preferred language set in the client. It should be English, as shown in Figure 1 (click here to see a larger image).


    Figure 1. Language preference
    Settting the language                     preference

    Browse the URL for the user registration (NewUserRegistration.jsp) page as shown in Figure 2 (click here to see a larger image).


    Figure 2. User registration
    Setting the user                     registration

    Enter all the values except for the User Name field. Leave this one empty. Press Submit. See the validation message by the User Name row (in English) as shown in Figure 3 (click here to see a larger image).


    Figure 3. Entering values for validation
    Entering values for                     validation

    Change the language in the browser (Internet Explorer). Remove English and add Hindi.


    Figure 4. Changing language preference to Hindi
    Changing the language                     preference to Hindi

    Close this instance of the browser and open a new one.

    Browse the same (user registration) page once again. Enter all fields correctly leaving "User Name" field empty. See the validation message alongside "User Name" row (in Hindi), as shown in Figure 5 (click here to see a larger image).


    Figure 5. Updated validation message
    See the updated                     validation message


    Back to top


    Steps at a glance

    1. Prepare the server-side resource to resolve the message key:
      1. Prepare the resource bundle (Messages*.properties). Define a Java utility (ResourceManager.java) to resolve the locale-specific message value for the given key and locale.
      2. Define a server-side request handler JSP (for example, MessageResolver.jsp) for Ajax calls.
      3. This JSP should expect a request parameter, for example, message-key.
      4. The value of the message-key parameter should be a comma-separated list of valid Strings.
      5. There should be an even number of entries after splitting the value using a "," (comma) as the delimiter.
      6. Each pair of the (consecutive) entries should be treated separately. The first entry should be the ID of the HTML element where the resulting message should be displayed. The second entry should be the actual message key to be resolved from the resource bundle.
      7. Prepare the response following a predefined structure — <Message Begin Delimiter> <HTML element ID> <Message-Key Delimiter> <Resolved internationalized message for the key> <Message End Delimiter>.
      8. Repeat the same format for each pair of HTML element and message key.
    2. During validation on the client side:
      1. Prepare the list of pairs of placeholder HTML element IDs and message keys. Validate each and every HTML form field and prepare the pair as said.
      2. Create a comma-separated String from the above list of pairs (Element ID and message key).
      3. Make an Ajax call to the server-side resource (the JSP created in step 1.a) appending a Query String for "message-key=<Comma-separated list of Strings created in step 2.2>"
    3. On client side, while handling the response from the Ajax call:
      1. Parse the response generated by the JSP (as described in step 1.f to 1.g).
      2. Separate out the paired list of HTML element IDs and corresponding message-values.
      3. Make the appropriate JavaScript calls to display the messages against each HTML element properly.


    Back to top


    Conclusion

    It should now be clear to you how simple it can be to display localized client-side validation messaging using Ajax. Although I tried to keep the set-up simple and out-of-the-box, this approach can be improved by using some ready-made systems and techniques. For example, if your Web application uses Jakarta Struts or any similar framework, which provides ready-to-use, more sophisticated ways of using resource bundles, you can use them instead of the homegrown ResourceManager.java utility class. Similarly, the JavaScript (and Ajax) routines can be written in more sophisticated ways that conform to the standards defined for your Web application.

    discuss this topic to forum

    relation tutorial

    No information

    Category

      AJAX (66)
      Content Management (12)
      Cookies (6)
      Date and Time (17)
      Development (18)
      DHTML (23)
      Forms (10)
      Frequently Asked Questions (3)
      Image Display (14)
      Introduction to Javascript (11)
      Links and Buttons (8)
      Menus (2)
      Miscellaneous (24)
      Mouse Tricks (3)
      Navigation (13)
      Randomizing (6)
      Security (5)
      Text Effects (10)
      User Authentication (2)
      User Information (5)
      Windows and Frames (9)

    New

    Hot