Localizing an extension
If you haven't already created an extension, or would like to refresh your memory, take a look at the previous articles in this series:
You can download this article's sample code so you can look at it side-by-side with the article, or to use it as a basis for your own extension.
Each XUL file that comprises the user interface for your extension should have a locale file in its locale directory.
Each locale file maps entity names referenced in the XUL files to the strings themselves. The preference dialog, whose XUL file is
options.xul, has a corresponding
options.dtd file that looks like this:
<!ENTITY options_window_title "StockWatcher 2 Preferences"> <!ENTITY options_symbol.label "Stock to watch: ">
The "options_window_title" entity maps to the string "StockWatcher 2 Preferences", which is used as the title of the preference window.
stockwatcher2.dtd file contains the mappings for the
<!ENTITY panel_loading "Loading..."> <!ENTITY menu_refresh_now.label "Refresh Now"> <!ENTITY menu_apple.label "Apple (AAPL)"> <!ENTITY menu_google.label "Google (GOOG)"> <!ENTITY menu_microsoft.label "Microsoft (MSFT)"> <!ENTITY menu_yahoo.label "Yahoo (YHOO)">
Each XUL file needs to reference its corresponding locale file. We also need to update the code to use the entities instead of the strings, so that the substitutions take place based on the currently active locale.
To add a reference to the correct locale file for a given XUL file, we need to add one line to the XUL file. To
options.xul, we add this line:
<!DOCTYPE window SYSTEM "chrome://stockwatcher2/locale/options.dtd">
We add a similar line to the
<!DOCTYPE overlay SYSTEM "chrome://stockwatcher2/locale/stockwatcher2.dtd">
In larger applications you might need to use entities from several locale files in a single XUL file. Using multiple DTDs describes how to do it.
Note that the URLs of the DTD files don't actually include the name of the localization to use. The Chrome Registry resolves the URIs based on the user's current locale setting and the data you provide in your Chrome manifest.
Then we simply replace each text string in our XUL files with the corresponding entity. For example, in
stockwatcher2.xul, we change this line:
<menuitem label="Refresh Now" oncommand="StockWatcher.refreshInformation()"/>
<menuitem label="&menu_refresh_now.label;" oncommand="StockWatcher.refreshInformation()"/>
Do this for every string used in each XUL file.
To let Firefox know about the locale files, we need to make a revision to our
chrome.manifest file, adding one line for each localization:
locale stockwatcher2 en-US chrome/locale/en-US/
This instructs Firefox that the en-US localization is located in the
changeString=Chg: openString=Open: lowString=Low: highString=High: volumeString=Vol:
stockwatcher2.properties file shown above maps five keys (
volumeString) to the corresponding text in English.
The next step is to modify the
stockwatcher2.xul file to reference this property file. We do this by creating a string bundle, using the following code:
<stringbundleset id="stringbundleset"> <stringbundle id="string-bundle" src="chrome://stockwatcher2/locale/stockwatcher2.properties"/> </stringbundleset>
This establishes a new string bundle, referenced by the ID "string-bundle", whose keys and values are to be loaded from the
stockwatcher2.properties file we've already created.
refreshInformation() function to load the strings, and its enclosed
infoReceived() function to use the loaded, localized, strings instead of string literals.
We add to
refreshInformation() the following code:
var stringsBundle = document.getElementById("string-bundle"); var changeString = stringsBundle.getString('changeString') + " "; var openString = stringsBundle.getString('openString') + " "; var lowString = stringsBundle.getString('lowString') + " "; var highString = stringsBundle.getString('highString') + " "; var volumeString = stringsBundle.getString('volumeString') + " ";
This code gets a reference to the string bundle element we added to
stockwatcher2.xul by calling
document.getElementById(), specifying the ID "string-bundle". Then it fetches all the strings we need from the bundle, one by one, by calling the string bundle's
getString() method, passing the appropriate key for each string.
In this case, we're also appending a space to the end of each string. That's just how this particular program works, and isn't something that you have to do.
Then we replace any occurrences of the literal strings with the appropriate variables:
samplePanel.tooltipText = changeString + fieldArray + " | " + openString + fieldArray + " | " + lowString + fieldArray + " | " + highString + fieldArray + " | " + volumeString + fieldArray;
To add another localization for a new language, all you need to do is add another line to the chrome manifest referencing the new locale. For example, to add a Spanish localization, you would add:
locale stockwatcher2 es-ES chrome/locale/es-ES/
Then just create a subdirectory
chrome/locale/es-ES and add the needed DTD files; in this case,
stockwatcher2.dtd. These files should map the same tags to the Spanish translations of the strings used by the extension.
chrome/locale/es-ES directory as well. Only the strings should be localized; the keys should be the same for each localization.