String bundles are flat text files that contain text for the UI that is accessed in JavaScript, C++, and theoretically any language that fits within the Mozilla framework. These bundles are strings that can be presented visually to the user via some functionality in the application at any time. This may be anything from a dynamically changing menu item to an alert box, or from a URL to a placeholder that is filled depending on the context in which it is accessed. The bundle files are given an extension of .properties and they commonly reside in the locale directory with the DTD files.
A user interface can use one or more string bundles, each of which is defined in a <stringbundle> element and surrounded by a <stringbundleset> element. Example 11-3 contains the bundles used by the Mozilla browser.
As you can see from their names and their locations in the chrome, each bundle serves a different purpose. They include a file that contains the bulk of the strings for the browser (navigator.properties), a file that includes branding strings, and a couple of files for regional information. This model is useful if you need to output many strings to the UI from your source code and would like to organize them into meaningful groups.
A string bundle (.properties) file has a very simple format. It contains one or more lines that have the identifier associated with the localizable string. The format of a string bundle string with an identifier is:
Identifier=String
The format for comments in a bundle file requires the hash notation (#). Comments are useful for notifying translators of the context of strings, or flagging a string that should be left as is and not localized. Comments in properties files are formatted in the following manner.
# DO NOT TRANSLATE applicationTitle=xFly
Spaces in bundles are treated literally -- spaces between words are observed, with the exception of the start and the end of the string.
The next section shows the methods and properties specific to the <stringbundle> element that are available to you when you use it. The implementations are contained in the binding for the element.
Defining your bundle in XUL and then creating the file with the values is only half the story. This section shows how to extract the values from the bundle and place them in UI. The language of choice in these examples is JavaScript. This process is necessary when you have to change values in the UI because DTD entities can not be updated dynamically.
Our bundle is defined in XUL like this:
<stringbundle id="bundle_xfly" src="chrome://xfly/locale/xfly.properties"/>
To access the methods of the bundle object in your script, you have to get a handle on the XUL element by using its id. First declare the variable globally that will be holding the bundle:
var xFlyBundle;
Then assign the variable to the bundle. A good place to do this is in the load handler function of your XUL window, or in the constructor for your binding if you are using it from there:
xFlyBundle = document.getElementById("bundle_xfly");
Now that you have access to the bundle, you can use the available methods to retrieve the strings. The two main functions are getString and getFormattedString.
The most straightforward string access method, getString, takes one parameter (namely the identifier of the string) and returns the localizable string value for use in the UI:
var readonly = xFlyBundle.getString(`readonlyFile'); alert(readonly);
The string bundle entry looks like this:
readonlyfile=This file is read only
This function takes an extra parameter -- an array of string values, which are substituted into the string in the bundle. Then the full string with the substituted values is returned:
var numFiles = numberInEditor numFilesMsg = xflyBundle.getFormattedString("numFilesMessage", [numFiles]);
You can have more than one value replaced in the string, each one delimited within the square brackets by using a comma:
fileInfo = xflyBundle.getFormattedString("fileInformation", [fileName, fileSize]);
The string bundle entry looks like this:
flyFileInformation=The file is called %1$s and its size is %2$s
The %x numerical value refers to the ordering of the values to be substituted in the string. The type of the value is determined by the dollar ($) symbol. In this case, there are two possibilities -- $s is a string value and $d is an integer value.
Some binding properties that are exposed to your script accompany the methods. These properties are not often needed for routine retrieval of string values, but are useful to know nonetheless if you ever need to discover or share the meta information related to your bundle and locale.
This property is the string bundle object that queries the nsIStringBundleService interfaces and initializes the XPCOM interface, making methods available to it. It is the direct way of getting a string from a bundle:
var appBundle = document.getElementById("bundle_app"); return appBundle.stringBundle.GetStringFromName("chapter11");
This property is the attribute used to get and set the properties file that will be used as a string bundle:
var appBundle = document.getElementById("bundle_app"); dump("You are using the properties file " + appBundle.src);
The implementation for setting up your string bundle just described is hidden from the XUL author. You only need to point at the bundle you want to use by using the source attribute. There is however, an alternative way to do this if you do not favor using <stringbundle> or would like to extend that binding.
The alternative is to use utility routines that come bundled with Mozilla and are contained in a string resources JavaScript file: strres.js. With this file, creating a bundle is a three-step process.
Include the JavaScript file:
<script type="application/x-javascript" src="chrome://global/content/strres.js"/>
Set up your bundle:
var bundle = srGetStrBundle("chrome://mypackage/locale/mypackage.properties");
Access the strings:
var greeting = bundle.GetStringFromName( "hello" );
The result retrieves the string corresponding to "hello" in your bundle file and is the equivalent of the getString call when using the XUL bundle method.
If your chrome is independent of Mozilla's chrome and you do not want to use their UI files, you can create the bundle directly by using the nsIStringBundleService XPCOM interface, as seen in Example 11-4.
The first step is to get the application locale -- the language that is currently registered with the chrome service. This is done via the nsILocalService component. The nsIStringBundleService is then initialized and the CreateBundle method is called, returning an instance of nsIStringBundle that provides access to the methods for querying strings.