XUL 和 XML 提供的实体(entities)是一个本地化的好方法。
实体
不少软件都希望将界面上的语言尽可能简单的翻译为另外的语言。通常他们会为每一种语言创建一份字符串列表,来代替在代码中进行硬编码。代码中的每一段文本都代表字符串列表中的一项, XML 提供的实体正好实现这一目的。
如果你写过 HTML 代码,你应该对实体很熟悉 , 像这样的代码 <
和 >
是作为“小于”和“大于”在文本中的替代出现的。XML 的语法允许你定义用户实体。你可以使用这些实体代替它实际的值,当然这个值可以是一段文本。实体可以用在任何文本应该出现的地方,包括属性。下面的例子在一个按钮中使用了实体。
<button label="&findLabel;"/>
出现在按钮上的文本将是 &findLabel;
所代表的值。对每一种所要支持的语言需要创建一个文件包含对实体的定义。在英语中,&findLabel;
实体可能代表文本 "Find"。
DTD 文件
实体在 Document Type Definition (DTD) 文件中定义。这类文件往往用于特定 XML 文件的语法和语义的定义,当然也可以用来定义实体。在 Mozilla 的 chrome 体系中,你会在 locales
子目录中找到 DTD 文件。对应一个XUL 文件,一般会有一个 DTD 文件 (extension .dtd
)。
如果你查看 chrome 目录,你会看到一个针对你所用语言的压缩包 (en-US.jar
默认是英语的语言包) 。你也可能会找到多种语言的语言包:美式英语 (en-US) 、法语 (fr)等。在这些压缩包中,你会发现它保存着每个窗口的本地化文本。这种压缩包的结构与 skins 是很相似的。
你可以把你定义实体的 DTD 文件放置到语言包里,一般的,你应该为每一个 XUL 文件建立一个 DTD 文件,通常使用相同的文件名但以 .dtd
作为后缀。因此对应我们的 findfile 对话框,我们需要一个 findfile.dtd
文件。
对于非安装的 chrome 文件,你可以简单的把 DTD 文件放在与 XUL 相同的目录中。
一旦为你的 XUL 文件创立 DTD 文件,你应该在 XUL 文件上添加一行以声明使用 DTD 文件。否则会发生找不到实体的错误,在 XUL 文件的头部加如下一行。
<!DOCTYPE window SYSTEM "chrome://findfile/locale/findfile.dtd">
这一行表示这个 URL 被当作一个 DTD 使用。在这个例子中,我们引入了我们需要的 findfile.dtd
文件,这一行通常放在 window 元素之前。
你同样需要在 chrome.manifest 文件中添加本地化信息,如下:
locale findfile en-US locale/
申明实体
实体的申明使用如下语法:
<!ENTITY findLabel "Find">
上例创建了一个名为 findLabel
值为 "Find" 的实体,这意味着在文本中的任意位置出现的 "&findLabel;" 都将会被 "Find" 替代。注意实体申明无需反斜杠结束。在不同语言的 DTD 文件中,文件使用的不同的语言替代即可。
日文: <!ENTITY findLabel "検索">
例如,下面的文字:
<description value="&findLabel;"/>
被翻译为:
英文版: <description value="Find"/> 日文版: <description value="検索"/>
你应该为你在界面中出现的每一个标签或字符串申明一个实体,在XUL文件中不应出现任何的显示文本。
补充来说你可以在任何因语言不同而不同的的地方使用实体。以 Access keys 及 keyboard shortcuts 为例。
XUL <menuitem label="&undo.label;" accesskey="&undo.key;"/> DTD <!ENTITY undo.label "Undo"> <!ENTITY undo.key "u">
上面的例子使用了两个实体,一个由于 Undo 菜单项的标签,第二个用于快捷键。
改写 Find Files 的例子
让我们看一看如何使用DTD文件修改我们的find files 对话框并将所有文本放在一起。整个文件列在下面。
<?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin/global.css" 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" orient="horizontal" onload="initSearchList()" xmlns="https://www.mozilla.org/keymaster/gat...re.is.only.xul"> <script src="findfile.js"/> <popupset> <menupopup id="editpopup"> <menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;"/> <menuitem label="©Cmd.label;" accesskey="©Cmd.accesskey;"/> <menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" disabled="true"/> </menupopup> </popupset> <keyset> <key id="cut_cmd" modifiers="accel" key="&cutCmd.commandkey;"/> <key id="copy_cmd" modifiers="accel" key="©Cmd.commandkey;"/> <key id="paste_cmd" modifiers="accel" key="&pasteCmd.commandkey;"/> <key id="close_cmd" keycode="VK_ESCAPE" oncommand="window.close();"/> </keyset> <vbox flex="1"> <toolbox> <menubar id="findfiles-menubar"> <menu id="file-menu" label="&fileMenu.label;" accesskey="&fileMenu.accesskey;"> <menupopup id="file-popup"> <menuitem label="&openCmd.label;" accesskey="&openCmd.accesskey;"/> <menuitem label="&saveCmd.label;" accesskey="&saveCmd.accesskey;"/> <menuseparator/> <menuitem label="&closeCmd.label;" accesskey="&closeCmd.accesskey;" key="close_cmd" oncommand="window.close();"/> </menupopup> </menu> <menu id="edit-menu" label="&editMenu.label;" accesskey="&editMenu.accesskey;"> <menupopup id="edit-popup"> <menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" key="cut_cmd"/> <menuitem label="©Cmd.label;" accesskey="©Cmd.accesskey;" key="copy_cmd"/> <menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" key="paste_cmd" disabled="true"/> </menupopup> </menu> </menubar> <toolbar id="findfiles-toolbar"> <toolbarbutton id="opensearch" label="&openCmdToolbar.label;"/> <toolbarbutton id="savesearch" label="&saveCmdToolbar.label;"/> </toolbar> </toolbox> <tabbox> <tabs> <tab label="&searchTab;" selected="true"/> <tab label="&optionsTab;"/> </tabs> <tabpanels> <tabpanel id="searchpanel" orient="vertical" context="editpopup"> <description> &findDescription; </description> <spacer class="titlespace"/> <groupbox orient="horizontal"> <caption label="&findCriteria;"/> <menulist id="searchtype"> <menupopup> <menuitem label="&type.name;"/> <menuitem label="&type.size;"/> <menuitem label="&type.date;"/> </menupopup> </menulist> <spacer class="springspace"/> <menulist id="searchmode"> <menupopup> <menuitem label="&mode.is;"/> <menuitem label="&mode.isnot;"/> </menupopup> </menulist> <spacer class="springspace"/> <menulist id="find-text" flex="1" editable="true" datasources="file:///mozilla/recents.rdf" ref="https://www.xulplanet.com/rdf/recent/all"> <template> <menupopup> <menuitem label="rdf:https://www.xulplanet.com/rdf/recent#Label" uri="rdf:*"/> </menupopup> </template> </menulist> </groupbox> </tabpanel> <tabpanel id="optionspanel" orient="vertical"> <checkbox id="casecheck" label="&casesensitive;"/> <checkbox id="wordscheck" label="&matchfilename;"/> </tabpanel> </tabpanels> </tabbox> <tree id="results" style="display: none;" flex="1"> <treecols> <treecol id="name" label="&results.filename;" flex="1"/> <treecol id="location" label="&results.location;" flex="2"/> <treecol id="size" label="&results.size;" flex="1"/> </treecols> <treechildren> <treeitem> <treerow> <treecell label="mozilla"/> <treecell label="/usr/local"/> <treecell label="&bytes.before;2520&bytes.after;"/> </treerow> </treeitem> </treechildren> </tree> <splitter id="splitbar" resizeafter="grow" style="display: none;"/> <spacer class="titlespace"/> <hbox> <progressmeter id="progmeter" value="50%" style="display: none;"/> <spacer flex="1"/> <button id="find-button" label="&button.find;" oncommand="doFind()"/> <button id="cancel-button" label="&button.cancel;" oncommand="window.close();"/> </hbox> </vbox> </window>
每一个字符串均被实体引用代替。一个 DTD 文件被包含在XUL文件的开头。每一个被引用的实体必须在DTD文件中申明,如果发现引用的实体没有申明,则窗口不会显示。
注意实体的名字并不重要,在上面的例子里实体的名字被分成几段来写,请你不要这样,请按照其他代码的书写习惯来写实体引用。
你可能已经注意到里字符串 '2520 bytes' 被两个实体代替。这是因为在别的语言中可能会有不同的语法要求。比如,可能要求数字写在后面而把 'bytes' 写在前面。当然对于 KB 、MB 会有更复杂的顺序要求。
键盘访问键及快捷键也被翻译为实体因为对不同的语言这些也会不同。
下面是 DTD 文件 - 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">
现在,为一个新语言添加文本仅需创建一个新的DTD文件。使用 chrome 系统把 DTD 文件加到另一个 locales
中,这样同一个 XUL 文件就可以使用不同的语言。
Find files example so far: Source
下一章,看看 property files.