2\input texinfo

     [file version.texi not included]

This file provides documentation for GNU gettext utilities. It also serves as a reference for the free Translation Project.

Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.

Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.

Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.

Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be stated in a translation approved by the Foundation.

GNU gettextユーティリティ
******************

イントロダクション
*********

     このマニュアルはまだドラフト状態であり、幾つかのセクションはまだ空かそれに近い状態です。我々は作成が遅れている部分を統合しながら、他の情報源 (特に電子メールのフォルダ)から適切な統合が遅れている資料を取り込み続けています。

このマニュアルでは、プログラマや保守担当者について話すときには彼を、翻訳者として彼女を、翻訳済みプログラムをインストールする人やエンドユーザとして彼らという単語を使用すします。これはこのドキュメントを明快にするためだけのものであり、決して男性や女性が何からの役割を果たすのにより適しているということを暗示するものではありません。そしてまた、あなたが考える通り、GNU gettextは性別や人種、宗教や国籍を問わずコンピュータを扱う人々にとってとても便利なものなのです。

この章では GNU gettextの創造と、フリーの翻訳プロジェクト (Translation Project)が目指すゴールについて説明します。そしてまた、ネイティブランゲージ(Native Language)のサポートを取り巻く幾つかの幅広いコンセプトについて説明し、(プログラムに適用可能なものとしての)国または文化的な特徴に関するメッセージ翻訳の位置付けについて規定します。同様に、ファイルの翻訳の流れと、それらのファイルの作成から後々の保守のサイクルに至るまでの各種ツールの関係についても説明します。

提案や訂正については、以下へ連絡して下さい。

     Internet address:
         bug-gnu-utils@prep.ai.mit.edu

マニュアルの版番号と、メッセージを送信した日付を付記して下さい。

GNU gettextの目的
==============

通常、プログラムとドキュメントは英語で記述されます。また、プログラムがユーザと対話する際にも英語が用いられます。これは GNU によるソフトウェアばかりではなく、多くの商用ソフトウェアやフリーソフトウェアでも同様です。共通言語 (common language)は、開発者(developer)や保守担当者(maintainer)が全ての国のユーザとコミュニケーションを取るためには実に便利です。その一方で、多くの人々は母国語ほど上手には英語を使えません。そして日々の作業でも出来ることなら母国語が使えることを望むでしょう。多くの人はコンピュータの画面には主に母国語が表示され、英語は必要最小限であることが望ましいと考えています。

しかしながら多くの人々にとって、それについて考える時間を費やすことさえも価値のないことだと考えられるほど、この夢は実現には遠いものだったかもしれません。彼らはそんな夢が実現するなんて考えてもみなかったのです。それでも、多くの人は希望を捨てず、彼らの組織を結成しました。「翻訳プロジェクト(The Translation Project)」は、この希望を実現するためのものであり、プログラムの真のマルチリンガルセット(multi-lingual set)の達成に至るためのよい機会です。

GNU gettextは、様々な他のステップを踏み出すための財産となる、翻訳プロジェクトの大切な第一歩です。このパッケージはプログラマ、翻訳者そして全てのユーザに、統合されたツール群とドキュメント類を提供します。特に、GNU gettextユーティリティは他のGNUパッケージがマルチリンガルなメッセージを扱えるようなフレームワークを提供します。これらのツールには、メッセージカタログをサポートするプログラムの作成方法の定義、メッセージカタログのディレクトリ及びファイル群の命名規則、翻訳済みメッセージを取得するためのランタイムライブラリ、様々な方法で翻訳された文字列の集合を取り扱うための独立したプログラム群を含んでいます。これらを準備したり最新のものにするためには、 GNU Emacs用の特別なモードもまた役に立つでしょう。

GNU gettextはプログラムソースに対する国際化の影響が可能な限り小さく、そして目立ちにくくなるようにデザインされています。プログラムのソースを眺める際、国際化の影響がごく僅かか、あるいは少なくとも影響が小さいと思えるのであれば、国際化はより成功しやすいことでしょう。

翻訳プロジェクトはまた、その構造及び方法を伝達するための媒体としてGNU gettextディストリビューションを使用しています。これは gettextを記述する厳密な専門的事項となります。これによって、翻訳者は翻訳の対象となるもの全てを可能な限り一ヶ所で見つけることができるようになります。この補助ドキュメントも、GNU gettextがどのように翻訳プロジェクトの残作業に関連しているかについてを理解するために、その結果として大きな一枚絵を垣間見るために、プログラマ及び好奇心の強いユーザの役に立つことでしょう。

国際化, 地域化, およびそれに類するもの
=====================

プログラムでのネイティブランゲージサポートについて議論するときは、常に二つの厳密な意味を持つ長い単語が登場します。internationalizationと localization、この二つの単語には、このドキュメントのためにここで説明するだけの価値があります。この二つの長い単語を繰り返し繰り返し書くことに疲れてしまった多くの人々は、それをきちんと書く代わりに、先頭と最後の文字を残し、その間にある文字列をその長さで置き換えてi18nと l10n と書くようにしました。しかしこのマニュアルでは物事を明確にするために、そのたびにきちんと正確に書くことにします...

国際化(internationalization)によって、プログラムまたはパッケージとなるプログラム群における処理は複数の言語を認識しサポートできるようになります。これは、プログラムから英語の文字列やその他の英語特有の慣習を切り離して、その代わりに同じことを行う一般的な方法を考慮するという点で、一般化のプロセスであると言えます。 プログラムの開発者は、自分が作成するプログラムに対して様々な国際化の技法を使うことができ、そのうちの幾つかは標準的なものとなっています。gettextはこれらの標準の一つを提案するものです。 See Programmers 。

地域化(localization)によって、既に国際化されているプログラム群における操作において、プログラムに(そのプログラムが)必要とする全ての情報を与えることにより、プログラムに対する入力やプログラムからの出力をあるネイティブランゲージや文化的特性(cultual habits)について正しく取り扱うことができるようになります。国際化されたプログラムで既に実装された一般的な手法を、特殊な方法で使用するという点で、これは特殊化のプロセスであると言えます。プログラミング環境には、プログラマが実行時に設定を調整出来るような幾つかの関数が用意されています。ある国で使われる言語を対象とした全ての関連した翻訳、そしてそれと共に使われるその国における文化的特性の固有の集合を、その言語、国に対する ロカール(locale)といいます。ユーザはプログラムを実行する前に、どのロカールを使用するのかを示すために特殊な環境変数に適切な値を設定します。これによってプログラムの地域化が行われます。

実際のところ、ロカールによるロカールメッセージサポートは、特定のロカールを構成する文化的データ(cultural data)の要素の一つに過ぎません。国際化されたソフトウェアを開発するプログラマの手助けをしたり、ある特定のロカールに属するデータへのアクセスを実行するための、完全なルーチン群や関数群が存在します。今、ある人があるロカールを参照するとすれば、それらの関数群やルーチン群は該当するロカールに格納されているデータを参照しています。同様に、あるプログラマが「ロカールルーチン群にアクセスする」と言った場合、それらの関数群やルーチン群とはロカールの情報全てにアクセスできるルーチン群の完全な集合 (complete suite)のこととなります。

プログラム中で複数の言語を使用可能とする、国際化と地域化の両方を包含する特徴及びその活動全体を表現するために、ネイティブランゲージサポート、または稀にNLSという表現が使われます。要約すれば、国際化とは地域化を可能とするものを越えた処理であると言えるでしょう。

また、非常に大雑把に言ってしまえば、マルチリンガルメッセージを実現する際、国際化は一般的にプログラマが注意することであり、地域化は一般に翻訳者が注意することとなります。

ネイティブランゲージサポートの各側面
==================

完全なマルチリンガルのディストリビューションのためには、出力メッセージ以外にも翻訳すべき事柄があります。

  * 現在、GNU gettextはCプログラムが出力するメッセージ群を翻訳するための完全なツールセットを提供します。perlスクリプトとシェルスクリプトもまた翻訳が必要です。しかし、たとえこれを成し遂げるためのいくつかのフックが現在存在したとしても、それらはまだきちんと統合されていません。

  * autoconfやbisonのような幾つかのプログラムは、他のプログラム (やスクリプト)を生成することが出来ます。それらの生成プログラム自身が国際化されていたとしても、それらの生成プログラムが生成したプログラムも国際化されなければなりません。そして、この間接的な国際化は生成側のプログラムによって自動化することもできるかもしれません。実際のところ、ほとんどの場合には生成側のプログラムと生成されたプログラムをそれぞれ別個に国際化するような労力が必要になるでしょう。

  * 幾つかのプログラムは、翻訳が必要となるような(プログラムに含まれる文字列からは独立している)テキストのテーブルをインクルードしています。一例を挙げると、RFC 1345はGNU recodeが実行時に再構成することができる各キャラクタに対する英語表記を提供しています。これらの記述はRFCから機械的に展開されたものですので、これらを翻訳するためには当然RFC自身の翻訳が事前に必要となります。

  * ほとんど全てのプログラムは、英語を使う人にわかりやすいように名前がつけられたオプションを受け付けます。オプションまでもが翻訳されたバージョンを希望する人間がいるかもしれません。

  * 多くのプログラムは予約語(keyword)、識別子(identifier)や応答といった、本質的に翻訳することのできるものから構成されたテキストを入力ファイルとして読んだり、解釈したり、コンパイルをしたりして動作します。例えば、ある人は gccが区別された文字(diacriticized characters)を識別子中や予約語中に使ったりすることができるようにしたいと望むかもしれません。rm - iはそれに対する返事として、yやn以外のものを受け付けるかもしれません。プログラムが最終的にその出力のほとんどを外国語で行うようになったとしても、入力の構文やオプションの値なども地域化するのかしないのかを決定する必要があります。

  * パッケージに同梱されたマニュアルもまた、配布物中のドキュメントファイルと同じように翻訳されねばなりません。常に最新版にしようと心がけているとしたら、マニュアルの翻訳はそれ自身が大仕事です。

既に強調したように、メッセージの翻訳はロカールの一側面でしかありません。他の国際化の側面は現在のGNU gettextではまだサポートされていませんが、おそらく将来のバージョンではサポートされることになるでしょう。その国の文化的慣習(cultual conventions)を定義するために必要な属性には様々な種類があります。これらの属性にはその国の言語、日付や時刻の表記方法、数値の表記方法、通貨記号などが含まれます。これらのローカルルールは国のロカールと呼ばれます。ロカールは、その国の独自属性(native attributes)をサポートするために必要となる知識を表わしたものです。

国ごとに大きく異なり得るため、ロカールが必ず記述しなければならない定義項目が幾つか存在します。以下に挙げるリストは、ロカールに関係する他のタスクの適切な文脈にマルチリンガルメッセージを配置するときの助けとなるものであり、また同様に、GNU gettextが最終的には取り掛かるかもしれないその他の領域を表わすものです。

文字とコードセット

     米国全土と英語圏において、もっとも一般的に使われているコードセットは ASCIIコードセットです。しかしながら、ASCIIコードセットでは対応できないような沢山の文字を使用する必要があるロカールもあります。8ビットのISO 8859-1コードセットは主なヨーロッパ圏言語を取り扱うために必要となる特別な文字のほとんどを含んでいます。しかし多くの場合、ISO 8859-1フォントは妥当なものではありません。このため、各地域によってどのコードセットを使う必要があるのかを明示する必要があり、コードセットに対処するための適切な文字取扱いルーチン(character handling routines)を準備する必要があります。

通貨

     通貨記号は、それが記号として使われるときの位置と同じ程、国ごとによってに異なるものです。ソフトウェアは、ロカール毎の通貨記号に対する方法をきちんと扱えるようにする必要があります。

日付と時刻

     日付のフォーマットはロカールによって様々です。例えば、1994年のクリスマスを米国では12/25/94と書きますが、オーストラリアでは25/12/94と書きます。その他の国々ではおそらくISO 8061で定められた日付記法などが使われるでしょう。

     時刻はHH:MM、HH.MMなどと記述されます。幾つかのロカールは午前 / 午後の指定を使用せずに24時間制で記述するようになっています。さらに、夏時間の性質や夏時間が実施される期間は国ごとによって大きく違います。

数値

     数値は各ロカールごとに異なる表記法を取ります。例えば、次の数はそれぞれのロカールごとの表記法によって正しく記述されたものです。

          12,345.67       英語圏
          12.345,67       フランス
          1,2345.67       アジア

     一部のプログラムではもっと進んでいて、異なる単位系、例えば英単位(English units)やメートル法、あるいは数値をまるまる綴るといった方法のような、様々な単位系を使用するかもしれません。

メッセージ

     もっとも大切なのは、ロカール中の言語サポートです。開発者にとってもユーザにとっても、ソフトウェアがユーザと対話する際の言語を簡単に変更できること、それこそがGNU gettextが提供するものです。

近い将来に、メッセージハンドリング以外のロカール構成要素が他のパッケージ中で使用可能になるということは考えられません。なぜなら、ほとんどの最近のシステムでは、そのシステムに欠けているロカール中の構成要素の少なくとも幾つかを、多少なりとも合理的にサポートしているからです。もう一点は、GNU libcとLinuxが、サポートが欠けているシステムにおいて利用できるような新しく完全で合理的なロカール機能全体の実装を実現するからです。

ファイルの翻訳の流れ
==========

.poファイルの「PO」とは「Portable Object」を意味します。これは .moファイルと区別するためです。なお、「MO」は「Machine Object」に由来します。この考えは、POファイルのフォーマットと同様、Uniforumで開発されたNLS標準、そしてSolarisにおいてSunが実装した方法から来ています。

POファイルは人間が読み書きすることを前提にしています。特定のターゲットとされた言語用に、指定されたパッケージ内の元々の文字列と、翻訳後の文字列を含んでいます。対象の言語一つに一つのPOファイルが用意されます。パッケージが複数の言語をサポートする場合、サポートする言語の一つずつにそれぞれ対応するPOファイルが必要です。これらのPOファイルはxgettextプログラムによって生成されるものがベストであり、msgmergeプログラムによって更新されます。 xgettextプログラムはCソースファイル群からマーク付けされたメッセージを抽出し、空の変換後文字列で初期化されたPOファイルを作成します。 msgmergeプログラムははPOファイルとそれにに対応するソースとの調整を行います。使われなくなったエントリをコメントアウトし、新しいエントリを追加し、ソース行との対応を更新します。「.pot」で終るファイルはPOファイルフォーマットに従った基本翻訳ファイルです。また、「.pox」で終るファイルはしばしば一時的に使用するPOファイルとして扱われます。

MOファイルは、通常はバイナリファイルで、プログラムによって読み込まれます。既に幾つかのシステムでは、システムに付属しているネイティブランゲージサポートの一部として、MOファイルの作成や取り扱いをサポートしています。しかしMOファイルのフォーマットはしばしばシステム毎に異なるものであり、移植性に欠けます。エンドユーザやインストーラはファイルの拡張子として.moを使う必要はありませんが、システムライブラリもこれらのファイルにアクセスするときはこの拡張子を使うので、同じように矛盾なく使用出来るでしょう。GNU gettextと既にシステムに提供されているツール群との間に互換性があるのなら、必然的に MOファイルの作成にはシステムが用意したそれらのツールを使えることになるでしょう。しかしそれらのツールが見つからないか、または使えないようであれば、 GNU gettextがそのシステムにおけるMOファイルの作成手段となり、MOファイルのフォーマットとなります。.gmoで終わっている名前のファイルは実はMOファイルで、これらのファイルがGNUフォーマットを使っていることを示しています。

GNU gettextの概要
==============

以下のダイアグラムは、GNU gettextによって操作されるファイルと、そのファイルを使って動作するツールの間の関係をまとめたものです。より詳細な説明が後に続きますが、この図から目を放さないで下さい。この関係の正確な理解は、プログラマにも翻訳者にも保守担当者にも役に立つことでしょう。

     オリジナルのCソース ---> POモード ---> マーク付きのCソース ---.
                                                                   |
                   .---------<--- GNU gettextライブラリ            |
     .--- make <---+                                               |
     |             `---------<--------------------+----------------'
     |                                            |
     |   .-----<--- PACKAGE.pot <--- xgettext <---'   .---<----- 要約PO
     |   |                                            |             ^
     |   |                                            `---.         |
     |   `---.                                            +---> POモード ----.
     |       +----> msgmerge -------> LANG.pox --->-------'                  |
     |   .---'                                                               |
     |   |                                                                   |
     |   `-------------<---------------.                                     |
     |                                 +--- LANG.po <--- 新しいLANG.pox <----'
     |   .--- LANG.gmo <--- msgfmt <---'
     |   |
     |   `---> install ---> /.../LANG/PACKAGE.mo ---.
     |                                              +---> "こんにちは、世界!"
     `-------> install ---> /.../bin/PROGRAM -------'

POモードはこの図で二回登場しています。POモードは好きなエディタを使った単なる「手作業による編集」と読むこともできますが、GNU Emacsを使っているのであれば、POファイルを編集したり修正したりするのに便利な環境であるPOモードというものが使えます。POファイルを編集している間、POモードはPOファイルを生成する元となるCのプログラムソース群を参照するのと同じくらい簡単に、補助 POファイル(auxiliary PO file)や要約POファイル(compendium PO file)をブラウズできます。このモードには、プログラム中の文字列を翻訳可能であるとマーク付けしたり、エラーを表示するためにPOファイルの当該行にジャンプすることによってPOファイルの確認を行うといった、幾つかの特別な機能があります。

プログラマとしてGNU gettextを導入する第一段階は、Cソースの中にある文字列が翻訳すべきものなのか翻訳しないでおくものなのかを確認することです。この単調で退屈な作業はEmacsのPOモードを使うことで幾分楽にはなりますが、使い慣れた様々な方法でCソースを変更していくこともできます。それ以外にも、翻訳ライブラリ(translation library)を適切に初期化するために、単純かつ標準的な変更が必要となります。これに関する詳細は see sourcesを参照して下さい。

新しくソフトウェアを書くにあたって、ソフトウェアを書きながら文字列にマーク付けすることはもちろん可能ですし、またそうすべきです。gettextのアプローチはこれをとても簡単にします。以下に挙げる行を、各ファイルの先頭または中心となるヘッダファイルに記述します。

     #define _(String) (String)
     #define N_(String) (String)
     #define textdomain(Domain)
     #define bindtextdomain(Package, Directory)

これによってソースの国際化の準備が出来ます。gettextライブラリを使う局面に至ったと思ったら、単純にこれらの定義を取り去り、libintl.hをインクルードしてlibintl.aをリンクします。変更する必要があるのはそれだけです。

Cソースを変更するとxgettextプログラムが全ての翻訳可能な文字列を検索して展開し、それを元に最初のPOファイルが作成されます。この PACKAGE.potファイルはプログラムの全てのオリジナル文字列を含んでいます。それはそれぞれの文字列がCソース中のどこで使われているかを示す正確なポインタの集合です。全ての翻訳は空に設定されています。.potの tという文字はそれがPOファイルのテンプレートであって、どの言語に対しても使うことができるものであることを示しています(xgettextプログラムの呼び出しに関しての詳細についてはsee xgettext Invocation)。もしあなたが本当に不精なのでしたら、すぐにたくさん働いてディストリビューション全体のセットアップを行うことに興味を持っているかもしれません (see Maintainers)。そうすることにより、今やmakeがあなたのために適切なものを自動的に生成するので、あなた自身はgettextコマンドをタイプせずにすむようになります!

一回目の処理では、LANG.poはまだ存在しませんので、 msgmergeのステップがスキップされて、単純に PACKAGE.potをLANG.poxにコピーするだけになるでしょう。ここで、LANGは目的とする言語を表わします。

それからメッセージの最初の翻訳となります。翻訳はそれ自身が問題であり、未だに、このマニュアルの水準をはるかに越える複雑さを備えた人間のみが出来ることなのです。にも関らず、このマニュアルの他の章で幾つかのヒントが与えられています(see Translators)。同様にあなたは、翻訳チームへのコンタクトの仕方についての指標や、そのチームに所属して、あなたが関心を持っているのと同じ言語をターゲットとしている人と翻訳を共有するための指標を見つけられるでしょう。

翻訳メッセージをPOファイルLANG.poxに追加している間に、もしあなたがGNU Emacsを便利に使えないのなら、あなたが自分で完全にPOファイルフォーマットと、クォート処理(quoting conventions)(see PO Files)を考慮することを保証しなければなりません。これは決して不可能な作業ではなく、既に UniforumやSolarisで多くの人がPOファイルを扱うのに行っているやり方です。一方、GNU EmacsのPOモードを使うのでしたら、POファイルフォーマットのほとんどの詳細部分は(Emacsが)面倒を見てくれますが、その代わりにPOモードそれ自体について慣れておく必要があります。主なPOモードコマンド(see Main PO Commands)について、エントリ間の移動の仕方(see Entry Positioning)と未翻訳エントリの扱い方(see Untranslated Entries)は知っておいたほうが良いでしょう。

もし一部の共通翻訳(common translations)が既に要約POファイル(compendium PO file)に保存されているのでしたら、翻訳者はPOモードを、要約から未翻訳エントリを初期化するため、また選択した翻訳語を要約にセーブしてアップデートするために使用することができます(see Compendium)。要約ファイルは、翻訳チームのメンバーの間で交換するためのものです。

プログラムやプログラムのパッケージは、本質的に動的なものです。ユーザはバグレポートや改良案を書き、保守担当者はさまざま方法でプログラムを修正してそれに反応します。パッケージが既に国際化されているという事実が、保守担当者に新しい文字列を追加したり既に翻訳した文字列を変更することを恐がらせるようにさせるべきではありません。彼らは自分のベストな仕事を行うだけです。翻訳プロジェクトを順調に進めるため、保守担当者には更なる余計な重荷を負わせず、そして翻訳者をプログラムについての関心からできる限り自由にすることが重要です。

保守担当者が関心を持つべき唯一のことは、翻訳可能な新しい文字列を注意深くマークすることであり、それが翻訳可能であるかどうかを彼らの作業中に気にすることではありません(いずれ、それにふさわしい機会が来ます)。その結果として、プログラムとその文字列が保守担当者によって調整されたとき、そして通常は翻訳に関係ない問題のために、xgettextは何度もPACKAGE.potファイルを構築します。そしてLANG.poに格納されている翻訳は少しづつ実情にそぐわないものとなっていきます。

パッケージの翻訳は、パッケージのライフサイクルにおける継続的な作業であって、一回やってしまえば終わりというものではないということを認識しましょう。これは、翻訳者(と保守担当者も)にとって重要なことです。与えられたパッケージに対する最初の翻訳作業の後も、時々検査を行うことが必要です。なぜなら、そこかしこの翻訳済みのエントリが用済みとなり、同時に翻訳が必要な未翻訳のエントリが現れるからです。

msgmergeプログラムの目的は、xgettextによって更新されたCソースから抽出されたより新しいテンプレートファイルPACKAGE.potと比較することによって、既にあるLANG.poファイルを更新することです。この更新作業はCソース中の文字列の移動を全て調整します。文字列の移動があったプログラムは、変更されたプログラムであるからです。同様に、 msgmergeはLANG.poxにある廃止されたものをコメントアウトします。これらは、もはやプログラムソース中では使われていないような翻訳済みエントリであるためです(see Obsolete Entries)。結果的に、このツールは新しい文字列を見つけ出し、そしてそれをPOファイルに未翻訳エントリとして挿入します(see Untranslated Entries)。msgmergeが実際に行っていることに関する詳細はsee msgmerge Invocation。

目標は、翻訳したほうがよい全ての文字列が提供されている(アップデートされた) LANG.poxを得ることです。この作業が完了した時点で、このファイル、LANG.poxは公式のLANG.poファイルを置き換えることになるでしょう。

