"gettext" --- 多言語国際化サービス
**********************************

**ソースコード:** Lib/gettext.py

======================================================================

"gettext" モジュールは、 Python のモジュールやアプリケーションの国際化
(I18N, I-nternationalizatio-N) および地域化  (L10N, L-ocalizatio-N) サ
ービスを提供します。 このモジュールは GNU **gettext** メッセージカタロ
グの API と、より高水準で Python ファイルに適しているクラス形式の API
の両方をサポートしてます。 以下で述べるインターフェースを使うことで、
モジュールやアプリケーションのメッセージをある自然言語で記述しておき、
後から提供する翻訳されたメッセージのカタログによって様々な自然言語環境
で実行できます。

ここでは Python のモジュールやアプリケーションを地域化するためのいくつ
かのヒントも提供しています。


GNU **gettext** API
===================

"gettext" モジュールでは、以下の GNU **gettext** API に非常に良く似た
API を提供しています。 この API を使う場合、アプリケーション全体の翻訳
に影響します。 アプリケーションが単一の言語しか扱わず、ユーザのロケー
ルに従って言語が選ばれるのなら、たいていはこの API が求めているもので
す。 Python モジュールを地域化していたり、アプリケーションの実行中に言
語を切り替える必要がある場合は、この API ではなくおそらくクラス形式の
API を使いたくなるでしょう。

gettext.bindtextdomain(domain, localedir=None)

   *domain* をロケールディレクトリ *localedir* に対応付けます。 具体的
   には、 "gettext" は与えられたドメインに対するバイナリ形式の ".mo"
   ファイルを探しに、(Unixでは)
   "*localedir*/*language*/LC_MESSAGES/*domain*.mo" というパスを見に行
   きます。 ここで *language* はそれぞれ環境変数 "LANGUAGE" 、
   "LC_ALL" 、 "LC_MESSAGES" 、 "LANG" の中から検索されます。

   *localedir* が省略されるか "None" の場合、現在 *domain* に対応付け
   られているロケールディレクトリが返されます。 [1]

gettext.textdomain(domain=None)

   現在のグローバルドメインを変更したり調べたりします。 *domain* が
   "None" の場合、現在のグローバルドメインが返されます。それ以外の場合
   には、グローバルドメインに *domain* を設定し、その設定されたグロー
   バルドメインを返します。

gettext.gettext(message)

   Return the localized translation of *message*, based on the current
   global domain, language, and locale directory.  This function is
   usually aliased as "_()" in the local namespace (see examples
   below).

gettext.dgettext(domain, message)

   "gettext()" と同様ですが、指定された *domain* からメッセージを探し
   ます。

gettext.ngettext(singular, plural, n)

   "gettext()" と同様ですが、複数形を考慮しています。 翻訳が見つかった
   場合、複数形の選択公式を *n* に適用し、その結果得られたメッセージを
   返します (言語によっては二つ以上の複数形があります)。 翻訳が見つか
   らなかった場合、 *n* が 1 なら *singular* を返します; そうでない場
   合 *plural* を返します。

   複数形の選択公式はカタログのヘッダから取得されます。 選択公式は自由
   変数 *n* を持つ C または Python の式です; その式の評価結果はカタロ
   グにある複数形のインデックスになります。 ".po" ファイルで用いられる
   詳細な文法と、様々な言語における選択公式については GNU gettext ドキ
   ュメント を参照してください。

gettext.dngettext(domain, singular, plural, n)

   "ngettext()" と同様ですが、指定された *domain* からメッセージを探し
   ます。

gettext.pgettext(context, message)

gettext.dpgettext(domain, context, message)

gettext.npgettext(context, singular, plural, n)

gettext.dnpgettext(domain, context, singular, plural, n)

   Similar to the corresponding functions without the "p" in the
   prefix (that is, "gettext()", "dgettext()", "ngettext()",
   "dngettext()"), but the translation is restricted to the given
   message *context*.

   Added in version 3.8.

Note that GNU **gettext** also defines a "dcgettext()" method, but
this was deemed not useful and so it is currently unimplemented.

