Localizing an extension

  • Revision slug: Localizing_an_extension
  • Revision title: Localizing an extension
  • Revision id: 78014
  • Created:
  • Creator: Sheppy
  • Is current revision? No
  • Comment Added info on localizing JS code.

Revision Content

Introduction

This article expands upon the previous samples on extension writing by adding localization support to our stock watcher extension. Performing a few simple steps makes your extension much easier to localize into various languages without having to edit the XUL or JavaScript files themselves.

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:

Download the sample

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.

http://developer.mozilla.org/samples/extension-samples/localizedstockwatcher.zip

Create the needed locale files

Each XUL file that comprises the user interface for your extension should have a locale file in its locale directory.

Each locale file maps tags representing strings 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" symbol maps to the string "StockWatcher 2 Preferences", which is used as the title of the preference window.

The stockwatcher2.dtd file contains the mappings for the stockwatcher2.xul file:

 <!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)">

Update the XUL files

Each XUL file needs to reference its corresponding locale file. We also need to update the code to use the tags 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 stockwatcher.xul file:

 <!DOCTYPE overlay SYSTEM "chrome://stockwatcher2/locale/stockwatcher2.dtd">

Note that the URLs of the DTD files don't actually include the name of the localization to use. The word "locale" that appears in these URLs is aliased to the appropriate directory based on the user's current locale setting.

Then we simply replace each text string in our XUL files with the corresponding tag. For example, in stockwatcher2.xul, we change this line:

 <menuitem label="Refresh Now" oncommand="StockWatcher.refreshInformation()"/>

to

 <menuitem label="&menu_refresh_now.label;" oncommand="StockWatcher.refreshInformation()"/>

Do this for every string used in each XUL file.

Update the chrome manifest

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 chrome/locale/en-US directory.

Localizing strings in JavaScript code

If your JavaScript code contains literal strings that need to be localized, as does our stock watcher sample, we need to make those localizable as well. We can do this by moving the strings into a string bundle. String bundles are created by establishing a property file that maps keys to string values. For a detailed explanation of how this works, see XUL Tutorial:Property Files.

Create a properties file

The first thing we do is create a property file for the literal strings used by the stockwatcher2.js JavaScript code:

changeString=Chg:
openString=Open:
lowString=Low:
highString=High:
volumeString=Vol:

