Internationalism, as defined by Wikipedia, is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes (see Resources). Using this definition, in order to have internationalized software, the presence of three elements is essential:
- An element that is responsible to tell the software the locale in which the software is running
- A set of translation files that can be applied to the software's current locale
- A component that is responsible to connect the previous two elements
In this article, we will create a simple internationalized Web application and demonstrate the Dojo toolkit standards for the first two elements (see Resources for more on Dojo). Then we will examine two Dojo usage scenarios for the third element: an html file or a widget.
Setting the locale in a Dojo-based application
The locale is a parameter defining the language that should be used in the application's user interface. There may be situations where specifying the language may not be enough. Different countries may use the same language with subtle changes, ranging from spelling changes (for example, "center" in the United States as opposed to "centre" in the United Kingdom) to different words for the same meaning ("Elevator" in the U.S. as opposed to "Lift" in the UK). Therefore, the locale parameter may contain a country code in addition to the language code, to specify the specific language flavor that should be used.
Dojo provides two mechanisms of specifying the locale of the Web application. The first is programmatically (either in run time or design time), and the other is to rely on the default locale of the browser (which is defined during the browser’s installation).
Setting
the locale programmatically is done by specifying the value of the "locale" attribute in the
global object djConfig. The example in Listing 1 shows how to set the locale to en-us
locale in design time:
Listing 1: Setting the locale programmatically in design time
<script type="text/javascript" SRC="dojo.js" djConfig="locale:'en-us'"></script> |
Another possibility is programmatically calculating the locale (for example, by extracting it from the URL) and then setting locale. The example in Listing 2 shows how to set the locale programmatically in run time:
Listing 2: Setting the locale programmatically in run time
<script type="text/javascript">
// Note: this must be done before dojo loads!!!
var djConfig = new Object();
var theLocale = getLocale(); // this function is responsible to determine
// the appropriate locale
djConfig.locale = theLocale;
function getLocale()
{
return “en-us”;
}
</script> |
The last possibility is to not specify the locale, which results in using the default locale of the browser, which Dojo extracts from the navigator object. The default behavior, like any usage of default for that matter, is useful in cases where you can predict how your client’s browser and machine are configured. This is rarely the case, and installing the browser in a language which is not the user’s mother tongue, or a locale with which your application is not familiar, is not uncommon (so do try to avoid using this approach).
The translation files in Dojo applications
Now that you understand how to state the application’s locale, it is time to learn to provide the set of the translation files, called "resource bundle(s)," to Dojo applications.
A resource bundle is a group of files, all named the same, in which each file is applicable for a specific locale. Each file should be in the JSON format. For our greeting application, the resource bundle files are all named greeting.js. Their contents are shown in Listing 3.
Listing 3: Resource bundles for different English locales
Content for the American English locale:
{
PAGE_TITLE:"God bless America",
GREETING: “What’s up”,
PERSON: “dude”,
GREETING_SENTENCE: “${greeting} ${personTitle}”
}
|
You now need to place these files in the proper location, so that Dojo can read them. In
order to do so, you need to construct a hierarchy of folders, in the way that Dojo expects.
Assume that the application defines a module path called "sampleApp" in the location "../../sample/application"
relative to Dojo. So the directory structure you need to create is like the one seen in
Figure 1.
Figure 1. Directory structure
Basically, a folder named “nls” should be under the
defined module. It contains the locale folders, which are lower cased, and a hyphen separates
the language code from the optional country code.
Note that in the translation files we have
separated the greeting into two parts, since different languages may have different sentence
structures and may build the greeting differently. Hence, we need a way to construct the
greeting from its components in an internationalized way. For this we use Dojo’s utility
dojo.string.substitute, which receives a template for a string,
with parameterized values, and substitutes the placeholders in the templates with the given
values. The template itself is the entry GREETING_SENTENCE in the resource bundle.
Connecting the pieces within an HTML file
In order to connect all the previously defined pieces, we create the html file that contains our application, shown in Listing 4.
Listing 4: The application's HTML file
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title id="titleId"></title>
<script type="text/javascript">
var djConfig = { modulePaths: {sampleApp: "../../sample/application"},
parseOnLoad: true,
locale:'en-us'};
</script>
<script type="text/javascript" src="dojoBase/dojo/dojo.js"></script>
<script type="text/javascript">
dojo.require("dojo.string");
dojo.requireLocalization("sampleApp", "greeting");
function init()
{
var nlsStrings = dojo.i18n.getLocalization("sampleApp","greeting");
var greetingString = nlsStrings.GREETING; var personString = nlsStrings.PERSON;
var greetingTemplate = nlsStrings.GREETING_SENTENCE;
var finalGreeting = dojo.string.substitute(greetingTemplate,
{greeting:greetingString,
personTitle:personString});
dojo.byId("helloDiv").innerHTML = finalGreeting;
dojo.byId("titleId").text = finalGreeting;
}
dojo.addOnLoad(init);
</script>
</head>
<body>
<div id="helloDiv"></div>
</body>
</html>
|
The most important thing to notice in this application is the usage of two Dojo commands
that extract the localized information. The first one is: dojo.requireLocalization("sampleApp", "greeting"). This call is responsible for
requesting from the server the resource bundle called "greeting" that is found under the
defined module "sampleApp." The second one is: var nlsStrings = dojo.i18n.getLocalization("sampleApp","greeting").
This call is responsible for transforming the received "greeting" resource bundle (which was
found under the "sampleApp" module) into a JavaScript object,
which is returned. Once you have this object (the nlsStrings
object), you can refer to any of its fields in order to set the appropriate dom element with
the needed strings.
Connecting the pieces and displaying the content using custom-created widgets
Usually, when the application gets a little bigger than a sample application, custom-created widgets are introduced. In this section, we recreate the sample application, now using custom-created widgets. You'll see how properly to separate all the mechanisms of internationalism from the content displaying widgets.
First of all, create the widget that is responsible for handling all the internationalism issues, starting with the "mixin," shown in Listing 5, which is responsible for all the native language support (NLS) operations.
Listing 5: The NLS mixin
dojo.require("dojo.i18n");
dojo.require("dojo.string");
dojo.requireLocalization("sampleApp", "greeting");
dojo.provide("sampleApp.widget.NlsMixin");
dojo.declare("sampleApp.widget.NlsMixin",null,
{
data: {__initialized: false},
constructor: function()
{
if(!this.data.__initialized)
{
this.data.nlsStrings = dojo.i18n.getLocalization("sampleApp",
"greeting");
this.data.__initialized = true;
}
},
/**
*
* @param {String} key - the name of the key in the translation file
* @param {Object or Array?} substitutes - in cases where the translated
* string is a template for string substitution, this parameter
* holds the values to be used by dojo.string.substitute on that
* template
*/
getString: function(/*String*/ key,
/*Object or Array?*/ substitutes)
{
var str = this.data.nlsStrings[key];
return (substituteValues)?
dojo.string.substitute(str,substitutes):
str;
},
postMixInProperties: function()
{
this.inherited('postMixInProperties', arguments);
this.initializeStrings();
},
initializeStrings: function(){}
}
);
|
There are three things to note in this mixin:
- In order to reduce memory usage, the resource bundle object is static and singleton.
- Widgets that use this mixin need to override the function '
initializeStrings', and populate it with calls to thegetStringfunction. This method is invoked during the call topostMixinPropertiesin order to make all the internationalized strings available to use while rendering this widget (in other words, using these strings within the widget's template). - The
getStringfunction receives two parameters. One is the key within the resource bundle. If the returned string needs further handling bydojo.string.substitute, then the second parameter is used to supply the substitution object or array (depending on the way that the template was defined in the resource bundle).
Now we'll show you how to create the simple greeting widget, which would be placed within your page to greet your application's users. See Listing 6.
Listing 6: The greeting widget's code
dojo.provide("sampleApp.widget.Greeting");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("sampleApp.widget.NlsMixin");
dojo.declare("sampleApp.widget.Greeting",
[dijit._Widget,dijit._Templated ,sampleApp.widget.NlsMixin],
{
templateString: "<div>${greeting}</div>",
greeting:””,
initializeStrings: function()
{
var greetingString = this.getString("GREETING");
var personString = this.getString("PERSON");
this.greeting = this.getString("GREETING_SENTENCE",
{greeting:greetingString,
personTitle:personString});
}
}
);
|
As you can see, the template refers to a variable that gets a "real" value during the call
to initalizeStrings, which uses the getString method in order to get the proper internationalized content.
Now, all that needs to be done is use this widget in an HTML file to view the internationalized content, seen in Listing 7.
Listing 7: The greeting page for the en-au locale
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<script type="text/javascript">
var djConfig = { isDebug: false,
modulePaths:
{ sampleApp: "../../sample/application" },
parseOnLoad: true,
locale:'en-au'
};
</script>
<script type="text/javascript" src="dojoBase/dojo/dojo.js.uncompressed.js"></script>
<script type="text/javascript">
dojo.require("sampleApp.widget.Greeting");
</script>
</head>
<body>
<div dojoType="sampleApp.widget.Greeting" id="helloDiv"></div>
</body>
</html> |
Translating the application to languages that are not the language used by developers during development adds the necessity for three types of tests to be done on the software.
- The first one is to test the quality of the translation, which is something that is usually very difficult to do, since it requires another person besides the translator that is fluent in the translated language. This kind of testing is beyond the scope of this article.
- The second type is to test the way that the application's user interface displays each of the languages. Sentences or words may turn out to be much longer or shorter in other languages, and this change may cause the UI to look different. (There are also other aspects of i18n, which are not discussed here, such as the need to sometimes use a different set of colors and icons in different locales due to cultural differences.) This testing is done by the development team, and may result in using different style sheets for different locales. This kind of testing is also beyond the scope of this article.
- The third type is a coverage test. It basically means that the developer needs to verify that each and every string that is presented to the end user by the application is not hard coded, but comes from the proper resource bundle. We'll present one way of doing this test. There may be others, but this one is simple, efficient, and there is no need to do something more complicated here.
Testing for hard-coded elements
Below is a step-by-step guide to performing a coverage test on the internationalized content:
- Create a dummy locale, called "
en-num" (add a folder as a sibling of the other locale folders). - Copy the resource bundle you want to test to this folder .
- Either by hand or automatically using some small program or a script written in your
favorite programming language, change all the strings in this file (not including
substitution markers for
dojo.string.substitute) to numbers. - Set the application's locale to be "
en-num" and go over all the screens of your application. If you see a string that is not composed of numbers, then most likely this string did not originate from the resource bundle.
In conclusion, you have seen how to perform internationalism in Web applications using the Dojo toolkit. This article presented a way to design, implement, and test the translation aspect of i18n. There are other aspects, more related to localization, such as formatting some of the displayed elements according to the locale (different countries use different methods to present numbers, currencies, time, and date). Other aspects of i18n that are not addressed here are things that are derived from cultural differences. Icons that mean one thing in one country may mean the opposite in another country. For example, using an owl icon to signify a wise thing in the UI won't work in some Asian countries, as an owl sometimes symbolizes stupidity.
Still, the translation aspect of i18n is the most resource demanding, requiring much work from the software developers. Using information presented in this article may save you some time and money. If so, then our work here was worth doing.
Learn
- See Wikipedia's page on Internationalization.
- The Dojo
toolkit is a JavaScript toolkit that has many components to support Ajax application
development.
- IBM globalization has
many resources that handle various aspects regarding globalization concerns.
- Blogamundo is an excellent blog that discusses various internationalization aspects.
- Global
by design is a blog that discusses various Web globalization aspects.
- The Dojo
campus holds many good tips and cookies
regarding using Dojo.
- Expand your Web development skills with articles and
tutorials that specialize in Web technologies in the developerWorks Web development zone.
- Browse the technology
bookstore for books on these and other technical topics.
Discuss
- Check out developerWorks blogs and get involved
in the developerWorks community.
Yoav Rubin holds a B.Sc. in Information Systems Engineering from the Computer Science faculty in the Technion, Israel Institute of Technology, and for the last eight years, he has been working as a Software engineer in IBM Haifa Research Lab, mostly in the fields of visual application development tools and complex event processing. His main interests are Java and Web 2.0 technologies, end user development and usability.





