Tuesday, November 25, 2008

The power of Dojo i18n

I had said at an early stage that I was going to use Dojos i18n system to translate snippets of text in World Change Network. Now that the time came to actually implement it, I realized I had some learning to do to get all bits and pieces right. As it turned out, using Dojos i18n system for you own purposes is really simple.

If you take a look at the unit test for i18n at http://download.dojotoolkit.org/current-stable/dojo-release-1.2.2/dojo/tests/i18n.js you see that it uses three major tricks;

1. Load the i18n strings bundle of your choice using dojo.requireLocalization("tests","salutations",locale);
2. Get a specific bundle using var salutaions = dojo.i18n.getLocalization("tests", "salutations", "en");
3. Get the specific string for a given key using salutations['hello'];

Let's check the arguments for number (1) and (2; Only the first two are needed, which describe the dojo package to look for bundles in and the name of the bundle. If you want to specify another locale than the one the browser declares, it can be added as third argument, "sv" for Swedish, when the browser would say "en-us", et.c.

All well and good, but where do we put our different version of strings? As it turns out, in the dojo/test directory a directory named 'nls' can be found. In the root of that is a file called 'salutations.js'. This is the default key-value translation that i18n falls back on if the locale cannot be found.

Then comes a host of subdirectories ; zh, nl, il, dk, de, et.c et.c., one for each locale (that you want or can define). In each of these is a separate 'salutations.js' containing locale-specific resources.

The file format look like this;

{
it: "Italian",
ja: "Japanese",
ko: "Korean",
......
hello: "Hello",
dojo: "Dojo",
hello_dojo: "${hello}, ${dojo}!",
file_not_found:"The file you requested, ${0}, is not found."
}

for the default file, and like this in the 'it' subdirectory;

{
it: "italiano",
hello: "Ciao"
}

And that's it.

I began creating a small naive custom widget, which subtituted the content of the element where it is declared with the localized string found for the content used as key. It's not very fast, but simple to understand and use.

dojo.provide("layout.translate");

dojo.require("dijit._Templated");
dojo.require("dijit._Widget");
dojo.require("dojo.i18n");

dojo.declare("layout.translate", [ dijit._Widget ],
{
widgetsInTemplate : false,
string : "",

postCreate: function()
{
if (!this.string)
{
this.string = this.domNode.innerHTML;
}
console.log("postCreate for layout.translate called. locale == '"+dojo.locale+"'");
dojo.requireLocalization("layout", "salutations");
var res = dojo.i18n.getLocalization("layout", "salutations");
console.log("Translation key was '"+this.string+"'");
var str = res[this.string];
this.domNode.innerHTML = str;
}

});

Note that I just copied the whole nls directory from dojo/test for my own uses, and edited the files, leaving the original filename intact, just in case :) A better way of utilizing i18n would be to have a base class for all custom widgets, which read in a i18n bundle, and injects all keys into each class, so that every subclass has a lot of this._i18n_command_delete (If we have a naming convention that let all i18n keys begin with '_i18n_' for example).

Then we could have all custom widget templates just sprinkle their markup with a lot of ${_i18n_command_delete} and so on, which would pull in the current value of that 'this' property of the widget when it is rendered in the page.

Hmm....

Come to think of it, it seems to be possible for this to be put inside dijit._Widget, or possibly _Templated, which would make it spread to all custom widgets automatically. The only thing needed would be to prepend '_i18n_' to all key names, so that 'command_delete' inside a bundle file would become this_i18n_command_delete in the widget.

One would also need to have a convention that this only worked if the developer put a 'nls' directory under the package directory where widgets of a certain package are declared, following the order declared earlier.

Actually, this would be a pretty neat idea. Just my fault for using a blog post instead of TRAC to add feature requests! Oh, you mean I could do it myself? OK, I'll add it to the queue :)

Cheers,
PS




8 comments:

Alex said...

I find it much more useful if you can use the original strings as keys and not some arbitrary ones. Eg. have

var msg = i18n.getTranslation("The file you requested, ${0}, is not found.");

instead of

var msg = i18n.getTranslation("file_not_found").

In the global method i18n.getTranslation() you can then return the original string if no translation was found in the resource bundle.

First of all it makes the code more readable because the messages you build for the user are also very helpful comments for understanding the code. Needless to say it removes the burden of looking into the translation file to know what is actually said in the message.

And this also has the advantage that you can write a tool that extracts all strings to be translated from your source code - it just has to look for those i18n.getTranslation() calls (or however you like to name that one).

True xgettext style ;-)

Sean said...

Hi there Peter,

I've been looking into dojo internationalization a little myself. I had this conversation with peller on the dojo forums on this topic: http://dojotoolkit.org/forum/dojo-core-dojo-0-9/dojo-core-support/internationalization-first-steps. I ended up developing a base I18N class from which a lot of my widgets extended. I'd be interested in hearing your opinions on it if you want to take a look ...

Sean

Peter Svensson said...

@Alex: I agree that this looks much better. I'm still struggling with the most unintrisive way to use i18n for large masses of strings - essentialy the whole page, or ContentPane.

Ideally, one would want a system which just exchanged identified strings without magic - but that might lead to other artifacts upstream I guess.

Cheers,
PS

Peter Svensson said...

@Sean: I'll go have a look. Brb.

Cheers,
PS

Sean said...

Hey Peter,

Re-posted what I ended up doing for my dojo i18n example on that same forums posting.

Sean

Dissertation Help said...

This blog is very nice and informative. it is pretty hard task but your post and experience serve and teach me how to handle and make it more simple and manageable.
-------------------------------------------
Dissertation Writing | Dissertation Advice

dissertation said...

it's good to see this information in your post, i was looking the same but there was not any proper resource, thanx now i have the link which i was looking for my research.

UK Dissertations Help

Alexa said...

The power of Dojo i18n <-- that's what i was looking for
Dissertation Help UK