最終更新: 1999.4.30
本稿では、アプリケーションプログラマを対象に、 プログラムの出力メッセージの国際化(多言語対応)の手法について解説します。 プラットフォームはUNIX(CUI)とMS-DOSです。
システムを特定の言語・地域・文化環境に適応させること。 ハードコーディングによる場合と、国際化による場合がある。 複数の環境に対応する場合は多地域化(multilocalization)という。
特定の言語・地域・文化環境に依存する部分を、 システムから分離・排除すること。 (多)地域化の手法の一つでもある。
UNIX上のCプログラミング環境における国際化サポートは、 ISO/ANSI Cのlocaleという概念をもとに構成されており、 その機能はおおむね以下のように分類されます。
setlocale(3), localeconv(3)など
LC_CTYPE,LC_COLLATE)
mbstowcs(3), strcoll(3)など
TZ,LC_TIME)
strftime(3)など
LC_MESSAGES)
catopen(3)など
LC_MONETARY,LC_NUMERIC)
strfmon(3)など
本稿では4.を扱います。
localeとは、特定の言語や地域に依存する情報のことです。 本稿の範囲では、 localeとは英語や日本語といった言語の区別のことであると考えて差し支えありません。 ただし、同じ日本語でも、日本語EUCとシフトJISのようにコードセットが異なる場合は、 別のlocaleとして扱います。
localeはlocale名によって識別されます。 使用可能なlocaleは、システムのlocaleデータベースに登録されています。
| locale名 | locale |
|---|---|
C | デフォルト(英語) |
en_US | 英語・米国 |
en_GB | 英語・英国 |
fr_FR | フランス語・フランス |
ja_JP.EUC | 日本語・日本・EUC |
ja_JP.SJIS | 日本語・日本・シフトJIS |
出力メッセージの多言語化とは、 プログラムが出力するメッセージの言語を実行時に選択できることをいいます。 一般に、以下のような方法によって実現されています。
1.や2.のようなハードコーディングによる方法では、 新たな言語を追加したり、メッセージを修正するたびに、 コードの変更と再コンパイルが必要となります。 これに対して、3.のような国際化による方法では、 再コンパイルなしで言語を選択・追加することができます。 これを実現するメカニズムを、メッセージカタログといいます。
XやWindowsなどのGUI環境では、一般にリソースというメカニズムがあり、 文字列やアイコンなどの情報をプログラムの外部にもたせることができます。 したがって、メッセージカタログは特に必要ありません。 ここでは、CUI環境でのメッセージカタログに対象を絞ります。
UNIXのメッセージカタログの仕様としては、X/OpenのXPG(X/Open Portability Guide)仕様インターフェースや、
SunOS/GNUのgettextインターフェースがあります。
このうち前者はPOSIXを補完する事実上の標準として広く採用されていますので、
これについて解説します。
メッセージカタログを利用するプログラムの作成および実行にあたっては、 以下のような手順を踏みます。
サポートする各localeについて、
出力メッセージを集めたメッセージテキストファイル(.msg)を作成します。
gencat(1)により、
メッセージカタログファイル(.cat)を生成し、
所定のディレクトリに置きます。
catopen(3)により、
指定されたlocaleに対応するメッセージカタログファイルを開きます。
catgets(3)によりメッセージを取得し、出力します。
catclose(3)によりメッセージカタログファイルを閉じます。
環境変数LANGに希望のlocale名を設定し、プログラムを実行します。
ではさっそく、おなじみのHello Worldプログラムを例にとって、 実際の手順を見ていきます。
国際化されていない通常のプログラムは以下のようになっています。
hello.c:
#include <stdio.h>
void main (void)
{
printf ("Hello, world.\n");
}
コンパイルと実行:
% cc -o hello hello.c % ./hello Hello, world
これを国際化します。
まず、メッセージテキストファイル(.msg)を作成します。
ここでは、日本語(ja_JP.EUC)とフランス語(fr_FR)をサポートすることにします。
hello.ja.msg:
hello.fr.msg:
これをもとに、メッセージカタログファイル(.cat)を生成します。
格納先ディレクトリは/usr/lib/nls/$LANG/とします。
% gencat /usr/lib/nls/ja_JP.EUC/hello.cat hello.ja.msg % gencat /usr/lib/nls/fr_FR/hello.cat hello.fr.msg
一方、プログラムの方は次のようになります。
hello.c:
#include <stdio.h>
#include <nl_types.h>
void main (void)
{
nl_catd catd;
catd = catopen ("hello", 0);
printf (catgets (catd, 1, 1, "Hello, world.\n"));
catclose (catd);
}
コンパイルと実行:
% cc -o hello hello.c % unsetenv LANG; ./hello Hello, world % env LANG=ja_JP.EUC ./hello 世界よ、こんにちは。 % env LANG=fr_FR ./hello Bonjour, tout le monde. % env LANG=C ./hello Hello, world % env LANG=No_Such_Locale ./hello Hello, world
詳しい説明は後述しますが、だいたい何をやっているかは想像がつくことと思います。
なお、locale名やメッセージカタログのディレクトリ名はOSによって違いますので、
実際に試す場合は注意してください(ディレクトリは変更可能です)。
また、OSによってはlibxpg4などのライブラリをリンクする必要があります。
| OS | 日本語EUC | フランス語 | メッセージカタログ |
|---|---|---|---|
| SunOS5 | ja |
fr |
/usr/lib/locale/%L/LC_MESSAGES/ |
| FreeBSD | ja_JP.EUC |
fr_FR.ISO_8859-1 |
/usr/share/nls/%L/ |
| HP-UX |
japanese.euc |
french |
/usr/lib/nls/%L/ |
| Digital UNIX | ja_JP.eucJP |
fr_FR.ISO8859-1 |
/usr/lib/nls/msg/%L/ |
%Lは$LANGのことです(後述)。
ここでは、メッセージカタログの使用法を詳しくみていきます。
プログラムで使用する文字列定数のうち、
翻訳する必要のあるもの(メッセージ)を抜き出し、
メッセージテキストファイルを作成します。
拡張子は何でもかまいませんが、通常は.msgとします。
メッセージテキストファイルの各行は、コマンド行またはメッセージ行です。
コマンド行には$に続いてコマンドを記述します。
メッセージ行にはメッセージ番号とメッセージを記述します。
空白やタブで始まる行、あるいは$と空白またはタブで始まる行はコメントとなります。
メッセージ番号は、プログラムからメッセージを取得する際のIDとして使われます。
これは1〜NL_MSGMAXの整数で、昇順に並んでいなければなりません。
番号とメッセージの間は空白かタブで区切ります。
$quoteで引用文字を指定している場合(後述)には、
引用文字でメッセージを囲みます。
デフォルトでは引用文字は使わず、メッセージをそのまま記述します。
メッセージを複数行にわたって記述したい場合、
\による継続行が使用できます。
その他、C言語と同様のエスケープ文字が使用可能です。
メッセージカタログ内のメッセージは、 任意にグループ化することができます。 これをセットといいます。 各メッセージカタログは1つ以上のセットを含み、 各セットは1つ以上のメッセージを含みます。 メッセージは、セット番号とメッセージ番号によって識別されます。 セットが異なれば、メッセージ番号が同じでも違うメッセージとなります。 セットを利用することで、メッセージを用途などによって分類し、 また、メッセージ番号の増大を抑えることができ、管理が容易になります。
コマンドは何種類かありますが、通常使うのは$quoteと$setです。
$quote [引用文字]
引用文字を指定し、あるいは取り消します。 引用文字が指定されている場合と、そうでない場合では、 メッセージ番号とメッセージとの間の空白文字の扱いが若干異なります。 デフォルトでは、引用文字は指定されていません。
$set セット番号
セット番号を1〜NL_SETMAXの範囲で指定します。
これ以降のメッセージは指定のセットに属します。
デフォルトのセット番号は1(NL_SETD)です。
gencat(1)により、メッセージテキストファイルをメッセージカタログファイルに変換します。
メッセージカタログファイルの拡張子は一般に.catです。
gencat メッセージカタログファイル [メッセージテキストファイル...]
メッセージカタログファイルがすでに存在する場合は、 メッセージテキストファイルの内容によって追加・更新します。
できたメッセージカタログファイルは、
所定のディレクトリ(OSによって異なります)に置きます。
ディレクトリは一般にlocale名によって分かれています。
なお、プログラムの実行時に、
環境変数NLSPATHによってディレクトリを指定することができます。
ホームディレクトリにカタログを置きたい場合などに便利でしょう。
NLSPATHに指定するディレクトリ名には、以下のような変数が使えます。
| 変数 | 値 | 例 |
|---|---|---|
%N |
メッセージカタログファイル名(catopen(3)の第1引数) |
hello |
%L |
locale名 | ja_JP.EUC |
%l |
locale名の言語(language)部分 | ja |
%t |
locale名の地域(territory)部分 | JP |
%c |
locale名のコードセット(codeset)部分 | EUC |
メッセージカタログファイルは一般にバイナリファイルであり、 OS・機種間の互換性はありません。
メッセージカタログファイルをオープンするには、catopen(3)を使います。
#include <nl_types.h> nl_catd catopen (const char *name, int oflag);
nameはメッセージカタログファイル名です。
oflagは0を指定します。
これは、$LANGによってlocaleを決定することを意味します。
これにより、所定のディレクトリ(または$NLSPATHで指定されたディレクトリ)でメッセージカタログファイルを検索し、オープンします。
拡張子.catが自動的に付加されるようです。
成功した場合、カタログ記述子(ファイル記述子のようなもの)を返します。
失敗した場合は、(nl_catd)-1を返します。
ただし、エラーチェックをする必要は特にありません(後述)。
メッセージカタログからメッセージを取得するには、catgets(3)を使います。
#include <nl_types.h> char *catgets(nl_catd catd, int set_num, int msg_num, const char *dfl_msg);
カタログ記述子catdの示すカタログの、 セット番号set_numのセットから、 メッセージ番号msg_numのメッセージを取得し、 そのメッセージへのポインタを返します。
dfl_msgには、
メッセージが取得できない場合に使われるデフォルトメッセージを指定します(通常これは英語です)。
引数の値が不正であるなどの理由でメッセージが取得できない場合、
catgets()はdfl_msgを返します。
つまり、取得に成功すればそのメッセージが、
失敗すればデフォルトメッセージが得られます。
catopen()が失敗してcatdが-1になっている場合でも、
気にせずcatgets()を呼び出してかまいません。
この場合、常にdfl_msgが返されますので、
自動的にデフォルトメッセージを選択したことになります。
メッセージカタログをクローズするには、catclose(3)を使います。
#include <nl_types.h> int catclose (nl_catd catd);
使用するカタログの数がNL_MAXOPENを超える場合は、
明示的にクローズする必要があります。
OSによっては、perror(3)/strerror(3)が
メッセージカタログによって国際化されている場合があります。
XPG3の仕様では、catopen(3)の第2引数oflagは0であり、
$LANGによってlocale名を決定します。
XPG4では、NL_CAT_LOCALEという値を指定することもでき、
この場合LC_MESSAGESカテゴリによってlocale名を決定します。
あらかじめsetlocale(3)を呼び出しておく必要があります。
ここでは、拙作
mei/moeのメッセージカタログ対応化の経験をまじえながら、
注意点について述べます。
既存のコードを書き換える場合でも、あるいは新規に開発する場合でも、
文字列定数の代わりにcatgets()の呼び出しを記述することになりますので、
いくつか注意が必要です。
catgets()により取得したメッセージは、
静的領域に置かれている可能性があります。
したがって、次のcatgets()の呼び出しの後は内容が保証されません。
たとえば、
printf (catgets(catd, NL_SETD, 1, "function: %s"),
func == READ ?
catgets(catd, NL_SETD, 2, "read") :
catgets(catd, NL_SETD, 3, "write"));
などとすると、期待した結果が得られないことがあります。
こういう場合は、以下のようにする必要があります。
char msg2 [NL_TEXTMAX], msg3 [NL_TEXTMAX];
strcpy (msg2, catgets(catd, NL_SETD, 2, "read"));
strcpy (msg3, catgets(catd, NL_SETD, 3, "write"));
printf (catgets(catd, NL_SETD, 1, "function: %s"),
func = READ ? msg2 : msg3);
char msg1 [10] = "foo";
のような場合、msg1が自動変数なら
char msg1 [10]; strcpy (msg1, catgets (catd, NL_SETD, 1, "foo"));
でいいのですが、静的変数の場合はこれができませんので、 適宜変更する必要があります。
また、配列を初期化している場合、
char *ordinal [] = { NULL, "1st", "2nd", "3rd", "4th" };
printf (ordinal [i]);
以下のようなメッセージカタログを作り、
次のようにするとスマートです。
printf (catgets (catd, 2, i, ordinal [i]));
このように、セットを利用すると、 一連のメッセージに連続したメッセージ番号を割り当てることが容易で、 しかも将来メッセージが増えても安心です。
メッセージカタログをサポートしないOSへの移植や、 サポートしていても何らかの理由で使わない場合などを考えると、 メッセージカタログを使うかどうかをコンパイル時に選択できるのが好ましいといえます。 そのためには、以下のようにするとよいでしょう。
#ifdef NLS
#include <locale.h>
#include <nl_types.h>
#else
#define catgets(i,sn,mn,s) (s)
#endif
void main (int argc, char **argv)
{
#ifdef NLS
nl_catd catd;
setlocale (LC_ALL, "");
catd = catopen ("foo", 0);
#endif
:
:
:
#ifdef NLS
catclose (catd);
#endif
}
CUI環境のプログラムでは、ユーザーに対してyes/noの選択を求める際に、
y/nの文字を入力させるのが一般的です。
しかし、これもlocaleに従うほうが望ましいといえます。
たとえば、フランス語ではoui/nonですから、
o/nとなります。
これを実現するには、そのlocaleでのyes/noを表す文字(列)を取得する必要があります。
一般には、nl_langinfo(3)を使います。
#include <nl_types.h> #include <langinfo.h> int yes, no; setlocale (LC_ALL, ""); yes = tolower (*nl_langinfo (YESSTR)); no = tolower (*nl_langinfo (NOSTR ));
しかし、nl_langinfo(3)を持たないOSのことを考えて、
メッセージカタログで代用することもできます。
このようにlocaleに合ったメッセージカタログを作ります。
#include <nl_types.h> int yes, no; yes = tolower (*catgets (catd, 3, 1, "yes")); no = tolower (*catgets (catd, 3, 2, "no" ));
printfでの語順の変更printfの書式指定文字列を翻訳した場合、
語順が変わるために後続の引数を入れ替える必要が生じることがあります。
たとえば、
printf ("error at offset %d in file %s\n", offset, filename);
これを自然な日本語にすると
printf ("ファイル%sのオフセット%dでエラー\n", filename, offset);
となります。
これは、書式指定文字列をcatgets()で取得するだけでは対応できず、
コードの変更を余儀なくされることになります。
このような状況に対応するため、printfの書式指定子が拡張されています。
これを利用すると、上記の例は、
printf ("ファイル%2$sのオフセット%1$dでエラー\n", offset, filename);
となります。%sや%dの間にはさまっているn$は、
後続の引数のうちn番目のものを使うことを表しています。
ここでは、%2$sには2番目の引数であるfilenameが使われ、
%1$dには1番目の引数であるoffsetが使われます。
この機能を使うことにより、翻訳で語順が変わっても、
printfの引数の順序を変更する必要はなく、
書式指定文字列をcatgets()で書き換えるだけですみます。
なお、一つの書式指定文字列の中で、 上記の形式の書式指定子と通常の書式指定子を混在させることはできません。
scanfにも同様の拡張機能がありますが、
こちらはあまり使うことはないと思われるので割愛します。
メッセージカタログは通常、
Cで記述したアプリケーションから利用する機能ですが、
できればシェルスクリプトでも利用したいところです。
一部のOSでは、シェルスクリプトからメッセージカタログを利用するための
dspmsg(1)というコマンドがあります。
dspmsg [-s セット番号] カタログ名 メッセージ番号 ['デフォルトメッセージ' [引数...]]
指定のメッセージをカタログから取得し、標準出力に出力します。
メッセージの中には、printfのように%s
(または%n$s)を含むことができ、
対応する後続の引数で置き換えて表示されます。
XPGのメッセージカタログのメカニズムは、いささか原始的であり、 プログラマの負担を抑えるためには工夫が必要です。
メッセージを番号で指定しなければならないというのはかなり大変です。
せめて記号定数を使いたいところです。
これを実現するために、一部のOSにはmkcatdefs(1)というプリプロセッサがあります。
これは、メッセージ番号の代わりに記号定数を使った形式のメッセージテキストファイルを入力とし、
記号定数を適当なメッセージ番号で置き換えて出力します。
さらに、記号定数とメッセージ番号の対応をヘッダファイルとして出力します。
デフォルトメッセージ(C locale用のメッセージ)は、
catgets()の引数という形でコード中に埋め込まれますので、
メッセージテキスト/カタログを作成する必要は本来ありません。
しかし、デフォルトメッセージのメッセージテキストがリファレンスとして用意されていないと、
他のlocaleのメッセージテキストの翻訳・作成作業が困難ですので、
実際にはメッセージテキストは必要になります。
また、他のlocaleとの扱いを統一するという観点から、
C localeに対してもメッセージカタログを作成することもありえます。
さて、そうなると、ソースコード中のデフォルトメッセージと、 デフォルトメッセージのメッセージテキストとの間で、 メッセージ内容を同一に保たなくてはなりません。 それには、一方から他方を自動生成するのが確実です。 そこで、両方の場合を比較検討してみましょう。
ソースコード中の文字列定数
(すでにメッセージカタログ対応化されている場合は、catgets()の引数)
を抜き出し、メッセージテキストを生成します。
printf (catgets (catd, 1, 1, "Hello, world\n"));
strcpy (yesstr, catgets (catd, 1, 2, "yes"));
fp = fopen ("/etc/passwd", "rt");
↓
catgets()の引数だけを抜き出すのであれば比較的容易です)。
OSによってはそのようなツールが用意されていることもあります。
メッセージテキストの文字列をマクロ定義するヘッダファイルを生成します。
↓
#define MSG_1_1 "Hello, world\n"
#define MSG_1_2 "yes"
printf (catgets (catd, 1, 1, MSG_1_1));
strcpy (yesstr, catgets (catd, 1, 2, MSG_1_2));
fp = fopen ("/etc/passwd", "rt");
#defineされたマクロなどの形をとるため、
ソースを見てもメッセージの内容がわかりません。
C localeのメッセージテキストの代わりに、
目的のlocaleのメッセージテキストを使うようにします)。
前者の方法が一般的ですが、mei/moeでは、
以下のような理由により、後者の方法を採用しています。
最近の商用UNIXシステムでは、メッセージカタログの作成や 既存プログラムのメッセージカタログ対応を支援するための ツールが付属していることがあります。 ここでは、HP-UXとDigital UNIXを例にとり、 そういったツールの機能を概観することによって、 作業の流れを見ていくことにします。
findstr(1), insertmsg(1)
この2つは組み合わせて使い、 既存のCソースをメッセージカタログに対応させる作業を支援します。
findstrは、Cソース中の文字列定数を抜き出し、
ソースファイル名・行番号と共に出力します。
ただしcatgets()のデフォルトメッセージは対象としません。
findstrの出力から、
不要な(メッセージカタログに含めるべきでない)文字列を手作業で除去したら、
これをinsertmsgの入力とします。
insertmsgは、
入力にリストされているソースファイル名・行番号・文字列を見て、
ソース中の文字列をcatgets()呼び出しに変換した
新たなソース(nl_*.c)を生成します。
メッセージ番号は自動で付加されます。
また、gencat用のメッセージテキストを標準出力に出力します。
これはデフォルトlocaleのメッセージカタログとして利用可能です。
生成されたソースは、catopen()/catclose()呼び出しを加えることで、
メッセージカタログに対応します。
findmsg(1)
findmsgは、
メッセージカタログを意識して書かれたCソースから文字列定数を抜き出し、
メッセージテキスト形式で出力します。
具体的には、catgets()のデフォルトメッセージと、
/* catgets n */
なるコメントを従えた文字列定数を抜き出します(nはメッセージ番号)。 出力はデフォルトlocaleのメッセージカタログとして利用可能です。
strextract(1), strmerge(1)
strextractは、HP-UXのfindstrと似たような動作をしますが、
入力の各ソースファイルごとに個別に出力ファイル(.str)を作ることと、
抜き出す文字列のパターンを正規表現で指定できるところが大きく異なります。
出力は、不要な文字列を除去した後、strmergeの入力とします。
strmergeは、strextractの出力に基づき、
ソース中の文字列をcatgets()呼び出しに変換した
新たなソース(nl_*.c)を生成します。
また、各ソースファイルごとにメッセージテキストファイル(.msg)を作ります。
生成されたソースは、catopen()/catclose()呼び出しを加えることで、
メッセージカタログに対応します。
extract(1)
strextractとstrmergeをあわせた動作をしますが、
変換の可否などを画面上でインタラクティブに指示することができます。
trans(1)
メッセージテキストの翻訳支援に特化した、 マルチウィンドウのスクリーンエディタです。
mkcatdefs(1), runcat(1)
mkcatdefsは、すでに述べたとおり、
メッセージテキストの記号定数をメッセージ番号に変換します。
runcatは、mkcatdefsとgencatをパイプでつないで実行します。
MS-DOSアプリケーション(とくにCUIアプリケーション)の多言語化は、 一般に、以下のようなハードコーディングによって実現されています。
筆者の知る限り、MS-DOS上でメッセージカタログなどの国際化の手法を用いて多言語化したアプリケーションの例はありません。 そもそも、メッセージカタログを実装した例すらありません。
mei/moeの多言語化にあたって、
UNIX版ではメッセージカタログを使用しました。
MS-DOS版もソースコードは共通ですので、
UNIX版と同じ手法で国際化するのが正当な方法といえます。
そのためには、メッセージカタログを実装する必要があります。
また、他人がやっていないことをやるのはそれ自体において愉快なことです:-)。
そんなわけで、国際化サポートの基礎となるsetlocale(3)や、
XPG仕様メッセージカタログのサブセットをMS-DOS上に実装しました。
以下に実装上の特徴を述べます。
setloc.c: setlocale(3), localeconv(3)
locale名が引数または環境変数で与えられていないときは、
MS-DOSの国番号とコードページ番号から暗黙のlocale名を決定します。
これは、MS-DOSでは環境変数LANGなどの使用が一般的でないことと、
DOS/Vで日本語環境と英語環境を行き来する場合を考慮した仕様です。
locale名の別名が使えます。
NLSPATHが設定されていないときはsetlocale()は失敗します
(以前はC:\USR\LIB\NLS\%Lを見に行っていたのですが、
98で不幸になる場合があるのでやめました)。
LC_ALLにより、一回のsetlocale()呼び出しで、
各localeカテゴリに異なるlocaleを設定したり、
全localeカテゴリの設定状況を問い合わせたりできます。
genloc.c: genloc(1)
これは独自のコマンドで、
locale名定義ファイル(locdir.def, locali.def)から
locale名ファイル(locale.dir, locale.ali)を生成します。
locale.dirは、MS-DOSの
国番号・コードページ番号・キーボードコードとlocale名の対応を定義したものです。
setlocale()でlocale名が与えられていないときに、
暗黙のlocale名を決定するために使われます。
locale.aliは、locale名の別名と標準名の対応を定義したものです。
setlocale()でlocale名の別名が与えられたときに、
標準のlocale名を取得するために使われます。
UNIXやXではこれらに相当するファイルはテキスト形式で置かれていますが、 この実装では検索の効率化のためにバイナリに変換することにしました。
locale.c: locale(1)
setlocale()の動作確認用です。
msgcat.c: catopen(3), catgets(3), catclose(3)
現在の実装では、gencatを使わず、
メッセージテキストをそのままメッセージカタログとして使用します。
そのため、catopen()はメッセージテキストを直接読み込んで処理します。
また、すべてオンメモリで読み込むので、ラージデータモデルでのみ使用可能としています。
catopen()はXPG4の仕様をサポートし、LC_MESSAGESカテゴリが使用可能です。
NLSPATHが設定されていないときはcatopen()は失敗します。
perror.c: perror(3), strerror(3)
エラーメッセージをメッセージカタログからとるようになっています。
Turbo Cでは、エラーメッセージの内容がUNIXやMS-Cと違ったり、
strerror()の仕様が変だったりするので、それに対応しています。
本稿では出力メッセージの国際化という高尚なテーマについて解説してきました。 重要な点は、プログラムコードを修正することなく、 メッセージの表記を切り替えることができ、 さらにユーザーがメッセージカタログを追加・修正することができるというところです。
しかし、勘の鋭い人はお気づきの通り、 この手法は異なる方面への応用が考えられます。 すなわち、メッセージを特定の人物の口調に変更することが容易になるということです(笑)。 もはや、バイナリパッチをあてたり、ソースを書き換えたりする必要はありません。 メッセージカタログを用意するだけでいいのです。 場合によってはlocaleを新設する必要がありますが、 それもたいていは新規ディレクトリを作る程度で済みます。
実際にそのような応用を行っている例として、 tcshの日本語メッセージカタログがあります。
マルチバイト文字・ワイド文字関連は除く。
| 機能名 | FreeBSD 2.1.5 | SunOS 4.1 | SunOS 5.3 | GNU | HP-UX 9.0 | Digital UNIX 3.2 | AIX 3.2 | 備考 |
|---|---|---|---|---|---|---|---|---|
| locale基本機能 | ||||||||
setlocale(3) | ○ | ○(3V) | ○ | ○ | ○ | ○ | ○ | |
localeconv(3) | ○ | ○ | ○ | ○ | ○ | ○ | ○ | |
nl_langinfo(3) | × | ○ | ○ | ○ | ○ | ○ | ○ | |
| XPGメッセージカタログライブラリ | ||||||||
catopen(3) | ○ | ○ | ○ | × | ○ | ○ | ○ | |
catgets(3) | ○ | ○ | ○ | × | ○ | ○ | ○ | |
catgetmsg(3) | × | ○ | × | × | ○ | × | × | |
catclose(3) | ○ | ○ | ○ | × | ○ | ○ | ○ | |
printf(3) %n$ | × | ○ | ○ | × | ○ | ○ | ○ | |
scanf(3) %n$ | × | ○ | ○ | × | ○ | ○ | ○ | |
perror(3) NLS | × | × | ○ | ? | ○ | ○ | ○ | |
strerror(3) NLS | × | − | ○ | ? | ○ | ○ | ○ | |
| XPGメッセージカタログ関連コマンド | ||||||||
gencat(1) | ○ | ○ | ○ | × | ○ | ○ | ○ | |
dspcat(1) | × | × | × | × | × | ○ | ○ | .catからメッセージを取り出す |
dspmsg(1) | × | × | × | × | × | ○ | ○ | シェルスクリプト用 |
mkcatdefs(1) | × | × | × | × | × | ○ | ○ | .catの記号定数をメッセージ番号に変換 |
runcat(1) | × | × | × | × | × | ○ | ○ | mkcatdefs|gencat |
findmsg(1) | × | × | × | × | ○ | × | × | Cソース中のマークされた文字列から.catを生成 |
dumpmsg(1) | × | × | × | × | ○ | × | × | gencatの逆 |
findstr(1) | × | × | × | × | ○ | × | × | Cソース中の文字列定数を抜き出す |
insertmsg(1) | × | × | × | × | ○ | × | × | findstrの出力を元にCソースにcatgets()呼び出しを埋め込む |
extract(1) | × | × | × | × | × | ○ | × | Cソース中の文字列定数をcatgets()呼び出しに変換 |
strextract(1) | × | × | × | × | × | ○ | × | Cソース中の文字列定数を抜き出す |
strmerge(1) | × | × | × | × | × | ○ | × | strextractの出力を元にCソースにcatgets()呼び出しを埋め込む |
trans(1) | × | × | × | × | × | ○ | × | .msgの翻訳用スクリーンエディタ |
patterns(4) | × | × | × | × | × | ○ | × | extract(1)等の変換パターンを定義 |
| Sun/GNUメッセージカタログライブラリ | ||||||||
gettext(3) | × | ○ | ○ | ○ | × | × | × | |
dgettext(3) | × | × | ○ | ○ | × | × | × | |
dcgettext(3) | × | × | × | ○ | × | × | × | |
textdomain(3) | × | ○ | ○ | ○ | × | × | × | |
bindtextdomain(3) | × | × | ○ | ○ | × | × | × | |
| Sun/GNUメッセージカタログ関連コマンド | ||||||||
xgettext(1) | × | × | ○ | ○ | × | × | × | Cソース中の文字列定数を抜き出し.poを生成 |
msgfmt(1) | × | × | ○ | × | × | × | × | .poから.moを生成 |
msgmerge(1) | × | × | × | ○ | × | × | × | ソースの変更を.poに反映 |
installtxt(8) | × | ○ | × | × | × | × | × | |
| SVR4 MNLS | ||||||||
setcat(3) | × | × | × | × | × | × | × | |
gettxt(3) | × | × | ○ | × | × | ○ | × | |
gettxt(1) | × | × | ○ | × | × | × | × | gettxt(3)のコマンド版 |
exstr(1) | × | × | ○ | × | × | × | × | Cソース中の文字列定数を抜き出しgettxt(3)呼び出しに変換 |
mkmsgs(1) | × | × | ○ | × | × | × | × | gettxt(3)用のメッセージデータベースを生成 |
srchtxt(1) | × | × | ○ | × | × | × | × | メッセージデータベースを検索 |
| その他 | ||||||||
LC_MESSAGES | × | ○ | ○ | ○ | ○ | ○ | ○ | |
locale.h | ○ | ○ | ○ | ○ | ○ | ○ | ○ | |
nl_types.h | ○ | × | ○ | × | ○ | ○ | ○ | |
langinfo.h | × | ○ | ○ | ○ | ○ | ○ | ○ | |
msgcat.h | × | × | × | × | ○ | × | × | |
libintl.h | × | × | ○ | ○ | × | × | × | |
locale(1) | × | × | × | ? | ○ | ○ | ○ | |
localedef(1) | × | × | × | ○ | ○(1M) | ○ | ○ | |
rpmatch(3) | × | × | × | × | × | ○ | ○ | |
strftime(3) | ○ | ○ | ○ | ○ | ○ | ○ | ○ | |
strptime(3) | × | ○ | × | ? | × | ○ | ○ | |
strfmon(3) | × | × | × | × | × | ○ | ○ | |
locale(4) | × | ○(5) | × | ? | × | ○ | ○ | |
charmap(4) | × | × | × | ? | ○ | ○ | ○ | |
langinfo(5) | × | × | ○ | ? | ○ | × | × | |
environ(5) | × | ○ | ○ | ? | ○ | ○ | ○ | |
| ベンダー固有機能 | ||||||||
| 機能名 | FreeBSD | SunOS 4.x | SunOS 5.x | GNU | HP-UX | Digital UNIX | AIX | 備考 |
mklocale(1) | ○ | × | × | × | × | × | × | |
colldef(1) | ○ | × | × | × | × | × | × | |
nl_init(3) | × | ○ | × | × | ○ | × | × | |
chrtbl(1M) | × | × | ○ | × | × | × | × | |
nl_types(5) | × | × | ○ | × | × | × | × | |
nlsinfo(1) | × | × | × | × | ○ | × | × | |
buildlang(1M) | × | × | × | × | ○ | × | × | |
langinfo(3) | × | × | × | × | ○ | × | × | |
nl_tools_16(3) | × | × | × | × | ○ | × | × | |
printmsg(3) | × | × | × | × | ○ | × | × | |
localedef(4) | × | × | × | × | ○ | × | × | |
hpnls(5) | × | × | × | × | ○ | × | × | |
lang(5) | × | × | × | × | ○ | × | × | |
| FreeBSD | /usr/share/nls/%L/%N.cat |
| SunOS 4 | /usr/lib/locale/%L/LC_MESSAGES/%N.cat |
| SunOS 5 | /usr/lib/locale/%L/LC_MESSAGES/%N.cat/usr/lib/locale/%L/LC_MESSAGES/%N.mo |
| SVR4 MNLS | /usr/lib/locale/%L/LC_MESSAGES/%N |
| GNU | /usr/share/%L/LC_MESSAGES/%N.po |
| HP-UX | /usr/lib/nls/%L/%N.cat |
| Digital UNIX | /usr/lib/nls/msg/%L/%N.cat |
| AIX | /usr/lib/nls/msg/%L/%N.cat |
SunOS 4.xは現在でも広く使われていますが、古いBSD系のOSであるため、
XPGメッセージカタログのサポートが弱く使用には注意が必要です。
基本的には、/usr/xpg2include/nl_types.hをインクルードし、
/usr/xpg2lib/libxpg.aをリンクします。
簡単な方法は、コンパイラとして/usr/xpg2bin/ccを使うことです
(これは/usr/5bin/ccを-I/usr/xpg2include -L/usr/xpg2lib -lxpgで呼ぶのと等価です)。
しかし、これではソースをK&Rスタイルで書かなければならず、
またSystem V仕様のライブラリが使われるなど不都合な場合があります。
gccで使う場合、
単に-I/usr/xpg2includeで#include <nl_types.h>としたのでは、
他のヘッダファイルも/usr/xpg2includeからインクルードされてしまい不都合なので、
nl_types.hをフルパスでインクルード
(#include </usr/xpg2include/nl_types.h>)するか、
-idirafter /usr/xpg2includeとします。
さらに、catopen/catclose/catgetsのプロトタイプ宣言がないので、
明示的に補って使用します。
リンクも、libxpg.aは/usr/5libのライブラリを前提としているため、
単に-L/usr/xpg2lib -lxpgとだけすると不都合が生じる
(libxpg.aのmalloc()が/usr/5lib/libc.aの_assert()を要求するなど)ので、
-L/usr/xpg2lib -lc -lxpgとします。
なお、JLE(Japanese Language Extensions)のマニュアルページによれば、
JLEではXPGメッセージカタログはサポートされていません。
具体的には、libxpg.aのgettext.oが必要とする関数や変数が、
libcのsetlocale.oから削られているため、
リンクすることができません。
ただし非JLE環境でリンクしたバイナリの運用は可能なようです。
メッセージカタログの使い方や Xの国際化プログラミングの基本を日本語で解説している、貴重なページです。
GNU gettextの使い方を簡潔に解説しています。
Digital UNIXのマニュアルのHTML版です。 このサイトには、Digital UNIXの各種マニュアル類がHTMLやPS、PDFでおいてあります。
DECの技術情報誌DTJの国際化特集号です。
GNUにおける国際化プログラミングについて解説しています。
よくわからない文書の一部ですが、 主にUI的側面から国際化の問題について詳述しています。
UNIX/Cの国際化関連の用語について解説しています。
[目次]
Copyright (C) 1998 ITO Takayuki, All rights reserved.