JFP 開発ガイド

第 5 章 メッセージ処理

この章では、メッセージ処理のためのインタフェースを紹介し、国際化・日本語化されたアプリケーションでの日本語のメッセージを取り扱い、表示させる方法を説明します。

概説

アプリケーションプログラムは、その使用目的や動作の形態によって、一般ユーザーに対してさまざまな局面でテキストメッセージを利用したフィードバックを行います。エラーメッセージや入力を促すプロンプト文字列、GUI を用いたアプリケーションのボタンラベルや階層化メニューなどがこれに相当します。

このようなアプリケーションを開発する場合、開発者はメッセージとして日本語テキストを使用できますが、ソースコード中に直接日本語テキストを挿入するようなプログラミングはお勧めできません。挿入されたテキストは特定のロケール環境に依存したエンコーディングで表現されるため、結果としてアプリケーション全体がロケールに依存することになります。また、メッセージだけを変更する場合にも、アプリケーション全体を再コンパイルしなくてはなりません。

代わりに、メッセージをソースコード本体から切り離し、メッセージファイルとして提供して、アプリケーションが実行時に動作ロケールに応じて動的にメッセージファイルを参照できるような仕組み (以下「メッセージ処理」と記述) を利用できます。これにより、アプリケーションを一度コンパイルすると、メッセージを表示したいロケールの翻訳メッセージファイルを用意するだけで、アプリケーション中のテキストを容易に翻訳できます。

Solaris では、メッセージ処理の API が 2 種類提供されています。1 つは米国 Sun Microsystems, Inc. 独自のインタフェースである gettext() を使用する方式で、もう 1 つは X/Open で規定された catgets() を使用する方式です。これらの間に互換性はありません。アプリケーションの移植性が問題になる場合には、X/Open で規定されている方式でメッセージ処理を行うことをお勧めします。

アプリケーションのメッセージ処理を行う基本的な手順を以下に概説し、その手順に従って X/Open 方式でのメッセージ処理 API について説明します。

アプリケーションのメッセージ処理手順

  1. 翻訳したいメッセージに印を付け、そのメッセージが属する集合 (セットまたはテキストドメイン) を指定します。

    これは、複数のアプリケーションで同じメッセージが存在する可能性があるためです。

  2. 翻訳したいメッセージをソースコードから抽出し、オリジナルメッセージだけのファイルを作ります。

  3. 手順 2 で作成したファイルに翻訳したメッセージを追加し、アプリケーションが参照できる形式に変換します。

  4. 変換したファイルを一定の場所に置き、アプリケーションがパス名とファイル名から翻訳メッセージのあるファイルを特定し、参照できるようにプログラムを変更します。

X/Open 方式

X/Open 方式では、翻訳メッセージは「メッセージカタログ」と呼ばれるファイルに格納されます。メッセージカタログファイルは 1 つ以上のセットに分かれ、それぞれのセットの中には 1 つ以上の翻訳メッセージがメッセージ ID と共に存在します。アプリケーションは、オリジナルのメッセージに対して、メッセージカタログファイル、セット ID、メッセージ ID を指定して翻訳メッセージを使用します。

メッセージ処理の手順は次のとおりです。

  1. 翻訳したいメッセージに印を付け、そのメッセージが属する集合を指定します。

    印をつける操作は、メッセージ文字列を catgets(3C) インタフェースで囲むことで行います。その際に、翻訳メッセージを格納するカタログファイル、セット ID、インデックス ID をあらかじめ決めておきます。catgets() では、「メッセージカタログ記述子」を介してカタログファイルを利用するため、catopen(3C) インタフェースで取得しておきます。

    nl_catd hook;
    hook= catopen("catalog", 0)
    fprintf(stdout, catgets(hook, SET_1, ID_1, "Defaults:"));

  2. 翻訳したいメッセージをソースコードから抽出し、オリジナルメッセージだけのファイルを作ります。

    メッセージカタログファイルの中身を作成します。各メッセージはメッセージ ID を持ち、セット ID と共に管理されます。アプリケーションからは、これらの ID だけで利用されるので、オリジナルメッセージと別の ID にならないように注意が必要です。

    sun% cat my_message.orig
    $set 1 my_message set
    $ 1 Here's the 1st message.¥n
    $ 2 Here's the 2nd message from %s.¥n

  3. 手順 2 で作成したファイルに翻訳したメッセージを追加し、アプリケーションが参照できる形式に変換します。

    カタログファイルの変換は gencat コマンドを使用します。gencat コマンドが処理できるファイル形式については gencat(1) のマニュアルページを参照してください。

    sun% cp my_message.orig my_message.ja
    sun% vi my_message.ja
    sun% cat my_message.ja
    $set 1 my_message set
    $ 1 Here's the 1st message.¥n
    1 これは第 1 のメッセージです。¥n
    $ 2 Here's the 2nd message from %s.¥n
    2 これは %s からの第 2 のメッセージです。¥n
    sun% gencat my_catalogue.cat my_message.ja
    

  4. 変換したファイルを一定の場所に置き、アプリケーションがパス名とファイル名から翻訳メッセージのあるファイルを特定し、参照できるようにプログラムを変更します。

    ここで、カタログファイルを利用するには、catopen() でメッセージカタログ記述子を取得する必要があります。不要になった記述子は catopen() インタフェースを介して解放できます。

    catopen() でカタログファイルを利用するには、カタログファイル名を絶対パス名で指定する方法と、NLSPATH 環境変数とデフォルトパスを利用した相対パス名で指定する方法があります。詳細は catopen(3C)のマニュアルページを参照してください。

    なお、これらの API を使用する場合は、nl_types.h ヘッダーファイルを取り込み、処理の最初の段階で setlocale を呼び出し、動作ロケールを適切に設定する必要があります。

    sun% cat my_message.c
    #include <stdio.h>
    #include <locale.h>
    #include <nl_types.h>
    #include <errno.h>
    
    #define		MY_CATALOGUE		"my_catalogue.cat"
    #define			SAMPLE_SET		1
    #define			SAMPLE_MES_IND		1
    
    int
    main(int argc, char *argv[])
    {
    	int				retval;
    	nl_catd			hook;
    
    	setlocale(LC_ALL, "");
    	hook = catopen(MY_CATALOGUE, 0);
    	if (hook == (nl_catd)-1) {
    		perror("catopen()");
    		exit(-1);
    	}
    	fprintf(stdout, catgets(hook, SAMPLE_SET, SAMPLE_MES_IND,
    			"Here's the 1st message.¥n"));
    	fprintf(stdout, catgets(hook, SAMPLE_SET, (SAMPLE_MES_IND + 1),
    			"Here's the 2nd message from %s.¥n"), argv[0]);
    	errno = 0;
    	retval = catclose(hook);
    	if (retval != 0) {
    		perror("catclose()");
    		exit(-1);
    	}
    
    	return (0);
    }
    sun% mkdir -p /usr/tmp/C /usr/tmp/ja
    sun% cp my_catalogue.cat /usr/tmp/ja
    sun% setenv NLSPATH /usr/tmp/%L/%N
    sun% cc -o my_message my_message.c
    sun% env LANG=C ./my_message
    Here's the 2nd message from ./my_message.
    sun% ./my_message
    これは第 1 のメッセージです。
    これは ./my_message からの第 2 のメッセージです。