以下にこの API の典型的な使用法を示します:

   import gettext
   gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
   gettext.textdomain('myapplication')
   _ = gettext.gettext
   # ...
   print(_('This is a translatable string.'))


クラス形式の API
================

The class-based API of the "gettext" module gives you more flexibility
and greater convenience than the GNU **gettext** API.  It is the
recommended way of localizing your Python applications and modules.
"gettext" defines a "GNUTranslations" class which implements the
parsing of GNU ".mo" format files, and has methods for returning
strings. Instances of this class can also install themselves in the
built-in namespace as the function "_()".

gettext.find(domain, localedir=None, languages=None, all=False)

   この関数は標準的な ".mo" ファイル検索アルゴリズムを実装しています。
   "textdomain()" と同じく、 *domain* を引数にとります。オプションの
   *localedir* は "bindtextdomain()" と同じです。またオプションの
   *languages* は文字列を列挙したリストで、各文字列は言語コードを表し
   ます。

   *localedir* が与えられていない場合、標準のシステムロケールディレク
   トリが使われます。 [2] *languages* が与えられなかった場合、以下の環
   境変数: "LANGUAGE" 、 "LC_ALL" 、 "LC_MESSAGES" 、および "LANG" が
   検索されます。空でない値を返した最初の候補が *languages* 変数として
   使われます。この環境変数は言語名をコロンで分かち書きしたリストを含
   んでいなければなりません。 "find()" はこの文字列をコロンで分割し、
   言語コードの候補リストを生成します。

   "find()" は次に言語コードを展開および正規化し、リストの各要素につい
   て、以下のパス構成:

   "*localedir*/*language*/LC_MESSAGES/*domain*.mo"

   からなる実在するファイルの探索を反復的に行います。 "find()" は上記
   のような実在するファイルで最初に見つかったものを返します。該当する
   ファイルが見つからなかった場合、 "None" が返されます。 *all* が与え
   られていれば、全ファイル名のリストが言語リストまたは環境変数で指定
   されている順番に並べられたものを返します。

gettext.translation(domain, localedir=None, languages=None, class_=None, fallback=False)

   Return a "*Translations" instance based on the *domain*,
   *localedir*, and *languages*, which are first passed to "find()" to
   get a list of the associated ".mo" file paths.  Instances with
   identical ".mo" file names are cached.  The actual class
   instantiated is *class_* if provided, otherwise "GNUTranslations".
   The class's constructor must take a single *file object* argument.

   複数の ".mo" ファイルがあった場合、後ろのファイルは前のファイルのフ
   ォールバックとして利用されます。 フォールバックの設定のために、
   "copy.copy()" を使いキャッシュから翻訳オブジェクトを複製します; こ
   うすることで、実際のインスタンスデータはキャッシュのものと共有され
   たままになります。

   ".mo" ファイルが見つからなかった場合、 *fallback* が偽 (デフォルト
   値) ならこの関数は "OSError" を送出し、 *fallback* が真なら
   "NullTranslations" インスタンスが返されます。

   バージョン 3.3 で変更: 以前は "IOError" が送出されました; それは現
   在 "OSError" のエイリアスです。

   バージョン 3.11 で変更: *codeset* parameter is removed.

gettext.install(domain, localedir=None, *, names=None)

   This installs the function "_()" in Python's builtins namespace,
   based on *domain* and *localedir* which are passed to the function
   "translation()".

   *names* パラメータについては、翻訳オブジェクトの "install()" メソッ
   ドの説明を参照ください。

   As seen below, you usually mark the strings in your application
   that are candidates for translation, by wrapping them in a call to
   the "_()" function, like this:

      print(_('This string will be translated.'))

   For convenience, you want the "_()" function to be installed in
   Python's builtins namespace, so it is easily accessible in all
   modules of your application.

   バージョン 3.11 で変更: *names* is now a keyword-only parameter.


"NullTranslations" クラス
-------------------------

