mozdev.org

czplugins

Localizing Chatzilla Plugins

Author: John J Foerch

In this article I will show you the simple steps to internationalize your plugin. Mozilla makes this task easy for you by providing tools for determining the locale and loading localized strings into memory. Chatzilla also facilitates internationalization of plugins by full support for Unicode.

We will build graceful fallback into our locale support from the beginning. This will ensure that missing or incomplete locale files will not break your plugin. Define a constant __default_locale in the global scope of your plugin. It does not matter what language the locale is in, so much as it matters that it be complete, and always packaged with the plugin.

const __default_locale = 'default';

The core code for determining the current locale name follows. On disk, Mozilla stores locale information in a file called chrome/chrome.rdf in each profile directory. Browsing this file, you can see that each individual component of Mozilla can have its own locale. In this example, we will request the locale name used by 'global'. We could also request the locale name for 'chatzilla'.

var cls = '@mozilla.org/chrome/chrome-registry;1';
var service = Components.interfaces.nsIXULChromeRegistry;
locale = Components.classes[cls].getService(service).getSelectedLocale('global');

This code will throw an exception if it encounters a problem fetching the name. We will put it into a try block, and put that try block into a function.

function localeName () {
    try {
        var cls = '@mozilla.org/chrome/chrome-registry;1';
        var service = Components.interfaces.nsIXULChromeRegistry;
        return Components.classes[cls].getService(service).getSelectedLocale('global');
    } catch (e) {
        return __default_locale;
    }
}

Your plugin needs to know where to find the locale data. We will store locale data in a subdirectory of the plugin directory, called 'locale'. We can get a specific resource locator for this directory by chopping the script file name off of a variable provided by Chatzilla, this.plugin.url, and appending 'locale/'. The locale files in this directory will be named as simply as possibly with no file extension. Make the following variables in the global scope of your plugin. Read these lines carefully and grok what each one contains.

var pluginURL = String(this.plugin.url).replace(/\/[^\/]*$/,'/');
var localeURL = pluginURL + 'locale/' + localeName();
var fallbackLocaleURL = pluginURL + 'locale/' + __default_locale;

Now the ball is starting to roll. It is time to make your default locale file. Make the directory 'locale' in your plugin directory, and create a file with the same name as your __default_locale constant. In this tutorial, the file name will be "default". Mind capitalization of the directory and file names, as some operating systems are case sensitive.

The locale file contains lines that look like:

some.variable = Text associated with this variable.

We will name our variables so that they will not conflict with other locale variables, by prefixing them with plugin.name_of_plugin. For example:

plugin.myplugin.shortdescription = This is my super plugin.

Your locale file can contain blank lines and comments. Any line that begins with # will be treated as a comment and ignored by the parser.

Now for the code that loads the locale strings into memory. This is done by passing the URL of the locale file to client.messageManager.addBundle(). First add two variables to the global scope of your plugin. One called defaultBundle, which will point to the preferred bundle. And one called fallbackBundle, which will point to a bundle that is known to be present and functional.

var defaultBundle;
var fallbackBundle;

Then make a function to load the two locale bundles into memory. The calls to function addBundle need to be in try blocks, because addBundle will throw an exception if it has a problem when loading the data.

function loadLocale() {
    try { 
        fallbackBundle = client.messageManager.addBundle (fallbackLocaleURL);
    } catch (e) {
        display (__id + ' plugin: '+ __default_locale +
                 ' locale file required, but not found',MT_ERROR);
        plugin.enabled = false;
        return;
    }
    if (localeURL == fallbackLocaleURL) {
        defaultBundle = fallbackBundle; 
        return;
    }
    try {
        defaultBundle = client.messageManager.addBundle (localeURL);
    } catch (e) {
        defaultBundle = fallbackBundle;
    }
}

The function loadLocale() should be called first thing in your initPlugin() function.

function initPlugin () {
    loadLocale ();

    plugin.id = __id;
    plugin.major = __maj_version;
    plugin.minor = __min_version;
    plugin.version = __maj_version +'.'+ __min_version;
    plugin.description = getMsg ('description');
}

The mechanism for loading strings from the locale are the methods getMsg and getMsgFrom in client.messageManager. The function getMsg will search all locale bundles for the given message. The function getMsgFrom will retrieve a message from one specific bundle. We will make a function in our plugin that calls client.messageManager.getMsgFrom first on the prefered locale bundle (defaultBundle) and if that fails, on the fallback bundle (fallbackBundle). As an added convenience, our function will prefix the passed name with 'plugin.' + __id + '.' if it receives a name that does not contain any dots. Therefore, to load the string associated with plugin.myplugin.description, you call getMsg('description').

function getMsg (name) {
    if (name.indexOf ('.') == -1) { name = 'plugin.' + __id +'.'+ name; }
    var msg = client.messageManager.getMsgFrom (defaultBundle, name, null);
    if (msg == name || msg == null) {
        msg = client.messageManager.getMsgFrom (fallbackBundle, name, null);
    }
    return msg;
}

This should get you up and running with localization of your plugins. If you have questions about this topic, feel welcome to ask them in the czplugins newsgroup.

Send questions and comments about project czplugins to the czplugins newsgroup.
For questions or comments about mozdev.org, please visit the mozdev feedback page.
Copyright © 2005. All rights reserved. Terms of Use.