The stockwatcher2.properties file shown above maps five keys (changeString, openString, lowString, highString, and volumeString to the corresponding text in English.

Create the string bundle

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 XML:

 <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.

Update the JavaScript code

Now we're ready to revise the JavaScript code to load the strings from the string bundle instead of using literal strings. This involves rewriting the 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:

 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 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[4] + " | " +
     openString + fieldArray[5] + " | " +
     lowString + fieldArray[6] + " | " +
     highString + fieldArray[7] + " | " +
     volumeString + fieldArray[8];

Adding more localizations

To add another localization for a new language, all you need to do is add another line to the chrome manifest referencing the new language. 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, options.dtd and stockwatcher2.dtd. These files should map the same tags to the Spanish translations of the strings used by the extension.

Likewise, if we have any property files containing localizable strings for our JavaScript code, we need to create localized versions of those property files in the chrome/locale/es-ES directory as well. Only the strings should be localized; the keys should be the same for each localization.

Revision Source

<h2 name="Introduction">Introduction</h2>
<p>This article expands upon the previous samples on extension writing by adding localization support to our stock watcher extension.  Performing a few simple steps makes your extension much easier to localize into various languages without having to edit the XUL or JavaScript files themselves.
</p><p>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:
</p>
<ul><li> <a href="en/Creating_a_status_bar_extension">Creating a status bar extension</a>
</li><li> <a href="en/Creating_a_dynamic_status_bar_extension">Creating a dynamic status bar extension</a>
</li><li> <a href="en/Adding_preferences_to_an_extension">Adding preferences to an extension</a>
</li></ul>
<h2 name="Download_the_sample">Download the sample</h2>
<p>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.
</p><p>http://developer.mozilla.org/samples/extension-samples/localizedstockwatcher.zip
</p>
<h2 name="Create_the_needed_locale_files">Create the needed locale files</h2>
<p>Each XUL file that comprises the user interface for your extension should have a locale file in its locale directory.
</p><p>Each locale file maps tags representing strings to the strings themselves.  The preference dialog, whose XUL file is <code>options.xul</code>, has a corresponding <code>options.dtd</code> file that looks like this:
</p>
<pre class="eval"> &lt;!ENTITY options_window_title "StockWatcher 2 Preferences"&gt;
 &lt;!ENTITY options_symbol.label "Stock to watch: "&gt;
</pre>
<p>The "options_window_title" symbol maps to the string "StockWatcher 2 Preferences", which is used as the title of the preference window.
</p><p>The <code>stockwatcher2.dtd</code> file contains the mappings for the <code>stockwatcher2.xul</code> file:
</p>
<pre class="eval"> &lt;!ENTITY panel_loading "Loading..."&gt;
 &lt;!ENTITY menu_refresh_now.label "Refresh Now"&gt;
 &lt;!ENTITY menu_apple.label "Apple (AAPL)"&gt;
 &lt;!ENTITY menu_google.label "Google (GOOG)"&gt;
 &lt;!ENTITY menu_microsoft.label "Microsoft (MSFT)"&gt;
 &lt;!ENTITY menu_yahoo.label "Yahoo (YHOO)"&gt;
</pre>
<h2 name="Update_the_XUL_files">Update the XUL files</h2>
<p>Each XUL file needs to reference its corresponding locale file.  We also need to update the code to use the tags instead of the strings, so that the substitutions take place based on the currently active locale.
</p><p>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 <code>options.xul</code>, we add this line:
</p>
<pre class="eval"> &lt;!DOCTYPE window SYSTEM "chrome://stockwatcher2/locale/options.dtd"&gt;
</pre>
<p>We add a similar line to the <code>stockwatcher.xul</code> file:
</p>
<pre class="eval"> &lt;!DOCTYPE overlay SYSTEM "chrome://stockwatcher2/locale/stockwatcher2.dtd"&gt;
</pre>
<p>Note that the URLs of the DTD files don't actually include the name of the localization to use.  The word "locale" that appears in these URLs is aliased to the appropriate directory based on the user's current locale setting.
</p><p>Then we simply replace each text string in our XUL files with the corresponding tag.  For example, in <code>stockwatcher2.xul</code>, we change this line:
</p>
<pre class="eval"> &lt;menuitem label="Refresh Now" oncommand="StockWatcher.refreshInformation()"/&gt;
</pre>
<p>to
</p>
<pre class="eval"> &lt;menuitem label="&amp;menu_refresh_now.label;" oncommand="StockWatcher.refreshInformation()"/&gt;
</pre>
<p>Do this for every string used in each XUL file.
</p>
<h2 name="Update_the_chrome_manifest">Update the chrome manifest</h2>
<p>To let Firefox know about the locale files, we need to make a revision to our <code>chrome.manifest</code> file, adding one line for each localization:
</p>
<pre class="eval"> locale stockwatcher2 en-US chrome/locale/en-US/
</pre>
<p>This instructs Firefox that the en-US localization is located in the <code>chrome/locale/en-US</code> directory.
</p>
<h2 name="Localizing_strings_in_JavaScript_code">Localizing strings in JavaScript code</h2>
<p>If your JavaScript code contains literal strings that need to be localized, as does our stock watcher sample, we need to make those localizable as well.  We can do this by moving the strings into a string bundle.  String bundles are created by establishing a property file that maps keys to string values.  For a detailed explanation of how this works, see <a href="en/XUL_Tutorial/Property_Files">XUL Tutorial:Property Files</a>.
</p>
<h3 name="Create_a_properties_file">Create a properties file</h3>
<p>The first thing we do is create a property file for the literal strings used by the <code>stockwatcher2.js</code> JavaScript code:
</p>
<pre class="eval">changeString=Chg:
openString=Open:
lowString=Low:
highString=High:
volumeString=Vol:
</pre>
<p>The <code>stockwatcher2.properties</code> file shown above maps five keys (<code>changeString</code>, <code>openString</code>, <code>lowString</code>, <code>highString</code>, and <code>volumeString</code> to the corresponding text in English.
</p>
<h3 name="Create_the_string_bundle">Create the string bundle</h3>
<p>The next step is to modify the <code>stockwatcher2.xul</code> file to reference this property file.  We do this by creating a string bundle, using the following XML:
</p>
<pre class="eval"> &lt;stringbundleset id="stringbundleset"&gt;
   &lt;stringbundle id="string-bundle" src="chrome://stockwatcher2/locale/stockwatcher2.properties"/&gt;
 &lt;/stringbundleset&gt;
</pre>
<p>This establishes a new string bundle, referenced by the ID "string-bundle", whose keys and values are to be loaded from the <code>stockwatcher2.properties</code> file we've already created.
</p>
<h3 name="Update_the_JavaScript_code">Update the JavaScript code</h3>
<p>Now we're ready to revise the JavaScript code to load the strings from the string bundle instead of using literal strings.  This involves rewriting the <code>refreshInformation()</code> function to load the strings, and its enclosed <code>infoReceived()</code> function to use the loaded, localized, strings instead of string literals.
</p><p>We add to <code>refreshInformation()</code> the following code:
</p>
<pre class="eval"> 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') + " ";
</pre>
<p>This code gets a reference to the string bundle by calling <code>document.getElementById()</code>, 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 <code><a class="external" href="http://www.xulplanet.com/references/elemref/ref_stringbundle.html#prop_getString">getString()</a></code> method, passing the appropriate key for each string.
</p><p>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.
</p><p>Then we replace any occurrences of the literal strings with the appropriate variables:
</p>
<pre class="eval"> samplePanel.tooltipText = changeString + fieldArray[4] + " | " +
     openString + fieldArray[5] + " | " +
     lowString + fieldArray[6] + " | " +
     highString + fieldArray[7] + " | " +
     volumeString + fieldArray[8];
</pre>
<h2 name="Adding_more_localizations">Adding more localizations</h2>
<p>To add another localization for a new language, all you need to do is add another line to the chrome manifest referencing the new language.  For example, to add a Spanish localization, you would add:
</p>
<pre class="eval"> locale stockwatcher2 es-ES chrome/locale/es-ES/
</pre>
<p>Then just create a subdirectory <code>chrome/locale/es-ES</code> and add the needed DTD files; in this case, <code>options.dtd</code> and <code>stockwatcher2.dtd</code>.  These files should map the same tags to the Spanish translations of the strings used by the extension.
</p><p>Likewise, if we have any property files containing localizable strings for our JavaScript code, we need to create localized versions of those property files in the <code>chrome/locale/es-ES</code> directory as well.  Only the strings should be localized; the keys should be the same for each localization.
</p>
Revert to this revision