2.1. i18n ヘッダーファイル

このチュートリアル全体を通してのゴールは、 アプリケーションを常にビルド可能なままに保つことです。 文字列を何らかの新しい関数でラップしたら、 プログラムがこれらの関数を確実に認識しているようにする必要があります。 それからアプリケーションの最初の部分で i18n コードを初期化し、 コード全体を通して文字列をマークアップして行きます。

2.1.1. 国際化ヘッダーファイル

様々な i18n マクロを含むことになる新しいヘッダーファイルを作成します。 次のリストは追っていくのに適切な例です。

例 1. slice-i18n.h

#ifndef __SLICE_INTL_H__
#define __SLICE_INTL_H__

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#ifdef ENABLE_NLS
#include <libintl.h>
#define _(String) gettext(String)
#ifdef gettext_noop
#define N_(String) gettext_noop(String)
#else
#define N_(String) (String)
#endif
#else /* NLS is disabled */
#define _(String) (String)
#define N_(String) (String)
#define textdomain(String) (String)
#define gettext(String) (String)
#define dgettext(Domain,String) (String)
#define dcgettext(Domain,String,Type) (String)
#define bindtextdomain(Domain,Directory) (Domain) 
#define bind_textdomain_codeset(Domain,Codeset) (Codeset) 
#endif /* ENABLE_NLS */

#endif /* __SLICE_INTL_H__ */

注意

このチュートリアルの例のために Slice-n-Dice という架空のプロジェクトで作業することとします。 私はこのアプリケーションが何をするものか知りませんが、 きっと世界中のユーザーが利用できるようになるでしょう。

当然 slice-i18n.h ではない何か別のファイル名にもできます。 しかし、ENABLE_NLS 変数名については後で 項3. 「パッケージのビルドインフラに i18n [の仕組み]を取り込み 」 で見るように特別な意味を持っているので変えるべきではありません。

もしアプリケーションが libgnome に依存しているなら、 slice-i18n.h (または他の名前のファイル)の代わりに <libgnome/gnome-i18n.h> を単純に include することで、独自の i18n サポートファイルを作成する必要はなくなります。 ここでは、メタファ的な"食物連鎖”のより低次のところで作業している開発者が便利なように、 上のファイルを読み込むこととします。

ライブラリ開発者は上記ファイルにいくつか変更を加える必要があるでしょう。 以下のセクションはなおも適用可能ですが、 項2.2.1.1. 「ライブラリについて特別に必要なこと」 まで飛ばすと必要な変更数行をみつけることができます。

これらの関数各々の目的についてはすぐに説明するつもりですが、 開発者から見て重要なのはまさに文字列を _()N_() 呼出しでラップしようとしているということです。 これらの関数をソースコード内で用いるのなら、前述のヘッダーファイルがきちんと include されていることを確認する必要があります。 もちろん include しない場合の失敗は全く明らかなもので、 いくつかの関数が宣言されていないので単純にアプリケーションはビルドできなくなります。

2.1.2. i18n サポートコードの初期化

実際に必要なコード変更を施す前に、 GNU gettext アプリケーションについてあまり詳しくない方のために、 大まかな状況について簡単に説明します。 ここでは、いくつかの詳細については省略して、主に GNOME に関連する部分について集中します。 もしすべての詳細について知りたいのなら gettext マニュアル ([gettext] を参照) が非常に参考になるでしょう。

インストールされている翻訳文字列のコレクションは、 別々の メッセージドメインに分けられています。 各々別々のアプリケーションまたはライブラリは、ほとんど常に異なるドメインを持ち、 ドメイン名は通常パッケージにまとめた後に決定されます。 与えられたドメインの文字列はインストール時に効率的なバイナリ形式 (各々のロカールについて一つのファイル) にコンパイルされ、 パッケージと一緒にインストールされます。 当然のことながら、プログラムがこれらファイルを必要なときにどこにあるか見付け出せるように指示する必要があります。

