|
本サイトは移転しました。旧アドレスからのリダイレクトは2025年03月31日(月)まで有効です。
|
🛈 | ✖ |
GNU gettextによる国際化機能で英語アプリケーションを日本語対応させる方法を記述する。
我々としては逆に日本語アプリケーションを開発してから必要に応じ英語対応させたいが、文字コードに起因するトラブル予測が難しく推奨されない。
英語文字列を真の表示文字列を検索するメッセージIDと割り切れば、少しは気持ちが楽になるかもしれない。
本記事においてソースコードファイルおよびインクルードファイルは全てASCIIで記述されているものとする。コメントに日本語を混在させる場合はコンパイラオプションに適切な入力文字コードを設定すれば良いが、ここは覚悟を決めて英語に統一しよう。
GNU gettextはバイナリ形式翻訳ファイルを事前作成し、実行ファイルが実行時にこれを参照して翻訳された文字列に置き換える。以下に本サイトが標準とする翻訳ファイル作成ワークフローを示す。ターゲットの実行形式ファイル名をProject1.exeと仮定する。
ソースコードファイルが変更されたら以下の手順で翻訳ファイルを更新する。
本サイトのxgettext、msginit、msgmerge、msgfmtはMSYS2が導入したものでPOSIXサブシステムからはそのまま利用できるが、ウィンドウズ環境からは以下の環境変数を必要とする。環境変数LANGの必要性はC:\msys64\usr\share\gettext\ABOUT-NLSに記述されている。GNU gettextはmingw32/mingw64サブシステムにも存在するが本サイトは気付かずmsys2サブシステムを利用した。
煩雑なのでProject1.poのエディタ編集を除く各手順をバッチファイル化し、プロジェクト毎にカスタマイズする。なおPoeditでProject1.poを編集すれば自動的にProject1.moも作成するのでmsgfmtは使わずにすむ。
コンソールアプリケーションは英語文字列をシフトJIS日本語文字列に翻訳する。以降においてターゲットの実行形式ファイル名をConsole1.exeと仮定する。
このサンプルではソースコードファイル(*.cpp)のファイルからのみ文字列を検索するが、例えばインクルードファイルも検索に加えるならxgettextの実引数に*.hを加える。その場合プロジェクトがインクルードファイルを持たないとxgettextは失敗するが、これはMSYS2でビルドされた実行形式のグロッビング(ワイルドカードによるファイル名補完)機能制約による。
翻訳はlibintlライブラリを使う。ライブラリは翻訳ファイルの文字コードからターゲット文字コードに変換するため、-lオプションの一部で指定する文字コードはシフトJIS(SJIS)に拘る必要は無い。例えば-lオプションをja_JP.UTF-8としても問題なく翻訳できる。
統合開発環境Code::Blocksでの運用を前提とし、翻訳ファイルの配置を開発時と配布時で変える。さもなくば開発時に複数の翻訳ファイルをメンテナンスする事になる。
開発時にバッチファイルは翻訳ファイルを以下に配置する。テキスト形式翻訳ファイルの文字コードはシフトJISとして扱う。
ディレクトリ | ファイル | 内容 |
---|---|---|
[プロジェクトディレクトリ] | Console1.cbp | Code::Blocksプロジェクト |
│ | *.cpp, (*.h) | ソースコード、(インクルード) |
│ | k_xgettext.bat | xgettext実行バッチ |
│ | k_msginit.bat | msginit実行バッチ |
│ | k_msgmerge.bat | msgmerge実行バッチ |
│ | k_msgfmt.bat | msgfmt実行バッチ |
├ bin | messages.pot | 翻訳テンプレート |
│├ Debug32 | Console1.exe | 32ビット実行形式デバッグ版 |
│├ Release32 | Console1.exe | 32ビット実行形式リリース版 |
│├ Debug64 | Console1.exe | 64ビット実行形式デバッグ版 |
│├ Release64 | Console1.exe | 64ビット実行形式リリース版 |
│└ ja | ||
│ └ LC_MESSAGES | Console1.po | 日本語テキスト形式翻訳 |
│ | Console1.mo | 日本語バイナリ形式翻訳 |
└ obj | ||
├ Debug32 | *.o | 32ビットオブジェクトデバッグ版 |
├ Release32 | *.o | 32ビットオブジェクトリリース版 |
├ Debug64 | *.o | 64ビットオブジェクトデバッグ版 |
└ Release64 | *.o | 64ビットオブジェクトリリース版 |
配布時に必要なのはバイナリ形式翻訳ファイルのみで、インストーラが(あるいは手動で)以下に配置する。
ディレクトリ | ファイル | 内容 |
---|---|---|
[インストールディレクトリ] | Console1.exe | 実行形式 |
└ ja | ||
└ LC_MESSAGES | Console1.mo | 日本語バイナリ形式翻訳 |
コンソールアプリケーションは翻訳ファイルを利用する国際化機能ライブラリとしてGNU gettextのlibintlライブラリを用いる。例として動作確認(1)で作成したConsole1プロジェクトに国際化機能を加える。プロジェクトに以下のライブラリを追加する。
シフトJISのリテラル文字列はどこにも存在せず、コンパイルオプション-fexec-charset=CP923は必要ない。main.cppを以下に修正してビルドする。
翻訳ファイルの配置を開発時と配布時で変える目的でTMyAppDirクラスを追加する。
ロケールをウィンドウズAPIで設定する。これをsetlocale関数で行わない理由は後述する。
プロジェクトが上記以外のソースコード/インクルードファイルを含む場合は、全てにおいて_マクロ定義して翻訳対象となる英語文字列を_マクロで囲む。ただし翻訳ファイル設定がmain関数内で行われるため、静的ストレージ(主にグローバル変数)に置かれた文字列を翻訳することはできない。
ユーザーデフォルトロケール(LOCALE_USER_DEFAULT)はウィンドウズスタートメニューから[設定|時刻と言語|地域|日付、時刻、地域の追加設定|地域|形式|形式]で設定する。システムロケール(LOCALE_SYSTEM_DEFAULT)なら[設定|時刻と言語|地域|日付、時刻、地域の追加設定|地域|管理|システムロケールの変更]で設定する。設定したロケールが日本語(日本)であれば翻訳された日本語が表示され、それ以外なら翻訳前の英語が表示される。LANGなどの環境変数はロケールの設定に優先する。例えばフランス語の翻訳ファイルが存在するとして、ロケールが日本語(日本)としてもLANG=fr_FRとすればフランス語へ翻訳する。
翻訳文字列はターゲット文字コードに変換して出力する。変換に失敗する場合は検索文字列(翻訳前の文字列またはメッセージID)をそのまま出力する。例えばフランス語翻訳文字列はシステムロケール日本語(日本)の場合に対応文字コードへ変換できず、検索文字列をそのまま表示する。
ロケールは言語や地域を定義するパラメータセットとして定義される。本サイトはcodecvtの考察で一般的なプログラミングの観点で議論したが、本記事はlibintlライブラリによる翻訳のコマンドプロンプト出力に議論を絞る。詳細はソースコードに譲りここでは結論のみを述べる。
ドキュメント(GNU gettext utilities 4.2 Triggering gettext Operations)はsetlocale(LC_ALL,"")でC言語ロケールを設定せよとするが、これはロケールを環境変数から取得するPOSIX互換環境の話でウィンドウズ環境はコマンドプロンプト出力に失敗する。LC_CTYPEが"C"ロケール以外で失敗するためで理由はソースコードで詳説する。ウィンドウズ環境はユーザーデフォルトロケールあるいはシステムロケールを用い、本サイトは設定としてウィンドウズAPI関数SetThreadLocaleの使用を提案する。通常はデフォルトのままで十分で不要だが、ユーザー意図と異なってしまう場合が存在し明示的にコールする。
この場合でも環境変数がユーザーデフォルトロケール/システムロケールに優先し、以下にロケール取得の優先順位を示す。通常のウィンドウズ環境はこれらの環境変数を設定しない。なおsetlocale(LC_MESSAGES,NULL)の返すロケールは何の影響も与えない。
環境変数が設定されてなければユーザーデフォルトロケールかシステムロケールのどちらかで明示的に指定した方が間違いない。ユーザーデフォルトロケールとするならSetThreadLocale(LOCALE_USER_DEFAULT)をコールする。
システムロケールとするならSetThreadLocale(LOCALE_SYSTEM_DEFAULT)をコールする。
翻訳文字列出力の文字コードは以下の優先順位で取得する。OUTPUT_CHARSET環境変数による設定は不必要で、さらに不都合となる場合があり行わない方が良い。
コマンドプロンプトのデフォルトはシステムロケールの"OEM"コードページ(整数値)でCHCPコマンドは任意に変更できる。これが文字列出力と一致しない場合はソースコードで詳説した。
wxWidgetsライブラリによるウィンドウズデスクトップアプリケーションについて説明する。ウィンドウズデスクトップアプリケーションは英語文字列をUTF-8日本語文字列に翻訳してから、全てwxWidgetsライブラリのwxString文字列型に変換する。以降においてターゲットの実行形式ファイル名をDesktop1.exeと仮定する。
開発時に各バッチファイルは翻訳ファイルを以下に配置する。テキスト形式翻訳ファイルの文字コードはBOMを持たないUTF-8として扱う。例えばウィンドウズメモ帳はUTF-8を編集できるが保存時に勝手にBOMを付加してしまいmsgfmtがエラーとなる。Poeditならそのような心配は無い。
ディレクトリ | ファイル | 内容 |
---|---|---|
[プロジェクトディレクトリ] | Desktop1.cbp | Code::Blocksプロジェクト |
│ | *.cpp, *.h | ソースコード、インクルード |
│ | resource.rc | ウィンドウズリソース |
│ | k_xgettext.bat | xgettext実行バッチ |
│ | k_msginit.bat | msginit実行バッチ |
│ | k_msgmerge.bat | msgmerge実行バッチ |
│ | k_msgfmt.bat | msgfmt実行バッチ |
├ bin | messages.pot | 翻訳テンプレート |
│├ Debug32 | Desktop1.exe | 32ビット実行形式デバッグ版 |
│├ Release32 | Desktop1.exe | 32ビット実行形式リリース版 |
│├ Debug64 | Desktop1.exe | 64ビット実行形式デバッグ版 |
│├ Release64 | Desktop1.exe | 64ビット実行形式リリース版 |
│└ ja | Desktop1.po | 日本語テキスト形式翻訳 |
│ | Desktop1.mo | 日本語バイナリ形式翻訳 |
├ obj | ||
│├ Debug32 | *.o | 32ビットオブジェクトデバッグ版 |
│├ Release32 | *.o | 32ビットオブジェクトリリース版 |
│├ Debug64 | *.o | 64ビットオブジェクトデバッグ版 |
│└ Release64 | *.o | 64ビットオブジェクトリリース版 |
└ wxsmith | *.wxs | wxSmithリソース |
配布時はインストーラが以下に配置する。
ディレクトリ | ファイル | 内容 |
---|---|---|
[インストールディレクトリ] | Desktop1.exe | 実行形式 |
└ ja | Desktop1.mo | 日本語バイナリ形式翻訳 |
ウィンドウズデスクトップアプリケーションは翻訳ファイルを利用する国際化機能の実装にwxWidgetsライブラリのwxLocaleクラスを利用する。例として動作確認(1)で作成したDesktop1プロジェクトに国際化機能を加える。Desktop1Appクラス(Desktop1App.hとDeskotp1App.cpp)を以下に修正してビルドする。
プロジェクトが上記以外のソースコード/インクルードファイルを含む場合は、全てにおいて翻訳対象となる英語文字列を_マクロで囲む。ただし翻訳ファイル設定がDesktop1App::OnInitメンバ関数内で行われるため、静的ストレージ(主にグローバル変数)に置かれた文字列を翻訳することはできない。Code::Blocksに付属するwxSmithで入力された文字列は自動的に_マクロで囲まれてソースコードへ挿入される。コンソールアプリケーションと同様に翻訳ファイル配置を開発時と配布時で変えるが、wxStandardPaths::IgnoreAppSubDirメンバ関数があらかじめ対応し加える修正は無い。
ウィンドウズスタートメニューから[設定|時刻と言語|言語|Windowsの表示言語]が日本語であれば翻訳された日本語が表示され、それ以外であれば翻訳前の英語が表示される。