Internationalizing a Module

Let's assume that you've gotten with the times and expanded on your magic genie module from the "Building a Magic Genie Example Module" in Chapter 2 to produce a psychic module. Let's assume that your default language is English, and you've determined that the first additional language you should support is Spanish.

Layout on Disk

Like any other module, your psychic readings module should simply be a source file contained in a typical directory structure:

dtdg/
  psychic/
    Psychic.js /* Lots of useful stuff in here */

Not surprisingly, an incredible utility provided by your psychic module is the ability to predict the future. As such, users of your module might stick it in a page and use it like so:

<script type="text/javascript">
    dojo.require("dtdg.psychic");
    dojo.addOnLoad(function(  ) {
      dtdg.psychic.predictFuture(  );
    });
</script>

Although there's an awful lot of real magic that happens in the predictFuture function, the part that we're interested in at the moment is where a String value actually gets written to the screen because that's where the internationalization work happens. As it turns out, the output from your module gets written out with the following logic:

dojo.byId("reading").innerHTML = predictFuture( /* magic */ );

As a first stab at internationalization, start out with plain old English and plain old Spanish, ignoring any particular dialects. Given this decision, the nls directory might look something like the following:

dtdg/
  psychic/
    Psychic.js
    nls/
        readings.js /* The default English translation bundle */
      es/
        readings.js /* The Spanish translation bundle */
      en/
        /* The default English translation folder is empty, so
           Dojo looks one level up for it in the nls/ directory */

By convention, each of the .js files containing translation information is called a bundle. The convention used is that the default translation bundle appears in the top level of the nls directory, but not in a language-specific directory. The basic rationale for this convention is that you always want a default translation to be available in the nls directory, which is the most logical place for it, and there's no value in including an exact copy of the default translation bundle in its own directory, (en in this case) because that would just be one more thing to keep up with.

Defining String Tables

Here's an excerpt from each of the readings.js files that shows some of the strings that are translated as part of the final reading.

First, the default readings.js file:

{
/* ... */
reading101 : "You're a Libra, aren't ya darling?",
reading102: "Can you please tell me your first name only, and your birthday please?",
reading103: "Yep, that's the Daddy."
/* ... */
}

And now, the es/readings.js file:

{
/* ... */
reading101 : "¿Eres un Libra, no, mi corazón?",
reading102: "¿Me puedes dar el nombre y tu cumpleaños por favor?",
reading103: "Sí, el es papá"
/* ... */
}

One of the beautiful things about localizing your application with Dojo is the simple manner in which you provide the listing of tokens.

Putting It All Together

It's time to put it all together and show just how easy it is to support multiple languages, but first, have a look at the relevant functions, listed in Table 6-1, that are involved in the process.

Table 6-1. Localization functions

Name

Comment

dojo.i18n.getLocalization(/*String*/moduleName, /*String*/bundleName, /*String?*/locale)

Returns an Object containing the localization for a given resource bundle in a package. By default, locale defaults to dojo.locale ; however, providing an explicit value allows you to look up a specific translation.

dojo.i18n.normalizeLocale(/*String?*/locale)

Returns the canonical form of a locale.

dojo.requireLocalization(/*String*/moduleName, /*String*/bundleName, /*String?*/locale)

Loads translated resources in the same manner as dojo.require would load modules. Note that this function is a Base function, not part of Core's i18n module.

Whereas you previously might have looked up reading102 value from a hash value like psychic.reading102, you now do it with help from the toolkit. If you've provided a translation for a particular user's locale, everything "just works." Looking up symbols for your various translations is as simple as the following generic piece of logic:

/* Require in Dojo's i18n utilities first... */
dojo.require("dojo.i18n");

/* Then, require in your various translations */
dojo.requireLocalization("psychic", "readings");

function predictFuture(  ) {

  /* Deep inside of your predictFuture function somewhere... */
  var future= dojo.i18n.getLocalization("psychic", "readings").reading597;
  return future;
}

Note that you can change your value of dojo.locale if you'd like to test out various translations. A good place to change this value is in djConfig block. Here's an example of how you might test out your Spanish translation from a local installation:

<head>
    <script type="text/javascript" src="your/path/to/dojo.js"
    djConfig="dojo.locale:'es'">
    </script>
</head>

<!--
  All of your internationalized modules now use the Spanish translation
-->

Warning

Just like any other module or resource, don't call dojo.i18n.getLocalization as part of an object property definition; instead, call dojo.i18n.getLocalization in a dojo.addOnLoad block:

dojo.addOnLoad(function(  ) {
     //Returns a localized Object
     var foo = {bar : dojo.i18n.getLocalization( /* ...*/)}
});

A nuance you may want to be aware of is that if your default locale is a variant of English and you are testing the Spanish localization, both the nls/es/readings.js and the nls/readings.js bundles are loaded. In fact, the default bundle that is contained in the nls/ directory will always be loaded. You can use Firebug's Net to verify this behavior for yourself.

Although this particular example didn't involve any dialects of either language, note that dialects are most certainly taken into account when loading localized bundles. For example, if your locale was en-us and there had been an en-us bundle provided, Dojo would have attempted to load both the en-us bundle and the en bundles, flattening them into a single collection for you to query via your various dojo.i18n.getLocalization calls. The working assumption is that when defining locale specific symbols for English, you want to provide as much general information as possible in the en bundle and then override or fill in gaps inside of the dialect specific bundles such as en-us.

Use build tools for snappy performance

As a final yet very important observation about internationalization, note that the Dojo build tools provided in Util can automatically take care of the myriad details associated with minimizing the number of synchronous calls and data redundancy when you perform a custom build of your module. It may not seem like much at first, but the build tools combine what could be lots of small resource files together and avoid all of the lookups and the latency that goes along with them. In terms of a snappy page load, it can really make all the difference. Util and the build tools are discussed in Chapter 16.

Get Dojo: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.