Sunday, February 5, 2012

JSTL Practice: I18N (Internationalization) practice, Locale, Bundle and Message.

We sometimes want to display a message in several different language,
i.e., I18N,Internationalization,
we can achieve this with JSTL by the steps below:

Step 1: Prepare the resource of message bundle.

For easy management, we create a package 'test.jstl.i18n.resources' and create four '.resource' file as below:

i18n_test_en_US.resource

i18n.coffee=coffee

i18n_test_fr_FR.resource

i18n.coffee=café

i18n_test_ja_JP.resource

i18n.coffee=コーヒー

i18n_test.resource

i18n.coffee=coffee default

actually you can put these files at any location, and use any sub name '.whatever',
because we do not use these files directly, they are just the resource file to generate the real bundle file.

Make sure to change their encoding to 'UTF-8', you can change their encoding as below:
Right click on file then click properties

Change Text file encoding to UTF-8 if it is not default



Step 2: Generate the message bundle from resource files

After the resource files prepared, we copy them to a tmp folder such like C:/tmp,
and use the native2ascii under jdk/bin to generate message bundle as below:

c:\Program Files\Java\jdk1.6.0_24\bin>native2ascii -encoding UTF-8 c:\tmp\i18n_test_en_US.resource c:\tmp\i18n_test_en_US.properties

c:\Program Files\Java\jdk1.6.0_24\bin>native2ascii -encoding UTF-8 c:\tmp\i18n_test_ja_JP.resource c:\tmp\i18n_test_ja_JP.properties

c:\Program Files\Java\jdk1.6.0_24\bin>native2ascii -encoding UTF-8 c:\tmp\i18n_test_fr_FR.resource c:\tmp\i18n_test_fr_FR.properties

c:\Program Files\Java\jdk1.6.0_24\bin>native2ascii -encoding UTF-8 c:\tmp\i18n_test.resource c:\tmp\i18n_test.properties

after the message bundle generated, we create a new package 'test.jstl.i18n.properties' and put the bundles into the package.

Step 3. Use 'setBundle' tag to load bundle then use 'message' tag to display message

Assume we have a page as below:

practice_three__internationalization.jsp

<%@ page isErrorPage="true" language="java"
    contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!-- This is the uri for JSTL 1.2 -->
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!-- EL is required -->
<%@ page isELIgnored ="false" %>
<!-- This page practice the setBundle message tag -->
<html>
    <head>
        <meta http-equiv="Content-Type" 
            content="text/html; charset=UTF-8"/>
        <title>practice three: I18N (Internationalization)</title>
        <script type="text/javascript" src="js/practice_three.js"></script>
    </head>
    <body>
        <!-- load bundle if it is null (without locale setting) -->
        <c:if test="${testBUndle == null}">
            <fmt:setBundle basename="test.jstl.i18n.properties.i18n_test"
                var="testBundle" scope="session"/>
        </c:if>
        <!-- The language select menu, call javascript to change locale when changed -->
        <select id="langVal" onChange="i18n.changeLocale(this.value, '${currentLocale}');" selectedIndex="1">
            <!-- The first option display the current language -->
            <option value="${currentLocale}">${currentLanguage}</option>
            <option value="en_US">English</option>
            <option value="ja_JP">日本語</option>
            <option value="fr_FR">Français</option>
            <option value="de_DE">Deutsch</option>
        </select>

        <!-- out put the message with respect to the key 'i18n.coffee' -->
        <fmt:message key="i18n.coffee"
                bundle="${testBundle}"/>
    </body>
</html>

The result will be


There is no locale setting, it will use the default setting from your browser, in this case is en_US,

The setBundle tag will load bundle which file nam
is [basename+locale].properties,
i.e., test.jstl.i18n.properties.i18n_test_en_US.properties

If it can not find such a file, it will use the default bundle,
which named [basename].properties,
i.e., test.jstl.i18n.properties.i18n_test.properties

The message tag will display the value of the key in the bundle,
i.e., coffee,
which is the value of the key 'i18n.coffee' in bundle test.jstl.i18n.properties.i18n_test.properties

