Subscribe to RSS Feed

I have already mentioned a few times that we'll take a look at how to properly localize our extension. Even if the process is nothing new for experienced extension programmers, I think it should be part of this tutorial.

First of all, let's extend our directory structure. We'll need a new place where to put localized strings.

helloworld@janodvarko.cz/
    chrome/
        content/
            helloworld/
                helloWorld.xul
                helloWorld.js
        locale/
            en-US/
                helloWorld.dtd
                helloWorld.properties
    defaults/
        preferences/
            prefs.js
    chrome.manifest
    install.rdf

There are two new files: helloWorld.dtd contains entities that are used to localize strings contained in XUL files and helloWorld.properies is used to localize strings in JavaScript code.

To let Firefox know about these files, we need to modify our chrome.manifest file and append following line to it.

locale  helloworld  en-US       chrome/locale/en-US/

Localizing strings in XUL files

All strings in our helloWorld.xul are localized using helloWorld.dtd locale file. This DTD file contains list of entities with corresponding localized strings. In our extension we have to localize just a toolbar button. So, we need two following entities defined in it.

<!ENTITY  helloworld.mybutton.label         "Say Hello">
<!ENTITY  helloworld.mybutton.tooltip       "Push to say hello">

In order to use these entities within the helloWorld.xul file we have to create a reference to the locale file.

<!DOCTYPE helloworldDTD SYSTEM "chrome://helloworld/locale/helloWorld.dtd">

Sometimes you need to reference multiple DTDs from single XUL file. Here is an article that explains how to do it.

Now, we have to replace the strings by the entities that comes from the locale file. Following source code shows how the toolbar button definition looks after the replacement (see label and tooltiptext attributes).

<toolbarbutton id="hwMyButton"
    class="toolbar-text-button"
    label="&amp;helloworld.mybutton.label;"
    tooltiptext="&amp;helloworld.mybutton.tooltip;"
    command="cmd_hwMyButton"/>

Localizing strings in JavaScript code

There is also a few strings in our JavaScript code that must be localized as well. We'll do it by using a standard string bundle that gets the strings from helloWorld.properties file (see more about property files). This file contains following set of strings (name=value pairs).

helloworld.option1=Option1
helloworld.option2=Option2
helloworld.paneltitle=Hello World!
helloworld.message=Hello World!

Now, when we have the property file ready, we need to create a string bundle. There are two ways how this can be done. We can use nsIStringBundleService and read the property file in JavaScript.

var src="chrome://helloworld/locale/helloWorld.properties";
var localeService =
    Components.classes["@mozilla.org/intl/nslocaleservice;1"]
    .getService(Components.interfaces.nsILocaleService);
var appLocale = localeService.GetApplicationLocale();
var stringBundleService =
    Components.classes["@mozilla.org/intl/stringbundle;1"]
    .getService(Components.interfaces.nsIStringBundleService);
var bundle = stringBundleService.CreateBundle(src, appLocale);

See chapter 11. Localization from Creating Application with Mozilla book for more information about string bundles.

The other way is to define the bundle in helloWorld.xul, using stringbundleset and stringbundle elements. This method is simpler and used in our extension.

<stringbundleset id="stringbundleset">
    <stringbundle id="strings_helloWorld"
        src="chrome://helloworld/locale/helloWorld.properties"/>

</stringbundleset>

As soon as the string bundle is ready we have to make sure the JavaScript code loads every string from it instead of using just literal strings. In order to make things a bit easier we'll define two helper functions that load a given string from the string bundle and returns its localized value.

function $HW_STR(name)
{
    return document.getElementById("strings_helloWorld").getString(name);
}

function $HW_STRF(name, args)
{
    return document.getElementById("strings_helloWorld")
        .getFormattedString(name, args);
}

Following source code shows how the $HW_STR method is used to localize Options menu items defined in the previous part.

getOptionsMenuItems: function(context)
{
    return [
        this.optionMenu($HW_STR("helloworld.option1"),
            "helloworld.option1"),
        "-",
        this.optionMenu($HW_STR("helloworld.option2"),
            "helloworld.option2")
    ];
}

If you need to put some dynamic values into the localized string use the $HW_STRF method. This method takes an extra parameter - array of arguments, which are substituted into the string in the bundle. See the following example how to use it.

Here is a localized string with placeholders where dynamic data should be inserted:

helloworld.formattedstring=Here is a value: %S and here is another: %S.

And here is the piece of JavaScript that loads the string:

var localizedString = $HW_STRF("helloworld.formattedstring",
    ["value 1", "value 2"]);

If you are interested in more details how to properly localize Firefox extensions, read this article.

Example extension (helloworld-0.0.4.xpi) can be downloaded here.

FBTrace Console

by Honza

I have already mentioned a few times how useful is FBTrace (made by John J. Barton) for debugging Firebug. So, for those who are interested how Firebug works internally, it's something I would definitely recommend to use.

As the name indicates, FBTrace represents support for tracing within Firebug, notice that it isn't part of the release version of Firebug. So, if you would like to try it out, you have to download a version that includes tracing support from public SVN repository. See this readme.txt for more details about individual branches.

At the present, I would recommend to use branch Firebug 1.2 (or download beta with tracing here) as it's currently the version under development.

If you have managed to successfully install Firebug with tracing support, you should see a new panel called FBTrace. This panel shows many options (you can see them in the Options menu too) that represent various areas in Firebug internal framework. If you want to see some log messages related to a specific area, just click appropriate option (it's bold if it's on) and refresh the current page (or restart Firefox) - depends on specific option. Now, Firebug should print bunch of log messages into the system console window.

In order to see the system console, don't forget to launch Firefox with -console option on the command line.

FBTrace panel

I am using the console quite often (sometimes it's only the way how to track down what is actually happening) and I have soon realized how much I am missing the following:

  1. Quickly find out where is the piece of code that prints the message.
  2. Search within all messages whether specific log is there.
  3. Put a separator at the end of the list.
  4. Clear the log.

A few weeks ago, I couldn't resist and I have written down a simple replacement of the tedious and black system console. The new console is based on domplate - quite powerful UI template system in Firebug (I'll write something about this too) - it intercepts FBTrace's logs and displays it in more readable way.

Every message is expandable so, you can see additional information. E.g. there is a stack trace for each log (you can click it and see the source code).

There are also some actions available in the toolbar.

  1. Clear - removes all messages from the log.
  2. Find - displays standard find bar at the bottom of the window.
  3. Bookmark - puts a separator at the end of the list.
  4. Restart Firefox
  5. Exit Firefox

All these simple features helped me a lot. I haven't spent much time on testing (used with: WinXP, FF3, FB 1.2) as it's done mainly for my personal usage. But, if you want to explore more how Firebug's code works and you think this could help, download the new trace-console from here.

If it turns out to be really useful, it could be perhaps core part of the FBTrace in the future.

Enjoy!