一つのアプリケーションがいつも一つのドメインだけからメッセージを取得しているわけではありません。 アプリケーション自身のメッセージはそれ自身のドメインからのものですが、 アプリケーションはまた、翻訳文字列を返す、または表示するライブラリ関数も呼出していて、 それら[メッセージ]はそのライブラリ固有のドメインからのものです。 そういうわけで[国際化の]仕組みは、(必ず一つだけ存在する) 現在アクティブなドメインを切り替えるために、C ライブラリ内に存在しています。

重要な最後の点は、翻訳された文字列は、 順々に回していくときに、 何らかの方法でエンコードされていなければならないということです。 中国語またはロシア語で使っているなら、英語圏のロカールで一般的な ASCII または ISO8859-1 エンコーディングは適切なものではありません。 デフォルトでは、gettext は与えられたロカール設定について最も自然なエンコーディングを知っていて、 適切なエンコーディングで文字列を返すでしょう。 しかし GNOME 全体ではすべての文字列は UTF-8 でエンコードされているので、 結局地域エンコーディングから UTF-8 に再度エンコードし直さなければなりません。 幸運なことに、C ライブラリにこのことを伝えることができて、 C ライブラリはロカール固有のエンコーディングに悩まされることはなく、 各々そして毎回の文字列の検索における多少の時間を節約することができます。

最後の三つの段落は次の三行のコードが何をするか非常に詳細に説明しているだけです。 これらのコードを、 可能な限り早くユーザーに可視な文字列の処理の準備をするために、 アプリケーションの main() 関数のすぐ最初に置きます。 もしクライアントアプリケーションが使うライブラリを書きたいのなら、 最初の二行をライブラリの初期化関数に入れておく必要があります。

bindtextdomain (GETTEXT_PACKAGE, SLICELOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);

ここで何が起きてるか説明すべきですね。 GETTEXT_PACKAGE 変数はビルドプロセスの間に (通常は configure.in または configure.ac) で定義され、このアプリケーションのメッセージドメイン名となります。 従ってこの値はプログラム名に設定できます。 SLICELOCALEDIR 変数 (この名前は普通はアプリケーション名に基づいていますが、 完全に任意であなたの判断にまかされています) はビルド時に定義され、 メッセージカタログを保持するディレクトリ階層のルートとなります。 [つまり]コンパイルされたメッセージカタログは SLICELOCALEDIR/locale/LC_MESSAGES/GETTEXT_PACKAGE.moとしてインストールされることになります。

注意

一般に LC_MESSAGES コンポーネントはどのようなカテゴリ名 (詳細については locale(7) マニュアルページの説明を見て下さい) にできます。 GNOME では LC_MESSAGES だけについて処理するべきなので他については無視します。

それから上述のコードの断片はアプリケーションに次のように指示します。

  • どこでメッセージカタログをみつけるか

  • すべての文字列について UTF-8 エンコーディングを使うように

  • 最初に、少くとも、このアプリケーションのドメインの文字列を使って翻訳するように

この最後の項目はライブラリでは用をなさない (クライアントアプリケーションがほとんど常にアクティブドメインを制御します) ので、そうする必要はありません。 代りに、ライブラリは、その固有の文字列を取得する前にメッセージドメインを設定し、 その後で元に戻す必要があります (詳細についてはこの後で)。

ついでに、 最後のセクションでふれたような i18n ヘッダーファイルを使っているのなら、 i18n サポートを利用可能にするかどうかに関らず、 上述のコード断片はコンパイルされるということにふれておくべきでしょう。 前のセクションのヘッダーファイルのメリットがこれです。 一度設定したらあたかも残りのコードも i18n サポートが常に利用可能であるかのように処理できて、 もしサポートが存在していなくても単に空の操作としてビルドされます。

注意

これを書いているときに libgnome/gnome-i18n.h ヘッダーファイルは ENABLE_NLS が未定義の場合に bind_textdomain_codeset() を定義しないことに気づきました。 従ってもし libgnome からコードを再利用しているのなら、 パッケージは (唯一の)特別なケースとしてその関数を処理する必要があるでしょう。