XUL and XML provide entities which are a convienient way of allowing localization.
Many applications are built such that translating the interface into a different language is as simple as possible. Usually, a table of strings is created for each language. Instead of hard-coding text directly into an application, each piece of text is only a reference into the string table. XML provides entities which can be used for a similar purpose.
You should already be familiar with entities if you have written HTML. The codes < and > are examples of entities which can be used to place less than and greater than signs into the text. XML has a syntax which allows you to declare custom entities. You can use these so that the entity is replaced with its value, which can be a string of text. Entities may be used whenever text occurs, including the values of attributes. The example below demonstrates the use of an entity in a button.
<button value="&findLabel"/> |
The text that will appear on the label will be the value that the entity &findLabel has. A file is created which contains the entity declarations for each supported language. In English, the &findLabel entity will probably be declared to have the text 'Find'.
Entities are declared in DTD (Document Type Declaration) files. These types of files are normally used to declare the syntax and sematics of a particular XML file. They also allow you to declare entities. In the Mozilla chrome system, you will find DTD files located in the locales subdirectory. You would normally have one DTD file (with an extension dtd) per XUL file.
If you look in the directory chrome/locales you should see a directory for each locale that you have installed. The structure is very similar to the directory structure used for skins.
For example, you might have locale files in two languages, US English (en-us) and French (fr). Additional subdirectories can be added for each additional language you have installed.
Inside the language directories, you would place your DTD files in which you declare entities. Typically, you will have one DTD file for each XUL file, usually with the same filename except with a .dtd extension. So, for the find files dialog, we will need a file called findfile.dtd.
For non-installed chrome files, you can just put the DTD file in the same directory as the XUL file.
Once you have created a DTD file for your XUL, you will need to add a line to the XUL file which indicates that you want to use the DTD file. Otherwise, errors will occur as it won't be able to find the entities. To do this, add a line of the following form somewhere near the top of the XUL file:
<!DOCTYPE window SYSTEM "chrome://findfile/locale/findfile.dtd"> |
This line specifies that the URL indicated is to be used as a DTD for the file. In this case, we have declared that we want to use the findfile.dtd DTD file. This line is typically placed just before the window element.
The entities are declared using a simple syntax as shown below:
<!ENTITY findLabel "Find"> |
This example creates an entity with the name findLabel and the value Find. This means that when the text &findLabel appears in the XUL file, the text Find will be used instead. In the DTD file for a different language, the text for that language will be used instead. Note that entity declarations do not have a trailing slash at the end of them.
For example, the following text would be translated as follows:
<text value="&findLabel"/> is translated as: <text value="Find"/> |
You would declare an entity for each label or string of text that you use in your interface. You should not have any directly displayed text in the XUL file at all.
In addition to using entites for text labels, you should use them for any value that could be different in a different language. Access keys and keyboard shortcuts for example.
<menuitem value="&undo.label" accesskey="&undo.key"/> <!ENTITY undo.label "Undo"> <!ENTITY undo.key "u"> |
The example above uses two entites, one for the label on the Undo menu item and the second for the access key.
Let's take a look at how we would put all of this together by modifying the find files dialog so that it uses a DTD file for all of its text strings. The entire XUL file is shown below with the changes shown in red.
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="findfile.css" type="text/css"?>
<!DOCTYPE window SYSTEM "chrome://findfile/locale/findfile.dtd">
<window
id="findfile-window"
title="&findWindow.title;"
persist="screenX screenY width height"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="initSearchList();">
<script src="findfile.js"/>
<popupset>
<popup id="editpopup">
<menuitem value="Cut" accesskey="&cutCmd.accesskey;"/>
<menuitem value="Copy" accesskey="©Cmd.accesskey;"/>
<menuitem value="Paste" accesskey="&pasteCmd.accesskey;" disabled="true"/>
</popup>
</popupset>
<keyset>
<key id="cut_cmd" xulkey="true" key="&cutCmd.commandkey;"/>
<key id="copy_cmd" xulkey="true" key="©Cmd.commandkey;"/>
<key id="paste_cmd" xulkey="true" key="&pasteCmd.commandkey;"/>
<key id="close_cmd" keycode="VK_ESCAPE"/>
</keyset>
<box orient="vertical" flex="1">
<toolbox>
<menubar persist="collapsed">
<menu id="file-menu" value="&fileMenu.label;"
accesskey="&fileMenu.accesskey;">
<menupopup id="file-popup">
<menuitem value="&openCmd.label;"
accesskey="&openCmd.accesskey;"/>
<menuitem value="&saveCmd.label;"
accesskey="&saveCmd.accesskey;"/>
<menuseparator/>
<menuitem value="&closeCmd.label;"
accesskey="&closeCmd.accesskey;" key="close_cmd" oncommand="window.close();"/>
</menupopup>
</menu>
<menu id="edit-menu" value="&editMenu.label;"
accesskey="&editMenu.accesskey;">
<menupopup id="edit-popup">
<menuitem value="&cutCmd.label;"
accesskey="&cutCmd.accesskey;" key="cut_cmd" oncommand="doCut()"/>
<menuitem value="©Cmd.label;"
accesskey="©Cmd.accesskey;" key="copy_cmd" oncommand="doCopy()"/>
<menuitem value="&pasteCmd.label;"
accesskey="&pasteCmd.accesskey;" key="paste_cmd" oncommand="doPaste()"/>
</menupopup>
</menu>
</menubar>
<toolbar persist="collapsed">
<button id="opensearch" value="&openCmdToolbar.label;"/>
<button id="savesearch" value="&saveCmdToolbar.label;"/>
</toolbar>
</toolbox>
<tabcontrol orient="vertical">
<tabbox orient="horizontal">
<tab value="&searchTab;" selected="true"/>
<tab value="&optionsTab;"/>
</tabbox>
<tabpanel>
<box id="searchpanel" orient="vertical">
<html>
&findDescription;
</html>
<spring class="titlespace"/>
<titledbox orient="horizontal">
<title><text value="&findCriteria;"/></title>
<menulist id="searchtype">
<menupopup popupanchor="bottomleft">
<menuitem value="&type.name;"/>
<menuitem value="&type.size;"/>
<menuitem value="&type.date;"/>
</menupopup>
</menulist>
<spring class="springspace"/>
<menulist id="searchmode">
<menupopup>
<menuitem value="&mode.is;"/>
<menuitem value="&mode.isnot;"/>
</menupopup>
</menulist>
<spring class="springspace"/>
<textfield id="find-text" flex="1" context="editpopup"/>
</titledbox>
</box>
<box id="optionspanel" orient="vertical">
<checkbox id="casecheck" value="&casesensitive;"/>
<checkbox id="wordscheck" value="&matchfilename;"/>
</box>
</tabpanel>
</tabcontrol>
<tree id="results" style="display: none;">
<treecolgroup>
<treecol flex="1"/>
<treecol flex="2"/>
<treecol flex="1"/>
</treecolgroup>
<treehead>
<treerow>
<treecell value="&results.filename;"/>
<treecell value="&results.location;"/>
<treecell value="&results.size;"/>
</treerow>
</treehead>
<treechildren flex="1">
<treeitem>
<treerow>
<treecell value="mozilla"/>
<treecell value="/usr/local"/>
<treecell value="&bytes.before;2520&bytes.after;"/>
</treerow>
</treeitem>
</treechildren>
</tree>
<splitter id="splitbar" collapse="before" resizeafter="grow" style="display: none;"/>
<box orient="horizontal">
<progressmeter id="progmeter" value="50%" style="display: none;"/>
<spring flex="1"/>
<button id="find-button" class="dialog" value="&button.find;" default="true"
onclick="doFind()"/>
<button id="cancel-button" class="dialog" value="&button.cancel;"
onclick="window.close();"/>
</box>
</box>
</window>
|
Each text string has been replaced by an entity reference. A DTD file has been included near the beginning of the XUL file. Each entity that was added should be declared in the DTD file. The window will not be displayed if an entity is found in the XUL file that hasn't been declared.
Note that the name of the entity is not important. In the example above, words in entities have been separated with periods. You don't have to do this. The entity names here follow similar conventions as the rest of the Mozilla code.
You might notice that the text '2520 bytes' has been replaced by two entities. This is because the phrase structure may be different in another locale. For example, the number might need to appear before the equivalent of 'bytes' instead of after. Of course, this might need to be more complicated in order to display KB or MB as needed.
The access keys and keyboard shortcuts have also been translated into entities because they will likely be different in a different locale.
Next, the DTD file (findfile.dtd):
<!ENTITY findWindow.title "Find Files"> <!ENTITY fileMenu.label "File"> <!ENTITY editMenu.label "Edit"> <!ENTITY fileMenu.accesskey "f"> <!ENTITY editMenu.accesskey "e"> <!ENTITY openCmd.label "Open Search..."> <!ENTITY saveCmd.label "Save Search..."> <!ENTITY closeCmd.label "Close"> <!ENTITY openCmd.accesskey "o"> <!ENTITY saveCmd.accesskey "s"> <!ENTITY closeCmd.accesskey "c"> <!ENTITY cutCmd.label "Cut"> <!ENTITY copyCmd.label "Copy"> <!ENTITY pasteCmd.label "Paste"> <!ENTITY cutCmd.accesskey "t"> <!ENTITY copyCmd.accesskey "c"> <!ENTITY pasteCmd.accesskey "p"> <!ENTITY cutCmd.commandkey "X"> <!ENTITY copyCmd.commandkey "C"> <!ENTITY pasteCmd.commandkey "V"> <!ENTITY openCmdToolbar.label "Open"> <!ENTITY saveCmdToolbar.label "Save"> <!ENTITY searchTab "Search"> <!ENTITY optionsTab "Options"> <!ENTITY findDescription "Enter your search criteria below and select the Find button to begin the search."> <!ENTITY findCriteria "Search Criteria"> <!ENTITY type.name "Name"> <!ENTITY type.size "Size"> <!ENTITY type.date "Date Modified"> <!ENTITY mode.is "Is"> <!ENTITY mode.isnot "Is Not"> <!ENTITY casesensitive "Case Sensitive Search"> <!ENTITY matchfilename "Match Entire Filename"> <!ENTITY results.filename "Filename"> <!ENTITY results.location "Location"> <!ENTITY results.size "Size"> <!ENTITY bytes.before ""> <!ENTITY bytes.after "bytes"> <!ENTITY button.find "Find"> <!ENTITY button.cancel "Cancel"> |
Now, to change a language all you need to do is create another DTD file. By using the chrome system to add the DTD file to a different locale, the same XUL file can be used in any language.
(Next) Next, we'll look at property files.