Internationalization with the Dojo Toolkit

In this tutorial, you'll learn how the Dojo Toolkit supports internationalization (i18n), including the use of dictionaries for string substitution, and how basic locale formatting for things such as Date objects, numbers and currencies are supported.

  • Difficulty: Intermediate
  • Dojo Version: 1.6

Getting Started

Creating a web application for a specific target market is usually a daunting task, but libraries such as the Dojo Toolkit ease that task by providing tools to quickly assemble user interfaces (i.e. Dijit), supporting code to ease the pain of cross-browser compatibility, and various add-ons/plug-ins for specific functionality such as the DojoX Charting package.

No such toolkit would be complete without the ability to localize an application—in other words, the ability for an application to be presented using language based on the location of the application's user. The Dojo Toolkit provides this ability for application developers through the use of dojo.i18n.

A Short History

In software development, two terms are used when discussing the ability to display application elements based on a user's locale: internationalization (or i18n) and localization (or L10n). While many use these terms interchangably, there is a subtle difference between the two:

  • An application is considered localized when language elements are hard-coded or built directly into the executable;
  • An application is considered internationalized when the preparation of language elements are built into the executable; in this case, it means that specific language elements are not substituted until run-time.

If you are wondering where "i18n" and "L10n" come from, it is an old computer science tradition that lists the number of letters in the term that are abbreviated, and is wrapped with the first and last letters of the term. The capital "L" for localization is normally used to distinguish the term from i18n. A good explanation of the concept and differences can be found at Wikipedia.

Because applications built using the Dojo Toolkit are dynamic in nature (i.e. compiled and executed at run-time), they are considered to be internationalized and not localized.

Internationalization with the Dojo Toolkit takes two forms: the ability to define resource bundles that can be loaded based on a user's locale, and special built-in i18n facilities based on the Unicode CLDR for dates, numbers and currencies.

Locales with the Dojo Toolkit

In order for an application to know what resources it may need to use for i18n, a locale must first be defined.

What are locales?

Locales are a short string, based on a language code and a country code, separated by a dash. For example, the locale for a typical user in the United States is en-us.

Normally, the locale is determined for a browser during the browser's installation, and cannot be easily configured.

When loading a Dojo Toolkit-based application, the user's locale is detected automatically and can be found programmatically through the dojo.locale property. This is the normal usage scenario; however, there may be times (particularly when testing your application with resource bundles) where you would like to specify a locale.

Specifying a locale

If you need to specify a locale, you may do so by setting a locale property on the Dojo configuration object, like so:

//	before dojo.js is loaded
var dojoConfig = {
	locale: "pt-pt"
};

//	or in the script tag:
<script src="path/to/dojo.js" data-dojo-config="locale:'pt-pt'"></script>