翻訳クラスは、元のソースファイル中のメッセージ文字列から翻訳されたメッ
セージ文字列への変換処理が実際に実装されているクラスです。 全ての翻訳
クラスで基底クラスとして使われているクラスが "NullTranslations" です;
このクラスは、独自の翻訳クラスを実装するのに使える基本的なインターフェ
ースを提供しています。 以下に "NullTranslations" のメソッドを示します:

class gettext.NullTranslations(fp=None)

   オプションの *ファイルオブジェクト* *fp* を取ります。この引数は基底
   クラスでは無視されます。このメソッドは  "保護された (protected)" イ
   ンスタンス変数 *_info* および  *_charset* を初期化します。これらの
   変数の値は派生クラスで設定することができます。同様に *_fallback* も
   初期化しますが、この値は "add_fallback()" で設定されます。その後、
   *fp* が "None" でない場合 "self._parse(fp)" を呼び出します。

   _parse(fp)

      基底クラスでは何もしない (no-op) ようになっています。このメソッ
      ドの役割はファイルオブジェクト *fp* を引数に取り、ファイルからデ
      ータを読み出し、メッセージカタログを初期化することです。サポート
      されていないメッセージカタログ形式を使っている場合、その形式を解
      釈するためにはこのメソッドを上書きしなくてはなりません。

   add_fallback(fallback)

      *fallback* を現在の翻訳オブジェクトの代替オブジェクトとして追加
      します。翻訳オブジェクトが与えられたメッセージに対して翻訳メッセ
      ージを提供できない場合、この代替オブジェクトに問い合わせることに
      なります。

   gettext(message)

      フォールバックが設定されている場合、フォールバックの "gettext()"
      に処理を移譲します。 そうでない場合、引数として受け取った
      *message* を返します。 派生クラスで上書きするメソッドです。

   ngettext(singular, plural, n)

      フォールバックが設定されている場合、フォールバックの
      "ngettext()" に処理を移譲します。 そうでない場合、 *n* が 1 なら
      *singular* を返します; それ以外なら *plural* を返します。 派生ク
      ラスで上書きするメソッドです。

   pgettext(context, message)

      代替オブジェクトが設定されている場合、 "pgettext()" を代替オブジ
      ェクトに転送します。そうでない場合、翻訳されたメッセージを返しま
      す。派生クラスで上書きするメソッドです。

      Added in version 3.8.

   npgettext(context, singular, plural, n)

      代替オブジェクトが設定されている場合、 "npgettext()" を代替オブ
      ジェクトに転送します。そうでない場合、翻訳されたメッセージを返し
      ます。派生クラスで上書きするメソッドです。

      Added in version 3.8.

   info()

      Return a dictionary containing the metadata found in the message
      catalog file.

   charset()

      メッセージカタログファイルのエンコーディングを返します。

   install(names=None)

      このメソッドは "gettext()" を組み込み名前空間にインストールし、
      変数 "_" に束縛します。

      If the *names* parameter is given, it must be a sequence
      containing the names of functions you want to install in the
      builtins namespace in addition to "_()".  Supported names are
      "'gettext'", "'ngettext'", "'pgettext'", and "'npgettext'".

      Note that this is only one way, albeit the most convenient way,
      to make the "_()" function available to your application.
      Because it affects the entire application globally, and
      specifically the built-in namespace, localized modules should
      never install "_()". Instead, they should use this code to make
      "_()" available to their module:

         import gettext
         t = gettext.translation('mymodule', ...)
         _ = t.gettext

      This puts "_()" only in the module's global namespace and so
      only affects calls within this module.

      バージョン 3.8 で変更: "'pgettext'" と "'npgettext'" が追加され
      ました。


"GNUTranslations" クラス
------------------------

The "gettext" module provides one additional class derived from
"NullTranslations": "GNUTranslations".  This class overrides
"_parse()" to enable reading GNU **gettext** format ".mo" files in
both big-endian and little-endian format.

