OBSOLETE? no. needs to be moved to the book.
This document will show how to make a widget show strings in the user's
native language. As a prerequisite, the reader should be familiar
with the
InternationalizationOverview.
There are many aspects to internationalization, but perhaps the most
obvious is to make sure that strings visible to the user are factored
out from the code and made available as translations in the user's
native language. Dojo has a framework to provide such
translations and load them on demand, and it works nicely with widget
templates which allow you to substitute variables directly into the
widget markup. As an example, we will use the validationtextbox widget,
which was localized by
changeset [5201]
Background
src/widget/templates/ValidationTextbox.html
The validationtextbox has three spans
which contain strings. (These strings were already parameterized,
but let's pretend they were hard-coded as English strings)
|
<span dojoAttachPoint='invalidSpan' class='$ {this.invalidClass}'>* The value entered is not valid.</span> |
|---|
| <span dojoAttachPoint='missingSpan' class='$ {this.missingClass}'>* This value is required.</span> |
|---|
| <span dojoAttachPoint='rangeSpan' class='$ {this.rangeClass}'>* This value is out of range.</span> |
|---|
Providing translated resources
We want to pull these strings
out and put them in a bundle so that we
can provide translations. So, we create a directory called "nls"
under src/widget. The nls directory has special meaning and is
assumed to have a directory structure under it representing all the
supported
locales. At the root of the directory is one or more fallback
translation files. We'll set that up as English. It is
essential to put something in the root so that you will provide support
for all users. In our example, this means that
anyone who has a locale which we don't directly support will see
English. There's no need to put a copy of this file under nls/en
by convention,
as that would be redundant. We'll call our
bundle "validate" since it is used by the widget code by the same
name. Bundles are JSON property lists. Javascript-style
comments are allowed, and they may be necessary to provide hints or
instructions to translation houses.
src/widget/nls/validate.js
| 1 |
({ |
|---|
| 2 |
invalidMessage: "* The value entered is not valid.", |
|---|
| 3 |
missingMessage: "* This value is required.", |
|---|
| 4 |
rangeMessage: "* This value is out of range." |
|---|
| 5 |
}) |
|---|
You'll want to provide translations for other languages, but in the
least-specific manner possible. For example, to translate
Japanese, you'd probably want to put the strings under "ja" for
Japanese, rather than "ja-jp" for Japanese spoken in Japan. Even though
pretty much all web usage is probably the latter, it's just better form
to leave things as generic as possible. Country specific overrides are called
variants and may be provided as sparse lists, so if there was a term
that was used only by English speakers in Australia, you could provide that one property separately under en-au.
So, now we can provide a translation for the xx locale:
src/widget/nls/xx/validate.js will contain all of the strings which change, in this case all strings need to be translated.
| 1 |
({ |
|---|
| 2 |
invalidMessage: "* xxxxxxx.", |
|---|
| 3 |
missingMessage: "* xxxxxxxx.", |
|---|
| 4 |
rangeMessage: "* xxxxxxxx." |
|---|
| 5 |
}) |
|---|
If any upper-ASCII or double-byte characters are included, you must
make sure to use UTF-8 encoding when you edit your file.
This is
something you need to determine for your own editor; unfortunately, it
seems there is no way to mark the encoding in SVN. While UTF-8 is
handled properly by most major browsers, there is a known bug in KHTML
(Safari and Konqueror). See
ticket #1010 for a workaround.
Next, you might have a variant xx-yy on xx where only one of those strings changes.
src/widget/nls/xx-yy/validate.js:
| 1 |
({ |
|---|
| 2 |
invalidMessage: "* yyyyyyyyyyy.", |
|---|
| |
|---|
|
|
|---|
3
|
}) |
|---|
Make sure to use lowercase for all your locale directories with dashes
as separators, not underscores. When xx-yy is loaded, it will
pick up "missingMessage" and "rangeMessage" translations from the xx
locale. [Someone please help me here with some actual translated
text!]
Using the translated resources in code
src/widget/validate.js
Now, we need to actually load this bundle into the widget object.
First, we need to declare the bundle at the top of the file:
dojo.requireLocalization("dojo.widget", "validate");This says to look under the dojo.widget package at runtime for the translation in
the user's locale and make sure it's loaded; the one we want
is called validate. Then we need to get and hold a reference to the generated
object. This can be done in the postMixInProperties method with a
single line:
this.messages = dojo.i18n.getLocalization("dojo.widget", "validate", this.lang);It looks a lot like the line you used above. This merely grabs
the appropriate Javascript hash object already in memory. "this.lang" is the lang attribute on
htmlwidget?.
If it is defined in the markup as an override, its value will be used
as the locale instead of the user's default locale. To use the
getLocalization method,
you need to put in the appropriate require at the top of the file:
dojo.require("dojo.i18n.common");We're almost done. Now, go back to the template
src/widget/tempaltes/ValidationTextbox.html
Let's replace those hard-coded English strings with indirect references to
this.messages. The strings are just properties on the object contained by this.messages.
| 5 |
<span dojoAttachPoint='invalidSpan' class='$ {this.invalidClass}'>$ {this.messages.invalidMessage}</span> |
|---|
| 6 |
<span dojoAttachPoint='missingSpan' class='$ {this.missingClass}'>$ {this.messages.missingMessage}</span> |
|---|
| 7 |
<span dojoAttachPoint='rangeSpan' class='$ {this.rangeClass}'>$ {this.messages.rangeMessage}</span> |
|---|
NOTE: Jot won't allow the $ {} syntax -- the space between the $ and the { should not be there!
Testing
You're ready to test. Getting your browser to run in a locale other than your own is
not easy, as most browsers don't let you change the language setting on
the navigator object; it's usually fixed depending on what version of
the browser you install. Fortunately, you can insert a Dojo override into your
test page to use a locale other than your own at runtime for testing:
djConfig = {locale: 'xx'}
If you wish to test
multiple locales on the same page, use djConfig.extraLocale, which may
take either a single locale or an array containing a list of locales.
Builds, performance, and deployment
Note that loading this widget now results in several hits over the
network -- in addition to the hits for the widget class itself, all
referenced Javascript, and the template, you now need to search for the
nls file which may require several hits on its search path. Just
as the build compresses and folds all the Javascript into a single
file, it will now do the same for your resources. All the locales
are flattened so that searching for variants is no longer necessary,
and all used bundles are flattened into a single file per locale.
These files will be put under the "nls" directory next to dojo.js in
the release/dojo directory. Be sure to deploy this directory and
its contents along with your Dojo build.