Chapter 6. Internationalization (i18n)
This chapter provides a brief synopsis of the tools Dojo provides for internationalizing a module. The key topics are defining bundles on a locale basis and using Core's facilities for formatting and parsing dates, currency, and numbers. In case it wasn't quite obvious, internationalization is usually abbreviated as i18n simply because it is such a long word to type; thus, the shorthand is the first and last letters, with the number 18 between them to take place of the 18 letters in between.
Introduction
If you have the good fortune of developing an application that becomes even mildly popular, you will certainly want to consider supporting more than one language. Although Dijit, which you'll learn about in Part II, “Dijit and Util” of this book, is already internationalized with several common locales, custom modules and widgets of your own devising will require some special attention. Fortunately, the toolkit provides techniques for supporting more than one language in a highly uniform way, saving you the headache of inventing your own system of mapping tokens back and forth; because Dojo manages how the loading takes place, you are also freed from thinking of ways to optimize the loading. Additional utilities also support common operations involving numeric formatting, currency, and more.
It's worth pointing out that the i18n facilities are technically
part of Core, not Base. XDomain builds; however, include the dojo.i18n module as part of
dojo.xd.js at the expense of an extra 2KB to
workaround some tricky loading issues involving i18n bundles.
Regardless, you should still dojo.require("dojo.i18n") into your page to
be explicit about your intention to use these facilities as a matter
of good form.
Internationalizing a module you've developed is simple because
the details of your string table stay compartmentalized inside of a
special nls directory that appears
in your module directory with your JavaScript source files; nls stands for native language
support. The nls
directory itself breaks down all of the various translations by
abbreviations for locales as defined in RFC 3066 (Tags for the
Identification of Languages).[14]
For example, the abbreviation for generic English is
en, the abbreviation for the dialect of English
as it is spoken in the United States is en-us,
and the abbreviation for generic Spanish is es. During the bootstrap process, Dojo
queries your browser for your particular locale, and stores it
internally. You can check it by typing dojo.locale in Firebug. The value of
dojo.locale is what Dojo uses to
determine the most appropriate translation when loading a module that
has been internationalized.
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, Language and Browser Utilities 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, “Localization functions”, that are involved in the process.
Table 6.1. Localization functions
|
Name |
Comment |
|---|---|
|
|
Returns an |
|
|
Returns the canonical form of a locale. |
|
|
Loads translated
resources in the same manner as |
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, Build Tools, Testing, and Production Considerations.
Dates, Numbers, and Currency
Additional Core facilities provide additional support for
manipulating and supporting internationalization of dates, numbers,
and currency via the dojo.date,
dojo.number, and dojo.currency modules, respectively. In
Part II, “Dijit and Util”, you'll learn that Dijit makes
extensive use of these modules to provide advanced support for
commonly used widgets. This section provides a quick inventory of
these features.
Dates
Table 6.2, “Summary of the date module” shows a quick
overview of the dojo.date module.
As you'll see, there are some real gems in here if you ever need to
perform any routine processing of the built-in Date object.
Table 6.2. Summary of the date module
|
Name |
Return type |
Comment |
|---|---|---|
|
|
|
Returns the number of
days in |
|
|
|
Returns |
|
|
|
Returns time zone
information as defined by the browser. A |
|
|
|
Returns 0 if the two
parameters are equal; returns a positive number if |
|
|
|
Provides a convenient
way to add an incremental amount to a |
|
|
|
Provides a convenient
way to calculate the difference between two |
Warning
As of version 1.1 of the toolkit, getTimezoneName is not localized.
Numbers
The dojo.number module
provides some handy functions, shown in Table 6.3, “Formatting options for the number module that are used in
the dojo.number.format and dojo.number.parse functions provided in
Table 6.4, “Summary of the number module”” and Table 6.4, “Summary of the number module”, for parsing String values into numbers, formatting a
Number in accordance with a
specific pattern template, or rounding to a specific number of
decimal places.
Table 6.3. Formatting options for the number module that are used in the dojo.number.format and dojo.number.parse functions provided in Table 6.4, “Summary of the number module”
|
dojo.number.format options |
Type |
Comment |
|---|---|---|
|
|
|
Can be used to override the formatting pattern. |
|
|
|
A format type based
on the locale. Valid values include |
|
|
|
Provides a fixed number of places to show, which overrides any information provided by pattern. |
|
|
|
Specifies rounding properties based on a multiple. For example, 5 would round to the nearest 0.5 and 0 would round to the nearest whole number. |
|
|
|
A currency code that meets the ISO4217 standard. For example, "USD" would signify U.S. Dollars. |
|
|
|
A localized currency symbol. |
|
|
|
Allows a specific locale to be provided which drives formatting rules. |
Table 6.4. Summary of the number module
|
Name |
Return type |
Comment |
|---|---|---|
|
|
|
Formats a |
|
|
|
Rounds a number to a given number of places after the decimal. |
|
|
|
Converts a properly
formatted |
Currency
The dojo.currency module,
described in Table 6.5, “Formatting options for the currency module as used by the
dojo.currency.format and dojo.currency.parse functions”
and Table 6.6, “Summary of the currency module”, is similar to
dojo.number in that it provides a
means of formatting numeric values, only this time it is currency
codes as defined in ISO427.[15]
Table 6.5. Formatting options for the currency module as used by the dojo.currency.format and dojo.currency.parse functions
|
Name |
Type |
Comment |
|---|---|---|
|
|
|
A three-letter
currency code as defined in ISO4217 such as |
|
|
|
A value that may be used to override the default currency symbol. |
|
|
|
Used to override the default currency pattern. |
|
|
|
Used to provide
rudimentary rounding: |
|
|
|
Override the default locale, which determines the formatting rules. |
|
|
|
The number of decimal places to accept (default is defined by currency.) |
Table 6.6. Summary of the currency module
|
Name |
Return type |
Comment |
|---|---|---|
|
|
|
Formats a |
|
|
|
Converts a properly
formatted |
Tip
Some of Dojo's build tools can be used to generate support for arbitrary locales and currencies since a lot of this work simply entails building lookup tables of information. See the file located at util/buildscripts/cldr/README for more details.





Add a comment



Add a comment