POファイルの時間移動性(temporal mobility)や時間流動性(time fluidity)というものは、翻訳ゲームの重要な部分であり、良く理解して受け入れるべきものです。人々は翻訳プロジェクトで困難な仕事に参加することを拒んだり、他の参加者(participants)にそれを押し付けようとするかもしれません! 特に、保守担当者は関係する配布アーカイブにある全ての使用可能なPOファイルを、それが最近更新されていないものであっても含めるのが良いでしょう。プレッシャーはユーザーのコミュニティが使っている特定の言語から来るべきものであり、保守担当者は翻訳ファイルを適切にしておくことに注意を払うべきです。その一方で、翻訳者は、パッケージが正式な配布に先立つプリテスト(pretest)の間に、自分達が扱っているPOファイルを更新すべきです。

POファイルが完全で独立したものになれば、実行時に必要になったときにいつでも POファイルから効率良く翻訳メッセージを取り出すことのできるマシン向けのフォーマットに変換するために、msgfmtプログラム使われます(see MO Files)。msgfmtプログラムへの実行様式の全てについての詳細は see msgfmt Invocationを参照して下さい。

最後に、修正されマーク付けされたCのソースはコンパイルされ、さらにGNU gettextライブラリと共にリンクされます。通常、この作業はプロジェクトのために与えられたMakefileに応じてmakeを通じ行われます。そして、その結果としての実行ファイルはユーザーが見つけることのできる場所にインストールされます。MOファイルそれ自身も適切にインストールすべきです。与えられた適切な環境変数をセットすると(see End Users)、プログラムが実行されればいつでも自動的に地域化されるでしょう。

本マニュアルの残りの部分は、このセクションで述べた概要をより掘り下げて説明するためのものとなります。

POファイルとPOモードの基本
***************

GNU gettextツールセットは、プログラマと翻訳者が主に(テキスト形式で、編集可能な)POファイルのような翻訳ファイルを生成したり、更新したり、あるいは使ったりすることを助けるものです。この章ではPOファイルの書式と、 POモードスタータ(PO mode starter)の構成を強調します。POモードの記述は一ヶ所にまとめられておらず、このマニュアル全体に散らばっていますので、ここではPOモードの基本についてのみ解説します。

GNU gettextのインストールを完了する
=======================

GNU gettextの配布キットを入手し、展開、コンフィグレーション、コンパイルした後、make installというコマンドによってxgettext、 msgfmt、gettext、msgmergeといったプログラム、そして同時にそれらのプログラムで使用可能なメッセージカタログが所定の場所にインストールされます。完全に快適なインストールのために、あなたはGNU Emacsのユーザ達がPOモードを使えるようにしたいかもしれません。