"GNUTranslations" parses optional metadata out of the translation
catalog. It is convention with GNU **gettext** to include metadata as
the translation for the empty string. This metadata is in **RFC
822**-style "key: value" pairs, and should contain the "Project-Id-
Version" key.  If the key "Content-Type" is found, then the "charset"
property is used to initialize the "protected" "_charset" instance
variable, defaulting to "None" if not found.  If the charset encoding
is specified, then all message ids and message strings read from the
catalog are converted to Unicode using this encoding, else ASCII is
assumed.

Since message ids are read as Unicode strings too, all "*gettext()"
methods will assume message ids as Unicode strings, not byte strings.

The entire set of key/value pairs are placed into a dictionary and set
as the "protected" "_info" instance variable.

".mo" ファイルのマジックナンバーが不正な場合や、メジャーバージョン番号
が予期されないものの場合、あるいはその他の問題がファイルの読み出し中に
発生した場合、 "GNUTranslations" クラスのインスタンス化で "OSError" が
送出されることがあります。

class gettext.GNUTranslations

   以下のメソッドは基底クラスの実装からオーバライドされています:

   gettext(message)

      カタログから *message* id を検索して、対応するメッセージ文字列を
      Unicode でエンコードして返します。 *message* id に対応するエント
      リがカタログに存在せず、フォールバックが設定されている場合、検索
      処理をフォールバックの "gettext()" メソッドに移譲します。 それ以
      外の場合は、 *message* id 自体が返されます。

   ngettext(singular, plural, n)

      メッセージ id に対する複数形を検索します。カタログに対する検索で
      は *singular* がメッセージ id として用いられ、 *n* にはどの複数
      形を用いるかを指定します。返されるメッセージ文字列は Unicode 文
      字列です。

      メッセージ id がカタログ中に見つからず、フォールバックが指定され
      ている場合は、メッセージ検索要求はフォールバックの "ngettext()"
      メソッドに移譲されます。 それ以外の場合、 *n* が 1 ならば
      *singular* が返され、それ以外なら *plural* が返されます。

      以下に例を示します。:

         n = len(os.listdir('.'))
         cat = GNUTranslations(somefile)
         message = cat.ngettext(
             'There is %(num)d file in this directory',
             'There are %(num)d files in this directory',
             n) % {'num': n}

   pgettext(context, message)

      カタログから *context* と *message* id を検索して、対応するメッ
      セージ文字列を、 Unicode でエンコードして返します。 *message* id
      と *context* に対するエントリがカタログに存在せず、フォールバッ
      クが設定されている場合、フォールバック検索はオブジェクトの
      "pgettext()" メソッドに転送されます。そうでない場合、 *message*
      id 自体が返されます。

      Added in version 3.8.

   npgettext(context, singular, plural, n)

      メッセージ id に対する複数形を検索します。カタログに対する検索で
      は *singular* がメッセージ id として用いられ、 *n* にはどの複数
      形を用いるかを指定します。

      *context* に対するメッセージ id がカタログ中に見つからず、フォー
      ルバックオブジェクトが指定されている場合、メッセージ検索要求はフ
      ォールバックオブジェクトの "npgettext()" メソッドに転送されます
      。そうでない場合、 *n* が 1 ならば *singular* が返され、それ以外
      に対しては *plural* が返されます。

      Added in version 3.8.


Solaris メッセージカタログ機構のサポート
----------------------------------------

Solaris オペレーティングシステムでは、独自の ".mo" バイナリファイル形
式を定義していますが、この形式に関するドキュメントが手に入らないため、
現時点ではサポートされていません。


Catalog コンストラクタ
----------------------

GNOME では、James Henstridge によるあるバージョンの "gettext" モジュー
ルを使っていますが、このバージョンは少し異なった API を持っています。
ドキュメントに書かれている利用法は:

   import gettext
   cat = gettext.Catalog(domain, localedir)
   _ = cat.gettext
   print(_('hello world'))

For compatibility with this older module, the function "Catalog()" is
an alias for the "translation()" function described above.

このモジュールと Henstridge のバージョンとの間には一つ相違点があります
: 彼のカタログオブジェクトはマップ型の API を介したアクセスがサポート
されていましたが、この API は使われていないらしく、現在はサポートされ
ていません。


プログラムやモジュールを国際化する
==================================

