About us
Products
Services
Articles
Contact us

11. Internationalization Example

Contents | Previous Chapter | Next Chapter

This chapter shows how to build internationalized application using the framework. The example was written in English and then it was localized in four languages: German, French, Italian and Spanish.


11.1. Data Bean

InternatBean is a Java bean that maps three of its properties (language, date and number) to an HTML form. The bean also has several properties that don't participate to the mapping process (locale, parsedDate, dateExample, dateFormat, parsedNumber, numberExample and numberFormat).

The language property acts as an intermediary between the locale property and the language element of the HTML form. The locale property cannot be mapped directly to a form element because it is neither a standard property nor a data bean. Also, locale is a read only property.

The date property acts as an intermediary between the parsedDate property and the date element of the HTML form. The parsedDate property cannot be mapped directly to a form element because it is neither a standard property nor a data bean.

The number property acts as an intermediary between the parsedNumber property and the number element of the HTML form. The parsedNumber property is a standard property, but it cannot be mapped directly to a form element because its value is converted from a String using java.text.NumberFormat, while that framework uses java.lang.Float for such a conversion.

The getDateFormat() and getNumberFormat() methods return instances of java.text.DateFormat and java.text.NumberFormat that are used to convert the values of date / number to the types of parsedDate / parsedNumber and vice-versa. The setter methods make sure that the values of these properties are kept in synch. The setters also do application-specific validation. The setDate() and setNumber() methods throw an IllegalArgumentException to signal any user error.

The getDateExample() and getNumberExample() return String examples that are inserted within the HTML form page in order to help the users.

InternatBean.java:

package com.devsphere.examples.mapping.internat;

import java.text.*;
import java.util.*;

/**
 * InternatBean class
 */
public class InternatBean implements java.io.Serializable {
    public static final Date DATE_EXAMPLE = new Date();
    public static final float NUMBER_EXAMPLE = 123.45f;
    public static final Locale LOCALES[] = {
        new Locale("en", "", ""), // English
        new Locale("de", "", ""), // German
        new Locale("fr", "", ""), // French
        new Locale("it", "", ""), // Italian
        new Locale("es", "", "")  // Spanish
    };

    private int language;
    private String date;
    private Date parsedDate;
    private DateFormat cachedDateFormat;
    private String number;
    private float parsedNumber;
    private NumberFormat cachedNumberFormat;

    /**
     * No-arg constructor
     */
    public InternatBean() {
    }

    /**
     * Gets the language property
     */
    public int getLanguage() {
        return this.language;
    }

    /**
     * Sets the language property
     */
    public void setLanguage(int value) {
        if (value < 0 || value >= LOCALES.length)
            throw new IllegalArgumentException();
        language = value;
        cachedDateFormat = null;
        cachedNumberFormat = null;
    }

    /**
     * Gets the locale object
     */
    public Locale getLocale() {
        return LOCALES[getLanguage()];
    }

    /**
     * Gets the date property
     */
    public String getDate() {
        return date;
    }

    /**
     * Sets the date property
     */
    public void setDate(String value) {
        try {
            setParsedDate(getDateFormat().parse(value));
        } catch (ParseException e) {
            throw new IllegalArgumentException();
        }
    }

    /**
     * Gets the parsed date
     */
    public Date getParsedDate() {
        return parsedDate;
    }

    /**
     * Sets the date property
     */
    public void setParsedDate(Date value) {
        parsedDate = value;
        date = getDateFormat().format(parsedDate);
    }

    /**
     * Gets an example for the date property
     */
    public String getDateExample() {
        return getDateFormat().format(DATE_EXAMPLE);
    }

    /**
     * Gets the date format
     */
    public DateFormat getDateFormat() {
        if (cachedDateFormat == null)
            cachedDateFormat = DateFormat.getDateInstance(
                DateFormat.SHORT, getLocale());
        return cachedDateFormat;
    }

    /**
     * Gets the number property
     */
    public String getNumber() {
        return number;
    }

    /**
     * Sets the number property
     */
    public void setNumber(String value) {
        try {
            setParsedNumber(getNumberFormat().parse(value).floatValue());
            number = getNumberFormat().format(parsedNumber);
        } catch (ParseException e) {
            throw new IllegalArgumentException();
        }
    }

    /**
     * Gets the parsed number
     */
    public float getParsedNumber() {
        return parsedNumber;
    }

    /**
     * Sets the number property
     */
    public void setParsedNumber(float value) {
        parsedNumber = value;
        number = getNumberFormat().format(parsedNumber);
    }

    /**
     * Gets an example for the number property
     */
    public String getNumberExample() {
        return getNumberFormat().format(NUMBER_EXAMPLE);
    }

