Adding internationalization and localization to web applications using the Dojo Toolkit

This article explores a Dojo feature that enables localization and internationalization of a web application or website. It describes how to build a sample website to showcase the strength and simplicity of using Dojo. By the end of the article, you should be able to apply localization and internationalization features to your own web application. This content is part of the IBM WebSphere Developer Technical Journal.

Share:

Kareem Weller, Staff Software Engineer, IBM

Kareem Weller is a staff software engineer at IBM currently part of IBM Software Group and working from Orlando, Florida. He has worked in several organizations within IBM and has 5 years experience in web application development and has worked on many governmental and commercial projects using different web technologies and products such as dojo toolkit, JSON, XML, IBM Web Content Management and Websphere Application Server.



20 March 2013

Also available in Chinese

Introduction

The Dojo Toolkit is a powerful JavaScript™ library that enables web developers to create Rich Internet Applications (RIA) using object-oriented widgets with minimal development time and effort. It comes with four packages, known as Dojo (the core), Dijit (the UI framework), dojox (the dojo extension) and util. You can use the functionality provided by the toolkit as is, or you can extend it and create your own widgets. The provided functionalities include DOM manipulation, development with Ajax, events, data stores, and more.

The Dijit (dojo widget) package, Dojo’s own UI library, contains a collection of dojo classes that enable you to create rich and powerful cross-platform Web 2.0 interfaces with minimal effort. These Dojo widgets, or dijits, are supported by themes that are easy to manipulate. Examples of the dijits in this package include buttons, text fields, editors, progress bars, and many others.

One of Dojo’s strengths is the support of internationalization and localization, and its aim to make this a standard feature in the toolkit through dojo/i18n, which in fact provides two different features:

  • Resource bundles that comprise a library of translated resources to be used on the interface.
  • A set of built-in features to format numbers, currencies, dates, and more to a specified locale based on the Unicode CLDR.

You will also see an example of locale-aware dates and will learn how to create custom widgets and other basic features provided by Dojo. Using the dojo toolkit, you can ensure the separation of content from development and simplicity in managing current content. Moreover, adding new locales won’t require any code changes.

This article will explore the internationalization feature provided in Dojo by developing a simple marketing webpage for a fictional business called “The Coffee House.” The website will be available in two languages: US English and French. By the end of this article, you will understand how to build a webpage that can be easily localized using the Dojo Toolkit.

What is the resource bundle?

Resource bundles hold objects which represent specific locales. For example, if your application requires a locale-specific resource, such as a string, the application will load it from the appropriate resource bundle that corresponds to the specified locale. All locale-specific resources should be isolated in the resource bundles to ensure separation of code development from content management and localization effort.


Building The Coffee House website

Figure 1 shows the sample website you will create in this article, followed by descriptions of the page elements.

Figure 1. Website design
Figure 1. Website design
  • Links at top right

    To define the preferred language, users are presented with two links at the top right for US English (EN-US) and French (FR). Clicking either of these links will set the locale for Dojo (adding a new parameter to the URL indicating the current locale) and reload the whole page with the parameter in the URL that indicates the selected locale. You use this technique to help Dojo identify the current locale to load the pages in the selected language.

  • Main body

    This section will hold the content (text, images, links, and so on), much like a container that holds all the pages of the website. The pages will load asynchronously. The three buttons shown help the user navigate among the different pages of the website. You can alter this to create your preferred navigation menu. When you click on different buttons, the widget will asynchronously load the corresponding HTML page. In this example, the buttons take the user to different pages that present information about The Coffee House’s products: coffee, tea, and juice, respectively.

  • The form

    This simple form is included as an example of how to manage dijit localization and field validation with the internationalization feature. For simplicity, this example does not validate the form on submit. The form contains three fields to capture a given name, surname, and telephone number.

To build this webpage, you will have an index page that will invoke your custom widgets. The application will consist of:

  • A controller handles the language selection and instantiates the two custom widgets.
  • A contentViewer widget handles loading the appropriate pages and content driven by the user selection of different buttons. The main method loadPage() in this widget is the one handling the loading of the different pages asynchronously from the resource bundles based on specified locale.
  • A customForm widget handles the form used by the website to collect customer contact information. It will typically handle the form validation and submission.

The sections that follow look at these components in more detail and how they fit together with dojo/i18n to implement localization.


Application components

Index page

In this file, typical of most websites, you will set the dojoConfig (Listing 1) where you will define the locale.

Listing 1. dojoConfig
    var dojoConfig = {
        async : true,
        parseOnLoad: true,
        locale: location.search.substring(1)===""?"en-us":location.search.substring(1)
    };

