国際化の事例
概要
wxWidgetsでgettextを使ってメッセージを国際化してみました。プラットフォームは VisualStudio Express 2012 (win7) です。
gettextはプログラムのメッセージを国際化するための仕組みです。ここで言うメッセージの国際化とは、ダイアログボタンの「Ok」や「Cancel」を、日本語実行環境で「はい」、「取り消し」などと表示することです。原理は、英語文字列をキーとした対訳辞書から日本語を取り出して表示するというものです。
国際化を考えたアプリではメッセージを英語で作成し、日本語はコード上に埋め込まないようにします(コメントは日本語でOk)。
サンプルソース(VisualStudioExpress2012) @gettext.zip
環境
抽出と対訳の編集
C++ソースコードからxgettext.exeを使ってキーリストを抽出し、messages.poを生成します。これに対訳を付加して、msgfmt.exeを使って、アプリ実行用のバイナリファイルmessages.moを作成します。
$ xgettext.exe --keyword='_' ../../MyApp.cpp ../../rcMyFrame.cpp (編集作業) $ msgfmt.exe messages.po
フォルダ構成と辞書のロード(初期化)の関係
詳細はソースコード MyApp.cpp のOnInit()を参照して下さい。
辞書引きの過程
ソースコードに埋め込まれたマクロ「_("msgid")」は、ビルド時にwxGetTranslation("msgid")関数として展開されます。関数内では、初期化時にロードされた辞書(messages.mo)から"msgid"を検索して、対訳の"msgstr"を取り出します。
サンプル開発のトピック
アプリケーション概観
サンプルはボタンが一つだけあるようなシンプルなフレームだけのアプリです。タイトルとボタンラベルが「_()」マクロでマークされています。ちなみにボタンを押しても何も起きません。
国際化をしない状態では、タイトルとボタンラベルの文字列は英語文字列になります。
GUI生成
GUIは、wxFormBuilderで作成しました。ボタンラベルの「MyButton」というのはwxFormBuilderが既定で自動で付けるラベルそのままです。
Code Generate(F8)ボタンでC++用のコードが生成されます。その際に注意すべきは、プロジェクトのプロパティで「internationalize」にチェックを入れておくことです。
これにより、生成されたコードで「_()」マクロが使用されます。自動で生成されたコード部分を見ると、ボタンラベルが「_("MyButton")」になっています(rcMyFrame.cpp)。
rcMyFrame::rcMyFrame( wxWindow* parent, wxWindowID id, const wxString& title , const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) { this->SetSizeHints( wxDefaultSize, wxDefaultSize ); wxBoxSizer* bSizer1; bSizer1 = new wxBoxSizer( wxVERTICAL ); m_buttonMyButton = new wxButton( this,wxID_ANY , _("MyButton") // 翻訳対象としてマーク , wxDefaultPosition, wxDefaultSize, 0 ); bSizer1->Add( m_buttonMyButton, 0, wxALL, 5 ); this->SetSizer( bSizer1 ); this->Layout(); this->Centre( wxBOTH ); }
アプリ初期化(辞書のロード)
さて、国際化機能を使うためには言語環境に応じた辞書をアプリにロードしなければなりません。これは、アプリの初期化部分でおこないます(MyApp.cpp)。
bool MyApp::OnInit() { if ( !wxApp::OnInit() ) return false; wxLocale::AddCatalogLookupPathPrefix("catalogs"); //辞書のあるフォルダ名(ドメイン) wxLocale locale; locale.Init( wxLANGUAGE_JAPANESE, wxLOCALE_DONT_LOAD_DEFAULT ); // 日本語環境の指定 locale.AddCatalog("messages"); // 辞書の名前 locale.AddCatalog("wxstd"); // wxWidgets標準の辞書 // and create a top level window _MyFrame = new MyFrame( _("My Application") /*GetAppName()*/ ); // 翻訳対象としてマーク(手作業) _MyFrame->SetIcon( wxIcon("APPLICATION_ICO") ); // start process ... return _MyFrame->Show(true); }
wxLocale 関連の関数でドメイン名、辞書名、言語を指定します。ここでは、原理的な動作を明らかにする目的で、言語を陽に指定していますが、システム(OS)の既定言語を取得して設定すれば、環境に応じて自動的に言語が設定されるはずです。
タイトルバーに表示されるアプリケーション名は、通常ですとGetAppName()関数などで取得しますが、今回は「_("My Application")」で与えました。つまり、表示アプリ名も翻訳の対象にします。
辞書の作成
つぎに、ソースコードからキー「_()」を抽出します。抽出にはxgettext.exeを使います。作業はコンソール(cygwin)上で行います。
$ cd catalogs/ja $ xgettext.exe --keyword='_' ../../MyApp.cpp ../../rcMyFrame.cpp $ ls messages.po
抽出されたキーリストは messages.po というファイルに書き込まれます。「messages」というのは、xgettext.exe の既定の辞書名です。例ではドメイン名(catalogs)のフォルダと、その下に言語名(ja)のフォルダを作成し、そのフォルダで実行しました。
開発、実行環境のフォルダ構成
この段階でのフォルダ構成を見ておきます。
. ├── MyApp.h エントリーポイント定義ヘッダ ├── MyApp.cpp エントリーポイント定義コード ├── rcMyFrame.h wxFormBuilderが生成したGUI定義ヘッダ ├── rcMyFrame.cpp wxFormBuilderが生成したGUI定義コード ├── MyFrame.h GUI定義ヘッダ ├── MyFrame.cpp GUI定義コード ├── MyApp.fbp wxFormBuilderファイル ├── MyApp.rc リソースファイル ├── application.ico アプリケーションアイコンファイル ├── stdwx.h プリコンパイルヘッダ ├── stdwx.cpp プリコンパイルコード ├── catalogs ドメイン │ └── ja 言語 │ └── messages.po 翻訳テキスト形式(xgettextが生成) ├── MyApp.sln VisualStudioソリューションファイル ├── MyApp.vcxproj VisualStudioプロジェクトファイル ├── wx3.0.0ud-vs2012.props wxWidgets Unicode Debug プロパティファイル └── wx3.0.0u-vs2012.props wxWidgets Unicode Release プロパティファイル
辞書(po)の編集
messages.poを編集します。初期状態では次のようになっています。
# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-07-13 16:43+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../../MyApp.cpp:40 msgid "My Application" msgstr "" #: ../../rcMyFrame.cpp:21 msgid "MyButton" msgstr ""
辞書として最低限必要なものにするとつぎのようになります。
msgid "" msgstr "" "Content-Type: text/plain; charset=CHARSET\n" msgid "My Application" msgstr "" msgid "MyButton" msgstr ""
大分すっきりしました。msgidが辞書のキーで、msgstrがその値になります。各々の対訳をmsgstrに書きます。また、charsetはこのmessages.poのエンコードを指定します。埋め草のCHARSETを「utf-8」に書き換えます。
書き換えるとつぎのようになります。
msgid "" msgstr "" "Content-Type: text/plain; charset=utf-8\n" msgid "My Application" msgstr "アプリケーション-ja" msgid "MyButton" msgstr "押せるだけ"
VisualStudioで編集作業をした場合は、必ずエンコードを「utf-8(シグネチャなし)」に指定して保存してください。
辞書のバイナリ形式(.mo)
テキスト形式の.poをアプリで使用するためにmsgfmt.exeを使ってバイナリ化します。コンソール上で作業します。
$ msgfmt.exe messages.po $ ls messages.mo messages.po
ここで、エラーが出力された場合は.poのエンコードを確認してください。エラーがある限り、gettextは動きません。
言語の指定方法
アプリ上で言語の指定を「wxLANGUAGE_JAPANESE」で行い、フォルダ構成上では「ja」としました。日本語の場合はこれでよいのですが、英語圏では「wxLANGUAGE_ENGLISH_UK、wxLANGUAGE_ENGLISH_US、wxLANGUAGE_ENGLISH_AUSTRALIA...」などがあり、フォルダ名としては、「en_UK、en_US、en_AU...」と細かく指定できます。日本語でこれに相当するのが「ja_JP」です。これを正準形(canonical)と呼びます。日本語の場合はこれひとつです。また、エンコードを指定できます。これは、Windowsではコードページ932 になります。
日本語の場合は「ja」フォルダを使用します。「ja_JP」、「ja_JP.WINDOWS-932」のように詳細に指定した言語名のフォルダが優先的に利用されるようです。例えば、つぎのようなフォルダ構成の場合は、「ja_JP.WINDOWS-932」が使用されます。
├── catalogs ドメイン │ ├── ja 言語 │ │ ├── messages.mo 翻訳バイナリ形式 │ │ └── messages.po 翻訳テキスト形式(xgettextが生成) │ ├── ja_JP 言語(正準形) │ │ ├── messages.mo 翻訳バイナリ形式 │ │ └── messages.po 翻訳テキスト形式(xgettextが生成) │ └── ja_JP.WINDOWS-932 言語(正準形+エンコーディング) │ ├── messages.mo 翻訳バイナリ形式 │ └── messages.po 翻訳テキスト形式(xgettextが生成)