    /**
     * Gets the number format
     */
    public NumberFormat getNumberFormat() {
        if (cachedNumberFormat == null)
            cachedNumberFormat = NumberFormat.getNumberInstance(getLocale());
        return cachedNumberFormat;
    }

}

11.2. HTML Form

At the beginning of the HTML form page, there are a few links that allow the user to change the language. The HTML form uses the "multipart/form-data" encoding type, which is preferred by internationalized applications.

Some of the properties of InternatBean are mapped to the form elements of InternatForm.html:

Name Property type Element type
     
language int hidden
date String text
number String text

The error messages were relocated below the form elements by declaring ERROR_MESSAGE variables. The form also has a few application-specific variables that are replaced with localized content by the JSP handler.

It would have been possible to replace the few labels with variables. This way, a single language-independent HTML form would have been sufficient. If necessary, a JSP could have generated dynamically the HTML form. In many applications, however, the content that must be localized is predominant. In such a case, it makes sense to have one HTML form for each locale.

The way the mapping between JavaBeans and HTML forms works was explained in a previous chapter.

InternatForm.html:

<HTML>
<HEAD><TITLE>Internat form</TITLE></HEAD>
<BODY>
<CENTER>
<P>
[English]
<A HREF="InternatHndl.jsp?language=1">[Deutsch]</A>
<A HREF="InternatHndl.jsp?language=2">[Francais]</A>
<A HREF="InternatHndl.jsp?language=3">[Italiano]</A>
<A HREF="InternatHndl.jsp?language=4">[Espanol]</A>
<P>
<FORM METHOD="POST" ENCTYPE="multipart/form-data">
    <INPUT TYPE="HIDDEN" NAME="language">
<TABLE>
<TR><TD ALIGN="LEFT">
    <P> Language: <!-- VARIABLE NAME="language_name" -->
</TD></TR>
<TR><TD ALIGN="LEFT">
    <BR>
</TD></TR>
<TR><TD ALIGN="LEFT">
    <P> Date (example: <!-- VARIABLE NAME="date_example" -->)<BR>
    <INPUT TYPE="TEXT" NAME="date" SIZE="40">
</TD></TR>
<TR><TD ALIGN="LEFT">
    <!-- VARIABLE NAME="[ERROR_MESSAGE.date]" --><BR>
</TD></TR>
<TR><TD ALIGN="LEFT">
    <P> Number (example: <!-- VARIABLE NAME="number_example" -->)<BR>
    <INPUT TYPE="TEXT" NAME="number" SIZE="40">
</TD></TR>
<TR><TD ALIGN="LEFT">
    <!-- VARIABLE NAME="[ERROR_MESSAGE.number]" --><BR>
</TD></TR>
<TR><TD ALIGN="CENTER">
    <INPUT TYPE="IMAGE" SRC="send.gif">
</TD></TR>
</TABLE>
</FORM>
</CENTER>
</BODY>
</HTML>

11.3. Bean Resources

The InternatBeanResources class is a resource bundle containing an application resource, the error messages, the processing order, the form's name and the processor's name.

The application resource ([internat.LANGUAGE_NAME]) is accessed by the JSP handler that inserts its value within the HTML form page.

There are two error messages. Their role is to help the users to correct the input errors.

The processing order is very important to this example because the setter methods of date and number need the Locale object that depends on language.

InternatBeanResources.java:

package com.devsphere.examples.mapping.internat;

public class InternatBeanResources extends java.util.ListResourceBundle {
    private static final Object[][] contents = {
        { "[internat.LANGUAGE_NAME]", "English" },
        { "[ERROR_MESSAGE.date]", "must be a valid date." },
        { "[ERROR_MESSAGE.number]", "must be a valid number." },
        {
            "[PROCESSING_ORDER]",
            new String[] {
                "language",
                "date",
                "number"
            }
        },
        { "[FORM_NAME]", "InternatForm.html" },
        { "[PROC_NAME]", "InternatProc.jsp" }
    };

    public Object[][] getContents() {
        return contents;
    }

}

11.4. JSP Handler

The InternatHndl.jsp handler is based on a template that was described in a previous chapter and is similar to the handler of the first example (SimpleHndl.jsp). There are, however, several differences.

The handler uses the second constructor of ServletFormData to allow the use of the "multipart/form-data" parser.

The JSP handler gets the language parameter to determine the user's locale. This Locale object is passed to HandlerUtils.getBeanResources() in order to get the localized bean resources. It is also passed to FormUtils.formToBean() and FormUtils.beanToForm() so that the mapping utilities can use the localized error messages and default values if any.

Before sending the HTML content of the form document to the user's browser, the JSP handler replaces the three variables of the HTML form page with localized content.

