ローカライズ (地域化)

XUL と XML には、実体 (entity)という仕組みがあり、これを利用すればアプリケーションのローカライズ (地域化)を簡単に行うことができます。

実体宣言と実体参照

一般的に、多くのアプリケーションでは、インターフェイス中の文字列を他の言語に翻訳した版を、可能な限り簡単に構築できるような工夫がされています。 このためには、通常は文字列のテーブルを対応する言語ごとに作成します。 つまり、テキストを直接アプリケーションにハードコーディングする代わりに、 文字列テーブルを参照するために使用する短いテキストだけを置くようにします。 XML の提供する仕組みである実体も、同様の目的に利用できます。

HTML の利用経験があれば、実体については既にお馴染みのはずです。 例えば、HTML の中に不等号の記号 ('<' と '>') を記述するために使用するコードである &lt;&gt; は実体を参照する例になります。 XML には、独自の実体を宣言するための構文があり、これを利用してテキストの列を実体として宣言できます。 それらは実体参照を使用して参照され、宣言した値と置換されることになります。 実体参照は、テキストが置ける場所ならば、属性値も含めてどこにでも置くことが可能です。 以下は、ボタンに対して実体参照を使用する例になります。

<button label="&findLabel;"/>

実体参照 &findLabel; に対応する実体の値が、ラベルに表示されるテキストになります。 ローカライズをするためには、サポートする各言語ごとに、その言語によって実体を宣言したファイルを作成します。 例えば、英語の場合は、&findLabel;に対応する実体は、テキスト "Find" が値になるように宣言します。

DTD ファイル

実体は、DTD (Document Type Declaration) ファイルで宣言します。 通常、DTD ファイルは、特定 (同じ種類) の XML ファイルに対して構文と意味内容を宣言するために使用されますが、 実体を宣言するためにも利用できます。 Mozilla の chrome システムでは、DTD ファイルは、<tt>locales</tt> サブディレクトリに置かれています。 なお、通常は、1 つの XUL ファイルに対して 1 つの DTD ファイル (拡張子が <tt>.dtd</tt> のファイル) を作成します。

chrome ディレクトリの中を探していくと、利用している言語に対応したアーカイブが見つかるはずです (英語の場合は、デフォルトでは en-US.jar です)。 ここには、例えば、 米国英語 (en-US) とフランス語 (fr) といったように、複数の言語に対応するために、複数のロケールファイルが置かれていることもあります。 これらのアーカイブの中には、それぞれのウインドウのためのローカライズされたテキストが入っています。 また、このアーカイブの構造は、スキンで使用しているディレクトリ構造と非常に良く似たものになります。

このアーカイブの中は、実体宣言を記述した DTD ファイルを置きます。 また、DTD ファイルは、各 XUL ファイルに対して 1 つずつ作成し、ファイル名は、通常 <tt>.dtd</tt> 拡張子を除いて同じにします。 したがって、ファイル検索ダイアログの場合は、<tt>findfile.dtd</tt> という名前のファイルが必要になります。

なお、インストールされていない chrome ファイルに対してなら、 DTD ファイルは単に XUL ファイルと同じディレクトリに置いておいても構いません。

注意: 非 ASCII 文字を含む DTD ファイルは、UTF-8 でエンコードしなければなりません。言い換えると (BOM なしの) UTF-8 で保存する必要があります。 詳細は、Mozilla の言語パック を参照してください。

XUL に対して DTD ファイルを作成した場合には、 XUL ファイルにその DTD ファイルを利用するよう指示する記述を加える必要があります。 このために、以下の形式の行を XUL ファイルの先頭近くに加えてください。 これを忘れた場合、実体の参照ができないためエラーが発生します。

<!DOCTYPE window SYSTEM "chrome://findfile/locale/findfile.dtd">

この行によって、この XUL ファイルでは、URL で指定したファイルを DTD として利用するように指示します。 この場合は、<tt>findfile.dtd</tt> を DTD ファイルとして利用することを宣言しています。 通常、この行は window 要素の直前に置くことになります。

実体宣言

実体は、以下に示すような簡単な構文で宣言します。

<!ENTITY findLabel "Find">

この例では、findLabel という名前の実体を、「Find」という値で宣言しています。 これによって、XUL ファイル中ならばどこであっても「&findLabel;」というテキストが現れた場合は、「Find」というテキストに置き換えられるようになります。 実体宣言の場合は (空要素で行うように) 最後にスラッシュを置かないことに注意してください。 別の言語用の DTD ファイルでは、下記のように、その言語のテキストを代わりに置くことになります。

日本語用:
<!ENTITY findLabel "検索">

例えば、以下のテキストは...

<description value="&findLabel;"/>

以下のように変換されます。

英語版:
<description value="Find"/>

日本語版:
<description value="検索"/>

上記のように、ローカライズをするためには、インターフェイスに含まれている各ラベルやテキストに対して実体を宣言していくことになります。 このため XUL ファイルには、直接表示されるテキストは、一切含めるべきではありません。

また、実体は、テキストラベルに加えて、言語環境が変わると異なる可能性がある任意の値に対して使用しておく必要があります。 具体的には、アクセスキーやキーボードショートカットなどが考えられます。

 XUL
 <menuitem label="&undo.label;" accesskey="&undo.key;"/>
 DTD
 <!ENTITY undo.label "Undo">
 <!ENTITY undo.key "u">

上記の例では、Undo メニュー項目のラベルとアクセスキーの 2 つに実体が使用されています。

ファイル検索ダイアログを変更する

それでは、ここまでの内容をまとめて適用する方法を確認するために、 ファイル検索ダイアログを変更してみることにします。 このために、すべてのテキスト文字列を DTD を使用するように変更します。 変更後の XUL ファイル全体を下記に示します。 今回の変更部分は赤い文字で示されています。

<?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"
  orient="horizontal"
  onload="initSearchList()"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<script src="findfile.js"/>

<popupset>
   <popup id="editpopup">
     <menuitem label="Cut" accesskey="&cutCmd.accesskey;"/>
     <menuitem label="Copy" accesskey="&copyCmd.accesskey;"/>
     <menuitem label="Paste" accesskey="&pasteCmd.accesskey;" disabled="true"/>
   </popup>
</popupset>

<keyset>
   <key id="cut_cmd" modifiers="accel" key="&cutCmd.commandkey;"/>
   <key id="copy_cmd" modifiers="accel" key="&copyCmd.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="&copyCmd.label;"
                  accesskey="&copyCmd.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="http://www.xulplanet.com/rdf/recent/all">
     <template>
       <menupopup>
         <menuitem label="rdf:http://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 ファイルで宣言されている必要があります。 対応する宣言のない実体参照が XUL ファイルで行われていた場合、ウインドウは表示されません。

なお、実体の名前は重要でないことを補足しておきます。 上記の例では、実体の名前に単語をピリオドで区切ったものを用いていますが、 特に必要なわけではありません。 上記の実体の命名規則は、Mozilla のコードでの慣例をまねているだけです。

「2520 bytes」というテキストが、2 つの実体を使用するように置き換えられていることに気が付いたでしょうか。 これは、この部分のフレーズの組み立てが、ロケールによって異なる可能性があるからです。 例えば、(英語のように) 「数値」「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 ファイルを与えるようにしておけば、 同じ XUL ファイルを任意の言語で利用することが可能です。

var el = env.locale; ここまでのファイル検索ダイアログの例 : ソース

次のセクションでは、プロパティ ファイルについて見ていきます。

Document Tags and Contributors

Contributors to this page: ethertank, Morishoji, Mgjbot
最終更新者: ethertank,