POモードがインストールされているときは、.emacsに以下のような数行の修正を追加したくなるでしょう。

     (setq auto-mode-alist
           (cons '("\\.po[tx]?\\'\\|\\.po\\." . po-mode) auto-mode-alist))
     (autoload 'po-mode "po-mode")

この修正後、.poファイル、.potファイル、.poxファイル、あるいは.po.という文字列をファイル名中に含むファイルを編集するときはいつでも、Emacsはpo-mode.elc(あるいはpo-mode.el)を必要に応じてロードします。そして、.poなどを編集しているバッファでは自動的に POモードのコマンドが有効になります。POモードが有効になっているバッファでは、 POという文字列がモードラインに表示されます。一つのEmacsセッションにおいて複数のPOファイルを扱っても、このアクティブ化は一度だけしか実施されません。

もしあなたが使用しているEmacsのバージョンが20かそれ以上であり、既に適切な多国語フォントをシステムにインストールしているのであれば、必要なときはいつでもそれらのフォントを自動的にロードしてEmacsの画面表示に使用することが出来ます。そのためには、以下の行を.emacsファイルに追加すればよいでしょう。

     (autoload 'po-find-file-coding-system "po-mode")
     (modify-coding-system-alist 'file "\\.po[tx]?\\'\\|\\.po\\."
                                 'po-find-file-coding-system)

POファイルのフォーマット
=============

POファイルは多くのエントリから構成されています。各エントリはオリジナルの翻訳されていない文字列と、それを翻訳したものとの関係を保持しています。POファイル中の全てのエントリは、通常一つのプロジェクトのみに関係し、また全ての翻訳文字列は一つの(翻訳先の)言語で表わされます。一つのPOファイルのエントリは次のような構造をとります。

     WHITE-SPACE
     #  TRANSLATOR-COMMENTS
     #. AUTOMATIC-COMMENTS
     #: REFERENCE...
     #, FLAG...
     msgid UNTRANSLATED-STRING
     msgstr TRANSLATED-STRING

POファイルの一般的な構成は、翻訳者が良く理解できるようなものであると良いでしょう。POモードを使用するときに、POモードが翻訳者に対して注意しているのと同じように、翻訳者がフォーマットのディテールに関することをあまり知る必要がないようにするべきです。

エントリは幾つかの(省略可能な)空白で始まります。通常、GNU gettextツールを通じて生成された場合にはエントリとエントリの間に一行だけ空行が入ります。これに、#で始まっているコメント行が続きます。コメントには二種類あります。一つは#の直後に幾つかの空白が続いているようなコメントで、これはもっぱら翻訳者によって作成や保守が行われるものです。もう一つは#の直後に空白ではないキャラクタが続いているもので、これはGNU gettextツールによって自動的に作成されたり保守されたりするものです。すべてのコメントはその種類によらず省略することもできます。

空白とコメントの後にエントリの二つの文字列があります。最初の文字列はオリジナルのソースプログラムにあったような未翻訳の文字列、もう一つはその文字列の翻訳です。オリジナルの文字列はmsgidというキーワードの後に置かれ、翻訳された文字列はmsgstrの後に置かれます。これら二つの文字列(翻訳前 /翻訳後)はPOファイル中で"や\を使った方法でクォートされますが、 POモードがクォートの面倒を見るので、翻訳者はクォートのフォーマットに対して注意を払う必要はありません。

msgidは自動的に作成されるコメントと同様、他のGNU gettextツールによって生成されたり、管理されたりするものです。POモードはmsgidの作成/管理に代わる機能を翻訳者に提供しません。彼女(翻訳者)が行えることの大部分は、単にそれらを削除することと、エントリを丸ごと削除することのみです。その一方でmsgstrという文字列は、翻訳者のコメントと同じように、翻訳者のためのものであり、彼女(翻訳者)は必要とする全ての制御をPOモードで行うことができます。

プログラムがコメントとして完全に無視するわけではないという点で、#,で始まるコメント行は特別なものです。FLAGのコンマ区切りのリストは 、幾つかの有用な診断メッセージをユーザに与えるものとして、msgfmtプログラムが扱います。現在のところ、二つの形式のフラグが定義されています。

曖昧(fuzzy)
     このフラグはmsgmergeプログラムによって生成されうるか、または翻訳者自身によって付与されうるものです。これはmsgstr文字列がおそらく正しい翻訳でないことを示しています。翻訳者は更に翻訳を進めるか、あるいは現在のままでいくかを決定するだけです。ひとたび翻訳が完了したなら、彼女はこの曖昧属性を取り除きます。msgmergeプログラムは曖昧検索(fuzzy search)の後でmsgidとmsgstrが結合されたときのみこの属性を付与します。See Fuzzy Entries。

c-format
no-c-format
     このフラグは人間によって付与されるものではありません。xgettextプログラムのみがこれを付与します。ここで提案された自動的なPOファイル処理システムによって、xgettextプログラムが新しいテンプレートファイルを生成するやいなや、ユーザによる変更は再度失われます。

     c-formatフラグが文字列に付与された場合、msgfmtは翻訳が正当なものであるかどうかについて更なるチェックを行います。See msgfmt Invocation。

空白やコメントが何行か、POファイルの最後のエントリの後に続くようなことがあります。このような行はどのエントリの一部でもなく、POモードはこのような行に対して何のアクションも取りません。POモードの関数M-x po-normalizeを使うことによって、翻訳者はそういった余計な行を取り除くことができます。See Normalizing.

このセクションの残りの部分は、POモードを使うに当たってスキップすることもできるでしょう。しかし、POファイルのフォーマットについてより良いアイディアを持っている人にとっては興味深いものであるかもしれません。一方、ここでは述べられていないGNU Emacsの便利な機能については注意深く読みつづけるべきです。

UNTRANSLATED-STRINGとTRANSLATED-STRINGは、それを囲むクォートや(文字列中の)バックスラッシュエスケープシーケンスも含めて、それぞれキャラクタ文字列に対するCの構文を反映しています。複数行にまたがる文字列を書く事態になったときは改行のエスケープを行うのではなく、その代わりに継続する行の最後のキャラクタに続けてクォートを置いて、その次の行の最初にもう一度クォートを置きます。例を挙げましょう。

     msgid ""
     "Here is an example of how one might continue a very long string\n"
     "for the common case the string represents multi-line output.\n"

(訳注: これは出力が複数行に渡るような非常に長い文字列を継続させるやり方の例です。)

この例では、Hereという単語のHとforのfを揃えるために、空文字列が最初の行で使われています。この例のキーワードmsgidは連結が行われる三つの文字列を伴っています。空文字列の連結は最終的な文字列全体に対して何の変更も行いません。しかし、これは我々がよりきれいに配置したように複数行の体裁を左寄せにしながらも、msgidの文字列は同じ行になければならないという制限に従うための方法です。空文字列は取り除くこともできますが、そのためには先の例ではHereで始まる行をがmsgidの右側に置かなければなりません (この制限はGNU gettextによって強制されたものではなく、Solarisのmsgfmtの実装から来ています。)。後ろの二つの文字列のクォートは改行\nの直後で切り替える必要があるわけではなく、その他の任意の文字の後で行うこともできます。我々が改行の後で区切ったのは、その方がすっきりしているからです。

メッセージ文字列の一部としてクォートの内側に存在する\nのような行の終端と、文字列のクォートの外側にあってメッセージ文字列には含まれない POファイルの行それ自身の行の終端とは、気をつけて区別した方が良いでしょう。

文字列の外側では、自由に空行やコメントを置くことができます。コメントは行の先頭の#から始まり、その行の終わりまでがコメントとなります。翻訳者によって書かれたコメントは、#のすぐ後ろに幾つかの空白が置くようにするのがよいでしょう。もしそのような空白がなければ、そのコメント行はGNUツールによって生成されたり扱われたりするものとほとんど同じであり、POファイルが msgmergeに与えられたときに、思いがけなく見失ったり置き換えられたりしてしまうかもしれません。

POモードの主要なコマンド
=============

Installationで設定したようにEmacsの設定が完了した後、EmacsがPOファイルを読み込んだならば、そのウィンドウではPOモードがアクティブになります。ウィンドウは読み取り専用となり、po-mode-mapが開始されます。 po-mode-hookとして関数が定義されている場合は、それが実行されます。

POモードがウィンドウに対してアクティブにされた時点で、POという文字列がウィンドウのモードラインに表示されます。モードラインには、POファイル中のあらゆる種別のエントリの数もまた表示されます。例えば、 132t+3f+10u+2oという文字列が表示された場合、それはPOモード中に132の翻訳エントリ(see Translated Entries)があり、3つの曖昧なエントリ (see Fuzzy Entries)があり、10の未翻訳エントリ(see Untranslated Entriesがあり)、2つの廃止されたエントリ(see Obsolete Entries)があることを示しています。一つも存在しないエントリは表示されません。例えば、もし曖昧なエントリが解決され、未翻訳のエントリが翻訳され、廃止されたエントリが削除されたなら、モードラインにはカウンタとして145tが表示されることでしょう。

主なPOコマンドは後のセクションにある他のカテゴリにはあてはまりません。これらはPOモードを終了するか、ウィンドウを特別な方法で管理するためのものです。

U
     POファイルに対する最後の変更を取り消します。

Q
     処理を中断し、POファイルを保存します。

q
     可能であれば確認してから、処理を中断します。

O
     POファイルのウィンドウから一時的に離れます。

?
h
     POモードに関するヘルプを表示します。

=
     POファイルの幾つかの統計を表示します。

V
     POファイル全体の書式の正当性のチェックを行います。

U(po-undo)コマンドはGNU Emacsのundo機能に対するインターフェースです。See Undoing Changes (The Emacs Editor). Uがタイプされる度に、翻訳者がPOファイルに対して行った変更は少し元に戻されます。アンドゥのために、POモードの各コマンドの処理はアトミックなものとなっています。これはRETコマンドのときに特にそうです。たとえ編集それ自身が幾つかのアクションによるものであったとしても、一回の RETコマンドによる編集は一度に取り消されます。しかしながら、編集ウィンドウで作業している間は編集作業を一つ一つチマチマとアンドゥできます。

Qコマンド(po-quit)とqコマンド (po-confirm-and-quit)は、翻訳者がPOファイルに対する操作を終えるときに使用します。前者は少しだけ冗長さに欠けるものです。もしファイルが変更されていれば、最初にファイルがディスクにセーブされます。両方のケースにおいて、そして全てを行う前に、このコマンドはPOファイルに翻訳されていないメッセージが残っているかチェックし、もしあれば翻訳者に対するPOファイルの編集を本当にやめるのかの問い合わせを行ないます。これはEmacs のPOファイルバッファを扱うのに好ましい方法です。バッファの削除を通常のC-x k (kill-buffer)コマンドで行うのは良くない方法です。

O(po-other-window)コマンドは、POモードから抜け出るためのもう一つの柔軟なやり方です(一時的に、ですが)。このコマンドはカーソルを別の Emacsウィンドウに動かして、必要ならばそのウィンドウをポップアップするというものです。例えば、翻訳者はPOモードでソースコードを見ているときに、修正する必要があるプログラムソース中に明らかなバグを見つけるかもしれません。このコマンドは翻訳者がプログラマとなり、(この時点でプログラマとなった)翻訳者が修正しようとしているウィンドウにカーソルを移すことを許します。この後でカーソルをPOファイルのウィンドウに戻すか、Emacsにこのファイルをもう一度編集するということを伝えれば、POモードは復活します。

hコマンド(po-help)は、すべての使用可能なPOモードコマンドのサマリを表示します。その後で翻訳者が任意のキャラクタをタイプすると、通常のPO モードオペレーションに復帰します。?コマンドはhと同じ効果を持ちます。

=コマンド(po-statistics)はPOファイル中のエントリの総数、カレントエントリの順番(1からカウントされます)、未翻訳のエントリの数、廃止されたエントリの数を計算し、これらの数字全てを表示します。

Vコマンド(po-validate) は、現在のPOファイルに対して msgfmtを饒舌モード(verbose mode)で起動します。このコマンドは最初に現在のPOファイルをディスクに保存することを提案します。GNU gettextの msgfmtツールはMOファイルをPOファイルから作成するという目的を持っています。そして、POモードはこのプログラムの機能を全ての個別のエントリのチェック、そしてPOファイル全体のフォーマットのチェックのために使っています。

msgfmtプログラムはEmacsと非同期に動作するので、翻訳者は即座にPOファイルの制御を取り戻します。エラー出力はGNU Emacsの*compilation*バッファに集められて別のウィンドウで表示されます。通常のGNU Emacsコマンド C-x`(next-error)は、他の通常のコンパイルコマンドと同様、POファイルの問題となった部分にすばやく移動することを可能とします。一旦カーソルがエラーのある行に移れば、翻訳者はエラーを修正するのに役立つ任意のPOモードアクションを取ることができます。

エントリの位置
=======

POファイルウィンドウにあるカーソルはほとんど常にエントリの一部です。唯一の例外はファイルの最後のエントリの後ろにある場合、あるいはPOファイルが空の場合といった特殊なものです。多くのPOモードコマンドはカレントエントリで動作するので、翻訳者はPOファイルを眺めて回ることよりもカーソルを移動することのほうが多くなります。カーソルを移動するということは、コマンドの操作対象となるエントリを選択することにもなります。

いくつかののPOモードコマンドは特殊なやり方でカーソル位置を変えます。これらの特殊な目的での位置の移動の幾つかをここで説明し、その他のものはこの後のセクションで説明します。

.
     現在のエントリを再表示します。

n
n
     現在のエントリの次のエントリを選択します。

p
p
     現在のエントリの前のエントリを選択します。

<
     POファイルの先頭にあるエントリを選択します。

>
     POファイルの最後のエントリを選択します。

m
     後で使用するときのために、現在のエントリの位置を記録します。

l
     直前に保存した位置へ復帰します。

x
     現在のエントリ位置と、直前に保存したエントリ位置とを交換します。

幾つかのGNU EmacsコマンドはPOモードにあるカレントエントリを選択するためにカーソルの位置設定をすることができます。文字、行、パラグラフ、スクリーン、あるいはページ単位で移動するコマンドや、検索コマンドです。しかしながら、 POモード中にあるカレントエントリを表示するための標準的な方法があります。これはカーソルの移動を強制しないような通常のGNU Emacsコマンドです。.コマンド(po-current-entry)は、カレントエントリが変更された後や、 Emacsのスクリーンなどの状態が変わった後で、適切なカレントエントリの再表示を行うというただひとつの目的を持っています。

翻訳者が作業をする間、強制的にウィンドウの制御全てを保持し続けることが翻訳者にとって作業しやすいのかそうでないのか、この点についてはまだ決めかねています。我々はウィンドウがこのように振る舞うのは素晴らしいアイディアだと思いましたが、しかし一方で、Emacsを使う人がウィンドウを完全にコントロール出来る方がよいのかもしれないとは思います。ウィンドウの制御については、翻訳者がそれを有効か無効かを切り替えるオプションを実験的にPOモード用に提供するかもしれません。誰もそのような必要性を感じないか、あるいはそれを作成するように強制されたら、我々はそのようなアイディアを放棄するでしょう。このことを実行するための動機となるのは、他人がどのように翻訳をするのかを考えているプログラマからよりも、むしろそれに価値を見いだす経験を積んだ翻訳者からの意見です。

n(po-next-entry)コマンドやp (po-previous-entry)コマンドは、それぞれ直後のエントリ、直前のエントリにカーソルを動かします。nが最後のエントリにあるときに指定されたり、 pが最初のエントリにあるときに指定された場合はカーソルを移動しません。

<(po-first-entry)と>(po-last-entry)というコマンドは、それぞれカーソルをPOファイルの最初のエントリ、最後のエントリに移動します。カーソルがPOファイルの最後のエントリにあったとき、ほとんどのPOモードコマンドはAfter last entryのエラーを報告するでしょう。しかし、 <と>はカーソルがPOファイルのエントリにないような状態でも動作が可能なようになっており、カーソルが最後のエントリに置かれた場合でも使うことができます。しかし、これらのコマンドを空のPOファイルで実行した場合にはエラーとなります。POモードを、対話的にソースから空のPOファイルを埋めていくようにする開発計画があります。See Marking.

特定のエントリを翻訳する前に、翻訳者は関連するエントリで使われている用語や語法を参照するためにPOファイルの残りの部分を眺める必要があるかどうかを決めることができます。翻訳者はもちろん、その時点でのカーソル位置を幾つかのレジスタに保存し、後でカーソル位置を戻すためにレジスタに保存した値を使うという Emacsの標準的なやり方を使うことができますし、location ringを使うこともできます。

POモードは別のアプローチを提供します。カーソル位置を特別なスタックに保存するというものです。mコマンド(po-push-location)は、単に現在の位置をスタックに追加して、以前に保存されている位置情報の上に新しいものをプッシュします。rコマンド(po-pop-location)はスタックの最上位の要素を取り出して、取り出した要素に結び付けられている位置へとカーソルを移動します。この位置はカーソルを直前に保存された位置へ移動させるrが次に使われたときに消費されます。この位置情報は使用されるまでスタック上に留まりつづけます。

翻訳者が位置スタック上に位置を保存しておきたいと望むのでしたら、スタックの最上位にある要素に結び付けられたエントリを見ることによってなされるでしょう。そして翻訳者はrの直後にmを使って、後で戻るための目印を伴いつつ任意の場所へ移動します。

x(po-exchange-location)コマンドは保存されたロケーションのスタックの最上位にある要素に関連付けられたエントリへのカーソルの再位置付け (reposition)をシミュレートして、移動前に現在のエントリの位置に対応する最上位要素を置き換えます。従って、xコマンドを繰り返し実行すると、二つのエントリの間で入れ替えが行われます。これを成し遂げるために、翻訳者は最初のエントリにカーソルを置いてmを使って、それからカーソルを二番目のエントリに動かします。そして入れ替えのためにxを使います。

エントリ内の文字列の正規化
=============

POファイルの各エントリに特定の文字列をエンコードしておくということについては、多くの異なった方法があります。なぜなら、複数行に渡る文字列の引用や分割の方法はたくさんありますし、更に、バックスラッシュの付いたエスケープシーケンスによる特殊キャラクタの表現方法も一つではないからです。POモードの幾つかの機能は、既に存在しているPOファイル中でmsgidフィールドにあるエンコードされた特定の文字列を検索するPOモードの機能に依存しています。この認識を簡単に実装するために、POモードがすべての組み込み機能を内部的に持っていたとしても、それを高速に行うのは技術的には困難です。この効率に関する問題を解決するため、我々は文字列を標準的(canonical)に表現することを決定しました。

POファイルにおける伝統的な(conventional)文字列の表現に関しては現在議論中であり、そしてPOモードの標準的記法についても実験中です。等価な文字列を表わすための統一的な方法に向けて、xgettextとPOモードの両方を統合することは有用です。GNU gettextのxgettextを使った時点で、POモードが必要とする内部的な正規化が自動的に実行されるのですから。事細かなPOモードによる正規化は、どこかからかインポートされるPOファイルに対してのみに、あるいはそれ自身を発展させるための議論のときのみに必要となります。

POファイル内にある、少なからず標準的記法を必要としている文字列を正規化するには、以下のPOモードのコマンドを使います。

M-x po-normalize
     POファイル全体を、エントリの形式を揃えるようにすることで整頓します。

特殊コマンドM-x po-normalize(特定のキーには割り当てられていません)はすべてのエントリを再検査し、オリジナルのものと翻訳されたものの両方の文字列のエントリがPOファイルにおける規定の形式でクォートされていることを保証します。このコマンドは最後のエントリの後ろにあるゴミも取り除きます。このコマンドはPOファイルをどこかから新規にインポートしてきたときや、標準的なクォートのフォーマットに変更する際に有用です。この標準的なクォートのフォーマットに変換することにより、よりクリーンなPOファイルを得ることが出来るだけでなく、他のPOモードコマンドが文字列を高速に検索することが出来るようになります。

M-x po-normalizeは各エントリに対して処理を三回試行します。最初の方法はGNU gettext 0.6より前に採用されていたPOファイル変換のヒューリスティクスで、msgidとmsgstrフィールドは複数行を扱う際にK&RスタイルのC文字列を使っていました。これらのヒューリスティクスは廃止されたエントリと関連づけられておらず、かつバックスラッシュで終了するコメントに対しては失敗していました。これらのヒューリスティクスは廃止されたエントリに続く行もコメントにしてしまっていたからです。一旦全ての古いPOファイルが修正されたなら、この最初の方法は消滅するでしょう。第二、第三の方法は全ての msgidと msgstr文字列を個別に正規化します。これらの方法は XViewの msgfmtが継続行に対して使用していたものであり、継続するバックスラッシュを削除します。

他のソースファイルからPOファイルへとインポートすることを可能にする明快な正規化コマンドがあるものの、今では現在の規定の進化はより芸術的に緩和されています。正規化のコマンドとして提案されている修正を行うのは簡単です。結局のところ、他の GNU gettextツール群が自動的に協調して動作すればよいのです。文字列の標準的記法について以下に述べます。これらの情報はGNU Emacs を使用せず、独自の方法でPOファイルを編集する人々に対して役に立つでしょう。

POモードでは、文字列は単一行でも複数行でもかまいません。 [^\n]\n+[^\n]にマッチするような埋め込まれた改行が文字列にあれば、その文字列は複数行となります。例を挙げてみましょう。

     msgstr "\n\nHello, world!\n\n\n"

しかし、スペースを改行で置き換えれば次のようになります。

     msgstr ""
     "\n"
     "\n"
     "Hello,\n"
     "world!\n"
     "\n"
     "\n"

我々はここで、問題点を明確にするためにちょっとおおげさな例を使いましたが、通常は複数行のメッセージはこんな体裁の悪いものではありません。次のような提案を実装することはあり得るでしょう。空文字列内の最初の改行文字をひとまとめとして扱い、全ての改行文字は行末で終了するというものです(ここで N > 1であるとします。N - 1 番目の改行は分割された文字列だとします)。前に挙げた例は次のようになるでしょう。

     msgstr "\n\n"
     "Hello,\n"
     "world!\n"
     "\n\n"

文字列の正規化の細部については、まだ幾つかの未決定事項が残されています。それらの問題が解決したら、このマニュアルに記載されます。

プログラムソースの準備
***********

Cプログラムソースコードの変更は3段階に分けて行われます。最初に、メッセージ変換が必要な全てのモジュールに、地域化用の関数群の存在を知らせなければなりません。次に、プログラム初期化時にGNU gettextを使う準備をする必要があります。これは通常main関数内で行われます。最後に、プログラム中で変換が必要な全文字列を捜し出してマーク付けしましょう。

ここで、プログラム又はパッケージが既に必要なGNU gettextファイルを利用できるように修正されており、そしてMakefileの修正も完了しており、 (see Maintainers) 、文字列の翻訳の対象となる全てのCプログラムに次の一行が追加されていると仮定します。

     #include <libintl.h>

Cのソースプログラムに対するその他の変更については、この章の後のセクションで説明します。

gettextの処理をトリガする
================

ロカールデータの初期化は、どのプログラムでもこのような形で行われます。下の例を参照してみて下さい。

     int
     main (argc, argv)
          int argc;
          char argv;
     {
       ...
       setlocale (LC_ALL, "");
       bindtextdomain (PACKAGE, LOCALEDIR);
       textdomain (PACKAGE);
       ...
     }

PACKAGEとLOCALEDIRは、共にconfig.h又はMakefile中で定義するのがよいでしょう。より多くの情報が必要であれば、gettextのソースを参照して下さい。

LC_ALLの使用は適切ではないかもしれません。LC_ALLは全てのロカールカテゴリ、特にLC_CTYPEを含んでいます。LC_CTYPEは、 ctype.h内で定義されるisalnum関数などが扱うキャラクタクラスの決定に影響します。これらは特に、ある種の入力言語を扱うプログラムに対してよくありません。例えば、ソースコード中でc + cedille(訳注: セディユ、フランス語で使用される文字)を使用している場合、そのプログラムはフランスでは動いても米国では動かないでしょう。

一部のシステムではまた、LC_ALLロカール以外のものが使われている場合、 scanf関数中の数値のパースに関する問題を抱えていることがあります。 "C"ロカール内にあっても認識される追加のフォーマットについて、規格では言及されています。。しかし、一部のシステムは"C"ロカールのフォーマットにある数値を無視するようです。このような状況下では、数値が"C"ロカールまたはローカルのフォーマットにあろうとなかろうと、数値表記方法の認識が不可能になるような問題があることでしょう。これは何千ものセパレータ文字が使われていることに起因します。一部のロカールは、"C"ロカールにおいて小数点として定義されている'.'を国家的事情のために別な用途に使うように定義しています。

このため、場合によっては上の例におけるLC_ALLの行を setlocaleの行の並びに置き換える必要があります。例えば、

     {
       ...
       setlocale (LC_TIME, "");
       setlocale (LC_MESSAGES, "");
       ...
     }

このようにします。全てのPOSIX準拠のシステムでは、LC_CTYPE、 LC_COLLATE、LC_MONETARY、LC_NUMERIC、そして LC_TIMEロカールカテゴリが使用可能になっています。一部の現代的なシステムでは、XPG2準拠システムでかつてLC_RESPONSESと呼ばれていた、 LC_MESSAGESロカールが定義されています。

ソース中でのマーク付けの方法
==============

Cソース中の翻訳を必要とする全ての文字列はマーク付け(marking)されるべきです。マーク付けがなされた翻訳可能な文字列は、ある特定の関数やプリプロセッサマクロの単一の引数として表されます。翻訳のために使うことのできる関数やマクロはほんの少ししかなく、それらの名前はマーク付け用キーワード(marking keyword)と呼ばれます。マーク付けは文字列に対して行うというよりはむしろ、、文字列それ自身と結び付けられるものです。この手法は他でも使われます。一例として、フォーマットされることにより出力されるエラーメッセージのことを考えてみましょう。エラーメッセージの書式文字列は翻訳を必要とします。このような書式指定文字列中では、書式指定文字列中の%s書式指定を通じて様々な文字列が挿入されますから、出力結果は様々な形になります。このような書式を取る error_string_out()のような関数のために、この関数の出力の結果全てをリストアップするのは得策ではありません。

このマーク付け操作には二つのゴールがあります。最初のゴールは実行時に翻訳文字列を取り出すための引き金となることです。キーワードは、適切な翻訳文字列を動的に返すことのできるルーチンを通じて解決されます。大部分の翻訳が必要な文字列は実行位置で検索され、変数に反映したり、関数に対する引数として与えられます。しかしこれは一般的な方法であり、一部の翻訳文字列は構造的な初期化方法によって検索されます。See Special casesを参照して下さい。

マーク付けの二番目のゴールは、xgettextがプログラムソースにある全ての翻訳対象文字列をスキャンして抽出し、POファイルのテンプレートを生成するのを手助けすることです。

翻訳可能な文字列のマーク付けをするための標準的キーワードはgettext です。これはGNU gettext全体の名前にもなっています。パッケージ中で、 gettextというキーワードやマクロ、関数だけを使うことは非常に簡単です。しかし、パッケージ中でgettextインターフェースを使うと煩雑になってしまうので、主なキーワードとしては短く便利で目立たないものを提供すべきでしょう。実際のところ、キーワードは国際化されたパッケージ中の多くの文字列に対して現れるでしょうが、プログラマは自分のプログラムソースにそういったものが溢れかえるようなことは普通望まないでしょう。更に、ソースコードを 79桁から80桁に収めるためには、水平方向にスペースを取る長いキーワードは不利になります。

パッケージの多くでは、キーワードとして_(単一のアンダーライン)を使っていて、gettext("Translatable string")の代わりに _("Translatable string")と記述しています。更に、GNU標準におけるコーディング規則で要求されている「キーワードと開き括弧の間に一つのスペースを置く」という規則が、これ特有な使い方を明確にするために緩められています。これで、翻訳文字列ひとつあたりに必要な文字数のオーバーヘッドは、アンダーラインと一つの括弧のペアの三文字に押えられます。しかしながら、GNU gettextがこのやり方を内部的に使っていたとしても、公式には勧めません。実際のところ、本当の正しいキーワードはgettextなのです。 gettextの代わりに_を使用できるようにするのは非常に簡単で、次のように宣言しておきます。

     #include <libintl.h>
     #define _(String) gettext (String)

単純に#include <libintl.h>とするより、上の例のようにします。

保守という作業は、最初の手間に比べれば相対的に簡単です。プログラマとして、あなたが文字列を追加・修正したりしたら、追加・修正された文字列が翻訳を必要とするかどうかを確認する必要があるでしょう。そしてそれが翻訳されるべきだと思ったのなら、それを_()の内側に置きます。"%s: %d"は翻訳を必要としない文字列の例です。

翻訳対象文字列のマーク付け
=============

POモードにおける機能は翻訳者向けと云うよりはプログラマ向けです。そして、プログラマに対してプログラムソース中にある文字列が翻訳可能であるか、そうでないかのマーク付けを対話的に行うことができるようになっています。そのような文字列を見つけだしてマーク付けをするという作業が、プログラマにとって自分の好きなエディタを使って行えるごく簡単な作業であったとしても、POモードはこの作業をより快適にするのです。更にPOモードは、国際化されたパッケージのプログラムソース中の翻訳対象となる文字列にマーク付けしている間、プログラマに翻訳者のような気分を、翻訳者にプログラマのような気分を味わせます。

ここで説明するPOモードのコマンドが対象とするプログラムソース群は、プロジェクトのために構築されたEmacsタグテーブルを持つべきです。これは簡単なことで、任意のシェルウィンドウ中でプロジェクトのルートディレクトリに移動し、次に挙げるようなコマンドを実行するだけです。

     etags src/*.[hc] lib/*.[hc]

ここで、src/やlib/といったディレクトリにある全ての .hと.cとを処理したい状況を考えてみましょう。このコマンドは全てのファイルを探索して、プロジェクトのルートディレクトリにTAGSというファイルを作成します。これはEmacsが理解することの出来る、特殊なフォーマットによる内容の要約になります。

GNUコーディング標準に従っているパッケージでは、ソースを構成する全てのファイルから構成されたタグファイル(tagsかTAGS)が全てのディレクトリに存在することになっています。

TAGSというファイルが用意されれば、プログラマがソースファイル中にある翻訳対象文字列をマーク付けするために後述するコマンド群が使用できます。これらのコマンドはPOファイルウィンドウの中で起動される必要があるのですが、まだPOファイルが作成されていないことということがありがちです。これはそれほど問題にはなりません。これらのコマンドを主に使用するために、安全に新しい空の POファイルを開くことができます。あなたがマーク付けしたプログラムソース中の翻訳対象文字列によって、この空のPOファイルは徐々に満たされてゆくでしょう。

,
     プログラムソースから、翻訳の候補になると思われる文字列を検索します。

M-,
     最後に見つかった文字列に_()で目印付けをします。

M-.
     最後に見つけた文字列に、可能なキーワードセットから取り出したキーワードで目印付けをします。このコマンドは、これらのキーワードの幾分の管理を許すような前置引数を取ります。

,(po-tags-search)コマンドは次に現れる翻訳の対象となるような文字列を検索します。そしてEmacsの別のウィンドウにプログラムソースを表示し、そのウィンドウの先頭付近に検索された文字列がくるようにします。もし文字列がウィンドウに収まらないほど大きければ、その終端部分だけが表示されます。このような場合、カーソルはPOファイルのウィンドウに残ります。表示されている文字列が別の言語では異なった表記をされるものであれば、M-,やM-.を使ってマークすることができます。その文字列を無視するのなら、単に,コマンドを繰り返すことによって次の文字列へとスキップすることが出来ます。

三文字以上の長さの文字列は翻訳対象となり得ます。二文字から成る文字列は、それが文字以外のものを含んでいなければ翻訳候補としてみなされるでしょう。コマンドは文字を含まない文字列や、同じ文字からのみ構成される文字列を無視します。また、コメント中にある文字列や、POモードが知っているキーワード(後述)によって既にマークされている文字列についても無視します。

もしまだEmacsに対してどのTAGSを使用するかを指定していないのであれば、このコマンドを最初に実行したとき、このコマンドはミニバッファでどの TAGSファイルを使用するかを尋ねます。EmacsのM-x visit-tags-tableコマンドを使って、使用したいTAGSファイルに後から変更することもできます。See Tag Tables (The Emacs Editor).

,コマンドを使うと、検索は直前に行われた検索の位置から再開されます。この処理はTAGSファイルに従って全てのプログラムソースが処理されるまで続けられます。しかし、前置引数(C-u ,)を与えることによって、最初のファイルから検索を再開するように指定することも出来ます。但しこの場合、 (翻訳するとして)既にマーク付けした文字列は自動的にスキップされます。

,コマンドを使うことによって、通常のEmacsのタグコマンドを邪魔してしまうようなことはありません。例えば、通常のtags-searchや tags-query-replaceといったコマンドは、,のサーチの流れを中断することなく使うことが出来ます。しかしながら、実装によっては最初の ,コマンド(もしくは 前置引数を伴った,コマンド)は最初のタグファイルを検索するための通常のEmacsタグを再び初期化してしまうかもしれず、そのためにこの再初期化は不当なものであると看做されるかもしれません。

M-,(po-mark-translatable)コマンドは、一番最近見つかった文字列を_キーワードでマーク付けします。M-. (po-select-mark-and-mark)コマンドは、ミニバッファからキーワードを一つ読み込み、そのキーワードを使って文字列のマーク付けをします。これら両方のコマンドは、マーク付けされた文字列を使ってPOファイル中に新しい未翻訳エントリを自動的に生成します(もし直ちに翻訳することが好きなのであれば、これによりすぐに翻訳が行えるようになります)。M-,やM-.による変更はプログラムソース中の行を80カラムよりも長くすることがあり、あなたは改行してその行を異なる形で再インデントしなければならなくなるでしょう。POモードから Oコマンドを使ったり、GNU Emacsからウィンドウを変更するコマンドを使うことによって、プログラムのソースウィンドウに移動して何らかの必要な修正を行うことができます。後続の文字列に対してM-,を使いたいのであれば、POファイルウィンドウにカーソルを戻すために通常のEmacsコマンドを使う必要があるでしょう。

M-.には、毎回キーワードを明示的にタイプしなくてもすむよう、作業をスピードアップさせる幾つかの機能が組み込まれています。最初のスピードアップは、好みのキーワードが表示された場合、それを使うにはただ単にプロンプトに対してRETをタイプすればよいというものです。第二のスピードアップは、キーワードの最初のほうをタイプすれば、コマンドがそれを自動的に補完してくれるというものです。PO modeは使用可能な全てのキーワードを知っており、これによってキーワードのミスタイプは弾かれるようになります。

キーワードのリクエストに対して?をタイプすると、コマンドはあなたが選択することのできる既知のキーワード全てをリストアップします。コマンドに前置引数が渡された場合(C-u M-.)、プログラムソースやPOファイルのバッファに対する更新は行われず、代わりに単純なキーワード管理が行われます。この場合、コマンドはキーワードを訊ねます。キーワードを完全に記述することにより、ここで設定したキーワードが以後のM-.で使用されるキーワードになります。更に、この新しいキーワードは自動的に以後の好みのキーワードとなります。C-u M-.に対する入力として既知のキーワードを入力した場合、ただ単に好みのキーワードが変更されるだけとなります。

M-.コマンドのために使用される全てのキーワードは、文字列の検索時に ,コマンドによっても認識されます。そしてあらゆる既知のキーワードによって既にマーク付けされている文字列は自動的にスキップされます。もし多くのPOファイルが同時に開かれている場合、それらは独自に既知のキーワードの組を保持しています。現在、POモードには既知のキーワードを削除する手段はありません。既知のキーワードを削除するためには、ファイルを閉じて(qコマンドが使えるでしょう)、新たに開き直す必要があります。POファイルがEmacsの新しいウィンドウとして開かれた場合、gettextと_だけが既知のキーワードとなり、 gettextがM-.の好みのキーワードとなります。事実、_を選択するのは好ましくありません。それはM-,コマンドの組み込みとして既に設定されているからです。

続く文字列に関して何かを伝えること
=================

Cプログラム中の文字列はしばしば、printfファミリの関数呼び出し中で使われます。これらのフォーマット文字列(format strings)は、それらが%から始まるフォーマット指定子(format specifiers)を含むことが出来るという点で特殊なものです。ここで、このようなコードがあると仮定します。

     printf (gettext ("String `%s' has %d characters\n"), s, strlen (s));

上記の文字列をドイツ語へと翻訳した場合、このようになるでしょう。

     "%d Zeichen lang ist die Zeichenkette `%s'"

たとえドイツ語が話せなかったとしても、Cプログラマはこれでは何かまずいと思うでしょう。二つのフォーマット指定子の順番が変更されているにも関わらず、 printfの引数はもちろんそのままなのです。ここでは、文字列の長さが文字列へのアドレスとして見做されるため、これはおそらく問題となることでしょう。

実行時に翻訳によって引き起こされるエラーを防ぐため、msgfmtツールは本来の引数と翻訳文字列が型と個数においてマッチしているかどうかを静的にチェックすることができます。もしそうでなかった場合は警告が出されることになり、実行時にエラーによって問題が発生することはなくなります。

先のドイツ語翻訳における語順が正しいとすれば、それはこのように書かれるべきです。

     "%2$d Zeichen lang ist die Zeichenkette `%1$s'"

msgfmt中のルーチンはこの特別な記法を扱えます。

全てのプログラム中の文字列がフォーマット文字列であることはないので、 .poファイル中の全ての文字列についてテストすることはmsgfmtにとって好ましくありません。printf中で使われていないにも関わらず、文字列がフォーマット指定子のように見える文字列を含んでいることがあるので、これは問題となる得るからです。

従って、xgettextはフォーマット文字列と考えられるそれらの文字列に対して特別なタグを付与します。これには絶対的なルールがあるわけではなく、あくまでもヒューリスティックなものです。.poファイル中で、そのエントリは c-formatとして#,コメント行にマーク付けされます(see PO Files)。

注意深い読者はここで、これは再び問題を起こすと言うことでしょう。ヒューリスティクスはそれを悪いと推測するでしょう。これは真実であり、 xgettextはプログラマに決定を行わせるための特別な種類のコメントを認識することができます。gettextキーワードと同じ行か、あるいは直前の行において、xgettextプログラムがxgettext:c-formatという語を含んだコメントを見つけ出したなら、どのような場合でのその文字列には c-formatフラグが付与されるでしょう。このコメントは、文字列がテストされそれが本当にフォーマット文字列であるにも関わらず、xgettextがそれをフォーマット文字列として認識できかった場合にも使用できます。コメントが gettextキーワードと同じ行にある場合は、それは翻訳される文字列よりも前になければならないことを覚えておいて下さい。

この状況は頻繁に発生します。printf関数はしばしばフォーマット指定子を含まない文字列と共に呼び出されます。もちろん、それはfputsを使用するときもも通常そうです。このような場合、xgettextはその文字列をフォーマット文字列として認識しませんが、もし翻訳が正しいフォーマット指定子を含んでいた場合はどうなるのでしょう。printf関数はパラメータにアクセスしようとしますが、本来のコードはいかなるパラメータも参照していないので、パラメータは存在しません。

もちろんxgettextはあべこべの間違った決定をすることがあります。フォーマット文字列としてマークされた文字列は実はフォーマット文字列ではありませんでした。このような場合、msgfmtは大量の警告を出し、.poファイルの翻訳をさせません。このような間違った決定をやめさせるために、先に使ったのと同じような方法が使用できます。xgettext:no-c-formatという文字列を含んだコメントを使用すればいいだけです。

c-formatとして文字列がマークされたにも関わらず、それが正しくない場合、信頼できる決定が行えるユーザはそれを発見することができます。 See xgettext Invocationを参照すれば、--debugオプションがこの問題を解決するために使えることがわかります。

特別な翻訳対象文字列
==========

注意深い読者はここで、gettextやその他の同様のツールは翻訳可能な文字列を常にマーク付け出来るものではないということを指摘するかもしれません。次のような場合を考えてみましょう。

     {
       static const char *messages[] = {
         "some very meaningful message",
         "and another one"
       };
       const char *string;
       ...
       string
         = index > 1 ? "a default message" : messages[index];

       fputs (string);
       ...
     }

"a default message"という文字列に対するマーク付けには問題ありませんが、messagesを初期化している文字列に対するマーク付けはできません。ここで行うことは何でしょう? 我々は二つの作業を行わねばなりません。最初に行うべきことは、xgettextプログラム(see xgettext Invocation)がそのような文字列を見つけることができるようにマーク付けをすることで、二番目に行うべきことは、実行時に文字列が表示される前に文字列を翻訳するということです。

最初の仕事は、no-opという名前の新しいキーワードを作成することにより達成できます。二番目の仕事として、配列中に格納された文字列を参照している全ての場所にマーク付けをする必要があります。解決例は次のようなものになります。

     #define gettext_noop(String) (String)

     {
       static const char *messages[] = {
         gettext_noop ("some very meaningful message"),
         gettext_noop ("and another one")
       };
       const char *string;
       ...
       string
         = index > 1 ? gettext ("a default message") : gettext (messages[index]);

       fputs (string);
       ...
     }

fputsによって出力される文字列はどの場合でも翻訳されたものであることを理解して下さい。追加キーワードgettext_noopをxgettextに知らせる方法については、xgettext Invocationで説明されています。

もちろん、先の例だけが解決方法ではありません。次のようにしても同じことが出来ます。

     #define gettext_noop(String) (String)

     {
       static const char *messages[] = {
         gettext_noop ("some very meaningful message",
         gettext_noop ("and another one")
       };
       const char *string;
       ...
       string
         = index > 1 ? gettext_noop ("a default message") : messages[index];

       fputs (gettext (string));
       ...
     }

しかしこれには幾つかの欠点があります。第一に、プログラマは自分が"a default message"という文字列のためにgettext_noopを使っているということに対して注意を払わなければなりません、gettextの使用は稀れに予測できない結果を生むことがあるからです。第二に、GNU gettextライブラリの内部の問題として、このやり方では効率が下がるからです。

出力が本当に全てのケースで翻訳されているかどうかということを確実にするために、制御フローを解析する必要がないということは一つの利点となります。しかしこの解析は一般的にそんなに難しいことではありません。もしあらゆる状況においてそうすべきであれば、あなたはこの状況下でこの二番目の手法を使うことができます。

最初のPOファイルの準備
************

xgettextプログラムの起動
================

     xgettext [OPTION] INPUTFILE ...

-a
--extract-all
     すべての文字列を抽出します。

-c [TAG]
--add-comments[=TAG]
     TAG(もしくはキーワードが先行している行)を持つコメント部分を出力ファイルに出力します。

-C
--c++
     C++形式のコメントを認識します。

--debug
     フォーマット文字列としてメッセージをマーク付けする責任を負う人間が、 c-formatフラグとpossible-c-formatフラグを表示するために使います。後者のフォームはxgettextプログラムによって決定されるもので、フォーマットフォームはプログラマがそう規定した場合に使われます。

     デフォルトではc-formatフォームのみが使われます。翻訳者はこの詳細について考えるべきではありません。

-d NAME
--default-domain=NAME
     出力先をmessages.poではなく、NAME.poにします。

     特別なドメイン名である-または/dev/stdoutは、標準出力に出力することを意味します。

-D DIRECTORY
--directory=DIRECTORY
     ソースファイルをスキャンする前にディレクトリをDIRECTORYに変更します。最終的に.poファイルは元々のディレクトリに書き出されます。

-f FILE
--files-from=FILE
     入力ファイル名をコマンドラインから得る代わりに、FILEから取得します。

--force
     たとえメッセージが定義されていなかったとしても、常に出力ファイルを生成します。

-h
--help
     このヘルプを表示して終了します。

-I LIST
--input-path=LIST
     入力ファイルを検索するディレクトリのリストを指定します。

-j
--join-existing
     既に存在するファイルに出力を追加します。

-k WORD
--keyword[=WORD]
     検索するキーワードを追加します(WORDを指定しない場合、デフォルトのキーワードを使わないという意味になります)。

     明らかに使用しないことを指定しない限り、 gettext、dgettext、dcgettextとgettext_noopがデフォルトのキーワードとなります。

-m [STRING]
--msgstr-prefix[=STRING]
     msgstrエントリの接頭辞として、STRINGや""を使用します。

-M [STRING]
--msgstr-suffix[=STRING]
     msgstrエントリの接尾辞として、STRINGや""を使用します。

--no-location
     #: FILENAME:LINEという行を出力しません。

-n
--add-location
     #: FILENAME:LINEという行を生成します(デフォルト)。

--omit-header
     msgid ""エントリを持つヘッダを出力しません。

     .gmoファイル生成時の変動を絶つことが出来るので、テスト目的に使用するのに便利です。これらのファイルはGNU gettextパッケージで使用でき、 msgfmtでも使用できます。両方の結果は同じものになります。

-p DIR
--output-dir=DIR
     DIRというディレクトリにファイルを出力します。

-s
--sort-output
     ソート済みの出力を生成し、重複したものは取り除きます。

--strict
     Uniforum規定のPOファイルフォーマットに厳密に従った形式で出力します。

-v
--version
     バージョン情報を出力して終了します。

-x FILE
--exclude-file=FILE
     FILEからのエントリを抽出しません。

追加のPOファイルのサーチパスは/usr/local/share/nls/src/となります。

INPUTFILEとして-が指定されたときは、標準入力から読み込みます。

xgettextのこの実装はプリプロセッサマクロ中の文字列、ANSI規格での隣接した文字列の連結、文字列の継続のための行末のエスケープなどのような、幾つかの厄介なケースの処理を可能とします。

Cソースの文脈
=======

POモードは、GNU gettextユーティリティを使って作られたPOファイルを扱うときに力を発揮します。GNU gettextユーティリティは生成したPOファイルに特殊なコメントを挿入します。これらの特殊なコメントの一部は、POファイルのエントリとプログラムソース中の未翻訳文字列が出現する場所を関連付けます。

翻訳者が未翻訳エントリを扱う際、彼女が扱う未翻訳文字列はしばしば簡潔すぎたり、暗号的であったり、曖昧であったりといった、わかりにくいものであることがあります。その文字列をどのように翻訳するかを考える前に、翻訳者はそれが何を意味しているのか、そしてどのような翻訳が適切なのかを理解する必要があります。そのような問題に直面したとき、翻訳者が判断を下すためにはその文字列が使われている本物のプログラムソースを読み、文字列の近くにあるプログラマが書き残したコメントを捜し出し、そして何らかの手掛かりを得るしかありません。

もちろん、翻訳者が手練のプログラマであればプログラムソースを読むことで更なる情報を得ることが出来るでしょう。しかし、彼女が経験を積んだプログラマでなく、Cのコードに戸惑いを感じたとしても、ソースを読むことについて臆病になってはいけません。翻訳者が必要とする何らかのヒントを得ることが出来るという可能性はまだあるのですから。翻訳者はすぐに、プログラマのコメント、(プログラマが理由を持って命名した)変数及び関数名、全体の構造について一層注意を払うことで、プログラムコードが不快なものではないということを学ぶでしょう。

以下のコマンド群は、翻訳者がPOファイルのエントリに関係するプログラムソースの文脈を調べるときに役立つようになっています。

s
     プログラムソースの文脈を(繰り返し)表示します。

M-s
     メニューから選択することによって、プログラムソースの文脈を表示します。

S
     ソースファイルを検索するパスにディレクトリを追加します。

M-S
     ソースファイルを検索するパスからディレクトリを削除します。

コマンドs(po-cycle-reference)とコマンド M-s(po-select-source-reference)は、両方ともソースプログラムファイルを別のウィンドウとして開き、現在の翻訳対象となる文字列を表示します。しかし、もしエントリにソースへの参照がなかった場合、またはソースへの参照を含むソースファイルがプログラムソースのサーチパス内に存在しなかった場合、コマンドはエラーを表示します。

s(又はM-s)は新しいウィンドウを開きますが、カーソルはPOファイルのウィンドウ内に留まります。もし翻訳者がカーソルをプログラムソースウィンドウに移動させたい場合、もちろんそうすべきなのですが、コマンドOを使用する必要があるでしょう。

最初にsがタイプされた時点、あるいは直前にソースコードの文脈を参照するために使われたものとは別のPOファイルエントリが選択された時点で、コマンド sはこのエントリのための最初の文脈を再度表示します。現在のPOファイルエントリにおけるある文脈が表示された時点で、翻訳者が別の文脈を試したい場合は、ただ単に再度sをタイプするだけで別のウィンドウに文脈が表示されます。翻訳者がソースファイル中でカーソルをその文脈から動かした場合は、コマンドsはカーソルを元の文脈に戻すでしょう。あるエントリ中で、他のコマンドを使用せずコマンドsを繰り返し使用した場合、かつ途中に他のコマンドを使用しなかった場合、POモードは最初に表示した文脈に戻るまで、そのエントリが参照可能な文脈を次々と表示します。

コマンドM-sの振舞いはそれとは異なります。参照箇所を次々と表示していく代わりに、参照箇所の一覧を表示して翻訳者にその中から特定の参照箇所を選択させます。これは補完機能(completion)と一緒に使うのが最善です。翻訳者が M-sの直後にTABをタイプすると、質問に対する(マッチする可能性のある)答として可能な参照個所全てがメニューとして提示されます。このコマンドはある一つの翻訳対象文字列が本当に複数の文脈で登場するときのみ便利です。

通常、プログラムソースファイルはPOファイルと同じディレクトリで検索されます。特定の規定として、ソースファイルの検索が失敗した場合、プログラムソース群は POファイルが置かれたディレクトリの直上のディレクトリで検索されます。POファイルを扱う場合、これら2つの対応によって通常問題は起きません。しかしながら、 POファイルが偶然別のディレクトリに配置されたり、通常のディレクトリとは異なるディレクトリで編集されることもあります。このような場合、翻訳者は正しい POファイルが通常どこに存在するのかをPOモードに伝えなければなりません。そのようなディレクトリが数多く指定された場合、それらはプログラムソースのためのいわゆるサーチパスを構成します。コマンド S(po-consider-source-path)は、対話的に新しいディレクトリをサーチパスの先頭に格納するために使われます。コマンドM-S (po-ignore-source-path)は、翻訳者がもはや必要としなくなったディレクトリをサーチパスから選択するために使われます(補完機能付きで)。

翻訳要約機能の使用
=========

要約機能はまだ実装されていません。

POモードの将来の実装では、翻訳者は既に翻訳したものの要約を保守することになります。要約とは、多くのパッケージで頻繁に用いられる翻訳文字列群をまとめた特別なPOファイルのことです。翻訳者は自分が使用する要約に新しいエントリを追加しておき、後で未翻訳エントリを初期化するとき、あるいは既に翻訳されたエントリを更新するときに、要約から翻訳文字列を引き出すことが出来ます。しかしこの作業のためには、要約は正規化されていなければなりません。 See Normalizing。

既存のPOファイルの更新
************

msgmergeプログラムの起動
================

翻訳済みエントリ
========

それぞれのPOファイルエントリについて、msgstrに翻訳が定義され、かつ曖昧(see Fuzzy Entries)であるとマーク付けされていない場合、そのエントリは翻訳済エントリと呼ばれます。翻訳済エントリは後からGNU msgfmtによってコンパイルされ、プログラム中で使用可能にされるだけです。その他のエントリタイプは締め出されます。それらについては翻訳が作成されなかったのです。

幾つかのコマンドはより密接に翻訳済エントリの処理と結び付いています。

t
     次の翻訳済エントリを探します。

M-t
     前の翻訳済エントリを探します。

コマンドt(po-next-translated-entry)とM-t (po-previous-transted-entry)は前後に移動し、翻訳済エントリを探し出します。見つからなかった場合、POファイルバッファを回り込んで検索が行われます。

翻訳済エントリは通常、翻訳者がエントリを翻訳して作成されます Modifying Translations。しかし、もし変数 po-auto-fuzzy-on-editがnilでなかった場合、最初に新しい翻訳がなされたエントリは曖昧なエントリとなります。そのエントリは後から、公式かつ本物の翻訳済エントリとなる前に曖昧ではなくされなければなりません see Fuzzy Entries。

曖昧な翻訳エントリ
=========

それぞれのPOファイルエントリは属性の組を持っています。それらは名前が付けられた品質であり、特別なシステムコメントを使用することでエントリの翻訳と明白に結び付けられます。これらの属性のうちの一つは曖昧と名付けられており、この属性を持つエントリは曖昧な翻訳を持っていると言われます。単純に曖昧なエントリとも呼ばれます。

曖昧なエントリは、たとえ多くの他の目的のためにそれらが翻訳済エントリとみなされるとしても、通常は翻訳者によって修正されなければなりません。幾つかの新しいmsgidが古いPOファイル中にあるmsgidからわずかに修正されたものであり、かつその新しく修正されたエントリに対応する、古い翻訳であると考えられるものとが対になるとmsgmergeプログラムが仮定したとき、 msgmergeプログラムが新しいPOテンプレートファイルによって古い翻訳済 POファイルを更新することによって、これらのエントリが作成されます。本来の文字列(msgidにある文字列)に対する若干の変更はしばしば翻訳済文字列に影響し、これは翻訳者による修正を要求します。このため、msgmergeはいくつかのエントリを曖昧であるとしてマーク付けすることでしょう。

翻訳者はまた、エントリに再び手を入れる必要があるということを記憶してきたいときに、自分自身の利便のためにエントリを曖昧であるとしてマークしておくことが出来ます。幾つかのコマンドはより密接に曖昧なエントリの処理と結び付いています。

f
     次の曖昧なエントリを探します。

M-f
     前の曖昧なエントリを探します。

TAB
     現在のエントリの曖昧属性を外します。

コマンドf(po-next-fuzzy) とM-f (po-previous-fuzzy)は前後に移動し、曖昧なエントリを探し出します。見つからなかった場合、POファイルバッファを回り込んで検索が行われます。

コマンドTAB(po-unfuzzy)はエントリに結び付いた曖昧属性を外し、通常は翻訳済にします。さらに、もし変数po-auto-select-on-unfuzzyが nilでない場合、TABコマンドは自動的に他に作業する必要があるエントリを探し出します。po-auto-select-on-unfuzzyの初期値は nilです。

po-auto-fuzzy-on-editの初期値はnilです。しかし、変数 po-auto-select-on-unfuzzyがtに設定されたならば、 RETコマンドによって編集されたエントリには曖昧属性が付与されます。それはダブルクリックを用いることと同じです。この場合、(常にではないにせよ)翻訳者が修正したときはいつでも、エントリは曖昧になるということが普通になります。もし彼女が翻訳に満足した場合、彼女は曖昧属性を吹き飛ばし、TABを使って他のエントリに対する作業にかかるでしょう。もし彼女がまだ満足していない場合は、そのエントリを曖昧なままにしたまま他のエントリに移るために、ただSPCを使うだけです。

翻訳者はまた、あるエントリに後で作業するために簡単に戻れるようにしたいと考えたのなら、翻訳済エントリを曖昧にするためにDELコマンド (po-fade-out-entry)を使うこともできます。

そして、qコマンドを使ってPOファイルバッファでの作業が完了させたときに曖昧なエントリがまだ残っていれば、POモードは翻訳者に対して作業を終了するかどうかを確認します。

未翻訳エントリ
=======

xgettextが最初にPOファイルを生成したとき、msgidは未翻訳文字列で初期化され、msgstrには空文字列が設定されます。空の翻訳文字列を持つエントリは未翻訳エントリと呼ばれます。その後、プログラマがプログラム中で一部の文字列をほんのちょっとだけ修正した場合、修正された文字列は未翻訳文字列として、POファイルに新しいエントリとして追加されることになります。

エントリ間を移動する通常のコマンドは、アクティブになったエントリと同じレベルにある未翻訳エントリに注意を払います。未翻訳エントリはmsgstr ""で終了するという事実から、簡単に発見することが出来ます。

翻訳者の作業というのは、未翻訳エントリを探しだして翻訳し、その作業を未翻訳エントリがなくなるまで繰り返すという意味では(本当に単純な)作業に思えるかもしれません。幾つかのコマンドは未翻訳エントリ処理に特化しています。

u
     次の未翻訳のエントリを検索します。

M-u
     前にある未翻訳のエントリを検索します。

k
     現在のエントリを未翻訳の状態にします。

u(po-next-untranslated-entry)コマンドとM-u (po-previous-untransted-entry)コマンドはそれぞれ、前方と後方に移動して未翻訳のエントリを探し出します。見つからなかった場合には、検索はPOファイルバッファを回りこんで継続します。

翻訳文字列を単に空にすることによって、エントリを未翻訳のエントリへと戻すことが出来ます。これにはkコマンド(po-kill-msgstr)を使います。 See Modifying Translations.

qコマンドを使用してPOバッファでの作業を終了する際、もし未翻訳の文字列が残ったままの場合は、POモードは翻訳者に対して作業を終了するかどうかを確認します。

廃止エントリ
======

地域化されているパッケージにおいて、これ以上翻訳する必要がないエントリは msgmergeを使用することによって発見され、コメントアウトされます。これらのエントリは、廃止エントリと呼ばれます。

エントリ間を移動する通常のコマンドは、アクティブになったエントリと同じレベルにある廃止エントリに注意を払います。廃止エントリはmsgid又は msgstrを含む全ての行が#で開始されているという事実によって簡単に発見することが出来ます。

幾つかのコマンドは、元々の未翻訳文字列によって翻訳文字列を初期化します。キルリングを扱うコマンドのインターフェースは、以前に保存されたテキストを翻訳文字列として格納することが出来ます。ユーザは翻訳を対話的に編集することが出来ます。これら全てのコマンドは、その結果として廃止エントリを廃止エントリのままにしておくことが出来ます。

更に、幾つかのコマンドは廃止エントリの処理に特化しています。

o
     次の廃止エントリを検索します。

M-o
     前にある廃止エントリを検索します

DEL
     アクティブなエントリを廃止エントリにします。又は、廃止エントリを削除します。

o(po-next-obsolete-entry)コマンドとM-o (po-previous-obsolete-entry) コマンドはそれぞれ前方と後方に向かって廃止エントリを検索します。もしそのようなエントリが見つからなければ、検索は POファイルバッファを回りこんで処理を継続します。

POモードは、廃止エントリのコメントをはずしたり、エントリをアクティブにするような方法を提供しません。なぜなら、これによってプログラムソース中にあるマーク付けされた文字列ではない未翻訳文字列が再び導入されてしまうことになるからです。これは無駄なmsgidを導入しないという哲学に基づいています。

しかし、アクティブなエントリをコメントアウトすることは可能であり、これによりエントリは廃止エントリとなります。GNU gettextユーティリティは後程、未翻訳文字列を使用することによる翻訳の消失に対して反応します。DELコマンド(po-fade-out-entry) はカレントのエントリをちょっとだけ消滅させる方向へと向かわせます。もしエントリがアクティブ(翻訳対象エントリ)なら、そのエントリはまず曖昧にされます。もし既に曖昧であれば、確認後にそのエントリはコメントアウトされます。エントリが既に廃止エントリであった場合、そのエントリはPOファイルから完全に削除されます。削除したものをPOファイル中の他の (通常は未翻訳の)エントリに置くことによって、翻訳文字列は簡単に再利用出来ます。See Modifying Translations.

眠れない夜のために、将来のPOモード開発によって解決する興味深い問題についてここで紹介しておきましょう。そのアイディアとは、POモードがより賢くなるであろうものです。新しく追加された未翻訳の文字列に対して、POモードは全ての廃止エントリの中からおそらく適当であろうと思われるものを探しだしてきて当てはめるようになります。これを実現するためには、アルゴリズム的に難しい問題があるでしょう。文字列の類似性を効果的に測定するための方法を開発しなければなりません。POモードが適切な廃止エントリを発見できるようになったとき、POモードは完全に翻訳者に決定をさせます。翻訳者の決定作業を簡単にするための使いやすいツールの準備をするだけになります。

翻訳の修正
=====

POモードでは、POファイルを直接編集してEmacsバッファの内容を変更することは出来ません。そうすることによって、翻訳者がファイルフォーマット全体における書式の間違いを防いだり、文字列を適切にクォートしたりすることが出来るのです。それ以外のエラーがまだ起きる可能性はありますが、その一部に関しては Vコマンドによって実行できるバッチ検証プロセスによって検査することが出来ます。それ以外のエラーは、翻訳者自身の判定、あるいは翻訳されたパッケージの(翻訳者と同じ母国語を使う)ユーザによるレポートに頼らざるを得ません。

翻訳文字列を作成するとき、あるいは機械的に検査されたりユーザから報告されたエラーを修正するときには、翻訳者は翻訳文字列を修正するために以下に挙げるコマンドの力を借りなければなりません。

RET
     翻訳を対話的に編集します。

LFD
     翻訳文字列を、オリジナルの未翻訳文字列で再初期化します。

k
     翻訳文字列をキルリングに保存し、それから削除します。

w
     翻訳文字列をキルリングに保存しますが、削除しません。

y
     翻訳文字列をキルリングから取り出したものに置き換えます。

RET(po-edit-msgstr)コマンドは、現在のPOファイルエントリから取り出した文字列のコピーが置かれた、新しいEmacsウィンドウを開きます。このウィンドウは直ちに編集することが出来、GNU Emacsの修正コマンドの補完機能を使いながら修正することも出来ます。(ウィンドウにある)文字列は、翻訳者のために引用符ははぎとられていますから、クォートされていない文字列をウィンドウ内で修正することが出来ます。作業が完了したなら、通常のEmacsコマンドM-C-c (exit-recursive-edit)を使って元のPOファイルの編集へと戻ることができます。このとき、翻訳文字列は新しいもので置き換えられます。C-c C-cというキーはM-C-cと同じ動作をするようにバインドされています。

もし翻訳者が自分自身の翻訳に満足できなくなり、RETコマンドで編集する前の翻訳を残したほうがよいと感じた場合、まだ元の翻訳が残っている間であれば Emacsの標準コマンドC-](abort-recursive-edit)を使用して、元の翻訳に戻すことが出来ます。C-c C-kはC-]と同じように動作するようにバインドされています。または、通常通りC-c C-cで終了したあと、Uをタイプすることによって編集結果を元に戻すことが出来ます。

po-subedit-mode-hookに関数が設定されていた場合、文字列が編集バッファに挿入された後、かつ再帰的編集が開始される前に設定されていた関数が実行されます。

翻訳文字列を編集している間に翻訳者が気をつけるべきことは、余計な RET(改行)文字を翻訳文字列に入れてしまったり、逆に必要な改行を取り除いてしまうということをしないようにするということです。この文字は編集バッファ上では目に見えないので、間違いを犯しやすいのです。これを防ぐために、 RETは自動的に<という文字を編集している文字列の終端に付加しますが、この<は文字列の一部ではありません。C-c C-cを使って編集ウィンドウから抜けるときに、POモードは自動的に(先程付加した) <を取り除きます。もし翻訳者が文字列の最後の<の後ろに文字を追加した場合、<に備わった終端属性は失われ、<は文字列の一部となってしまいます。もし翻訳者が<を自分で取り除いてしまったなら、編集した文字列はあるがままの状態に置かれ、目に見えない改行が文字列の後ろに続いていたとしてもそれは取り除かれません。また、翻訳文字列が本物の<で終わる文字列だったのなら、<は取り除かれません。文字列は編集ウィンドウの中に二つの<が最後に付いているような状態で見えることでしょう。

翻訳文字列(もしくはコメント)が編集されているとき、翻訳者はカーソルをPOファイルバッファに戻して、さらに別のエントリに移って眺めるようなことをするかもしれません。同時に修正可能なエントリは一つだけなので、編集されたエントリは編集が終了するとすぐに元通りになります。編集しているものがオープンされたままで翻訳者がPOファイルバッファをうろつきまわったとしたら、彼女は他のどのエントリも修正することができません。もし修正しようとしたなら、POモードは現在の編集を中断するか、あるいは、他のエントリを変更するために現在の編集を終了するようにアドバイスするでしょう。

LFD(po-msgid-to-msgstr) というコマンドは、翻訳語を元々の文字列で初期化、もしくは再初期化します。このコマンドはそれまでの作業を無視して、オリジナルの文字列で翻訳を再度元に戻したいというときに使われます。

未翻訳エントリを編集するときはいつでも、LFDコマンドが自動的に実行されるように設定することが出来ます。po-auto-edit-with-msgidを tに設定すれば、もしまだ翻訳が存在しない場合、オリジナルの文字列によって初期化されます。po-auto-edit-with-msgidの初期値はnilです。

事実、空の文字列から翻訳を開始するのとオリジナルの文字列のコピーと共に開始するのとのどちらがよいのかは、単に好みの問題です。あるときは、元々の言語とターゲットとする言語は、白紙から書き始めたほうがよいほど異なっています。また別の場合、元々の言語とターゲットとする言語はオリジナルの文字列して設定されているものの幾つかの単語を再度打ち直すだけですむかもしれません。翻訳者もまた、彼女の目の前にオリジナルの文字列があるほうを好むことでしょう。彼女はオリジナルの文字列を翻訳によって上書きしていくことになります。仮にオリジナルの文字列を消去するために余分な編集作業が必要となるにしても。

コマンドk(po-kill-msgstr)は、エントリを未翻訳エントリへと戻すことによって、翻訳文字列を空にするだけのものです。しかしこの間に、以前の翻訳文字列はキルリングとして知られる別の特別な場所に置かれます。コマンド w(po-kill-ring-save-msgstr)もまた翻訳のコピーをキルリングに置きますが、この場合は別の方法でエントリを抜け出します。つまり、翻訳はそのエントリから取り除かれないのです。両方のコマンドは正確にバッファ間で共有されるキルリングを使います。そしてそれはGNU Emacsを愛する人にとっては既によく知られたものです。

翻訳者はキルリングを様々な翻訳の置き場として使うために、kまたは wを頻繁に使うことになるでしょう。キルリングからは、文字列を後から様々なEmacsバッファに再挿入することが出来ます。特に、キルリングは一つのPOファイルバッファ中の異なるエントリ間での翻訳文字列の移動、またもし同時に複数のバッファを操作しているであれば、複数のPOファイルの間での翻訳文字列の移動を行うために使用することが出来ます

POモードを使用していないバッファとも容易にデータ交換が行えるように、 kコマンドは翻訳文字列を保存前のクォートされていない状態でキルリングに置きます。外部クォートは除去され、複数行からなる文字列は連結され、バックスラッシュによるエスケープシーケンスは対応するキャラクタに置き換えられます。廃止エントリに対する場合でも、翻訳は保存前のコメントが解除された状態となります。

コマンドy(po-yank-msgstr)は現在のエントリをキルリングから取り出された文字列で完全に置き換えます。GNU Emacsでの用語に従えば、置き換える文字列はPOファイルバッファにヤンクされたと言いますsee Yanking (The Emacs Editor)。最初にyが使用されたとき、翻訳は一番最近にキルリングに加えられたデータで置き換えられます。もし yが再度タイプされたなら、途中に何らかのキーストロークを必要とすることなく直ちに、挿入された翻訳は取り去られて二番目に新しくキルリングに加えられたデータで置き換えられます。ある行中で繰り返しyが使用された場合、翻訳者が本当に必要とした文字列を見つけ出すまで、翻訳はキルリング中に保存された文字列によって次々と置き換えられます。

POファイルエントリに文字列がヤンクされたとき、文字列はPOファイルのフォーマットに従うよう、完全にそして自動的に再度クォートされます。更に、もしエントリが廃止エントリであれば、POモードは挿入された文字列を適切にコメント化します。もう一度、翻訳者はにクォートに関する配慮、それにもちろん、プログラム中で使われる翻訳文字列それぞれの必要性に関する配慮を、翻訳者自身に負わせてはいけません。

kまたはwは、文字列をキルリングに格納する唯一のコマンドではないことを覚えておいて下さい。翻訳文字列(または翻訳者コメント)を置き換えるほとんどのPOモードコマンドは、自動的に古い文字列をキルリングに格納します。このルールの主な例外は、ヤンクするコマンド群自身です。

キルとヤンクの操作を明確にするために、一般的な状況を取り上げて実際の例を示しましょう。プログラマがプログラムに合うように文字列をちょっとだけ修正したとすると、その変更は後程、修正された文字列用に新しい未翻訳エントリとして POファイルに反映され、以前の翻訳エントリは廃止エントリとなります。多くの場合、翻訳者は自分の作業が楽になるように廃止エントリから翻訳文字列を戻しますので、未翻訳エントリのmsgstrフィールドは以前の翻訳で初期化されます。一度この作業を行えば廃止エントリは不要となり、安全に削除出来るようになるでしょう。

翻訳者が未翻訳のエントリを発見し、翻訳に多少の違いが存在すると感じたなら、彼女は現在のエントリの位置をマークするために直ちにmを使用し、未翻訳の文字列に対応する翻訳が見つかることを期待しながらoを用いて廃止エントリを探し出します。見つかったなら、彼女は翻訳を消去するDELコマンドを使用して廃止されたエントリを削除し、翻訳をキルリングに置きます。そして、rによって元々の未翻訳エントリの位置へと戻り、yを使って保存した翻訳をmsgstrフィールドへとヤンクします。翻訳者は RETを使って翻訳の内容をより良く修正し、後程u、そして mを再度使用して次の未翻訳文字列へと移動します。

キーの流れを繰り返しタイプしていると、これらのシーケンスを学習し要求時にそのシーケンスを再度実行するというEmacsの能力に習熟すれば便利になるということに翻訳者は気が付くでしょうsee Keyboard Macros (The Emacs Editor)。

コメントの修正
=======

真剣に行われたあらゆる翻訳作業は、決定されるべきこと、そして更なる文書化の選択について、多くの言語的困難を増大させます。これらのドキュメントは、翻訳者が自由に作成し、削除し、修正することが出来る翻訳者コメントの形でPOファイル中に保存されます。これらのコメントは、翻訳者が後からこのPOファイルで作業するときに便利なものとなるでしょう。

最初の#の直後に空白を伴わないコメント、例えば#.や#:は翻訳者コメントではありません。これらはもっぱら他のgettextツールによって作成されます。以下に述べるコマンドはコメントを追加する際に先の決まりを変更することはなく、翻訳者に修正させることはありません。See PO Files。

以下のコマンドは、それらが適応される一般的な傾向から、翻訳を修正するコマンドに幾分似ています。See Modifying Translations

#
     翻訳者コメントを対話的に編集します。

K
     翻訳者コメントをキルリングに保存して、削除します。

W
     御訳者コメントをキルリングに保存しますが、削除しません。

Y
     翻訳者コメントをキルリングから取り出したもので置換します。

これらのコマンドは翻訳文字列を修正するためのPOモードコマンドに似ており、同じような方法で動作しますが、翻訳文字列ではなく翻訳者が使うためのPOファイルのコメントを扱うという点で異なっています。ですので、以下に述べる説明はごく簡潔に抑えてあります。詳細は既に述べているのですから。See Modifying Translations

#(po-edit-comment)コマンドは、現在のPOファイルエントリにある翻訳者コメントのコピーと共に新たなEmacsのウィンドウを開きます。該当するコメントがなければ、POモードは翻訳者がそのエントリに対してコメントを付加しようとしていると判断して、空のウィンドウを開きます。コメント記号(#)とそれに続くスペースは編集前に自動的に取り除かれ、編集後に再度挿入されます。廃止エントリにある翻訳者コメントに対しては、コメント外し(uncommenting)と再コメント化(recommenting)が行われます。ウィンドウを一度編集したなら、翻訳者はC-c C-cによってコメントの編集を終えることができます。

po-subedit-mode-hookに定義された関数群がもしあれば、それらは文字列が編集バッファに挿入された後、かつ再帰的な編集に入る前に実行されます。

K(po-kill-comment)コマンドは全ての翻訳者コメントを切り取り、それをキルリングに保存します。W(po-kill-ring-save-comment)は翻訳者コメントのコピーを取得しそれをキルリングに置きますが、現在のエントリはそのままの状態にします。M-Y(po-yank-comment)コマンドは、キルリングの先頭から取り出した文字列で翻訳者コメントを完全に置き換えます。このコマンドがすぐに繰り返された場合、挿入されたコメントは回収され、キルリングから取り出した別の文字列によって置き換えられることになります。

キルリングにおいては、全ての文字列は自然に振る舞います。翻訳と翻訳者コメントの間に区別はありません。例えば、翻訳者がある翻訳をたった今完了したが、その翻訳があまりよいものではなく、問題点を記録しておこうと考えて新しい翻訳者コメントを作成しようとしていると仮定してみます。彼女は直前の翻訳を翻訳者コメント中に引用したいことでしょう。そのために、彼女は翻訳者コメントを、まだキルリングの先頭に置かれている直前の翻訳によって初期化します。編集によって直前の翻訳は既にキルリングに置かれているので、彼女がただ単に#の前にM-wをタイプすれば直前の翻訳はそこに置かれ、説明文を記述する準備は整います。

一方、既に何らかの翻訳者コメントが存在し、翻訳者がそれらのコメントを完全に別のもので置き換えたいとします。そのような場合、彼女は#を使ってコメントを編集します。編集ウィンドウに移ったならば、彼女は通常のGNU EmacsのコマンドであるC-y(yank)、あるいは以前の翻訳を取り出すために M-y(yank-pop)を使うことが出来ます。

補助POファイルを参考にする
==============

POモードは多くの言語に通じた知識のある翻訳者の助けとなります。既に作業を完了した翻訳の成果を、たまたま彼女が知っている別の言語に対して用いることが出来ます。POモードはこれら他の言語への翻訳を、翻訳者の仕事への追加のコンテキストとして準備します。さらに、このような方法で作業することを好む翻訳者に対しては、一度に多くの言語への翻訳を行うことを簡単にする機能があります。

補助POファイルとは、翻訳者が作業の対象としている一つのパッケージ中に存在する、異なった母国語をターゲットとするPOファイルです。補助POファイルを宣言し、操作するための、そして作業中のエントリのコンテキストを示すためのコマンド群が存在します。

POモード中で使用可能な補助ファイル用のコマンドには、このようなものがあります。

a
     同じエントリに対する別の翻訳への補助ファイルを検索します。

M-a
     特定の補助POファイルへと切り替えます。

A
     POファイルを補助ファイルとして宣言します。

M-A
     作業中のPOファイルを補助ファイルのリストから削除します。

コマンドA(po-consider-as-auxiliary)は、コマンド M-A(po-ignore-as-auxiliaryによって削除されるまで、現在のPOファイルを補助ファイルのリストへと追加します。

コマンドa(po-cycle-auxiliary)は、現在のエントリと同じ msgidフィールドを持つ別の言語の翻訳エントリを、全ての補助POファイル中から検索します。検索されたPOファイルがもしあれば、そのファイルは現在の POファイルとして(ウィンドウの上部に)表示されます。もしまだそうでなければ、この処理を行う前に現在のPOファイルもまた補助ファイルにします。この新しく表示されたPOファイルでaが実行されれば、別ののPOファイルを検索することになるでしょう。結局のところ、aはオリジナルのPOファイルへとyield backすることになります。

コマンドM-a(po-select-auxiliary)は翻訳者に選択した補助ファイル名を補完機能付きで尋ね、選択されたPOファイルへと切り替えます。このコマンドは選択されたファイルが現在のエントリと合致するmsgidフィールドを持っているかどうかを調べ、もし持っていればこのエントリをカレントにします。でなければ、選択されたファイルのカーソルはそのままです。

全てが完全に動作するためには、補助POファイルは正規化、つまり、 msgidは厳密に同じように書かれていなければなりません。同じ文字列を表現するmsgidフィールドは様々な方法で記述される可能性がありますが、異なった記述はPOモードの補助ファイルを扱うコマンドの元々の動作を妨害することでしょう。既存の多くのPOファイルは同じGNU gettextツール群によって作成されたmsgidエントリを持っているので、実際にはこれが問題とはなることは考えにくいです。

しかし、ソースファイル中の文字列をマーキングしながら最初にPOモードによって作られたPOファイルは異なった形で正規化されています。それらはM-x normalizeコマンドの結果作られたPOファイルです。POモードとその他のGNU gettextツール群の間にあるこれらの矛盾が完全に解決されるまでに、翻訳者は正規化の問題点に気が付いているでしょう。

MOファイルの作成
*********

msgfmtプログラムの起動
==============

     Usage: msgfmt [OPTION] FILENAME.po ...

-a NUMBER
--alignment=NUMBER
     文字列のアライメントをNUMBERバイト(デフォルトでは1)にします。

-h
--help
     このヘルプを表示して終了します。

-I LIST
--input-path=LIST
     入力ファイルを検索するディレクトリのリストを指定します。

--no-hash
     バイナリファイルにハッシュテーブルを含まないようにします。

-o FILE
--output-file=FILE
     出力するファイルの名前をFILEにします。

--strict
     Uniforum/Sunの実装に厳密に従うよう、プログラムに指示します。現状、これは出力ファイルの命名ににみ影響します。このオプションが与えられていないときは、出力ファイルの名称はドメイン名称と同じものとなります。もし「厳密な Uniforumモード」が指定されているならば、既に存在するのでなければファイル名の末尾に.moが付与されます。

     我々はSunの実装におけるこの動作はどちらかと言えばあまりスマートでないと考えますので、デフォルトではこのモードは選択されていません。

-v
--verbose
     翻訳エラーとなるような入力ファイルの異常を検査して見つけ出します。 msgidとmsgstrの文字列は調査、比較されます。一方の文字列が改行で始まったり、あるいは改行で終わっているにも関わらず、もう一方がそうでないのは正常でないと判断されます。

     同様に、文字列がprintf系関数で使われるフォーマット文字列として出現しているならば、両方の文字列は同じ数、そして同じ種類のの%書式指定子を持っているはずです。フラグc-formatまたは possible-c-formatが特別なコメント#,としてこのエントリに出現しているなら、チェックが実行されます。例えば、このチェックは%.*sに対する%sや、%dに対する%s、%dに対する %xなどを検査します。これは位置パラメータ(positional parameter)に対しても行われます。

     通常、xgettextプログラムは文字列がフォーマット文字列かそうでないかを自動的に決定します。しかし、このアルゴリズムは完全ではありません。 printf系関数で使われていないにも関わらず文字列をフォーマット文字列と見なすことがあり、そのような場合msgfmtはエラーを報告することでしょう。逆の事例、すなわち文字列がprintf系関数中で使用されているのに、フォーマット文字列として認識されないといったこともあります。

     この問題を解決するため、プログラマはxgettextプログラムに対してどちらであるかを指示することが出来ます (see c-format)。翻訳者は#,行からこのフラグを削除するということを考えてはいけません。この「修正」は、次回msgmergeが呼び出されたときに速やかに元に戻されます。

-V
--version
     バージョン情報を出力して終了します。

入力ファイルが-の場合、標準入力から読み込みが行われます。出力が -であれば、出力は標準出力に書き出されます。

GNU MOファイルのフォーマット
=================

生成されたMOファイルのフォーマットは、後で例示する図によってもっともわかりやすく記述されています。

最初の2ワードはファイルの識別に用いられます。マジックナンバーはGNU MOファイルであることを常に示します。このナンバーはMOファイルを生成した計算機のバイトオーダーで格納されており、したがってマジックナンバーは実際には 0x950412deと0xde120495の二種類があります。二番目のワードは、ファイルフォーマットの現在のリビジョンを表わしています。現在のところ、このリビジョンは0です。これは将来のバージョンでは変わる可能性があり、MOファイルを読み出すプログラムが新しいフォーマットと古いフォーマットを区別できることを保証します。それによってどちらのフォーマットも正しく扱うことができます。バージョンはマジックナンバーとは分離されており、異なるフォーマットのために異なるマジックナンバーが使われることはありません。これは主に、 /etc/magicがそれ程頻繁にはアップデートされないためです。内部フォーマットバージョン識別子とマジックナンバーを分離しておくのは良いことでしょう。

続いて、ファイル後方にあるテーブルへのポインタの数があり、MOファイルを読み込むプログラムをリコンパイルことなくにMOファイルの前方部分の拡張ができるようになっています。幾つかのフラグビット、使用しているキャラクタセットの指定、新しいテーブルなどを後から挿入するときに便利でしょう。

さらに続いて、図中ではオフセットOとオフセットTとして示される、文字列ディスクリプタの二つのテーブルがあります。このテーブルは両方とも、二つの32ビット整数を使った文字列ディスクリプタであり、一つは文字列の長さ、もう一つはMOファイル中の文字列に対する、ファイルの先頭を起点としてバイトで数えたオフセット位置を表わしています。最初のテーブルにはオリジナル文字列に対するディスクリプタが置かれており、オリジナルの文字列は昇順の辞書式順序で並べられています。二番目のテーブルには翻訳文字列のディスクリプタが置かれています。これは最初のテーブルと相似するものであり、適切な翻訳文字列を見つけるには、オリジナル文字列のあったのと同じインデックスで、二番目の配列の配列スロットにアクセスする必要があります。

オリジナル文字列はソートされていますので、MOファイルがハッシュテーブルを持っていなかったり、MOファイルにあるハッシュテーブルを使うのが現実的でないときには単純な二分検索を行うことが可能です。これには別の利点もあります。すなわち、GNU gettextのPOファイル中にある空文字列は通常、特定のMOファイルに添付される幾つかのシステム情報へと翻訳されます。そしてこの空文字列はオリジナル文字列のテーブルの先頭に、対応する翻訳も翻訳文字列のテーブルの先頭に置かれるので、システム情報を非常に簡単に見つけられるようになるのです。

ハッシュテーブルのサイズSは0であっても構いません。その場合、ハッシュテーブルはそのMOファイルの中にはないということになります。人によっては、事前に算出されたハッシュテーブルがディスクスペースを食い、スピードもそれほど向上するわけではないため、ハッシュテーブルがないことを好むかもしれません。このハッシュテーブルはMOファイルにある文字列のソート済み配列に索引を設定するものです。衝突の解決はダブルハッシング(double hasing)によって行われます。使用されている正確なハッシングアルゴリズムはGNU gettextのコードに依存していますので、ここでは説明しません。

ハッシュテーブルに続いて、文字列そのものが置かれます。各文字列は NULで終端されていますが、このNULは文字列ディスクリプタにあった文字列の長さとしては数えられていません。msgfmtプログラムはMOファイル文字列のアライメントを選択するオプションを持っています。このオプションを使うことで、各文字列は指定されたアライメントで始まるように配置されます。一部のRISCマシンでは正しいアライメントに置くことによってスピードが向上します。

MOファイルはNULが埋めこまれている文字列を持つことが出来ます。しかし、プログラムのインターフェースは現在すでに文字列がNULで終端されていることを仮定していますので、NULを埋めこむということは少々意味がないことです。しかし、MOファイルのフォーマットは後から他のインターフェースを扱えるほど汎用的です。たとえば、MOファイル中に(NULが思いがけず入ってしまうような)ワイド文字を正しくインプリメントしたいと考えたとしても問題ありません。

この独特の結果は、GNU gettext開発フォーラムにおいて非常に激しく討論され、MOファイルフォーマットは何度も発展し、変化することが出来るようにされました。後から同時に複数のフォーマットをサポートすることさえ可能とするものなのです。しかし確実に、我々はスタート地点となるものを得ました。ここで説明したMOファイルフォーマットはよい出発点です。コンクリートで固められたようなものは何もなく、フォーマットは後日簡単に進化できるものであり、我々は現在のアプローチを快適なものだと感じています。

             バイト
                  +------------------------------------------+
               0  | マジックナンバー = 0x950412de            |
                  |                                          |
               4  | ファイルフォーマットのリビジョン = 0     |
                  |                                          |
               8  | 文字列領域の長さ                         |  == N
                  |                                          |
              12  | オリジナル文字列テーブルへのオフセット   |  == O
                  |                                          |
              16  | 翻訳文字列テーブルへのオフセット         |  == T
                  |                                          |
              20  | ハッシュテーブルのサイズ                 |  == S
                  |                                          |
              24  | ハッシュテーブルのオフセット             |  == H
                  |                                          |
                  .                                          .
                  .  (更にエントリが追加される可能性がある)  .
                  .                                          .
                  .                                          .
                  |                                          |
               O  | 0番めの文字列の長さとオフセット   ----------.
           O + 8  | 1番めの文字列の長さとオフセット   ------------.
                   ...                                    ...   | |
     O + ((N-1)*8)| (N-1)番めの文字列の長さとオフセット      |  | |
                  |                                          |  | |
               T  | 0番目の翻訳の長さとオフセット  -----------------.
           T + 8  | 1番目の翻訳の長さとオフセット    -----------------.
                   ...                                    ...   | | | |
     T + ((N-1)*8)| (N-1)1番目の翻訳の長さとオフセット       |  | | | |
                  |                                          |  | | | |
               H  | ハッシュテーブルの先頭                   |  | | | |
                   ...                                    ...   | | | |
       H + S * 4  | ハッシュテーブルの終わり                 |  | | | |
                  |                                          |  | | | |
                  | NULで終端された0番めの文字列  <-------------' | | |
                  |                                          |    | | |
                  | NULで終端された1番めの文字列  <---------------' | |
                  |                                          |      | |
                   ...                                    ...       | |
                  |                                          |      | |
                  | NULで終端された0番めの訳語      <---------------' |
                  |                                          |        |
                  | NULで終端された1番めの訳語      <-----------------'
                  |                                          |
                   ...                                    ...
                  |                                          |
                  +------------------------------------------+

ユーザとしての視点
*********

GNU gettextが真にそのゴールにたどり着いたとき、平均的なユーザはある種の驚きの喜びを感じるでしょう。スクリーンのどこにでも彼ら自身のネイティブランゲージが表示されるという、奇妙な魔法の効果が現れるからです。愚直なユーザはそれについて全く驚くことなく、ただ単に彼らの言語が認められたと話し合うだけで、どちらかと言えば他の点で不幸になるかもしれません。

それでは、ユーザの視点がなるべく単純になるように、全ての方法の中で人が GNU gettextを見るように、その魔法についてここで説明することにしましょう。全ての他のソフトウェアエンジニア、すなわちプログラマ、翻訳者、保守担当者はこの魔法が実現するようにいっしょになって働きます。これは長くそして革新的な仕事で、進捗状況についての情報は翻訳プロジェクトから入手可能です。

パッケージが配布されたとき、そこには二種類のユーザが存在します。それは、自分や他の人が使うために配布物を受けとって展開し、コンフィグレーションしてコンパイルとインストールを行うインストーラと、インストールされたパッケージのプログラムを使うエンドユーザです。GNU gettextはインストーラとエンドユーザの両方に魔法の効果を提供します。

現在のABOUT-NLS表
=============

GNU gettextを使用している全てのパッケージにおいて、全ての言語が等しくサポートされているわけではありません。あるGNUパッケージがGNU gettextを使っているかどうかは、配布物中にNLS情報ファイルがあるかどうか、LL.poファイルがあるかどうか、intl/ディレクトリがあるかどうかということをチェックすることで確認できます。国際化対応されたパッケージは、通常多くのLL.poファイルを持っています。ここで挙げたLLは言語を示しています。End UsersではLLフォーマットについて完全な解説を行っています。

より一般的には、マトリックスは翻訳プロジェクトのその時点での状態を明らかにし、多言語メッセージ(multilingual messages)のために準備されたパッケージのリストアップや、各パッケージがサポートしている言語のリストアップを行っています。この情報は頻繁に変更されるので、このマトリックスはGNU gettextマニュアルの中には含まれていません。この情報はGNUの配布パッケージの中のABOUT-NLSファイルで確認することができますが、しかしこの情報は配布パッケージそのものと同じくらい古いものです。この ABOUT-NLSファイルの、更新された情報を含む最新のコピーは翻訳プロジェクトのサイト、そしてほとんどのGNUアーカイブサイトで見つけることができます。

インストーラへの魔法
==========

GNU gettextを内部で全面的に使用しているパッケージは、デフォルトでは翻訳されたメッセージが使用可能になるようにしてインストールされます。コンフィグレーション時に、それらのパッケージは基礎となるホストシステムが提供し使用可能となっているcatgetsやgettextといった関数を検出します。これらのどちらかが使用可能であれば、GNU gettextライブラリは自動的に準備されて使われるようになります。インストーラにはコンフィグレーション時に特別なオプションを与えることが出来、それによってインストーラの動作を変えることができます。./configure --with-included-gettextコマンドはシステムのcatgetsやgettextをバイパスして、代わりにGNU gettextを使用します。./configure --disable-nlsとすると、翻訳メッセージを全面的に使用しないプログラムを生成します。

国際化対応されたパッケージは通常、多くのLL.poファイルを持っています。翻訳が禁止されていない限り、これらのファイルはすべてパッケージといっしょにインストールされます。しかし環境変数LINGUASが設定されている場合は、コンフィグレーションに優先してインストールするファイルを制限します。 LINGUASに設定する内容は、使用する言語を示すためのスペースで区切られた2文字のコードのリストです。

エンドユーザへの魔法
==========

ここで、GNU gettextを内部的に使っているパッケージ、そしてインストーラがconfigure時に翻訳を無効にしなかった場合について考えてみましょう。ユーザがするべきことは、パッケージ中のプログラムを使う前に環境変数 LANGに適切なLLを設定するだけですsee Matrix。たとえば、ドイツのサイトの場合を考えてみましょう。ユーザはシェルプロンプトで setenv LANG de(cshの場合)、またはexport LANG; LANG=de(shの場合)と実行するだけです。ユーザーはこれを彼らの .loginや.profileの中で行うこともできます。

プログラマとしての視点
***********

GNU gettextによって提供される現在のメッセージカタログの実装における一つの狙いは、インストールする人間が望むならばシステムのメッセージカタログの取り扱いに使えるということでした。そのため、おそらく我々は既に知っている解決方法について検討すべきでした。POSIXコミュニティの人々は、以下に述べる準公式標準については意見を合わせようとしませんでした。事実彼らは全てのものと意見を合わせようとせず、インターフェースの例を含むものさえ決定しませんでした。主要なUNIXベンダは二つの重要な仕様のどちらを採用しているかで分けられます。X/Opensのcatgetsと、Uniforumのgettextインターフェースです。ここでは両方について記述した後、このジレンマに関して我々の取った解決方法について説明します。

catgetsについて
===========

catgetsの実装はX/OpenのX/Open Protability Guide, Volume 3, XSI Supplementary Definitions, Chapter 5. で定義されているものです。しかし、この標準を作成するプロセスは一部のUNIXベンダには遅すぎ、そのため一部のベンダは標準の仮バージョンの段階で実装を行ってしまいました。もちろんこれは、たとえcatgetsの使い方と同一のインターフェースを保証していたとしても、プラットホームに依存しないプログラムの作成には問題となります。

もう一つ、ただ委員会メンバの一グループだけによってこのインターフェースが作成されたということについて、個人的に言いたいことがあります。彼らは決して実際にこのインターフェースを使ってプログラムしようとはしてはいません。このインターフェースは高速かつメモリを節約する実装で、このインターフェースを使うということはユーザにとっては幸せでしょう。しかし、プログラマはこのインターフェースを嫌っています(少なくとも私と、その他数人はそうです...)。

しかし我々は、UNIX(tm)に関する権利の委譲に関する全てのトラブルの後で、最終的にX/Openへと至らしめたのはこのインターフェースを発行した人間と同じ人間だという一点を忘れてはなりません。これは私に、このインターフェースは将来の UNIX標準(たとえばSpec1170)となり、したがって全てのUNIXの実装(この名前を有することを許されている実装)の一部となると予想しています。

インターフェース
--------

catgetsに対するインターフェースの実装は、ファイルアクセスとして使用される三つの関数からなります。一つは使用するカタログをオープンする catopen、二つめはメッセージテーブルのアクセスを行うcatgets、三つめは作業が終了した後でクローズするためのcatcloseです。これらの関数のプロトタイプと(巻数が必要とする)定義は<nl_types.h>というヘッダファイルにあります。

catopenは次のように使用します。

     nl_catd catd = catopen ("catalog_name", 0);

この関数は引数としてカタログの名前をとります。これは通常、プログラムまたはパッケージの名前を参照します。二番目の引数は標準では指定しません。様々なシステムに対して実装されるためなのかもしれませんが、私は正確なところは知りません。そのため一般的な助言としては「この値として0を使いなさい」ということになります。この関数の戻り値はメッセージカタログのハンドルであり、 openが返すファイルハンドルと同様な意味を持っています。

このハンドルはもちろん、次のようにしてcatgetsの引数として使うことができます。

     char *translation = catgets (catd, set_no, msg_id, "original string");

この関数呼び出しの最初の引数はカタログディスクリプタです。2番目の引数はこのカタログ中のメッセージを特定するもので、msg_idで指定されるメッセージが取得されます。catgetsは従って、3段アドレッシング(three-stage adressing)を使用します。

     カタログ名 => 番号のセット => メッセージID => 翻訳

四番目の引数は翻訳文字列を特定するためには使われません。この引数は翻訳文字列の特定が失敗したときに使用されるデフォルト値として与えられます。忘れてはいけない重要なことは、catgetsの戻り値の型がchar *であるにも関らず、返された文字列を変更してはいけないということです。これは const char *としたほうが良いのですが、この標準はANSI Cの一年前、 1988年に出版されていました。

これらの関数の最後のものは期待通りに使われ、動作します。

     catclose (catd);

この呼び出しを行った後、この記述子を使ってcatgetsを呼び出すことは、もはや合法ではありません。

catgetsインターフェースにおける問題点とは?!
--------------------------

ここで、我々がお話する問題がどこにあるのかを明確にしましょう。事実、このインターフェースは現実的な方法で使うことはできますが、メッセージカタログの構築は苦痛です。この理由はcatgestsの第三引数、つまり一意なメッセージ IDにあります。ある単一のセット中の全てのメッセージは数値でなければなりません。おそらくあなたは、ソースコードを変更している間、ずっとこのようなリストを保持し続けるという問題について理解出来るでしょう。こっちで新しいメッセージを追加して、あっちで一つ削除します。もちろん、この混乱を収拾するのを助けるための多くのツールが開発されてはいますが、ある局面で使えるものは他の局面では使えなかったりするのです。我々は、別のアプローチには全く問題がないのに、それらはずっと扱いやすいとは言いたくありません。

gettextについて
===========

gettextのインターフェースの定義はUniformの提案から来ていて、また、少なくともひとつの主要なUNIXベンダ(Sun)による最新の実装に従っています。しかし、これは何らかの正式な標準(official standard)によって規定されている規格ではありません。

このソリューションにおける大きな特徴は、通常のファイル操作(オープン - 使用 - クローズ)の方法に従わず、プログラマにそれ程多くの、特にユニークなキーの扱いに関して、負荷を与えないということです。もちろん、この方法でもユニークなキーは必要となりますが、キーとしてメッセージそれ自身(どれほど長かろうと短かろうと)とを使用します。これら二つの手法の比較についての詳細は see Comparison。

次のセクションにはこのインターフェースの詳細な説明があります。このインターフェースはGNU gettextライブラリのために選ばれたインターフェースなので、詳しく説明します。このライブラリの使用法に興味を持ったプログラマは、ここでの説明にも興味を持つでしょう。

インターフェース
--------

インターフェースは最低限の機能として、a) 文字列を持ってくるドメイン (domain)の選択(単一ドメインを全てのプログラムに、というのは構築と保守が難しくなり、また恐らくは不可能ですので、現実的ではありません)、b) 選択したドメインの文字列へのアクセス、を備えていなければなりません。

これは主としてgettextのインターフェースの説明です。このインターフェースはグローバルドメインを持っています。もちろんこのドメインはユーザにより選択可能です。

     char *textdomain (const char *domain_name);

この関数によってLC_MESSAGEカテゴリの現在のグローバルドメインのステータスを変更したり、問い合わせたりすることができます。引数はNUL文字で終端された文字列で、その文字列はファイル名に使うことのできるものである必要があります。引数DOMAIN_NAMEがNULLであった場合、この関数は現在の設定値を返します。もし事前に値がセットされていなければ、デフォルトドメインの名前としてmessagesが返されます。ここで、textdomainの戻り値の型がchar *であるにも関らず、書き換えが許されていないことに注意して下さい。同様に、有効性のチェックがなされないということも重要です。名前が有効でない場合、対応する翻訳文字列が返ってこないということでそれが認識できるでしょう。

textdomain関数を使って設定したドメインを使うには

     char *gettext (const char *msgid);

のようにします。これは想像できる最も単純で現実的な形式です。もし現在のドメインで取得可能ならば、MSGIDを翻訳した文字列が返されます。もし取得できなければ、引数がそのまま返されます。引数がNULLであったときの結果は未定義です。

使用しているドメインは曖昧なものであるということを覚えておいていて下さい。 LC_MESSAGESロカールに対する現在のドメインの値が使用されます。プログラム中でgettextを呼び出す間にLC_MESSAGESロカールの変更を行ったなら、それぞれの呼び出しでは(他方と)異なったメッセージカタログが参照されます。

国際化されたパッケージが通常使用するもっとも単純なケースでは、 textdomainの呼び出しが最初に一度だけ行われ、ここでドメインにユニークな名前、通常はパッケージ名がセットされます。それ以降のコードでは、翻訳されるべき全ての文字列はgettext関数を通じて翻訳されます。これによって、パッケージはあなたの使う言語をしゃべるようになります。

曖昧さの解決
------

一つのドメインだけを使うという手法は殆んどのアプリケーションで有効に働きますが、二つ以上のドメインから翻訳文字列を取り出す必要があるかもしれません。もちろんtextdomainを呼び出して異なるドメインに切り替えることも出来ますが、これは便利ではありませんし、高速でもありません。このドキュメントを記述している間に議論されたケースはこの例に当てはまるでしょう。「一般的に使用される関数に関する全てのエラーメッセージは、分割されたerrorというドメインに格納するのが良い」というものです。これにより我々は、エラーメッセージを一度だけ翻訳すれば良くなるでしょう。

このため、さらに二つの関数の文字列取得用関数があります。

     char *dgettext (const char *domain_name, const char *msgid);
     char *dcgettext (const char *domain_name, const char *msgid,
                      int category);

これらの関数は両方とも先頭に追加の引数を取ります。この引数は textdomainの引数に対応するものです。dcgettextの第三引数は、 LC_MESSAGES以外の別のロカールを使うことを許可します。しかし私には、これが便利なのかどうかはわかりません。DOMAIN_NAMEがNULLであったり、CATEGORYが未知の値であった場合、その結果は未定義です。同様に、この関数はこの関数ファミリの二番目に知られた実装(Solarisでの実装です)の一部ではないということに注意するべきです。

二つめの曖昧さは、おそらくは一つ以上のドメインが同じ名前を持っているということです。これは必要なメッセージカタログが置かれている場所を指定することによって解決できます。

     char *bindtextdomain (const char *domain_name,
                           const char *dir_name);

この関数を呼び出すことにより、与えられたドメインを指定されたディレクトリに結び付けます(ファイルそのものを結び付ける方法については後述)。システムのデフォルトの位置にあるファイルについては、それ以上指定する必要はありません (単にtextdomainを使った場合と全く同じです)。DIR_NAMEを NULLにした場合は、DOMAIN_NAMEに結び付けられたディレクトリを返します。DOMAIN_NAMEそのものがNULLであった場合は何も起こらず、 NULLポインタが返されます。他の全ての関数と同じように、戻り値は変更してはいけません!

DIR_NAMEパラメータに与えられるパス名は問題を起こすことがありますので、このことをしっかり覚えておいて下さい。パスは常にカレントディレクトリからの相対位置として求められるため、プログラムがchdirコマンドを実行したときは異なった結果が返されます。パスを取得する際には常に依存性と不確実性を回避せねばなりません。

メッセージカタログファイルの所在
----------------

多くの異なった言語を多くの異なったパッケージに格納しなければならないので、何らかの方法で言語やパッケージに関する情報をメッセージカタログファイルに追加する必要があります。UNIX環境で通常使われている方法は、ファイル名にエンコーディングを含めるというものです。ここでも同様にしています。 bindtextdomainの第二引数(あるいはデフォルトのディレクトリ)で与えられたディレクトリ名には、以下に記述するようにロカールやドメイン名の値や名前が後ろに続いています。

     DIR_NAME/LOCALE/LC_CATEGORY/DOMAIN_NAME.mo

DIR_NAMEの既定値はシステムに依存します。GNUライブラリ、そしてGNUの方針を頑なに信奉するパッケージでの既定値は

     /usr/local/share/locale

です。

LOCALEは、LC_CATEGORYをいう名前を持つロカールの値です。 gettextとdgettextにとって、このロカールは常に LC_MESSAGESです。dcgettextは三番目の引数によってロカールを指定します (Ultrixなどの一部のシステムではLC_MESSAGESを持っていなません。ここで我々はこのために恣意的な値(arbitrary value)を使います。)。 (システムがsetlocaleをサポートしていないとき、ロカールの値を設定する動作は環境変数を参照することによりシミュレートされます。)

gettext関数群の最適化
--------------

この辺りで、GNU gettextの実装におけるアドバンテージについて話す必要があるでしょう。一部の読者は、内部ループ中で幾つかの文字列を翻訳しなければならないような国際化プログラムは、貧弱なパフォーマンスしかあげられないことを指摘するかもしれません。その文字列が一回のループの実行中に変更されるならば仕方ありませんが、ループ中で文字列が常に同じ値を取るならば時間の浪費でしかありません。以下の例を見て下さい。

     {
       while (...)
         {
           puts (gettext ("Hello world"));
         }
     }

ロカールが二回の実行の間で変更されないとき、文字列は常に同じ値を取ります。このような場合に取ることが出来る方法としては、以下のようなものがあります。

     {
       str = gettext ("Hello world");
       while (...)
         {
           puts (str);
         }
     }

しかしこの解決法はあらゆる局面で使えるわけではありませんし(例えば、ロカールが変更されるような場合)、読みやすいわけでもありません。

GNU Cコンパイラのバージョン2.7かそれ以上では、これに対して別の解決方法を準備しています。ここに挙げる数行を intl/libgettext.h ファイルに記述するというものです。式のコマンドブロック(expression command block)に関する説明については、Statements and Declarations in Expressions (The GNU CC Manual)を参照して下さい。

     #  if defined __GNUC__ && __GNUC__ == 2 && __GNUC_MINOR__ >= 7
     extern int _nl_msg_cat_cntr;
     #   define	dcgettext(domainname, msgid, category)           \
       (__extension__                                                 \
        ({                                                            \
          char *result;                                               \
          if (__builtin_constant_p (msgid))                           \
            {                                                         \
              extern int _nl_msg_cat_cntr;                            \
              static char *__translation__;                           \
              static int __catalog_counter__;                         \
              if (! __translation__                                   \
                  || __catalog_counter__ != _nl_msg_cat_cntr)         \
                {                                                     \
                  __translation__ =                                   \
                    dcgettext__ ((domainname), (msgid), (category));  \
                  __catalog_counter__ = _nl_msg_cat_cntr;             \
                }                                                     \
              result = __translation__;                               \
            }                                                         \
          else                                                        \
            result = dcgettext__ ((domainname), (msgid), (category)); \
          result;                                                     \
         }))
     #  endif

ここで興味深いのは__builtin_constant_pです。これはコンパイル時に評価され、直ちに最適化されます。ここで二つのケースに分岐します。 gettextの引数が定数でない場合は単純にdcgettext__が呼び出され、 dcgettext関数の実際の実装が用いられます。

文字列引数が定数の場合、ロカールが変更されていなければ既に取得されている翻訳を再利用することが出来ます。これは正確にこの場で行われます。 _nl_msg_cat_cntr変数は、libintl.a中で利用可能な loadmsgcat.cで定義されており、この変数は新しいメッセージカタログがロードされた時点で変更されます。

二つのインターフェースの比較
==============

以下に挙げる議論はおそらく染められた小さなビット(a little bit colored)です。既に述べたように、我々はGNU gettextをUniforumの提案に従って実装しましたし、これにはそうするだけの理由があります。しかし、我々がどのようにこの決定にいたったのかを説明したほうが良いでしょう。

第一に、我々は開発プロセスに注目しました。gettextにより提供される NLSを用いてアプリケーションを開発するとき、我々はいつも通りに作業します。ユーザが見ることになり、翻訳される必要があるであろう文字列を扱うときにだけ、 "..."の代わりにgettext("...")を置きます。各ソースファイル(もしくは中心的なヘッダファイル)の先頭で次のような定義を行います。

     #define gettext(String) (String)

この定義によって、システム自身のCライブラリによってgettextがサポートされている場合でも、それを無効にすることができます。このコードをコンパイルしても、その結果はNLSコードを使わない場合と同じものになります。GNU gettextのコードに注目したなら、我々がgettext("...")ではなく_("...")を使っていることに気がつくでしょう。これにより、翻訳可能文字列一つ当たりで増加する文字数を三つに押えられます。

プログラムの製品バージョンでは定義を

     #define _(String) (String)

から

     #include <libintl.h>
     #define _(String) gettext (String)

のように変更します。更に翻訳可能文字列を含むソースファイルに対して xgettextを実行します。翻訳が可能であるかどうかには依存しませんが、翻訳が使用可能であれば翻訳を使用できる実行プログラムが出来上がります。

gettext_noopに対しても同じ手続きが取られます(see Special cases)。最初に、gettext_noopをno-opマクロとして定義し、後から libintl.hでの定義を使用します。この名前はSunによる libintl.hの実装では使われていないので、あなたのプロジェクトに対して以下のコードを考慮する必要があるでしょう。

     #ifdef gettext_noop
     # define N_(String) gettext_noop (String)
     #else
     # define N_(String) (String)
     #endif

N_は_と同様の短い形式です。GNU gettextのpo/ディレクトリ中のMakefileは先に述べられた両方の短い形式をデフォルトで理解します。そのためあなたは、自分自身が楽になるためにこの提案を理解する必要があります。

現在のcatgetsにおける最も大きな問題はプログラマが行う作業です。翻訳文字列を扱うたび、プログラマはメッセージカタログに対しても対応する数値(もしくはシンボル的定数: symbolic constant)を定義しなければなりません。重複したエントリ、メッセージIDなどにも注意する必要があります。GNU gettextプログラムが提供するメッセージカタログと同程度のクオリティのメッセージカタログを必要とするのであれば、彼は全ソースコードファイル中の文字列とその出現位置に対する記述的なコメントをメッセージカタログに入れる必要があります。これは「ミッション: インポッシブル」に近い話です。

しかし、catgetsには長所と呼べるかもしれない幾つかのポイントがあります。あなたがある文字列中に一つの単語を持っており、この文字列が異なるコンテキストで使われるとしましょう。言語によってはこの単語に対して異なる訳語が割り当てられるかもしれません。

     printf ("%s: %d", gettext ("number"), number_of_errors)

     printf ("you should see %d %s", number_count,
             number_count == 1 ? gettext ("number") : gettext ("numbers"))

この例では、我々は"number"という文字列を二度翻訳しなければなりません。あなたが英語に似た言語を話さないとしても、二つの語が異なる意味を持っていることは認識できるでしょう。ドイツ語では最初の文字列は"Anzahl"に、二番目の文字列は"Zahl"に翻訳されます。

ここであなたはこの例が実に難解(esoteric)であると言うでしょう。それは正しいことです! 正にこの問題に対して、我々はさほど重要ではないという結論を得ました。この問題を解決するのは非常に簡単です。

     printf ("%s %d", gettext ("number:"), number_of_errors)

     printf (number_count == 1 ? gettext ("you should see %d number")
                               : gettext ("you should see %d numbers"),
             number_count)

我々はこの方法によって全ての衝突を解決できると考えています。もしこれが困難であれば、衝突している文字列をほんの少し変えるということも出来ます。しかし、この問題を克服することは不可能ではありません。

翻訳者ノート: 単一の's'を付加することによって名詞の複数形式を作ることが出来ないということを英語を話すプログラマに伝えることは、恐らく重要なことです。他のほとんどの言語では異なる手法を使っています。Rafal Maszkowski <rzm@mat.uni.torun.pl>のレポートによると、全ての言語に対して対処するには、上記のフォームでも不十分です。

     ポーランド語では我々は、例えばplik(ファイル)をこのように使います。

          1 plik
          2,3,4 pliki
          5-21 pliko'w
          22-24 pliki
          25-31 pliko'w

     (o'は8859-2 oachteを意味し、これはどちらかと言えばaogonekに似たokreskaです)

可能なアプローチは、おそらくPOSIX.2標準中にあるLC_TIMEで使用されているような方法を考慮に入れることでしょう。alt_digitsの値は1から 100を表現する100以上の文字列となり得ます。表現される数によって順序付けされた翻訳文字列の配列を用いれば、国際化されたプログラム中でこの方法を使用することが出来ます。単純な例を示します。

     void
     print_month_info (int month)
     {
       const char *month_pos[12] =
       { N_("first"), N_("second"), N_("third"),    N_("fourth"),
         N_("fifth"), N_("sixth"),  N_("seventh"),  N_("eighth"),
         N_("ninth"), N_("tenth"),  N_("eleventh"), N_("twelfth") };
       printf (_("%s is the %s month\n"), nl_langinfo (MON_1 + month),
               _(month_pos[month]));
     }

この方法は、数値の範囲が狭いときのみ有効であるということは明らかでしょう。

独自のプログラムでlibintl.aを使う
=====================

バージョン0.9.4以降、libintl.hは自己包括(self-contained)でなければならなくなっています。すなわち、追加の関数を用意することなくこれをあなた自身のプログラムで使用できます。Makefileはヘッダとライブラリを、 $(prefix)で指定されたディレクトリに格納します。

HP-UXシステムにおいては、上記に関する唯一の例外が発生します。Cライブラリが alloca関数を含んでいないのです(そしてHPのコンパイラはこれをインラインとして展開しません)。しかしそれはこのバカなシステムのためだけにライブラリ全体を書き直すといったことを意味しません。代わりに、libintl.aを使用する全てのパッケージがalloca関数をインクルードします。

Being a gettext grok
====================

ソースコードを読むことは、GNU gettextの機能を完全に活用するための助けになるでしょう。しかし、(時として込み入った内容の)コードを読むために時間を費やすことを望まない人々のために、幾つかのコメントを挙げておきます。

  * 実行時に言語を変更する

     対話的なプログラムにおいては、使用する言語を実行時に尋ねたほうが有用であることがあります。これを理解するためには、関数が使用する言語をどのように決定しているのかを知る必要があります。ここに示す方法はGNUによる gettext関数の実装においてのみ正しいものです。catgets関数やシステム付属のCライブラリによるgettext関数については当てはまらないでしょう。もちろん、メッセージハンドリングにGNU gettextライブラリを使用しているGNU Cライブラリは例外です。

     dcgettext関数は呼出しごとに、環境変数の中で最高のプライオリティを持つものを探し出して使用します。プライオリティは以下のリストで示されます。プライオリティは下にいくにつれて低くなります。

      1. LANGUAGE

      2. LC_ALL

      3. LC_xxx, 選択されたロカールによる

      4. LANG

     その後、検索された値を用いてパスが設定され、可能であれば翻訳ファイルがロードされます。

     「今」とは、LANGUAGEが変更されたときです。上で説明した過程に従い、この変数の新しい値はdcgettext関数が呼び出された時点で決定されます。これは(おそらく)異なったメッセージカタログがロードされることを意味します。即ち、使用する言語が変更されるのです。

     しかし、これは一つの小さなフックに過ぎません。 gcc 2.7.0以上のコードでは幾分の最適化が図られています。この最適化は通常、dcgettext関数の呼び出しによって新しいカタログがロードされる前に行われます。しかし、もし dcgettextが呼び出されなければ、プログラムもまたLANGUAGE変数の値が変更されたことを知ることができないでしょう(see Optimized gettext)。この解決方法は非常に簡単です。以下のコードを言語変更関数の前に置けばよいのです。

            /* 言語の変更 */
            setenv ("LANGUAGE", "fr", 1);

            /* 変更を知らせる */
            {
              extern int  _nl_msg_cat_cntr;
              ++_nl_msg_cat_cntr;
            }

     変数_nl_msg_cat_cntrはloadmsgcat.c中で定義されています。プログラマは、長時間動作し、実行時にユーザに言語を選択する余地を与えるようなプログラムの開発中に、このような構造の必要性を感じることでしょう。非対話的なプログラム(あらゆる小さなUNIXツールのような)は決してこのようなことを必要としません。

プログラマの章についての一時的なメモ
==================

一時的な情報 - 二つの実装
--------------

言語に依存せずにメッセージを扱う二つの手法があります。一つはX/Openの catgetsによるものであり、もう一つはUniforumのgettextによるものです。catgetsによる方法では、整数によってメッセージを指定します。 gettextによる方法では、英語のメッセージによって指定します。。 catgetsによる方法は長く使われ、多くのベンダでサポートされています。 gettextによる方法ははSunでサポートされていて、COSEマルチベンダイニシアティブ(COSE multivendor initiative)がサポートするようです。どちらも POSIX標準とはなりませんでした。POSIX.1 委員会では、この件に関して様々な見解の相違がありました。

二つの手法のいずれもPOSIX標準ではありません。gettextと catgets(XPG)ルーチンのいずれを標準として採用するかについて、 POSIX.1 委員会ではは様々な議論がなされました。委員会の終盤に至っても何らの合意は得られず、結局メッセージングシステムは標準規格には含まれませんでした。私はXPG3メッセージングインターフェースに関する追記を標準に付与するのは有益であると信じていますし``...メッセージングシステムの実例は既に実装されているのです...''

委員会は、ある一つのインターフェースの実装を使うのが良いということをどの場所でも言わないように非常に注意していました。この話題に関するこれ以上の情報については、国際化プログラミングFAQ(Programming for Internationalization FAQ)を参照して下さい。

一時的な情報 - catgetsについて
--------------------

catgetsを基盤として使用することに関する討議の末期には、幾つかの議論がありました。その議論の両側を提示することは大切だと思いますし、これから、私はちょっとしたことに対して「悪魔の弁護士」となってみることにします。

catgetsはもっと良くデザイン出来たでだろうということは否定しません。その実装には既に指摘したような制限が少なからずあります。

しかしながら、その一貫性と標準化の度合いについては申し分ありません。 UNIXソフトウェアを書くときに繰り返し発生する問題とは、UNIXプラットフォーム間での移植性に関する問題です。それは全ての UNIXベンダがオペレーティングシステム上を調べて改良する部分を見つけたようなものです。疑いもなく、これらの修正は革新的なものであり、現実の問題を解決するものです。しかしながら、ソフトウェアベンダがこれらの変更を多くのプラットフォームで行いつづけるには、多くの労力が必要です。

そしてこれは各UNIXベンダが自社のシステムを標準化することを促進します。 Spec1170に準拠するためにです。、各主要UNIXベンダはこの標準化のために委員会を設けました。そして全てのUNIXソフトウェア開発者はこの標準に従ってソフトウェアを作成し、異なるプラットフォームへソフトウェアを導入する際には (autoconfを使うことなく)リコンパイルするだけで済むようになる日を心待ちにしているのです。

私の理解しているところでは、Spec1170はX/Open Portability Guidelinesのバージョン4(XPG4)に基づいたものです。catgetsとその眷属がXPG4で定義されているので、私はcatgetsがSpec1170の一部であり、それがすべてのUNIXシステムの標準的なコンポーネントになることを信じています。

一時的な情報 - なぜ一つの実装なのか
-------------------

メッセージカタログにアクセスするために二つの異なるシステムをインストールすることは不経済なことのように思えます。我々がcatgetsの不足しているものを改善したいと思ったのなら、なぜ新しいシステムを実装するのではなく、 catgetsを(互換性を保ちながら)拡張しようとしないのでしょうか? いずれにせよ我々は、メッセージカタログにアクセスするためのシステムをオペレーティングシステムに対して二つインストールすることになるでしょう。一つはGNUソフトウェアのためルーチン群であり、もう一つはその他全てのソフトウェアのためのルーチン群(catgets)です。傲慢でしょうか?

カタログにアクセスする別のシステムが実装されたと仮定してみましょう。我々がお奨めするのはどちらでしょうか?  少なくともLinuxシステムに対しては、我々は可能な限り多くのソフトウェア開発者を呼び込む必要があります。そのため、我々はソフトウェア開発者が彼らのソフトウェアを移植しやすいようにする必要があります。そしてそれはcatgetsをサポートすることを意味します。我々は glocaleコードをGNU libc中に実装するでしょうが、GNU libcには別のメッセージカタログに対するアクセス方法を同じように取り込まなければいけないということなのでしょうか? そして、glocaleと非 catgetsルーチンを組み合わせて使おうとする人達に関してはどうでしょうか? ソフトウェア開発者が彼らのソフトウェアを他のプラットフォームに移植する際、彼らはそのソフトウェアに単にglocaleを含めるだけでなく、フロントエンド(glocale)コードと、バックエンド(非catgetsアクセスルーチン)コードを付け加えようとするでしょう。

しかしメッセージカタログのサポートは氷山の一角に過ぎません。他のロカールカテゴリのデータはどうでしょうか。それらもまた、多くの相違点を持っています。我々はそれに対処することを諦めて、重複したルーチン群を別々に開発せねばならないのでしょうか(glocaleをメッセージカタログサポート以上のものにすべきなのでしょうか)?

UNIX上の改良可能な多くの部分と同じように、我々は将来に向けて改良を加えつつも、過去のものに対する互換性を落とさないようにしていました。

一時的な情報 - ノート
------------

多くの実装が最終形式からかけ離れたものであったため、X/Openが標準形式を承認するのは非常に遅くなりました。私の使っている両方のシステム(古いLinux catgetsとUltrix-4)には奇妙なバリエーションがあります。

最後の変更を加えた後、私はGNU/Linux libcのgettext関数群を作成するために時間を割かねばなりませんでした。従って、将来的にはSolarisが gettextを備えた唯一のシステムであるということはなくなります。

翻訳者としての視点
*********

イントロダクション0
==========

GNUは国際化しつつあります! 翻訳プロジェクトは保守担当者、翻訳者、そしてユーザーを全てまとめるもので、そのためGNUソフトウェアは徐々に多くの言語を喋ることが出来るようになります。

GNU gettextツールセットには、保守担当者がパッケージのメッセージを国際化するために必要となる全てがあります。また、パッケージが国際化された後で、翻訳者がメッセージの地域化を行う際の助けになるような便利なツールもあります。

翻訳プロジェクトを完遂するために、我々は自分の国の言葉を愛し、良く書くことが出来、そして他の翻訳者が話しているのと同じ言語で助けることの出来る (synergize)能力を持った人間を数多く必要としています。もしあなたが翻訳チームでボランティアとして働くことを望むなら、該当する翻訳チームにメールを出して下さい。

各チームはLinux Internationalの好意による自身のメーリングリストを持っています。LL@li.orgというアドレスのLLをあなたの注目する言語のISO 639の二文字コードに置き換えることによって、その言語の翻訳チームに連絡できます。言語コードはISO 3166に定められている国コードと同じではありません。現時点では以下の翻訳チームが存在します。

     アイルランド語 ga、イタリア語 it、インドネシア語 in、エスペラント語 eo、オランダ語 nl、ギリシャ語 el、スウェーデン語 sv、スペイン語 es、チェコ語 cs、中国語 zh、デンマーク語 da、ドイツ語 de、トルコ語 tr、日本語 ja、ノルウェー語 no、フィンランド語 fi、フランス語 fr、ポーランド語 pl、ポルトガル語 pt、ロシア語 ru

仮に中国語翻訳チームにメールを出すとすれば、zh@li.orgとなります。翻訳チームのメンバーになるには、その言語チームのメーリングリストに登録する必要があります。例えば、スウェーデン人は本文に以下の内容を記述して sv-request@li.orgにメールを出します。

     subscribe

チームのメンバーは翻訳作業に興味を持つべきだということを心に留めて置いて下さい。そうでなければ翻訳を果たすことは難しいのです。もしが希望する言語のチームがまだ存在せず、その言語のチームを作りたいという場合には gnu-translation@prep.ai.mit.eduまで連絡して下さい。それによって全ての翻訳チームの調整者に連絡が取れます。

一握りのGNUパッケージには幾つかの言語に対するメッセージの翻訳が適用・提供されています。翻訳チームは組織化され始めており、これらのパッケージを起点として使っています。しかしまだまだ多くのパッケージがあり、多くの言語についてはボランティアの翻訳者がいません。もし翻訳チームでボランティアとして作業したいと思うのでしたら、gnu-translation@prep.ai.mit.eduに作業することの出来る言語を明記してメールを送って下さい。

イントロダクション1
==========

現在公式に、GNUは国際化しつつあります!  以下は1995年1月の GNU Bulletinで述べられた声明です。

     一握りのGNUパッケージには幾つかの言語に対するメッセージの翻訳が適用・提供されています。翻訳チームは組織化され始めており、これらのパッケージを起点として使っています。しかしまだまだ多くのパッケージがあり、多くの言語についてはボランティアの翻訳者がいません。もし翻訳チームでボランティアとして作業したいと思うのでしたら、gnu-translation@prep.ai.mit.eduに作業することの出来る言語を明記してメールを送って下さい。

本ドキュメントはその過程に興味を持ったり、貢献したいと考えている人々が持つ多くの疑問に答えます。願わくばざっと目を通し、GNUの国際化に対するこの集合的努力から産み出される大量のメールの幾ばくかでもを担当して下さい。

広く共用される多くのフリーソフトウェアのプログラミングは英語で行われています。そして現在のところ、英語はGNUプロジェクトに協力する国家的コミュニティ間での主要なコミュニケーション言語として使われています。このドキュメントでさえも英語で書かれています。これは当面変わらないでしょう。

しかしながら、多くのソフトウェアで自国語や自国の習慣を用いたいという、国家的コミュニティからの強い欲求があります。また、GNUソフトウェアをそのようにするための努力が現在も行われています。この試みは今までのところプリテスタからの熱心な反応の向上によって動かされており、我々はGNUの国際化は成功すると信じています。

このドキュメントに対する内容の明確化、追加、訂正に関する提案については、 gnu-translation@prep.ai.mit.eduまでメールを送って下さい。

議論
==

この国際化の効果を目の当たりにして、幾人かのユーザが彼らの考えを表明しています。紹介され議論されたこれらの疑問の幾つかをここに挙げてみましょう。

  * より小さいグループ

     幾つかの言語は多くの人々によって話されていないため、その言語を話す人々はフリーソフトウェアパッケージの当該言語版に対する必要性はあまりないと考えています。更に、幾つかの国にいるコンピュータの中にいる多くの人々は一般的に英語版ソフトウェアのほうを好むようです。

     一方で、人々は自分達の言語を非常に好きですし、彼らのお気に入りのフリーソフトウェアが彼らの母国語を喋ることが出来るように努力します。彼らは個人的な楽しみのためにそれを行います。そしてどのくらいの人間がその作業によって便益を得るかなどといったことは考えません。

  * 解釈の誤り

     ある種の誤ったプロパガンダのせいか、一部のユーザは自身の言語を押し出すことについて臆病になっています。ある人は、ネットワークの向こう側にはその言語をうるさくせがむユーザがいるに違いないと考えています。

     しかしあらゆる言語には地域化される価値があります。なぜなら、その言語が大切で敬愛されるものであると考える人々がその言語の向こうにいるからです。

  * 変な翻訳

     誰もがメッセージを理解出来るためには、正しい翻訳を見つけ出すことがもっとも大きな問題となります。翻訳は通常、少し変なものなのです。一部の人々は、「どちらかといえば押しが強く、嫌でときどき滑稽な」彼らの言語に対する翻訳を行うことが出来る程度には英語を扱うことが出来ます。フランス語を話す人間として、私は韓国または台湾において商品の取扱説明書を貧弱なフランス語へと翻訳した経験があります。...

     我々はときどきある種の国家的計算機文化を作り上げる必要があるというのは事実です。そして、その作業は彼らの母国語によって繋がっている多くの人間の協調作業なしに簡単に出来ることではありません。翻訳は彼ら自身の言語を知りそして愛する人々によってより良く行われ、より良い結果を得るという点において一緒に作業されるものなのです。

  * GPLへの依存

     何人かの人々は、彼らが彼らのプログラムをフリーにしたくない場合、又は別の種類の自由を与えたい場合に、GNU gettextを使うことによって彼らのパッケージをGNU一般公有許諾書(GNU General Public License)の保護の元で配布する必要があるのではないかと思案します。これに対する単純な答えは「はい」です。

     パッケージ中の僅かな地域化された文字列のマーキング、又は国際化のための条件つきの数行の包含はGPLのコードを含んでいません。しかしながら、地域化ルーチンそれ自身はGPLの元にあり、もしパッケージの残りの部分が地域化ルーチンと共に配布されるのであれば、残りの部分もGPLの元で配布されることになるでしょう。私は、問題となるものとして、地域化ルーチンそれ自身を準備しないが、地域化を準備したパッケージを、エンドインストーラにアセンブルすることの重荷を負わせることによって回避することができると仮定します。

組織
==

大きな尺度で見れば、真の解決方法は有志が参加できるようなある種の正しく厳密な集合を組織化することでしょう。私は、最近このアイディアについて幾つかの考察を行い、幾つかの微妙なポイントがあるであろうことを認識しています。私は、そのようなプロジェクトを開始するために、Richard Stallmanに連絡することを考えましたが、まず最初に我々の間でアイディアを揺り落とすことが良いだろうと感じました。おそらく、Linux Internationalは既にこの分野における幾らかの経験、または、有志の作業のオーケストラのようなものを持っています。あらゆる場合に、思考のための食物を!

我々は早々に何らかの方法で何かをセットアップせねばならないと考えます。同じ言語に対する作業のインターロックと重複を避けるという点で、それは多くの言語のコントリビュータを助けるでしょう。そして、更にそれらの言語(大部分の言語では技術的な英語の翻訳について独特な多くの問題点があります)についての独特な問題を共に解決するように連絡が取れるようにします。スウェーデンのコントリビュータはこれらの問題点を認め、そして私はフランス語においてのそれらの問題点に相当に気が付いています。

確かにこれは技術的問題ではありません、しかし我々は、コントリビュータ、及び管理者間の国家チーム層のインターフェースに関わらず、ロカールコントリビュータの努力が最大限に有益になるように管理するべきです。

翻訳プロジェクトは言語コーディネータを統合するためにある準備を必要とします。一度この作業が始められたなら、発展中のプログラムのローカライズは、確かにフリーソフトウェアコミュニティにおいて永久の、そして、連続的な動きになるでしょう。 GNU gettextが公式の現実になる前に、最小限の準備が完了し、そしてテストされているべきです。電子メールアドレス translation@iro.umontreal.caは、これらの話題に基づくボランティア、及び、一般的な電子メールから申し出を受けるための準備でした。このアドレスは、翻訳プロジェクトのコーディネータに届きます。

中央による調整
-------

そのことについて考えるよりも更に早く、GNUは誰かがそれらのグループを組織化し調整する方法を準備する必要があると私もまた考えます。ある種のグループのグループです。GNUは直ちに共同で働いているボランティアの小さなグループにこのタスクを委託することが良いだろうと、私は考えています。 おそらく、この国家委員会的なグループのリストはgnu.announceにおいて公表され得ます。

コーディネータとしての私の役割は単に、Ulrichをフリーソフトウェアの地域化に興味を持っているドイツ語を話すボランティアに紹介すること、そして国家的グループの準備が出来るまでの国家的登録機関のメンテナンス中に、国家的グループの最初の組織化を助けることです。実際、コーディネータは、ボランティアが国家チーム(言語または国(局地的言語)について1人のコーディネータを選択するべきです) を作成するために相互と連絡を取りやすくせねばなりません。これが正しく行われたならば、コーディネーションは不可抗力的作業を除いて便利なものとなり、代理人に時間を任せることが出来るようになります。

国家チーム
-----

私は、我々が個々の言語のための有志のコーディネータ/エディタを捜すことを提案します。これらの人々は彼ら自身の言語のために、様々なプログラムの翻訳ファイルを探し出し、そして、語法に対する高度で一定の標準を保証することになるでしょう。

今までの他の人々との間の私の現在の経験によれば、地域化を実現する人々はこのプロセスに対して非常に熱心であり、彼らは自分自身が地域化するプログラムよりも地域化のプロセスにほうに興味を持ち、それだけでなく多くのプログラムを地域化したいと思うものです。この事実は、各言語のためのコーディネータ/エディタを持つことは良いアイディアであることを確信させます。

我々は、問題となる言語において明瞭かつ簡潔な文章を書く際、適任となる人物を選択する必要があります。これは難しい作業です---我々は、自分自身でそれをチェックすることができません。従って、我々は数人の人間対してに互いの記述を判断するように要請し、そして最適任者を選択する必要があります。

私は私のプレリリースを20人から30人の人々に発表ましたが、そのプレリリースが既に生み出した全ての議論をあなたは信じないでしょう。私は、真に、公式に、世界中でこの作業が開始されるときに起こるであろうことを想像すると身震いがします。例えば、相互に反論しあう二人のチェコスロヴァキアのユーザーの間を仲裁するのは私なのでしょうか?

私がこれらの公式化について判断することが出来ないように、あなたのドイツ語が私のフランス語よりはるかに良いとは限らないと推測します。私が提案するものは、各言語に対してPOファイルをメンテナンスしその変更を判定する人々のグループを置くということです。そのような人々のグループがどのように行動するかについて、グループ間には文化的な相違点があると考えます。幾つかのグループはは緩い方法を採用し、簡単にコンセンサスの一致に達し、グループ中の誰もが保守者に関わることができます。一方は死ぬまで戦い、重い管理を国家の標準にまで組織化し、厳密なチャネルを使用するでしょう。

ドイツのチームは良い例を出しています。直ちに、彼らはおそらくお互いの翻訳を訂正する半数の人々と言語上の論点について議論する半ダースの人々です。私は全ての名前を知っているわけではありません。Ulrich Drepperはドイツのチームのコーディネートを担当しています。彼は私のプレテストのリストの購読を申し込みました。従って、私は彼に対して、連絡されるリリースの詳細について警告する必要は特にありません。

各言語を担当する翻訳チームを得るためには、それはよいアイディアだと思います。翻訳を更に良く首尾一貫した状態にするでしょう。

サブカルチャー
.......

フランス語を例に取ってみましょう。コンピュータの世界では、意味が異なる語彙を持つ幾つかのサブカルチャーがあります。組織化された方法でこの問題を提起することなしにあちこちでボランティアを選んでいると、プロジェクトにはすぐに国際化されたプログラムのごちゃ混ぜ状態が発生します。そしてことによると、実際にこの問題を気にする人々の間で終りなき口論が始まるでしょう。

国際化されたプログラムをフランス語へ地域化する過程において、ある種の統一を保つことは難しい(そしてデリケートな)仕事です。フランス人のラテンな人柄 (:-)を知っていても、もし我々がこのことを間違った方法で捉えれば、我々はどことも知れぬ場所で終わってしまうか、多くのエネルギーを無駄にしてしまうことでしょう。おそらく我々は、gettextが公式に発表される前に真剣にこの問題に取り組まなければならないでしょう。

組織化へのアイディア
..........

私は、公式リリース後に次の大きな変更があると考えています。どうか、私が短い GPLメッセージのドイツ語翻訳を用いることに注目してください。我々は、フリーソフトウェアコミュニティにおける真の地域化が消え去ってしまう前に2、3の良い例を示す必要があります、ここでは、議論が必要ないくつかのポイントを示します。

  * 各グループは、一つのFTPサーバ(少なくとも一つのマスタサーバ)を持つべきです。

  * サーバ上のファイルは、最新版(もちろん!)を反映すべきであり、そしてサーバは、対応するするアーカイブと共にRCSディレクトリ(私は今、これを持っていません) をもまた含んでいるべきです。

  * 同じくChangeLogファイル(これはRCSアーカイブより有益ですが、しかしEmacsによって後から自動的に生成することが出来ます)を含んでいるべきです。

  * コアグループは、疑わしい変更について判定するべきです(現在、このグループは私だけで構成されていますが、私は時折他の人間に「これもまた仕事に見える」と尋ねます)。

メーリングリスト
--------

GNU gettextに関するあらゆる問合せについては、以下に送ってください。

     translation@iro.umontreal.ca

*-pretestリストは、私にとって本当に有益です、アイディアはおそらく、多くのGNU、及び、非GNUパッケージへと一般化されるでしょう。しかし、保守者以外の、彼/彼女の方法!

François、我々は、チーム、チームをサポートするメーリングリストそしてログメンバを追跡するために、gnu.ai.mit.eduに適当なメカニズムを持っています。我々は、あなたが使うわずかな優先権を持っています。 これがあなたにとって問題ないならば、私はあなたに情報を与えることができます。

事物は変化しています! 2、3年前、Daniel Feketeと私がGNU地域化のメーリングリスト(FSFの中にあった)に尋ねたとき、我々は作業をどこででも組織化するように礼儀正しく勧められ、そして我々はそれを実行しました。私のプリテスタと連絡を取るために、私はmajordomoで管理される少数のメーリングリストを iro.umontreal.caに作成しました。これらのリストは今までのところ非常に信頼できました...

私は、ドイツ語のチームがドイツにあるメーリングリストを組織化し、他の国にも組織化をさせると思います。しかし組織化が行われる前に、FSFにおいて各国のチームのためのメーリングリストを提供することは確かに有益でしょう。そうです、私にどのようにメーリングリストを作成し扱えばよいかを説明して下さい。

我々は一時的なメーリングリストを、人々を組織化しやすいように国ごとに一つづつ作らねばなりません。なぜ一時的か、なぜなら一度再構成されたなら、各国のボランティアは彼らのリストへと戻ってきて、そして自分達が望むように管理するだろうからです。このことについては、個々のチームは自分達の国の中から自分達のリストを動かすだろうと思います。全てのチームが購読することの出来る、中央のメーリングリストも作る必要があるでしょう。

情報の流れ
=====

パッケージが最終的にリリースされた後、このメッセージについての幾つかの議論があることでしょう。今、人々が更に良い幾つかのメッセージを提案したとしたら、あなたはどうしますか? ジム、私が提供する1ダース近い地域化されたプログラムと同様、どうか直ちにそれらのメッセージに注目して下さい。私は翻訳とそれらに関する調整の両方を受け取るのです。

私が事前にテストするものを一つ置いたならば、Ulrichはその告知を受け取り、そしてそれを最後に修正するドイツ語のチームに渡します。そして彼は保守者として私に翻訳ファイルを引き渡します。私が保守していないフリーのパッケージについては何も聞きません。私が思うに、全ての翻訳プロジェクトにおいてこのスキームが作られるでしょう。セキュリティに関わる理由のために、おそらく Ulrich(実際の国家的コーディネータ)は時折翻訳プロジェクト(Jim、私、又は Lenの新人)によって保持される中央のレジストリをアップデートすべきです。

私は小さなGNUパッケージは一週間に一つずつ、より大きなパッケージは数週間か数ヵ月をかけるという責務を私自身に課し、12月か1月には私はにGNUの全パッケージを国際化する準備を積極的に整えていました。しかし、それはそのようには動きません。私は最初に、私が責任を持つ全てのことを行いました。私は他の保守者の幾らかの伝道作業に対して何も持っていませんでした。しかし私もまた多くのエネルギーを失いました---同じ議論を繰り返します。

そして、最初に地域化されたパッケージがリリースされるとき、我々は、醜悪な翻訳:- )についての多くの反応を得るでことしょう。確かに、そして我々は事前に、パッケージ保守者と国家チームの間の情報の流れを制御することに関する良いアイディアを持つ必要があります。

どうかどこかに各POファイルの迅速なヒストリを保存し始めて下さい。コメントを認めることによってファイルフォーマットがいずれ変更されるであろうことを私は知っています。各ファイルがログのようなもの、そしてコメントや不平の申し立て、又はその他の貢献をしたいと思う人々へのリファレンスを持つほうがよいでしょう。私は高速でフレキシブルなフォーマットに関する申し立てをしましたが、しかしそれはまだGNUの意思決定者によって受け入れられていません。私がこれについてより多くの情報を得たなら、このことについてお話しすることになるでしょう。

保守担当者としての視点
***********

パッケージの保守担当者は多くの責任を持ちます。一つは多くのプラットホームでパッケージを簡単にインストール出来るようにすること、そして既に述べた魔法 (see Users)がインストーラやエンドユーザに対して働くようにすることです。

もちろん、GNU gettextを配布物に統合するためには様々な方法があります。しかし、この章ではそれら全ての一般法則について述べることはしません。その代わり、GNU標準に従う、またさらに良ければGnits標準に従う配布物、そしてその他多くの素晴らしいフリーのパッケージに対して可能な限り最も適切な(なぜなら、 GNU gettextは特に全GNUプロジェクト、そしてそして可能な限りその他多くの素晴らしいフリーのパッケージの国際化を支援するためのものですから)一つのアプローチについて詳説します。従って、ここで提示する保守担当者の視点は、パッケージが既にconfigure.inファイルをその中に含み、そして Autoconfを使用しているものであると仮定します。

それでもなお、GNU gettextはGNU標準やGNUの習慣に従っていない他のフリーのパッケージにとっても本当に便利なものです。しかしそれらのパッケージの保守担当者は、gettextがいかなる状況下でもパッケージと共に動作するように、配布物の組織化について想像力とイニシアティブを示さなければなりません。

たとえgettextの手法が現在安定しているとしても、続くgettextのバージョンの間では僅かな修正が必要となることでしょう。従って、次のリリース時にはこの章をそれに合う形に訂正するべきです。

フラットまたはノンフラットなディレクトリ構造
======================

幾つかのフリーソフトウェアパッケージは一つのディレクトリ中に展開される tarファイルによって配布されます。これらはフラットな配布物と呼ばれます。その他のフリーソフトウェアパッケージは1階層のサブディレクトリ構造を持ちます。例えば、doc/というサブディレクトリにはTexinfoマニュアルやmanページが格納され、lib/というディレクトリにはCライブラリを置き換えたり補完する関数群が収められ、src/というサブディレクトリにはパッケージ独自のソースコードが収められています。これらの配布物はノンフラットと呼ばれます。

ここでは、フラットな配布物についてこれ以上言及しません。フラットなディレクトリ構造は、GNU gettextの新しいバージョンへのアップグレードが難しくなっていくので不利です。多くのPOファイルを扱う場合、単一のディレクトリは幾分散らかってしまうことにもなります。GNU gettext配布パッケージには、 misc/ディレクトリ中にcombine-shという名前のシェルスクリプトが含まれています。このスクリプトはintl/ディレクトリ内の全CファイルをCファイルのペア(一つの.cファイルと一つの.hファイル)にするために使われます。これら二つの生成されたファイルはフラットなディレクトリ構造により簡単に適応できるので、プロジェクトにこれら二つのファイルを追加する必要が出てくるでしょう。

おそらく、GNU gettext自身がノンフラット構造を持っているため、我々にとってはこの方法のほうが扱いやすいのです。この章の残りの部分ではこの方法について記述していくことになるでしょう。何人かの保守担当者はこれをいい機会だと捉え、パッケージ構造を非フラットなものにするでしょう。GNU gettextをフラットな配布物に対して適応させる経験を今後積んだなら、フラットな構造を処理する方法について新たに記述することにします。

事前に必要な作業
========

パッケージにGNU gettextを適用させる前に、やっておかなければならない幾つかの作業があります。これらの作業は、この章での注意を逐一記述せずに済ませるための一種の一般法則です。それらの作業について記述します。

  * 作業を始める前に、最初に幾つかのその他のパッケージをインストールします。 GNU m4、GNU AutoconfそしてGNU gettextの最新のバージョンが既にあなたのサイトにインストールことを確認します。もしインストールされていないならば、最初にそれらをインストールして下さい。これらが全てインストールされているにしても、GNU Autoconfが構成される前にGNU m4が完全にインストールされねばならないことに気をつけて下さい。

     automakeパッケージは、パッケージ管理者の作業がより簡単になるように設計・実装されました。現在、GNU gettextはこのツールを使っており、 intl/とpo/にあるMakefileはautomake及びプロジェクト中にあるlibintlを使用するために必要な全てのゴールについて知っています

     これら4つのパッケージは保守担当者だけが必要とします。パッケージのインストーラやエンドユーザには、メッセージが適切に変換されているパッケージをインストールし動作させるために、GNU m4、GNU Autoconf、GNU gettextそしてautomakeが本当に必要だというわけではありません。しかし、もしパッケージ中に国際化されたシェルスクリプトが含まれているなら話は変わってきます。もしエンドユーザがシェルスクリプトのメッセージを翻訳して使いたいと思う場合は、GNU gettextがユーザのサイトにインストールされている必要があります。

  * パッケージはAutoconfを使用し、configure.inファイルを含んでいなければなりません。そうでない場合、どのようにすればいいか学ぶ必要があります。 Autoconfのドキュメントは完全に記述されています。Autoconfのドキュメントを印刷し、よく読んでおくのが良いでしょう。

  * Cのソースは、このマニュアルで既に述べられた手順に従って修正されている必要がありますsee Sources。

  * po/ディレクトリには翻訳チームから受け取ったPOファイル (LL.poというような名前が付けられたもの)が全て収められていなければなりません。通常、パッケージが国際化され利用可能になる前に、翻訳作業を完了させるのは簡単なことではありません。どの時点から作業が始められたとしても、保守担当者にとって最も簡単なのは、POファイルが何もないところから始めることです。そして様々な翻訳チームがあなたのパッケージに興味を持ち、POファイルを送ってくれるのを待ちましょう。

保守担当者がPOファイルの提供に対しどのように理想的に振舞うべきかについて、少し説明する必要があるでしょう。保守担当者としての役割は、翻訳プロジェクトの適切な翻訳チームの代表者(疑問がある場合は提出物を translation@iro.umontreal.caに転送します)としての提供元を承認することです。POファイルの内容が激しく壊れていないこと、正常なインストールを妨げないことが保証出来たら、最後はただ単にこれらのPOファイルを配布物の po/ディレクトリに入れるだけです。

保守担当者として、あなたは翻訳が適切か完全かというようなチェックの報告について責任を負う必要はなく、言語上の問題に没頭することは避けなければなりません。翻訳チームは自ら動き、そして翻訳プロジェクトに対する言語の選択について充分に責任を負っています。翻訳チームは保守担当者によって動かされているのではないということを心に止めておかなければなりません。保守担当者は言語上の問題に関わるユーザとのコミュニケーションや、ユーザからの報告の全てを注意深く転送することによって、適切な翻訳チームを助けることが出来ます。また、それらをいかにして翻訳チームに届けるか、あるいはどのようにして翻訳チームに加わればよいかをユーザに対して説明することも出来ます。もっとも単純な方法は、ユーザにABOUT-NLSファイルを送ることでしょう。

保守担当者は決して翻訳チームを無視してPOファイルのバグレポートを適用してはいけません。もし何人かの翻訳者が彼女のチームの中で話の確信を掴むことが難しいというのであれば、彼女は直に保守担当者と翻訳を取り決めてもよいでしょう。しかし通常、翻訳チームはチーム自身でそれらの問題を解決するべきです。保守担当者が翻訳チームに関して本当に問題があると考えているとしても、保守担当者は決してチームの問題を解決してはならないのです。

gettextizeプログラムの起動
==================

GNU gettextを使って国際化された全てのパッケージには、幾つかのファイルが常にそして同じように必要です。扱いやすいように、gettextizeプログラムはこれらのファイルをパッケージ中に正しく置きます。このプログラムは以下の書式を取ります。

     gettextize [ OPTION... ] [ DIRECTORY ]

以下のオプションが使用可能です。

-c
--copy
     シンボリックリンクを作成する代わりに、必要なファイルをコピーします。シンボリックリンクを使用することによってパッケージは常にシステムの最新の gettextコードを使用できますが、保守担当者がソースに適用する幾つかのメカニズムを妨害するかもしれません。gettextizeを実行するのは簡単なことですので、コピーを使うことに関する問題が発生するべきではありません。

-f
--force
     既に存在するファイルを強制的に置き換えます。

-h
--help
     ヘルプを表示して終了します。

--version
     バージョン情報を表示して終了します。

DIRECTORYが与えられていれば、それはGNU gettextが使われるパッケージの最上位のディレクトリとなります。与えられていなければ、カレントディレクトリがそのパッケージの最上位ディレクトリであると仮定されます。

gettextizeプログラムは以下のファイルを用意します。--force (-f)オプションが指定されていない限り、存在するファイルが置き換えられることはありません。

 1. ABOUT-NLSファイルはパッケージの最上位ディレクトリにコピーされます。このファイルにはプログラムのインストール方法とナショナルランゲージサポートの使用方法に関する主要な指示が記述されています。もしこのファイルが簡単に手に入るのなら、gettextizeが準備するABOUT-NLSファイルよりも新しいファイルをパッケージに入れたいと考えるかもしれません。最新の ABOUT-NLSファイルは殆んどのGNUアーカイブサイトから手に入れることができます。

 2. po/ディレクトリは全ての翻訳ファイルをいつの日か格納するために作られますが、最初はGNU gettext配布パッケージ中の po/Makefile.in.inファイルが格納されているだけです(ファイル名に二つの.inがついていることに注意して下さい)。もしpo/ディレクトリが既に存在するのであれば、poディレクトリとディレクトリ中のファイルはそのままにして、 Makefile.in.inのみが上書きされます。

 3. intl/ディレクトリは、GNU gettextの配布パッケージにある intl/ディレクトリ中のファイルの殆んどが格納された状態で作成されます。もしオプション--force(-f)が与えられた場合、intl/ディレクトリは最初から空のまま作成されます。

もしサイトがシンボリックリンクをサポートしているのなら、 gettextizeはパッケージ中に本当にコピーすることはせず、その代わりにシンボリックリンクを作成します。これにより、全てのパッケージに必要なディスクスペースの重複を避けることが出来ます。配布物からtarアーカイブを作る際、ただ単に-hオプションを使うだけで、それぞれのリンクは実際に配布アーカイブの中にコピーされることになります。強調したいのは、メインの Makefile.inのために必ずtarと共に-hオプションを使わねばならないということです。

1つのパッケージにおいてGNU gettextをサポートするための新しいファイルの殆んどがintl/、及び、 po/サブディレクトリへ分かれている理由を理解するのは興味深いことです。これらの2つのディレクトリの間の1つの区別は、intl/がGNU gettextを使う全てのパッケージにおいて完全に同じものであり、一方、最近作成された全てのファイル(パッケージごとに異なるはず)はpo/に入るということです。po/ディレクトリが自分のためのMakefileを必要とするため、po/には一般的な Makefile.in.inがあります。そして、Makefile.in.inは全てのパッケージにおいて同じであり得るように設計されています。

作成するか変更しなければならないファイル
====================

gettextizeによって自動的に加えられるファイル以外にも、 GNU gettextと共に適切に使用するためには多くのファイルに対して変更を加えなければなりません。Makefileの設計やオートコンフィグレーション (auto-configuration)がGNU標準に密接に従っているなら、変更作業自体は更に簡単になります。ここでは、個々のファイルに対して必要となる変更について一つ一つ説明します。

以下にファイルの一覧を示します。個々のファイルに対する必要な全ての変更については、それに続いて記述します。多くの例は、GNU gettext VERSIONそれ自身から採用されています。gettextパッケージのソースコードを参照すると良いでしょう。GNU gettextパッケージは良い例であり、そしてそれ自身の機能を使うための実装の原本として使えるからです。

po/以下のPOTFILES.in
-----------------

po/ディレクトリにはPOTFILES.inファイルが必要です。このファイルは全てのプログラムソースに対して、翻訳が必要な文字列がどのファイルにあるかを示しています。以下に例を示します。

     # List of source files containing translatable strings.
     # Copyright (C) 1995 Free Software Foundation, Inc.

     # Common library files
     lib/error.c
     lib/getopt.c
     lib/xmalloc.c

     # Package source files
     src/gettextp.c
     src/msgfmt.c
     src/xgettext.c

コメント行と空白行は無視されます。その他の全ての行は、翻訳が必要な文字列を含んでいるファイルのリストです(see Mark Keywords)。このリストでは、各ファイルはPOTFILES.inファイルからの相対位置ではなく、パッケージの最上位ディレクトリからの相対位置で記述します。

トップレベルに存在するconfigure.in
-----------------------

 1. パッケージとバージョンの宣言

     宣言が行われた例を示します。

          PACKAGE=gettext
          VERSION=VERSION
          AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE")
          AC_DEFINE_UNQUOTED(VERSION, "$VERSION")
          AC_SUBST(PACKAGE)
          AC_SUBST(VERSION)

     もちろん、gettextはあなたのパッケージの名前に、 VERSIONはあなたのパッケージのバージョン番号に置き換えます。これらはソフトウェアのtarパッケージのファイル名に含まれることになるでしょう(ここではgettext-VERSION.tar.gzとなります)。

 2. 利用可能な翻訳の宣言

     利用可能な言語は、空白で区切られた引用文字列としてALL_LINGUASに一行で定義します。例えばこのように。

          ALL_LINGUAS="de fr"

     この例は、ドイツ語とフランス語がパッケージでサポートされるべく、この二つの言語のPOファイルが使用可能なことを表しています。インストール時のインストール言語の設定について更なる制限をかけたいとしても、それは configure.in中のALL_LINGUASを修正することによって行うべきではありません。その代わりに環境変数LINGUASを使用します (see Installers)。

 3. 国際化サポートのチェック

     これは、国際化サポートを実現するためのメインのm4マクロです。 configure.inに次の行を追加して下さい。

          AM_GNU_GETTEXT

     これによってチェックやアクションのためにconfigureに長い時間がかかるようになりますが、この呼出そのものは実に簡単です。

 4. 出力ファイルの作成

     configure.inの最後にあるAC_OUTPUTディレクティブは二つの方法で修正されなければなりません。

          AC_OUTPUT([EXISTING CONFIGURATION FILES intl/Makefile po/Makefile.in],
          EXISTING ADDITIONAL ACTIONS])

     AC_OUTPUTの最初の引数に対する修正は、intl/とpo/ディレクトリの置換を確認します。.inサフィックスがpo/に対してのみ使われていることに注意して下さい。配布されるファイルが実際に po/Makefile.in.inであるためです。

トップレベルに存在するaclocal.m4
---------------------

パッケージ中にaclocal.m4ファイルがない場合、もっとも簡単な解決策は、 GNU gettextからaclocal.m4をコピーすることです。しかし厳密には、必要となるのはマクロAM_LC_MESSAGES、AM_WITH_NLS、 AM_GNU_GETTEXT、AM_PATH_PROG_WITH_TEST(AM_WITH_NLSによって呼びだされる)だけです。従って、エディタを使うことによって必要でないマクロを取り除く必要があるかもしれません。

既にaclocal.m4ファイルが存在するならば、前述のマクロをあなたの aclocal.m4にマージしなければならないでしょう。もし以前のリリースの gettextからアップグレードしているのならば、通常、GNU gettextのリリースの間には小さな変更が含まれているため、おそらく前述のマクロを置き換える必要があるであろうことに注意して下さい。それらの内容は、変わったシステムに対して我々が対応するために変更されるかもしれません。

これらのマクロは国際化をサポートする関数とその関連情報をチェックします。安定化したなら、おそらくこれらのマクロは標準のAutoconfのセットに組み込まれることでしょう。なぜなら、このm4コードはgettextを使用する全てのプロジェクトで同じものになると思われるからです。

トップレベルに存在するacconfig.h
---------------------

配布物中にacconfig.hが含まれていない場合、もっとも簡単な解決策は GNU gettextからacconfig.hをコピーすることです。しかし厳密には、必要となるのはENABLE_NLS、HAVE_CATGETS、 HAVE_GETTEXT、HAVE_LC_MESSAGES、HAVE_STPCPY、 PACKAGE、VERSIONが含まれた行とコメントだけです。従って、エディタを使うことによって必要としない部分を全て取り除く必要があるかもしれません。既にacconfig.hファイルが存在するなら、前述の定義をあなたの acconfig.hにマージする必要があります。

トップレベルに存在するMakefile.in
----------------------

トップレベルに存在するのMakefile.inファイルに対する幾つかの修正点について述べます。

 1. dist:ゴールが正しく機能するように(以下で説明されるように)、 Makefile.inの先頭近くに以下の行を追加して下さい。

          PACKAGE = @PACKAGE@
          VERSION = @VERSION@

 2. DISTFILES定義にABOUT-NLSファイルを追加して下さい。これによってこのファイルは配布対象となります。

 3. Makefile.in中のどこででサブディレクトリを処理するにしても、 intlとpoもまた処理されることを確認して下さい。 Makefiles中の特別なルールは、国際化が必要とされない場合に処理を行います。

     automakeから生成されるか、或いはGNUコーディング規約に注意深く従った手書きのMakefileを使用している場合、新しいサブディレクトリに対して実行されるゴールとして、installdirs、install、uninstall、 clean、distcleanを含む必要があります。

     ここに厳密な処理の順番の例を示します。この例では後ほどdist:ゴールで使えるように、Makefile.inでSUBDIRSをもまた定義しています。

          SUBDIRS = doc lib @INTLSUB@ src @POSUB@

     これが、あなたのパッケージに対して適用せなければならないことです。

 4. dist:ゴールは大切なポイントです。なぜなら、intl/Makefileと po/Makefileは共に、適切な配布ディレクトリがメインの Makefileによってセットされていると後から仮定するからです。ここで dist:がどのようなものであるかの例を示します。

          distdir = $(PACKAGE)-$(VERSION)
          dist: Makefile
          	rm -fr $(distdir)
          	mkdir $(distdir)
          	chmod 777 $(distdir)
          	for file in $(DISTFILES); do \
          	  ln $$file $(distdir) 2>/dev/null || cp -p $$file $(distdir); \
          	done
          	for subdir in $(SUBDIRS); do \
          	  mkdir $(distdir)/$$subdir || exit 1; \
          	  chmod 777 $(distdir)/$$subdir; \
          	  (cd $$subdir && $(MAKE) $@) || exit 1; \
          	done
          	tar chozf $(distdir).tar.gz $(distdir)
          	rm -fr $(distdir)

src/以下のMakefile.in
------------------

メインのMakefile.inに対して行われた幾つかの修正は、あなたのパッケージソース(ここではsrc/サブディレクトリに置かれていると仮定される)の Makefile.inに対しても必要になるでしょう。src/Makefile.inに必要となる全ての修正を示します。

 1. dist:ゴール用に、これらの行をsrc/Makefile.inの最初のほうにおく必要があります。

          PACKAGE = @PACKAGE@
          VERSION = @VERSION@

 2. まだ行われていないのであれば、top_srcdirが定義されることを保証しなければなりません。これはcppインクルードファイルに貢献するでしょう。この行を追加するだけです。

          top_srcdir = @top_srcdir@

 3. 全てのMakefile.in中でほぼ同一にdist:ゴールを使用できるように、 subdirとしてsrcもまた定義したいと考えるでしょう。後から述べるdist:ゴールに関する項では、次の定義がなされていると仮定します。

          subdir = src

 4. 最終的なリンクでは、ライブラリとして@INTLLIBS@が使用されるようにしなければなりません。それをLIBSに追加してしまうのが簡単です。このようにします。

          LIBS = @INTLLIBS@ @LIBS@

     GNU gettextによって国際化された多くのパッケージには、ビルド用の補助関数群が含まれるライブラリを置くためのlib/ディレクトリがあるでしょう(少なくとも、GNU gettextライブラリ自身が必要とする幾つかの関数が必要になります)。しかしlib/中の幾つかの関数は、当然ながら翻訳されるべきメッセージをもまた含んでいます。この問題を解決するためには、補助ライブラリ(libsupport.aとしましょう)を前述の例における @INTLLIBS@と@LIBS@の間に置くだけでは不十分です。その代りに、このようにします。

          LIBS = ../lib/libsupport.a @INTLLIBS@ ../lib/libsupport.a @LIBS@

 5. intl/ディレクトリもまた、いかなる場合でもその中にあるインクルードファイルがCプリプロセッサによって検索されるようにしなければなりません。従って、 -I../intlと-I$(top_srcdir)/intlの両方がCコンパイラに与えられるようにする必要があります。

 6. dist:ゴールは他のものと一致する必要があります。このための簡単な定義は次のようなものです。

          distdir = ../$(PACKAGE)-$(VERSION)/$(subdir)
          dist: Makefile $(DISTFILES)
          	for file in $(DISTFILES); do \
          	  ln $$file $(distdir) 2>/dev/null || cp -p $$file $(distdir); \
          	done

結論
**

我々はこのGNU gettextマニュアルを翻訳プロジェクトの歴史でもって締めくくろうと思います。最後にネイティブランゲージサポートについて読んだり、調べたりしたい人のために幾つかのポインタを示します。

GNU gettextの歴史
==============

国際対応の重要性やアルゴリズムは、非公式にではありますがが毎日のように数年に渡ってGNUで論議されてきました。ときにはGNU libに関係して、あるときはHurdに関係して、あるいはその他のものに関係してです(誰もはっきりとは覚えていません)。そしてそれから作業は実際に始まりましたが、これは先だっての議論とは独立したものでした。

全ては、Patrick D'CruzeがGNU fileutilsバージョン3.9.2の国際対応についてのアイディアそして主導権を得たとき、つまり1994年の7月に始まりました。彼はそれから保守担当者であるJim Meyeringに、国際対応に関する変更をどのようにして正式リリースに含めるかについて尋ねました。最初のドラフトは #ifdefの塊で面食らうような代物でしたので、Jimはもっと良い方法を求めていました。PatrickとJimはこの分野においての試みや経験を共有していました。そして、結局のところこの作業はGNUに大きなインパクトを与えると感じたので、 Jimは標準がどのようなものであるかを知りたくなり、そして彼はRichard Stallmanにコンタクトを取りました。Richardはその時点で、glocaleとなるコード全体のデザインについて非常に手早くかつ丁寧に記述しました。

Jimはglocaleを実装し、PatrickやRichardから多くのフィードバックを受けました。もちろん、Mitchum DSouza(catgetsのようなパッケージを記述した)やRoland McGrath, またDavid MacKenzieやFrancois Pinard、 Paul Eggertらからのフィードバックもありました。様々な方向性が入り乱れ、しかもそれら全てに互換性があるというわけではなかったため、二、三のテストリリースの後、glocaleは破棄されました。

Jimがある程度距離と時間をおき、そして二人目のパパになった間に、Rolandは GNU libcを国際対応させたいと望んでおり、Ulrich Drepperがそのプロジェクトに加わりました。glocaleから作業を始める代わりに、Ulrichは一からコードを書き直しましたが、それはglocaleの作業で明らかになったガイドラインにより一層従う形になっていました。それからUlrichは以前のフォーラムから新しいプロジェクト用に人員を獲得し、glocaleをまず msgutilsと改称しました。それは後にnlsutilsという名前になり、さらにgettextという名称になりました。これは1995年5月頃にRichardによって公式に受諾されました。

Ulrich Drepperが1995年の四月にGNU gettextで書いたことに言及してまとめとしようかと思います。POモードを含んだパッケージの最初の公式リリースは 1995年の7月に行われ、そのバージョン番号は0.7でした。その他の人々は Ulrichの回りの議論フォーラムで作成された、小さなコードのかけらやテストの結果といった諸々のものを寄贈しました。彼らの名前はGNU gettextに付随するTHANKSファイルに納められました。

この作業が進んでいた間、Françoisは最初に半ダースのGNUパッケージに glocaleを、続いてgettextを現在のように適用してプリテストの状態にし、進化するツールを微調整するための効果的なユーザ環境を準備しました。彼はまた、翻訳プロジェクトを組織化し統合する責任をも引き受けました。 Patrick D'Cruzeは多くのネイティブランゲージのための20の非モデレートなメーリングリスト、そして二つのモデレートされたリスト(一つは全てのチームに直ちに届くもの、もう一つは国際化されたフリーソフトウェアパッケージの全ての自発的な管理者に連絡するためのもの)を作成し管理しました。そしてほぼ一年の間に多くの国々の人々の間で非公式な情報交換が行われ、翻訳者チームが1995年5月に発足しました。

FrancoisはまたGreg McGaryの協力を受け、1995年の六月にPOモードを書きあげています。これはUlrichのパッケージに寄贈されました。彼はまたGNU gettextのTexinfoマニュアルも寄贈しています。

参考文献
====

Eugene H. Dorr(dorre@well.com)はInternationalization Reference Listという国際化対応に関する興味深い文献の保守を行っています。これは次の場所で入手可能です。

     ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/i18n-books.txt

Michael Gschwind(mike@vlsivie.tuwien.ac.at)はProgramming for Internationalisationというタイトルの「良くある質問」(Frequently Asked Questions (FAQ))のリストを保守しています。このFAQは異なる言語慣習、キャラクタセットなどを扱うことが可能なプログラムの記述について論じています。そしてこれは、特にISO 8859-1及び全てのキャラクタセットエンコーディングに対して適用できます。これは通常、

comp.unix.questions, comp.std.internat, comp.software.international, comp.lang.c, comp.windows.x, comp.std.c, comp.answers、 news.answersといったUsenetのグループに投稿されています。このドキュメントの入手先は以下の通りです。

     ftp://ftp.vlsivie.tuwien.ac.at/pub/8bit/ISO-programming

Patrick D'Cruze(pdcruze@li.org)はNLSに関するチュートリアルを書きました。そしてJochen Hein(Hein@student.tu-clausthal.de)はそれに関する保守作業を引き継ぎました。この文書は次の場所にあります。

     ftp://sunsite.unc.edu/pub/Linux/utils/nls/catalogs/Incoming/...
          ...locale-tutorial-0.8.txt.gz

このサイトは以下のサイトへミラーリングされています。

     ftp://ftp.ibp.fr/pub/linux/sunsite/

同じチュートリアルのフランス語版は以下にあります。

     ftp://ftp.ibp.fr/pub/linux/french/docs/

ここには、フランス語に訳されたLinux関係のドキュメントもあります。

国家コード
*****

ISO 639は多くの国家に対して2文字のコードを規定しています。翻訳プロジェクトが国家や言語に対して使用している省略形には、この標準が採用されています。

          [file iso-apdx.texi not included]