国際化 (I18N, I-nternationalizatio-N) とは、プログラムを複数の言語に対
応させる操作を指します。地域化 (L10N, L-ocalizatio-N) とは、すでに国際
化されているプログラムを特定地域の言語や文化的な事情に対応させることを
指します。Python プログラムに多言語メッセージ機能を追加するには、以下
の手順を踏む必要があります:

1. プログラムやモジュールで翻訳対象とする文字列に特殊なマークをつけて
   準備します

2. マークづけをしたファイルに一連のツールを走らせ、生のメッセージカタ
   ログを生成します

3. 特定の言語へのメッセージカタログの翻訳を作成します

4. メッセージ文字列を適切に変換するために "gettext" モジュールを使いま
   す

In order to prepare your code for I18N, you need to look at all the
strings in your files.  Any string that needs to be translated should
be marked by wrapping it in "_('...')" --- that is, a call to the
function "_".  For example:

   filename = 'mylog.txt'
   message = _('writing a log message')
   with open(filename, 'w') as fp:
       fp.write(message)

この例では、文字列 "'writing a log message'" が翻訳対象候補としてマー
ク付けされており、文字列 "'mylog.txt'" および "'w'" はされていません。

翻訳対象の文字列を抽出するツールもあります。 オリジナルの GNU
**gettext** は C と C++ のソースコードしかサポートしませんが、拡張版の
**xgettext** は Python を含めた多くの言語で書かれたコードを読み取り、
翻訳できる文字列を発見します。 Babel は Python の国際化ライブラリで、
翻訳文字列の抽出とメッセージカタログのコンパイルを行う "pybabel" スク
リプトがあります。 François Pinard が開発した **xpot** と呼ばれるプロ
グラムは同じような処理を行え、彼の po-utils package の一部として利用可
能です。

(Python には **pygettext.py** および **msgfmt.py** という名前の pure-
Python 版プログラムもあります; これをインストールしてくれる Python デ
ィストリビューションもあります。 **pygettext.py** は **xgettext** に似
たプログラムですが Python のソースコードしか理解できず、 C や C++ のよ
うな他のプログラミング言語を扱えません。 **pygettext.py** は
**xgettext** と同様のコマンドラインインターフェースをサポートしていま
す; 詳しい使い方については "pygettext.py --help" と実行してください。
**msgfmt.py** は GNU **msgfmt** とバイナリ互換性があります。 この2つの
プログラムがあれば、 GNU **gettext** パッケージを使わずに Python アプ
リケーションを国際化できるでしょう。)

**xgettext** や **pygettext** のようなツールは、メッセージカタログであ
る ".po" ファイルを生成します。 このファイルは人間が判読可能な構造をし
ていて、ソースコード中のマークが着けられた文字列と、その文字列の仮置き
の訳文が一緒に書き込まれています。

生成された ".po" ファイルは翻訳者個々人へ頒布され、サポート対象の各自
然言語への訳文が書き込まれます。 ある言語への飜訳が完了した
"<language-name>.po" ファイルは翻訳者により返送され、 **msgfmt** を使
い機械が読み込みやすい ".mo" バイナリカタログファイルへとコンパイルさ
れます。 この ".mo" が "gettext" モジュールによる実行時の実際の飜訳処
理で使われます。

"gettext" モジュールをソースコード中でどのように使うかは単一のモジュー
ルを国際化するのか、それともアプリケーション全体を国際化するのかにより
ます。次のふたつのセクションで、それぞれについて説明します。


モジュールを地域化する
----------------------

モジュールを地域化する場合、グローバルな変更、例えば組み込み名前空間へ
の変更を行わないように注意しなければなりません。GNU **gettext** API で
はなく、クラス形式の API を使うべきです。

仮に対象のモジュール名を "spam" とし、モジュールの各言語における翻訳が
収められた ".mo" ファイルが "/usr/share/locale" に GNU **gettext** 形
式で置かれているとします。この場合、モジュールの最初で以下のようにしま
す:

   import gettext
   t = gettext.translation('spam', '/usr/share/locale')
   _ = t.gettext


