ここでは、国際化(i18n)を念頭に置いてFreecivがどのように開発されているかについての情報を見つけることができます。 対象読者は、翻訳者やプレーヤーではなく、コーダーです。
Freecivは、国際化とローカリゼーションのサポートにgettextシステムを使用しています。 詳細については、gettextに関する情報ファイルを参照してください。 gettextの概要については、Freecivのgettextガイドを参照してください。 翻訳を追加または変更する方法については地域化を参照してください。
貢献[]
変更の送信方法など一般的な情報はここにあります。 gettextに関する情報ファイルも参照してください。
国際化用のCソースファイルを準備する方法[]
変換する必要のある文字列を含むすべてのCソースファイルには fc_config.h と fcintl.h をincludeしてください。
次の行がファイルの先頭近くにない場合は追加します。
#ifdef HAVE_CONFIG_H #include "fc_config.h" #endif
(通常ヘッダーコメントブロックの直後に追加されます) 以下も含まれていることを確認してください。
#include "fcintl.h"
(./utility からincludeします)
また、これらのCソースファイルはそれぞれ、ビルド時に xgettext によって処理される必要があります。 ビルド時にxgettextが処理するファイルのリストは、 ./po/POTFILES.in に保持されます。これは1行1ファイルで最上位のFreecivディレクトリからの相対パスを持ちます。
... common/support.c common/unit.c common/version.c server/barbarian.c server/cityhand.c server/citytools.c ...
./po/POTFILES.in リストに新しいファイルを追加するには、サブディレクトリセクション内にアルファベット順に新しい行を追加するだけです。 たとえば、./common ディレクトリに thing.c を追加すると、次のように変更されます。
... common/support.c common/thing.c common/unit.c common/version.c server/barbarian.c server/cityhand.c server/citytools.c ...
翻訳すべき文字列のマークアップ[]
翻訳される文字列の配置には、次の2つの部分があります。
- xgettextが文字列を freeciv.pot ファイル(翻訳者(人)が使用するマスターファイル)にコピーするために文字列は マークされる必要があります。。
- 文字列はその翻訳を検索して返すgettext()関数を介して実行される必要があります。
文字列が関数を呼び出すことができる場所で使用されている場合、上記の両方の部分は単純な_()マクロで実行されます。xgettextをCソースファイルに実行すると、_(と)で囲まれた文字列が全て freeciv.pot ファイルにコピーされます。つまり_()マクロは次のように実装されています: #define _(String) gettext(String)。
例。
void myfunction(void) { char *foo = _("new string");
(fooは翻訳されるべき文字列へのポインタです)
関数を呼び出せない場所で文字列が使用されている場合は、2つの部分を別々に処理する必要があります。まず、 N_()マクロで文字列をマークします。 _()マクロと同様xgettextを実行すると、N_(と)で囲まれた文字列が全て freeciv.pot ファイルにコピーされます。
例。
#include "fcintl.h" static char *bar = N_("new string");
ただし、これは文字列に対してgettext()を呼び出しません。 したがってこれを関数が呼び出される可能性のあるコンテキストで文字列が使用されるときに後で実行する必要があります。 この後の時点で文字列への参照を _(と)でラップするだけです。上記で見たようにこれはgettext()の呼び出しとして実装されます。
例。
void myfunction(void) { printf ("%s", _(bar));
(これは"new string"の翻訳を表示します。)
文字列からはは文脈が見えないので翻訳者のために文字列の意味についてコメントを追加することをお勧めします。 TRANS:で始まるコメントは文字列と一緒にpo-fileにコピーされます。 コメントの詳細については、コーディングスタイル[en]を参照してください。
例。
fc_snprintf(title_buf, sizeof(title_buf), /* TRANS: %s is a unit type */ _("Your %s Has Arrived"), unit_name_translation(punit));
po-fileでは次のように表示されます。
#. TRANS: %s is a unit type #: client/gui-gtk-2.0/caravan_dialog.c:103 #, c-format msgid "Your %s Has Arrived"
訳し分け文字列[]
一部の文は複数の解釈ができるため、簡単には翻訳できません。典型的な例は、英単語 "game" です。この単語はFreecivでは2つの役割で使用されます。
- 私たちがプレイする game。Freecivではメニューバーの先頭項目などで使用されます(日本語訳はゲーム)。
- 地形特徴の game。食べるために狩猟される動物です(日本語訳は獲物)。
まったく異なる2つの概念が含まれている単語は、ほとんどの場合は別の単語に訳す必要があります。しかし、これらはコード内で同一の文として登場することがしばしばあります。 例。
menu.name = "Game"; special.name = "Game";
これらを通常のgettextの方法で国際化する場合は、各文字列を単純に_(と)でラップします。 例。
menu.name = _("Game"); special.name = _("Game");
するとこれはfreeciv.potにおいて1つのエントリとして統合されてしまいます。
#: menu.c:123 terrain.c:321 msgid "Game" msgstr ""
そうなると、本来は別個の単語に訳し分けなければいけないのに、訳し分けることができません。 Freecivではこの問題を解決するために、訳し分け文字列(qualified translatable strings)という概念を導入しています。文字列が訳し分け可能であることを、単語の先頭に?と:で挟んだ説明タグを付けることで示します。
gameの例で言えば、訳し分け文字列にするには下記のようにします。
"?play:Game" "?animals:Game"
ただし、修飾子がユーザーに表示されないようにする必要があるので 修飾子を正しく削除するマクロ Q_()を使用します。 したがって、サンプルコードは次のようになります。
menu.name = Q_("?play:Game"); special.name = Q_("?animals:Game");
Q_()マクロは翻訳可能な文字列のマーカーとして機能しますが、二つの文字列はxgettextから見れば異なるため freeciv.pot ファイルに2つの異なるエントリとして表示されます。
#: menu.c:123 msgid "?play:Game" msgstr "" #: terrain.c:321 msgid "?animals:Game" msgstr ""
Q_()マクロは、関数を呼び出せるコンテキストでのみ使用するべきという点で、 _()マクロに似ていることに注意してください。
msgstrについては地域化を参照してください。
標準化の詳細[]
一貫性と翻訳のしやすさのためには、すべての言語が左から右に読んだり、名詞の前後に形容詞を使用したり、同じタイポグラフィを使用したりするわけではないことを意識しておいてください。
文と文の断片[]
可能な場合は常に一文を塊にしてください。 これにより翻訳者は自然に情報を再配置して翻訳できます。 文には、(C形式の)数値データまたは外部データを含めるべきではありません。 これらは、列テーブル形式でより適切に表されます。 複数の文を含む文字列の場合、ピリオドの後に単一のスペースを使用します。
_("This is some text. It has more than one sentence in it. Take " "care to be consistent.")
これは翻訳された文字列にも当てはまります。ただし、翻訳先の言語で他の規則がより適切である場合を除きます(たとえば、日本語は単語間にスペースがありません)。
メニュー項目や表の見出しなど文の断片が使用される場合、翻訳者が形式と語順を変更できるように、可能な限り多くのタイポグラフィを含める必要があります。 例。
_("Workers") _(": workers") _("Workers: Content") _("Content workers")
あいまいな単語や文の断片は修飾する必要があります。ただし、複数のコロンは翻訳者を混乱させるため使用しないでください。
Q_("?status:Workers: Content")
外部変数データが存在しない限り、文の断片は(C形式の)部分文字列で構成されるべきではありません。
"%s: %s", _("Workers"), Q_("?status:Content") _("Workers: %s"), Q_("?status:Content") _("%s workers"), Q_("?status:content")
同様に、翻訳されないログとルールセットの評価のみに使用される場合を除いて、文のフラグメントをパラメーターとして関数に渡さないで下さい。
void what_workers(void *pcity, const char *workers) ... ..., Q_(workers) ... what_workers(pcity, "?status:content");
列テーブル形式[]
一貫性のある表示のために、数値情報は左側にあり、その後にテキストによる説明が続く必要があります。 位置合わせのため、C形式は列データの最大幅を指定する必要があります。 列の形式は、可能な限りタイポグラフィを使用しないようにする必要があります。 例。
"%3d %-10s", workers[content], Q_("?label status:content")
上記の例では、訳し分け修飾子は列ラベルとしての使用されることを示しています。 一部の言語では、このような記述的な組み合わせに対して異なる形式が必要です。 また、値と次のラベルの間に「:」または「=」はありません。代わりに単一のスペースが使用されます。翻訳者が適切に翻訳できるようにするため、そのようなタイポグラフィが必要なときは常に翻訳されたラベルに含まれるべきです。
"%3d%s", workers[content], Q_("?label status:= content;")
しかし経験によれば、このような形式は翻訳者をしばしば悩ませます。 別の表示方法を検討してください」
翻訳されたラベルにはCフォーマットを含めるべきではありません。ただし、埋め込みタイポグラフィには例外があります。括弧、パーセンテージ記号、間隔、および単語の表示順序は、翻訳によって異なります。
_("%+4d (%+d%%) Bonus from %s\n"), ...
ただし、フィールドの置換の順序は不変です。このようなフォームは注意深く評価されるべきです。
外部データ[]
チーム名やルールセットデータなど外部データ文字列が含まれている場合は、外部文字列を必要に応じて翻訳するために、別種のタイポグラフィで囲む必要があります。
ログ[]
doc/HACKINGのルールを拡張したものです。 ほとんどのfreecivログには、翻訳された文字列が含まれていません。 これらのメッセージは多くの場合開発者に表示され、参照のために元の(翻訳されていない)形式でなければならないファイルおよびルールセットの参照が含まれています。 メッセージの内容はしばしば変更されるため翻訳の更新は追いつきません。
log_fatal()[]
log_fatal()は翻訳すべきでありません。 代わりにユーザーへのメッセージとしてバグレポートの提出方法を翻訳して表示する必要があります。ただし、ゲームのインストールに関連するメッセージは翻訳される場合があります。
log_fatal(_("Server: bad address: [%s:%d]."),
ruleset_error(LOG_FATAL, /* TRANS: message about an installation error. */ _("Could not find a readable \"%s.%s\" ruleset file."), name, extension);
log_error()[]
log_error()は翻訳すべきでありません。代わりに、ユーザーへのメッセージは、取るべき行動を説明の翻訳でなければなりません。 クライアントを実行しているユーザーがログメッセージを表示する可能性はほとんどありませんが、ゲームのインストールに関連するエラーメッセージは翻訳される場合があります。
/* TRANS: <FREECIV_PATH> configuration error */ log_error(_("\"%s\" is set but empty; using default \"%s\" " "data directories instead."), FREECIV_PATH, DEFAULT_DATA_PATH);
log_error(_("Metaserver: bad address: <%s %d>."), metaname, metaport);
log_normal()[]
log_normal() は翻訳すべきでありませんが、それらの頻度とユーザーへの説明の期待によります。 ただし、ユーザーへの個別の翻訳メッセージには、実行するアクションが記載されている必要があります。
log_normal(_("Loading rulesets.")); log_normal(_("Proceeding with sound support disabled."));
log_test()[]
log_test()メッセージは翻訳しないものとします。これらは通常(log_normal()と同じレベルで)ユーザーに表示されますが、目的はデバッグで、通常はデバッグコマンドの結果として表示されます。
log_test(" ** FOOD STARVATION **");
デバッグコマンドへの即時応答はすべて変換する必要があることに注意してください。
cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("No city at this coordinate."));
log_verbose()[]
log_verbose() メッセージは翻訳しないものとします。通常はユーザーに表示されません。
log_debug()[]
log_debug() は翻訳すべきでありません。通常はユーザーに表示されません。 ただし、これらのメッセージには、翻訳やその他の詳細をデバッグするために必要な翻訳情報が含まれている場合があります。
よくある質問[]
gettext()を直接呼び出すべきですか?[]
いいえ。構成時にNLS(Native Language Support)が無効になっている場合、_()とQ_()マクロはgettext()を呼び出しません。
"Freeciv"という単語を翻訳すべきですか?[]
いいえ。そのままにしてください。(ちなみに"Freeciv"の"c"は小文字です。)
関連[]
(日本語字幕で見てください)