You look for the locale parameter in the URL (value en-us or fr). If no value is found, the default is “en-us.” You can programmatically detect user locale automatically by using the dojo.locale property. This example sets the language of preference or defaults to English.

Listing 2 shows the other main piece of code, which instantiates the controller.

Listing 2. Instantiating the controller
require(["myUtil/controller", "dojo/domReady!"],
    function(customDijitController) {
		var myCustomDijitController = customDijitController();
		myCustomDijitController.placeAt("nodeId", "last"); 
});

Controller

The application controller concept is to have one object that manages and ties modular functionality. It controls the lifecycle of the dijits, manages data flow, and makes sure connections are established between different components, if needed, and so on. As mentioned earlier, you have three main sections on the web page, all of which are defined in the controller: links for the different available languages, custom content viewer widget, and the custom form widget

  • Controller.js

    In the controller class, you will have a postCreate function where you will simply initialize the contentViewer widget and the customForm widget, and connect them to the corresponding attach points that are defined in the template using placeAt(), as in Listing 3.

    Listing 3. Initializing the two widgets
    var myContentViewer = contentViewer();
    myContentViewer.placeAt(this.myContentViewerAP, "last");
    var myCustomForm = customForm();	
    myCustomForm.placeAt(this.myCustomFormAP, "last");
  • Controller.html

    This is the template for the controller class. It contains the links to the different languages, as in Listing 4.

    Listing 4. Languages links
    <a href="?en-us">EN-US</a> - <a href="?fr">FR</a>

    It also contains the two attach points for the two custom widgets instantiated in the controller class, as shown in Listing 5.

    Listing 5. Attach points to the two custom widgets
    <div data-dojo-attach-point="myContentViewerAP"></div> 
    <div data-dojo-attach-point="myCustomFormAP"></div>

ContentViewer

This custom widget is responsible for loading the main section content of the web page, including the special message at the top with the opening date, page content, and the three buttons:

  • contentViewer.js
    • postCreate

      This example uses dojo/date/locale, but there are other localization features to explore, such as dojo/number and dojo/currency. For this exercise, you will initialize a date and set it to April 1, 2013 and pass the date object to dojo/date/locale format function (Listing 6). This way, when you select EN-US the date will display in month/day/year format as 4/1/2013, but as day/month/year as 1/4/2013 when FR is selected (Listing 6). (i18n.openingMessage iis discussed in the next section)

      Listing 6
      var openingDate = new Date(2013,2,13);
      this.openingMessageAP.innerHTML = i18n.openingMessage + loc.format(openingDate);
    • Buttons

      Listing 7 shows an example of instantiating a dijit/form/Button.

      Listing 7
      new Button({
      	label: i18n.page1Button,
      	onClick: lang.hitch (this, function(){
      		this.loadPage(this.bodyContent,i18n.page1);
      	})	
      	}, this.page1ButtonAP);

      In this code:

      • label: Contains the text displayed on the button. Here, you use a string from the resource bundle.
      • onClick: Define the function that will be executed when you click on the button. You use dojo/_base/lang/hitch to give scope to call the loadPage function (which is in same class).

      You will define three buttons in the same way, but with different labels and URLs to represent the three pages.

    • loadPage

      This is a custom function you define to load pages from the resource bundles asynchronously. You will pass two parameters to this function: A URL of which page you want to load, and an attach point to define where you want the response to be placed. You send an xhr request using xhr.get. Listing 8 shows the code for the xhr.get.

      Listing 8
      xhr.get({
      	url: url,
      	handleAs: "text",
      	load: function(res){
                              // Resolve when content is received
                              def.resolve(res);
                          },
      	error: function(err){
                              // Reject on error
                              def.reject(err);
      	}	
      	});

      In this code:

      • url: The URL of the page to be loaded. In this case, which page from the resource bundle is to be loaded? Will it be the coffee, tea or juice page?
      • handleAs: Defines how to handle the data coming back from server(response). Here, you will set this to “text” because you will be loading it in HTML. Other values are json, javascript, or xml, to name a few options. Refer to the API for the complete list.
      • load: When response is received from server (success), this function is executed. In the load function, you call deferred’s .resolve() to perform the callback of the deferred’s .then()
      • Error: This is to handle an xhr.get failure. Here, you call deferred’s .reject() to reject the deferred, and the deferred handles the error.

      Listing 9 shows the deferred code.

      Listing 9
      var def = new Deferred();
      var bodyContent = attachPoint;
      def.then(function(res){
      	html.set(bodyContent, res);//,{parseContent: true});
      },function(err){
      	domConstruct.create("li", {
                              innerHTML: "Error: " + err
      	}, bodyContent);
      });

      Then() is used to set the callback for the deferred you instantiated. In your callback, you will set a function where you use dojo/html .set() to display the page received as is (as HTML) in the main page. The .set() can take up to three parameters:

      • bodyContent defines the node that will hold the response. Here, it is a reference to an attachpoint.
      • res defines the content that will be set. Here, it is the response coming back from the server.
      • If you need to instantiate any marked up objects, set parseContent to true as a third parameter.
  • contentViewer.html:

    In the template, you will set one attach point for the opening date message (Listing 10), one attach point for the content (Listing 11), and three attach points for the buttons to navigate to the other pages (Listing 12).

    Listing 10
    <h2><div data-dojo-attach-point="openingMessageAP"></div></h2>
    Listing 11
    <div data-dojo-attach-point="bodyContent"<</div>
    Listing 12
    <button data-dojo-attach-point="page1ButtonAP"></button> &nbsp
    <button data-dojo-attach-point="page2ButtonAP"></button> &nbsp
    <button data-dojo-attach-point="page3ButtonAP"></button>