アプリケーションを地域化する
----------------------------

If you are localizing your application, you can install the "_()"
function globally into the built-in namespace, usually in the main
driver file of your application.  This will let all your application-
specific files just use "_('...')" without having to explicitly
install it in each file.

単純な場合では、単に以下の短いコードをアプリケーションの主ドライバファ
イルに追加するだけです:

   import gettext
   gettext.install('myapplication')

ロケールの辞書を設定する必要がある場合、"install()" 関数に渡すことが出
来ます:

   import gettext
   gettext.install('myapplication', '/usr/share/locale')


動作中 (on the fly) に言語を切り替える
--------------------------------------

多くの言語を同時にサポートする必要がある場合、複数の翻訳インスタンスを
生成して、例えば以下のコードのように、インスタンスを明示的に切り替えて
もかまいません。:

   import gettext

   lang1 = gettext.translation('myapplication', languages=['en'])
   lang2 = gettext.translation('myapplication', languages=['fr'])
   lang3 = gettext.translation('myapplication', languages=['de'])

   # start by using language1
   lang1.install()

   # ... time goes by, user selects language 2
   lang2.install()

   # ... more time goes by, user selects language 3
   lang3.install()


翻訳処理の遅延解決
------------------

コードを書く上では、ほとんどの状況で文字列はコードされた場所で翻訳され
ます。しかし場合によっては、翻訳対象として文字列をマークはするが、その
後実際に翻訳が行われるように遅延させる必要が生じます。古典的な例は以下
のようなコートです:

   animals = ['mollusk',
              'albatross',
              'rat',
              'penguin',
              'python', ]
   # ...
   for a in animals:
       print(a)

ここで、リスト "animals" 内の文字列は翻訳対象としてマークはしたいが、
文字列が出力されるまで実際に翻訳を行うのは避けたいとします。

こうした状況を処理する一つの方法を以下に示します:

   def _(message): return message

   animals = [_('mollusk'),
              _('albatross'),
              _('rat'),
              _('penguin'),
              _('python'), ]

   del _

   # ...
   for a in animals:
       print(_(a))

This works because the dummy definition of "_()" simply returns the
string unchanged.  And this dummy definition will temporarily override
any definition of "_()" in the built-in namespace (until the "del"
command). Take care, though if you have a previous definition of "_()"
in the local namespace.

Note that the second use of "_()" will not identify "a" as being
translatable to the **gettext** program, because the parameter is not
a string literal.

もう一つの処理法は、以下の例のようなやり方です:

   def N_(message): return message

   animals = [N_('mollusk'),
              N_('albatross'),
              N_('rat'),
              N_('penguin'),
              N_('python'), ]

   # ...
   for a in animals:
       print(_(a))

In this case, you are marking translatable strings with the function
"N_()", which won't conflict with any definition of "_()". However,
you will need to teach your message extraction program to look for
translatable strings marked with "N_()". **xgettext**, **pygettext**,
"pybabel extract", and **xpot** all support this through the use of
the "-k" command-line switch. The choice of "N_()" here is totally
arbitrary; it could have just as easily been
"MarkThisStringForTranslation()".


謝辞
====

以下の人々が、このモジュールのコード、フィードバック、設計に関する助言
、過去の実装、そして有益な経験談による貢献をしてくれました:

* Peter Funk

* James Henstridge

* Juan David Ibáñez Palomar

* Marc-André Lemburg

* Martin von Löwis

* François Pinard

* Barry Warsaw

* Gustavo Niemeyer

-[ 脚注 ]-

[1] The default locale directory is system dependent; for example, on
    Red Hat Linux it is "/usr/share/locale", but on Solaris it is
    "/usr/lib/locale". The "gettext" module does not try to support
    these system dependent defaults; instead its default is
    "*sys.base_prefix*/share/locale" (see "sys.base_prefix"). For this
    reason, it is always best to call "bindtextdomain()" with an
    explicit absolute path at the start of your application.

[2] 上の "bindtextdomain()" に関する脚注を参照してください。