You should always include the full locale (i.e. language + country) when specifying a locale, even though many resource bundles are defined by language only (as you'll see later on).

Once a Dojo Toolkit-based application is loaded, it is not possible to change the locale.

When using the dojo/parser, the lang= setting on an ancestor DOMNode overrides the dojoConfig.locale setting. This behavior will change in Dojo 2.0. You can also specify the lang for individual widgets, overriding the dojoConfig.locale setting for only that widget.

Including extra locales

There may be times when you want to include more than one locale-specific resource—for instance, if you need to display date resources in more than one language. While this is not the normal use-case, the Dojo Toolkit allows you to include more locale-based resources through the use of the extraLocale property of the Dojo configuration object, like so:

//	before dojo.js is loaded
var dojoConfig = {
	locale: "pt-pt",
	extraLocale: ["zh-cn","zh-tw"]
};

//	or in the script tag:
<script src="path/to/dojo.js" data-dojo-config="locale:'pt-pt',extraLocale:['zh-cn','zh-tw']"></script>

These extra locales can be passed into methods such as dojo.date.format, or optionally used with specific Dijits by setting the lang attribute/property when instantiating them.

Resource Bundles

A resource bundle is a file containing a JavaScript object literal (or dictionary) of terms used by your application code for a specific locale. When a user's locale is detected (and dojo.i18n is required), you can use the i18n facilities within the Dojo Toolkit to load the resource bundle for a specific locale. For example, here is the Japanese resource bundle for Dijit's ValidationTextBox widget:

define(
({
	invalidMessage: "入力した値は無効です。",
	missingMessage: "この値は必須です。",
	rangeMessage: "この値は範囲外です。"
})
);

If you're wondering what define is here, it is part of the transition the Dojo Toolkit is making towards using an Asynchronous Module Loader. Stay tuned for more details as the Dojo Toolkit progresses towards 2.0!

There are a few things to note here about the structure of this resource bundle:

  • The fields of the bundle correspond to fields that are referenced within dijit.form.ValidationTextBox (for instance, myValidationWidget.invalidMessage);
  • Not all fields need to be defined in a specific resource bundle;
  • Not all language/country combinations need to be defined.

Let's take a look at how to create a resource bundle, as part of a larger set of resource bundles.

Creating resource bundles

The first step in creating resource bundles is to create a subdirectory called nls where your code lives, like so:

The directory structure for Dijit

Note that the directory must be named "nls", and must be a subdirectory of the code that will be using it. In our example above, we have i18n directories for both the root of Dijit (i.e. for any widget that lives directly under the dijit namespace) and for dijit.form.

Inside the nls directory for your namespace, you'll add both individual files and directories based on either the international language code, or the full locale (such as pt-pt):

Contents of nls for dijit.form

The name of each resource bundle does not matter, though by convention it should be close to the purpose of it. For example, in our screen shot above, there are three main resource bundles in the root of /nls: ComboBox.js, Textarea.js and validate.js. The first two resource bundles are aimed at a specific widget, while the last is intended for use by any validation widget.

The resource bundles in the root of the /nls folder are intended to be master bundles; what this means is that any and all properties to be consumed must be defined within these files. The reason for this is because the "master" bundle is the fallback for when a particular property does not exist in a specific language-based resource bundle. For example, if your master resource bundle defines a property called message but the locale resource fr does not define it, the value of message in the master resource bundle will be used in its place—in other words, when a locale is detected and a resource bundle is loaded, the locale-specific bundle will be mixed into the master bundle.

You do not have to create locale-specific bundles for all possible languages/locales; in the case that a locale is detected but no locale-specific resource exists, the master bundle will be used in full.

In your language or locale directory, create identically named resource bundles as the master bundles; in each locale-specific bundle, define whatever properties you will be using in your code.

Finally, in your master bundle (the main one in the root of the /nls folder, add properties matching each locale you've defined and set the value of each to true. As an example, here is the master bundle for the validate.js resource, followed by our localized Japanese version:

//	the master bundle, from dijit/form/nls/validate.js:
define({
	root: ({
		invalidMessage: "The value entered is not valid.",
		missingMessage: "This value is required.",
		rangeMessage: "This value is out of range."
	}),
	"zh": true,
	"zh-tw": true,
	"tr": true,
	"th": true,
	"sv": true,
	"sl": true,
	"sk": true,
	"ru": true,
	"ro": true,
	"pt": true,
	"pt-pt": true,
	"pl": true,
	"nl": true,
	"nb": true,
	"ko": true,
	"kk": true,
	"ja": true,
	"it": true,
	"hu": true,
	"he": true,
	"fr": true,
	"fi": true,
	"es": true,
	"el": true,
	"de": true,
	"da": true,
	"cs": true,
	"ca": true,
	"ar": true
});

//	our localized Japanese resource, from
//	dijit/form/nls/ja/validate.js:
define(
	({
		invalidMessage: "入力した値は無効です。",
		missingMessage: "この値は必須です。",
		rangeMessage: "この値は範囲外です。"
	})
);

Notice that in our "master" bundle, the properties to be consumed by your code is a full JavaScript object literal called root, while the localized Japanese version is a straight-up object literal. You must follow this form for i18n to work correctly.

Consuming resource bundles

To consume resource bundles in your application code, you'll rely on the dojo.i18n module along with two basic constructs: dojo.requireLocalization and dojo.i18n.getLocalization. These two methods work hand-in-hand; requireLocalization fetches and assembles the locale-specific bundle, and getLocalization returns the JavaScript object literal fully assembled.

Both requireLocalization and i18n.getLocalization have the same method signature: the main namespace under which the resource bundles live, and the name (without the .js extension) of the bundle to load. In addition, i18n.getLocalization can take one more argument: the locale of the resource bundle to load. Here's how the resources are loaded within dijit.form.ValidationTextBox:

//	at the top of ValidationTextBox.js:
dojo.require("dojo.i18n");
dojo.requireLocalization("dijit.form", "validate");

//	later on, in the declaration of the widget:
postMixInProperties: function(){
	this.inherited(arguments);
	this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
	if(this.invalidMessage == "$_unset_$"){
		this.invalidMessage = this.messages.invalidMessage;
	}
	if(this.missingMessage == "$_unset_$"){
		this.missingMessage = this.messages.missingMessage;
	}
}
//	Note that some lines in postMixInProperties have been removed for brevity.
View Demo

You'll see that i18n.getLocalization returns a JavaScript object, which we assign to the property messages; we then read that object to populate the properties of the widget that require localization.

With the release of the Dojo Toolkit 1.6 and the gradual transition to AMD, requireLocalization is no longer necessary; instead, the loading of the resource bundle is handled by an AMD plugin designed specifically for loading i18n resources.

You'll also note that these localized properties are set in the postMixInProperties method of dijit.form.ValidationTextBox; this is the proper place to set up your i18n within any custom widget code. See Understanding _Widget for more information.

Creating builds with resource bundles

A typical step in deploying a Dojo Toolkit-based application is to create a build of your application; a build minifies your JavaScript, inlines HTML and CSS, and generally makes your application run much more efficiently. The Dojo Build Tools also provides ways of flattening your resource bundles through the command line parameter localeList.

Normally you won't need to include this command line parameter to create your build; by default, the following list of locales is included:

"en-gb,en-us,de-de,es-es,fr-fr,it-it,pt-br,ko-kr,zh-tw,zh-cn,ja-jp"

However, if your application needs more than these locales defined, you can write your own comma-delimited list as the value of the localeList parameter.

A common "gotcha" when creating internationally-aware applications is to forget to copy over any /nls directories to your Dojo Toolkit release. If you create a build and run into errors loading the application the first time, it is likely that some /nls directories were not copied during the course of the build process; to fix simply copy the missing directories into your release, in the appropriate spots.

Dates, Numbers and Currencies in the Dojo Toolkit

Internationalization in a toolkit would not be complete without the ability to parse and format dates, numbers and currency in locale-specific formats. The Dojo Toolkit provides this functionality with dojo.date.locale, dojo.number and dojo.currency.

You can learn more about working localization and dates specifically in the Dojo Date tutorial.

Locale-aware dates with the Dojo Toolkit

As with our resource bundles, dojo.date.locale provides locale-specific formatting and parsing of dates, like so:

//	with our config object:
var dojoConfig = {
	extraLocale: [ "zh-cn", "ja-jp" ]
};

//	after dojo.js has been loaded:
dojo.require("dojo.date.locale");

var d = new Date(2006,9,29,12,30);

// to format a date, simply pass the date to the format function
dojo.date.locale.format(d);
// => "10/29/06 12:30 PM"

// the second argument may contain a list of options in Object syntax,
// such as overriding the default locale
dojo.date.locale.format(d, {locale:'zh-cn'});
// => "06-10-29 下午12:30"

For more information about working with Date objects with the Dojo Toolkit, see the Dojo Date tutorial.

Note that if you plan on formatting and parsing dates, numbers and currencies in a number of different locales, you must set the extraLocale property on the Dojo configuration object before loading dojo.js.

Locale-aware number formatting

Like dojo.date.locale, dojo.number uses the Dojo CLDR (see below) to correctly format numbers based on a locale. For example, an American user might expect to see the number "1000000" (one million) formatted like so:

// => 1,000,000

...but German, French and Indian users would expect the following formats:

// => 1.000.000,00  German
// => 1 000 000,00  French
// => 10,00,000.00  Indian

Like with dojo.date.locale, you can pass an optional arguments object that specifies the locale to format or parse a number with (otherwise it will use the current locale as detected by the browser):

dojo.require("dojo.number");

console.log(dojo.number.format(100000, { locale: "hi-in" }));
// => 10,00,000.00

console.log(dojo.number.parse("10,00,000.00", { locale: "hi-in" }));
// => 1000000

Locale-aware currencies

Finally, dojo.currency will display numbers formatted to the correct currency—or optionally based on the three-letter ISO code for that currency—using the same basic function signature as dojo.number:

dojo.require("dojo.currency");

// in the United States
dojo.currency.format(1234.567, {currency: "USD"});
// => "$1,234.57"

//	basic Euro formatting
dojo.currency.format(1234.567, {currency: "EUR"});
// => "€1,234.57"

// a French-speaking Swiss user would see
dojo.currency.format(-1234.567, {currency: "EUR"});
// => "-1 234,57 €"

// while a German-speaking Swiss user would see
dojo.currency.format(-1234.567, {currency: "EUR"});
// => "-€ 1,234.57"

Note that dojo.currency combines locale information with currency information, so that the formatting for a particular user is always correct. See the Dojo Toolkit API for more details.

Other internationalization resources

In addition to the basic i18n facilities, the Dojo Toolkit also implements a version of the Unicode CLDR and supports bi-directional text.

The Dojo CLDR

The dojo.cldr (or Common Locale Data Repository) contains tables with culturally sensitive information for hundreds of languages and country variants for locale-specific information, such as days of week, currency symbols and formats, and more. In addition to being used by date, number and currency-specific functions in the Dojo Toolkit (see the above section), it also contains some methods that allow you to find locale-based things. For example, if you want to find out how to format a currency (by learning how many places and rounding should be), you'd pass an ISO code to dojo.cldr.monetary.getData, like so:

dojo.require("dojo.cldr.monetary");

//	get the formatting data for a euro
console.dir(dojo.cldr.monetary.getData("EUR"));

//	should display the following:
{
	places: 2,
	round: 0
}

For more information on using the dojo.cldr directly, check out The Dojo Reference Guide.

Bi-directional Language Support

Some languages (particularly Middle Eastern in origin, such as Hebrew and Arabic) read right-to-left as opposed to the usual left-to-right. The Dojo Toolkit supports bi-directional text, but there are some caveats:

  • The DIR attribute on either the html or body element must be set to rtl;
  • Dojo functions, including those in Dijit, only support one text direction at a time;
  • Special considerations (such as CSS specific to right-to-left text) must be taken into account.

Full information on how bi-directional text is supported throughout the Dojo Toolkit can be found in the Dojo Toolkit Reference Guide, but the definition of resource bundles for right-to-left text remains the same as with any other locale.

Conclusion

The Dojo Toolkit provides full-features internationalization tools to aid you in creating localized applications. User interface elements benefit from the Dojo Toolkit's i18n resource bundle system, and also gives you built-in formatting and parsing capabilities for dates, numbers and currencies. In addition, the Dojo Build system is capable of "compiling" resource bundles et al to help make your application download and run more efficiently.

We hope that the information contained in this article aids you in creating full-featured applications that can be used by users all around the world!

Error in the tutorial? Can’t find what you are looking for? Let us know!