The first option of select element is
based on the variabl currentLocale and currentLanguage in the session scope.
after you select a locale in the selectbox,
an Ajax request will post to another page to change locale.

Step 4: Use Ajax to post change locale request.

The Javascript file used to send change-locale request in the page before is as below:

practice_three.js

if (!window.i18n)
    i18n = {};
i18n.changeLocale = function (locale, current) {
    var request;
    // do nothing if not changed
    if (locale == current)
        return;

    // get a request
    if (request = this.getXmlHttpRequest()) {
        // do post and bring locale value as param['lang']
        request.open('POST', 'practice_three__internationalization_lang_page.jsp?lang='+locale);
        request.send(null);
    }
    // refresh document after request success
    request.onreadystatechange = function() {
        if(request.readyState === 4 && request.status === 200)
            document.location.href = document.location.href;
    };
};

//get the XmlHttpRequest object
i18n.getXmlHttpRequest = function () {
    if (window.XMLHttpRequest
        && (window.location.protocol !== 'file:' 
        || !window.ActiveXObject))
        return new XMLHttpRequest();
    try {
        return new ActiveXObject('Microsoft.XMLHTTP');
    } catch(e) {
        throw new Error('XMLHttpRequest not supported');
    }
};

The function 'i18n.changeLocale' will check whether the locale is changed first, if it is changed,
post a request to page 'practice_three__internationalization_lang_page.jsp'
and bring the new locale value with param name 'lang'

After the request success, it will reload current page to display the message with new locale.

Step 5: Use 'setLocale' tag to change locale and use 'setBundle' to reload appropriate bundle

The page that receive the change locale request and change locale is as below

practice_three__internationalization_lang_page.jsp

<%@ page isErrorPage="true" language="java"
    contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!-- This is the uri for JSTL 1.2 -->
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!-- EL is required -->
<%@ page isELIgnored ="false" %>
<!-- This page practice setLocale and setBundle tag -->
<html>
    <head>
        <meta http-equiv="Content-Type" 
            content="text/html; charset=UTF-8"/>
        <title>practice three: I18N (Internationalization)</title>
    </head>
    <body>
        <!-- Set the locale by the value of parameter 'lang' -->
        <fmt:setLocale value="${param['lang']}" scope="session" />
        <!-- Should load bundle again with the new locale -->
        <fmt:setBundle basename="test.jstl.i18n.properties.i18n_test"
            var="testBundle" scope="session"/>
        
        <!-- set the value of currentLocale -->
        <c:set var="currentLocale" value="${param['lang']}" scope="session" />

        <!-- Set the value of currentLanguage -->
        <c:choose>
            <c:when test="${param['lang'] eq 'ja_JP'}">
                <c:set var="currentLanguage" value="日本語" scope="session" />
            </c:when>
            <c:when test="${param['lang'] eq 'fr_FR'}">
                <c:set var="currentLanguage" value="Français" scope="session" />
            </c:when>
            <c:when test="${param['lang'] eq 'de_DE'}">
                <c:set var="currentLanguage" value="Deutsch" scope="session" />
            </c:when>
            <c:otherwise>
                <c:set var="currentLanguage" value="English" scope="session" />
            </c:otherwise>
        </c:choose>
    </body>
</html>

It set the locale to the value of parameter 'lang' by setLocale tag,
then reload bundle to change to different bundle.

It also store two variable currentLocale and currentLanguage into session scope.

Finally we can display the 'coffee' in different language, note we do not have the bundle of locale de_DE,
it will display the message in default bundle (value is coffee default) when we select Deutsch:





Download:

You can download the full project at github
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/JSTLPractice

The files of this practice:
src/test.jstl.i18n.properties.*
src/test.jstl.i18n.resources.*
WebContent/practice_three_*
WebContent/js/practice_three.js

References:
http://java.sun.com/developer/technicalArticles/J2SE/locale/
http://www.tutorialspoint.com/jsp/jsp_standard_tag_library.htm

1 comment:

  1. Hello! If you ever need to internationalize a website, app or other software product, I suggest to have a look at like POEditor which is collaborative translation management platform aimed at automating and simplifying the workflow.

    ReplyDelete