InternatHndl.jsp:

<%@ page language="java" %>
<%@ page import="com.devsphere.mapping.*, com.devsphere.logging.*" %>
<jsp:useBean id="internatBean" scope="request"
    class="com.devsphere.examples.mapping.internat.InternatBean"/>
<%
    // Wrap the form data
    FormData formData = new ServletFormData(request, true);

    // Get the user's locale
    java.util.Locale userLocale = null;
    try {
        int language = Integer.parseInt(formData.getParameter("language"));
        userLocale = internatBean.LOCALES[language];
    } catch (Throwable t) {
    }

    // Get the bean resources
    java.util.ResourceBundle beanRes
        = HandlerUtils.getBeanResources(internatBean.getClass(), userLocale);

    // Construct the base path
    String basePath = request.getServletPath();
    int slashIndex = basePath.lastIndexOf('/');
    basePath = slashIndex != -1 ? basePath.substring(0, slashIndex+1) : "";

    // Determine the HTTP method
    boolean isPostMethod = request.getMethod().equals("POST");

    // Create a logger that wraps the servlet context
    ServletLogger logger = new ServletLogger(application);

    // Form-to-bean mapping: request parameters are mapped to bean properties
    java.util.Hashtable errorTable
        = FormUtils.formToBean(formData, internatBean, logger, userLocale);

    if (isPostMethod && errorTable == null) {
        // Construct the processor's path
        String procPath = basePath + beanRes.getString("[PROC_NAME]").trim();

        // Process the valid data bean instance
        application.getRequestDispatcher(procPath).forward(request, response);
    } else {
        if (!isPostMethod)
            // Ignore the user errors if the form is requested with GET.
            errorTable = HandlerUtils.removeUserErrors(errorTable);

        // Construct the form's path
        String formPath = basePath + beanRes.getString("[FORM_NAME]").trim();
        formPath = application.getRealPath(formPath);

        // Get the form template
        FormTemplate template = FormUtils.getTemplate(new java.io.File(formPath));

        // Get a new document
        FormDocument document = template.getDocument();

        // Bean-to-form mapping: bean properties are mapped to form elements
        FormUtils.beanToForm(internatBean, errorTable, document, null,
            logger, userLocale);

        // Insert the language name
        String languageName = beanRes.getString("[internat.LANGUAGE_NAME]");
        document.setValue("language_name", languageName);

        // Insert the date example
        document.setValue("date_example", internatBean.getDateExample());

        // Insert the number example
        document.setValue("number_example", internatBean.getNumberExample());

        // Send the form document
        document.send(out);
    }
%>

11.5. JSP Processor

Before doing any processing, InternatProc.jsp includes the content generated dynamically by a localized JSP whose base name is InternatIncl.

The InternatProc.jsp processor gets only valid bean objects and outputs the values of some properties: locale, parsedDate and parsedNumber. The values of language, date and number aren't printed since these properties act as intermediaries between the form elements and the properties whose values are shown.

InternatProc.jsp:

<%@ page language="java" %>
<jsp:useBean id="internatBean" scope="request"
    class="com.devsphere.examples.mapping.internat.InternatBean"/>
<HTML>
<HEAD><TITLE>Internat bean</TITLE></HEAD>
<BODY>
<H3>Internationalization Example</H3>
<HR>
<%
    String suffix = "";
    if (internatBean.getLanguage() != 0)
        suffix = "_" + internatBean.getLocale().toString();
    String inclName = "InternatIncl" + suffix + ".jsp";
%>
<jsp:include page="<%=inclName%>" flush="true"/>
<P><B>InternatBean properties: </B>
<P> locale = <jsp:getProperty name="internatBean" property="locale"/>
<P> parsedDate = <jsp:getProperty name="internatBean" property="parsedDate"/>
<P> parsedNumber = <jsp:getProperty name="internatBean" property="parsedNumber"/>
</BODY>
</HTML>

InternatIncl.jsp:

<%@ page language="java" %>
<P> This is a message in English.
<HR>

11.6. Localization

The application was localized to German, French, Italian and Spanish. The files that had to be translated were InternatForm.html, InternatBeanResources.java and InternatIncl.jsp.


11.7. Hints and Tips

All mapping utilities accept an optional Locale parameter. It is used to load the bean resources that contain default values and error messages. You may have application specific resources within those resource bundles, which can be obtained using the getBeanResources() method of HandlerUtils.

Some of the mapping utilities accept an encoding parameter. This is the ID of a character encoding. The CharacterEncoding class defines the IDs.

Contents | Previous Chapter | Next Chapter

Copyright © 2000-2020 Devsphere

About us
Products
Services
Articles
Contact us