Using resource bundles

Now, let’s return to our discussion about i18n. Using dojo/i18n helps with internationalization of your application by loading a specific resource bundle based on the locale. To do so, you need to define the location of the resource bundle like this:

Dojo/i18n!myUtil/nls/formContent

This means that your localized content is in the class formContent, which is located under myUtil/nls. Under myUtil/nls, you will create a folder for every language you need. In this example, you will create two folders: en-us and fr.

Under each folder, you will have a formContent.js object that will act as a library to all the translation you need. For example, the buttons you have will be labeled “Our Coffee,” “Our Tea,” and “Our Juice.” You need to define these strings in formContent.js in the EN-US folder and their translation in another formContent.js file in the FR folder.

In some cases, you only need short strings in the library to cover cases like button labels or validation messages, but you also need to load whole pages based on the selected language. The formContent.js file will contain references (URL) to the pages or templates that you need.

The folder “Templates” will contain HTML files for content to be displayed on the page.

Figure 2. Code file structure including resource bundles
Figure 2. Code file structure including resource bundles

Let’s look at two examples of properties defined in formContent.js in en-us. Listing 13 shows an example of a simple string and Listing 14 shows an example of a URL to a page.

Listing 13
define(
    {
        openingMessage: "The opening: "
    }
);
Listing 14
define(
    {
        page1: "js/myUtil/nls/en-us/templates/page1.html",
    }
);

Let’s take a look at the same two properties defined in formContent.js in the fr folder. Listing 15 corresponds to Listing 13, above, and Listing 16 corresponds to Listing 14.

Listing 15
define(
    {
        openingMessage: "L'ouverture: "
    }
);
Listing 16
define(
    {
        page1 : "js/myUtil/nls/fr/templates/page1.html"
    }
);

You can see that this holds the same property name but the value is translated in the first case, and for the second case, the url is slightly different, as en-us is replaced by fr to load the French version of the same page.

This method of defining page URLs in the resource bundle enables you to reference pages with the same name regardless of the language. The mapping is done in the resource bundle layer and handled by dojo/i18n.

There is one more formContent.js file, located at the root (myUtil/nls), which is the master resource bundle meant to be used as a backup in case one of the language specific resource bundles doesn’t define one of the properties. You also define the supported locales in this file. For this example, en-us and fr are defined. See Listing 17.

Listing 17
define({ root:
	({
		//set properties
	}),
"fr": true,
"en-us": true
});

customForm Widget

Your custom form is composed of three text fields (given name, surname, and telephone number) and a submit button.

  • customForm.js: You will instantiate all fields in the postCreate function. The text fields will be of type dijit/form/ValidationTextBox, which gives you access to more validation features than regular text boxes.
  • Text boxes: Listing 18 shows how to create the first field for a first name.
    Listing 18
    new ValidationTextBox ({
    	placeHolder: i18n.placeHolder,
    	required : true,
    	regExp: "[a-zA-Z]+"
    }, this.fName);

You instantiate a new ValidationTextBox and set three of its properties:

  • placeHolder: This defines the text to be displayed (grayed out) inside the box for user instructions, such as “Enter Text Here.”
  • Required: Set to true if you want this to be a required field.
  • regExp: A regular expression used for validation. For this example, one is set to validate that the user enters only one word with no numbers.

At the end, you set the node where you want this ValidationTextBox to be displayed. This.fName is an attach point in the template file.

