[ C++で開発 ]

メッセージカタログ(catgets)の導入

 国際化機能の1つで、プログラムで使用する文字列を外部ファイルから読み出すことで実行時にメッセージ文字列を切り替えることができます。切り替えにはロケールを使用するので、外部ファイルをロケール(言語)ごとに作成し配置することで、プログラムを実行した環境変数に応じて所定の言語のメッセージ文字列が使用されます。

 外部ファイルは、メッセージIDとメッセージ文字列がペアとなっており、プログラムからはIDをキーに文字列を取得することになります。

 このメッセージカタログの仕組みは、X/Openで定義されています。

簡単なサンプル

メッセージカタログの簡単なサンプルを示します。

hello_catalog.cpp
#include <nl_types.h>
#include <iostream>

int main(int argc, char* argv[]) {
    // メッセージカタログを開く
    nl_catd catd = ::catopen("hello.cat", 0);
    if (reinterpret_cast<int>(catd) == -1) {
        std::cerr << "メッセージカタログのオープンに失敗" << std::endl;
        return -1;
    }
    // メッセージカタログからメッセージを取得
    int setNumber = 1;
    int messageNumber = 123;
    const char* defaultMessage = "Default message";
    char* message = ::catgets(catd, setNumber, messageNumber, defaultMessage);
    std::cout << "message : " << message << std::endl;

    // メッセージカタログを閉じる
    int ret = ::catclose(catd);
    if (ret == -1) {
        std::cerr << "メッセージカタログのクローズに失敗" << std::endl;
        return -1;
    }
}

このプログラムのポイントは以下です。詳細は後に記述します。

メッセージカタログ hello.cat の作成

メッセージカタログのオープンで指定しているファイル名 "hello.cat" を作成します。hello.catはバイナリファイルで、gencatコマンドで生成するものです。そのため、gencatに読ませるテキストファイルを作成することになります。

hello.msg
$set 1
123  こんにちは 赤ちゃん

gencatコマンドでhello.msgからhello.catを生成します。

work$ gencat hello.cat hello.msg
work$

hello.catの配置場所

国際化では、hello.catはロケールごとに作成されます。ファイル名が同一なので、ロケールに応じたディレクトリに分けて配置します。配置場所の指定は、環境変数NLSPATHを使用します。

ここでは、hello_catalogプログラムのデータを置くディレクトリを、/usr/local/share/hello_catalogとして、その下にロケールに対応したディレクトリを作成します。

work$ sudo mkdir -p /usr/local/share/hello_catalog/ja_JP.UTF-8
work$ cp hello.cat /usr/local/share/hello_catalog/ja_JP.UTF-8/
work$

上記ディレクトリに置く場合、環境変数NLSPATHを以下に設定します。

work$ export NLSPATH=/usr/local/share/hello_catalog/%L/%N
work$ 

ここで、%Lはプログラム実行時にcatopen関数の中で、ロケールに応じて実際のディレクトリ名に展開されます。
%Nは、catopen関数に渡したメッセージカタログ名に展開されます。

プログラムのコンパイルと実行

work$ g++ -o hello_catalog hello_catalog.cpp
work$ ./hello_catalog
message : こんにちは 赤ちゃん
work$

関数仕様

メッセージカタログを扱う関数仕様について

catopen関数 

関数名
catopen
必要なヘッダー
<nl_types.h>
/usr/includeに置かれている
戻り値
nl_catd
オープンに成功したメッセージカタログのディスクリプタで、型はvoid* のtypedef
エラーのときは、-1となり、errnoにエラー原因が設定される
引数
const char* name
メッセージカタログ名
int flag
ロケールの決め方を制御するフラグ
0 : 環境変数LANGによる
NL_CAT_LOCALE : LC_MESSAGESによる

戻り値のエラー判定は、void*型とint型の比較演算がエラーとなるため、いったんint型へキャストする必要があります。しかし、本関数がエラーになってもプログラムの続行が可能であるため、通常エラー判定はしません。

引数のnameは、環境変数NLSPATHの%N部分になります。

引数のflagは、setlocale等でプログラム内でロケール設定をしていない場合、通常0でよいでしょう。

catgets関数

関数名
catgets
必要なヘッダー
<nl_types.h>
/usr/includeに置かれている
戻り値
char*
メッセージカタログに引数で指定したIDに合致する文字列がある場合はその文字列、ない場合もしくは引数で指定したディスクリプタが無効な場合は、引数messageで指定した文字列
引数
nl_catd catalog
catopenで生成されたメッセージカタログのディスクリプタ
int set_number
メッセージカタログの集合ID
int message_number
メッセージカタログのメッセージID
const char* message
メッセージカタログから文字列が取得できない場合、この文字列を戻り値として使用する

IDに合致した文字列がある場合、戻り値で返却されるアドレスは、関数内部で確保された領域となるので、変更/破棄はしてはなりません。

catclose関数

関数名
catclose
必要なヘッダー
<nl_types.h>
/usr/includeに置かれている
戻り値
int
0 : クローズに成功
-1 : クローズに失敗
引数
nl_catd
catopenで生成されたメッセージカタログのディスクリプタ

カタログファイル仕様

カタログファイルを生成するコマンドgencatに入力するテキストファイルの書き方をまとめます。

カタログファイルのTips

1から連番でIDを使いたい場合にsetを変更する

配列等でIDを1から連続で使用したいとき、$setで番号を変えると他のメッセージと重ならずに定義できます。

gencatコマンド

テキストで作成したファイルから、バイナリのカテゴリファイルに変換します。

work$ gencat hello.cat hello.msg
work$ 
work$

gencatは、カテゴリファイルが既にあれば、そのファイルに追加するので、たとえば複数のテキストファイルを1つのカテゴリファイルに結合することができます。

たとえば、$setの数値ごとに別ファイルにメッセージを記述し、それらを1つのカテゴリファイルにまとめるなどです。

環境変数NLSPATH

環境変数NLSPATHでは、以下の特殊な記法が使われます。

記法 内容
%L
ロケール名(正式名)
ja_JP.UTF-8
%l
ロケール名の言語部分
ja
%t
ロケール名の国部分
JP
%c
ロケール名のコードセット部分
UTF-8
%N
メッセージカタログファイル名
(catopen関数で指定)
hello.cat