The second text box is for a last name (surname), shown in Listing 19. Here, you define two more properties:

  • invalidMessage: Message to be displayed when the validation fails.
  • Required: Set to true if you want this to be a required field.
  • missingMessage: Message to be displayed when the field is left empty.

Be aware that in the previous example, when you didn’t set the invalid and missing messages, the ValidationTextBox dijit will use the default messages that are already set for you and will use their resource bundles for translation as well. Since the dijits have their own resource bundles, they fallback to them and load their messages from [dijit\form\nls\fr\validate.js].

Listing 19
new ValidationTextBox ({
	placeHolder: i18n.placeHolder,
	required : true,
	invalidMessage: i18n.invalidMessageName,
	missingMessage: i18n.missingMessageName,
	regExp: "[a-zA-Z]+"
}, this.lName);

The third text box (Listing 20) is for a telephone number. Here, you use a different validation method by overriding the validator function.

Listing 20
new ValidationTextBox ({
	placeHolder: i18n.placeHolder,
	required : true,
	invalidMessage: i18n.invalidMessageTelephone,
	missingMessage: i18n.missingMessageTelephone,
	validator: function (value){
		return validate.isNumberFormat(value, {
			format: ["##########", "###-###-####"]
		});
	}	
}, this.tn);

The labels for the three text fields are simply set as attach points in the template file. Assign their values in the class as shown in Listing 21.

Listing 21
this.fNameLabel.innerHTML = i18n.fname;
this.lNameLabel.innerHTML = i18n.lname;
this.tnLabel.innerHTML = i18n.telephone;

Submit Button: Create another dijit/form/Button instance (Listing 22).

Listing 22
new Button({
	label: i18n.submitButtonLabel,
	onClick: lang.hitch (this, function(){
	// Do something:
	this.formContent.hidden=true;
	this.submitFormSuccessMessageAP.innerHTML=i18n.submitSuccessMessage;
	})	
}, this.submitButtonAP);

(Form submission is beyond the scope of this article.)

When the button is clicked, you will simply hide the form and display a success message without any actual processing.

Be aware also that when you did not define a placeholder translation in French, the value is read from the master (fallback) file formContent.js; therefore, even if you select French, you can still get placeholder text in English. This is just an example to display how this master file works.

Figure 3. Dijit resource bundle
Figure 3. Dijit resource bundle

customForm template:

The template will contain the attach points for the text fields, the text fields labels (Listing 23) , the button (Listing 24) and a div that will show the confirmation message after the form has been submitted (Listing 25).

Listing 23
<tr>
	<td valign="top"><strong><div data-dojo-attach-point="fNameLabel">
</div> </strong></td>
	<td><input data-dojo-attach-point="fName"/></td>
</tr>
<tr>
	<td valign="top"><strong><div data-dojo-attach-point="lNameLabel">
</div> </strong></td>
	<td><input data-dojo-attach-point="lName"/></td>
</tr>
<tr>
	<td valign="top"><strong><div data-dojo-attach-point="tnLabel">
</div> </strong></td>
	<td><input data-dojo-attach-point="tn"/></td>
</tr>
Listing 24
<button data-dojo-attach-point="submitButtonAP"></button>
Listing 25
<div data-dojo-attach-point="submitFormSuccessMessageAP"></div>

Note about languages

Dojo Toolkit supports bi-directional text, as in Arabic and Hebrew, but you will have to pay extra attention to styling, and the DIR element must be set to rtl. As of Dojo 1.1, all dijits are bi-directional, but they can only support one direction at a time. In addition, for other languages, you need to take into consideration any special characters and make sure that you support them.

You have now completed your web page development and can begin testing.

Now, suppose that after you launch your website, you need to add more pages. All you need to do is edit the resource bundles and add buttons to your website to reference these pages.

Another scenario might be that you want to add a third language — Spanish, for example. All you need to do to accommodate this is translate the content and upload the result in their own Spanish folder. No code change is necessary.


Conclusion

Using a example website, you saw how you can use the Dojo Toolkit internationalization features to create a localized website that is available in more than one language. You saw how to use dojo/i18n and learned how to create a custom widget and other basic dojo functions, such as using dojo/date/locale, attach points, hitching, validating, and others. Hopefully, this has given you a clearer idea of some of the newest benefits of the Dojo Toolkit, particularly with regard to localization and internationalization.


Download

DescriptionNameSize
Code samplei18nDojo-Code.zip9KB

Resources

Learn

Get products and technologies

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Mobile development
ArticleID=861834
ArticleTitle=Adding internationalization and localization to web applications using the Dojo Toolkit
publish-date=03202013