2008/06/23(月) [n年前の日記]
#1 [python] wxPythonでメッセージ文字列を国際化する方法を勉強
wxPython なのか Python なのか gettext なのか wx.Locale なのか、よーわからんけど。
英語圏用に書かれたアプリを国際化することを、i18nとか、L10Nとか言うらしい。数字の18とか10とかは何を表してるのか、というと、省略された文字数なのだとか。
さておき、かなりジタバタしつつ、何とかなってくれそうな気配が。忘れそうなので手順をメモ。
英語圏用に書かれたアプリを国際化することを、i18nとか、L10Nとか言うらしい。数字の18とか10とかは何を表してるのか、というと、省略された文字数なのだとか。
i18nはinternationalizationを省略した記述法。ジャーゴン。
一般的な学習者用の辞書などにはあまり掲載されていませんが、L10N というのは Localization の慣例的な略記法です。これは地域化、具体的には英語のソフトを日本語化することなどを意味する言葉です。Localization は長い単語であるため、L と N の間に 10 文字あるという意味で L10N と表記されます。l10n とするとアルファベットの l と数字の 1 とが区別しにくいため L10n と大文字にします。ローカライズセンターでは n も大文字で表記していますが、これは単にデザイン上の理由によるものです。
さておき、かなりジタバタしつつ、何とかなってくれそうな気配が。忘れそうなので手順をメモ。
◎ 実験時の環境。 :
- Windows XP Home SP3
- Python 2.5.2
- wxPython 2.8.7.1
- XRCed version 0.2.0-beta
◎ 本体スクリプト中の文字列の記述を修正。 :
本体スクリプト中で、日本語化したい文字列を指定しておく。例としては下のような感じ。
print "test %s %s" % (str0, str1) ↓ print _("test %s %s") % (str0, str1)
◎ .xrcから文字列を抽出できるようにしておく。 :
XRCed で .xrc を作成・保存して、GUIのレイアウトを決めていくわけだけど。その際、File → Genarate Python を選択して、resource_xrc.py を出力する。「Genarate gettext strings」にチェックを入れた状態で、「genarate module」ボタンを押せば、出力された .py ファイル中に、
.xrc から直接、文字列を抽出することもできるらしい。pywxrc を使う。
_("&File") _("&Open") _("E&xit")といった形で、gettext用(?)の文字列が列挙された部分ができる。
.xrc から直接、文字列を抽出することもできるらしい。pywxrc を使う。
pywxrc -g resource.xrc > resource.txt
◎ メッセージカタログファイル(.mo)を格納するディレクトリを作成。 :
locale/ja/LC_MESSAGES といった感じのディレクトリを、本体スクリプトと同階層に作成する。
本来は、というか *NIX文化圏では、/usr/share/locale/ja/LC_MESSAGES/ といった感じで作って、その中にいろいろなアプリの .mo を入れるらしいのだけど。今回実験したのはWindows環境下なので、そのようなディレクトリをルートに作ってええのやろかと。なので、該当アプリの入ってるディレクトリに locale/(以下略) を作っておく。
mkdir locale\ja\LC_MESSAGESja ってのが、「ここに日本語のソレが入ってるよ」ということらしく。英語の場合は en とかになるのかしら。たぶん。
本来は、というか *NIX文化圏では、/usr/share/locale/ja/LC_MESSAGES/ といった感じで作って、その中にいろいろなアプリの .mo を入れるらしいのだけど。今回実験したのはWindows環境下なので、そのようなディレクトリをルートに作ってええのやろかと。なので、該当アプリの入ってるディレクトリに locale/(以下略) を作っておく。
◎ メッセージカタログファイル(.pot)を作成。 :
例えば、
.pot じゃなくて .po で出力しちゃってもいいらしいのだけど。
- gettext_test.py (本体スクリプト)
- resource_xrc.py (XRCedで出力したスクリプト)
[Pythonインストールフォルダ]\Tools\i18n\pygettext.py -o gettext_test.pot gettext_test.py [Pythonインストールフォルダ]\Tools\i18n\pygettext.py -o resource_xrc.pot resource_xrc.pyを実行して、
- gettext_test.pot
- resource_xrc.pot
[Pythonインストールフォルダ]\Tools\i18n\pygettext.py -o 出力ファイル名(.pot) 入力ファイル名(.py)ということらしい。
.pot じゃなくて .po で出力しちゃってもいいらしいのだけど。
- .pot は pygettext.py から出力されたもの。上書きしてOK。
- .po はエディタで開いて訳を追加したもの。上書きしちゃダメ。手修正すること。
◎ メッセージカタログファイル(.po)を作成。 :
gettext_test.pot と resource_xrc.pot をエディタで開いて、手作業で中身を混ぜて、ja.po を作成する。
ヘッダー部分は、最低でも下の修正が必要らしい? よくわからないけど。
全然関係ないけど。「こんにちは、世界!」って、「コイン いっこ いれる」に似てる気がする。
ヘッダー部分は、最低でも下の修正が必要らしい? よくわからないけど。
"Language-Team: LANGUAGE <LL@li.org>\n" .... "Content-Type: text/plain; charset=CHARSET\n" ↓ "Language-Team: japanese\n" ... "Content-Type: text/plain; charset=utf-8\n"また、大体は、下のような感じで訳を追加していく。らしい。
#: gettext_test.py:53 msgid "Hello world!" msgstr "" ↓ #: gettext_test.py:53 msgid "Hello world!" msgstr "こんにちは、世界!"
全然関係ないけど。「こんにちは、世界!」って、「コイン いっこ いれる」に似てる気がする。
◎ .po から .mo を作成。 :
例えば、本体スクリプト名が gettext_test.py だったら、gettext_test.mo というファイルを作成。(そういう命名をすれば後々管理が楽になる、ということなんだろう。たぶん…)
[Pythonインストールフォルダ]\Tools\i18n\msgfmt.py -o gettext_test.mo ja.po
[Pythonインストールフォルダ]\Tools\i18n\msgfmt.py -o 出力ファイル名(.mo) 入力ファイル名(.po)
◎ .mo を所定の場所に移動する。 :
locale/ja/LC_MESSAGES以下に、.mo をコピーする。
前述の例だと、
前述の例だと、
locale\ja\LC_MESSAGES\gettext_test.moとなる。
◎ メッセージカタログファイル(.mo)を参照して使うようにソースを修正。 :
本体スクリプト中に、以下のような記述を入れる。
本体スクリプト内でのメッセージ国際化を gettext で行う。
xrc部分でのメッセージ国際化を wx.Locale で行う。
途中で出てきた get_main_dir() は、 _py2exeモジュールについて - 問題点 を参考に。要するに、本体スクリプトが入ってるディレクトリを取得するためのメソッド。…なんとなくだけど、この場合は、カレントディレクトリを取得する os.getcwd() でもいいのかな?という気がしないでもないけど。
当初、GUIのレイアウトを規定してる、resource_xrc.py も書き換えないといけないのかと思っていたのだけど。どうやらその必要はないようで。あくまで、本体スクリプトのみに関連記述をしておけばOK、のように見える。
本体スクリプト内でのメッセージ国際化を gettext で行う。
# gettext(メッセージの国際化)関連 import gettext # .mo を格納してるディレクトリパス # ./locale/ のパスを求めている。 localedir = os.path.normpath(os.path.join(get_main_dir(), "locale")) # gettext を使うことを指定 gettext.install('gettext_test', localedir, unicode=True)
xrc部分でのメッセージ国際化を wx.Locale で行う。
# カタログファイルとやらの場所を指定 wx.Locale.AddCatalogLookupPathPrefix(localedir) self.i18n = wx.Locale(wx.LANGUAGE_DEFAULT, wx.LOCALE_LOAD_DEFAULT) self.i18n.AddCatalog('gettext_test')
途中で出てきた get_main_dir() は、 _py2exeモジュールについて - 問題点 を参考に。要するに、本体スクリプトが入ってるディレクトリを取得するためのメソッド。…なんとなくだけど、この場合は、カレントディレクトリを取得する os.getcwd() でもいいのかな?という気がしないでもないけど。
当初、GUIのレイアウトを規定してる、resource_xrc.py も書き換えないといけないのかと思っていたのだけど。どうやらその必要はないようで。あくまで、本体スクリプトのみに関連記述をしておけばOK、のように見える。
◎ 参考にしたページ。 :
_wxPythonの国際化について - Plone en sourdine
_2006-01-18 - himainuの日記
_A-Liaison BLOG 【Python】gettextモジュールを用いて国際的な男になってみた
_XRCAndI18N - wxPyWiki
_wxPythonヘルパーモジュールの作成
_py2exeモジュールについて - 問題点
_Cross Platform Desktop Applications with Python
_2006-01-18 - himainuの日記
_A-Liaison BLOG 【Python】gettextモジュールを用いて国際的な男になってみた
_XRCAndI18N - wxPyWiki
_wxPythonヘルパーモジュールの作成
_py2exeモジュールについて - 問題点
_Cross Platform Desktop Applications with Python
◎ 実験に使ったスクリプトのソースもメモ。 :
忘れそうなのでコレもメモ。
_wxPythonヘルパーモジュールの作成 の wx_utils.py も使わせてもらっていたり。
本体スクリプト gettext_test.py -----
GUIのレイアウト定義 xml resource.xrc -----
ja.po -----
_wxPythonヘルパーモジュールの作成 の wx_utils.py も使わせてもらっていたり。
本体スクリプト gettext_test.py -----
#!/usr/bin/env python # -*- coding: utf-8 -*- # # gettext_test.py # gettext のテスト import sys import os import imp # デフォルトのエンコーディングをUTF8にしてる? if hasattr(sys,"setdefaultencoding"): sys.setdefaultencoding("utf-8") def main_is_frozen(): u"""exe化して動いてる状態かどうかを判定してる?のかな?.""" return (hasattr(sys, "frozen") or # new py2exe hasattr(sys, "importers") # old py2exe or imp.is_frozen("__main__")) # tools/freeze def get_main_dir(): u"""スクリプトの保存場所(PATH)を得る.""" if main_is_frozen(): return os.path.abspath(os.path.dirname(sys.executable)) return os.path.abspath(os.path.dirname(sys.argv[0])) import wx import wx_utils from wx.xrc import XRCID, XRCCTRL # XRCedで出力した resource_xrc.py をインポート import resource_xrc # 別途作成したアイコン画像部分 main_icon.py をインポート(削除してもOK) import main_icon # gettext(メッセージの国際化)関連 import gettext # .mo を格納してるディレクトリパス localedir = os.path.normpath(os.path.join(get_main_dir(), "locale")) # gettext を使うことを指定 gettext.install('gettext_test', localedir, unicode=True) class MainFrame(resource_xrc.xrcMainFrame): u"""MainFrame class deffinition.""" # イベントを割り当てるための前準備(たぶん) binder = wx_utils.bind_manager() def __init__(self, parent=None): u"""メインフレームクラスの初期化""" # メッセージ国際化 self.initI18n() resource_xrc.xrcMainFrame.__init__(self, parent) # アイコンを指定(アイコン指定が要らなかったら削除) self.SetIcon(main_icon.getIcon()) # イベント割り当て(たぶん) self.binder.bindall(self) def initI18n(self): u"""XRCed で作成した resource.xrc 側のメッセージ国際化.""" # カタログファイルとやらの場所を指定 wx.Locale.AddCatalogLookupPathPrefix(localedir) self.i18n = wx.Locale(wx.LANGUAGE_DEFAULT, wx.LOCALE_LOAD_DEFAULT) self.i18n.AddCatalog('gettext_test') # イベント割り当て @binder(wx.EVT_ACTIVATE) def OnActivate(self, event): u"""ウインドウがアクティブになった際に呼ばれるメソッド.""" print _('Activate!') # アプリケーション起動 if __name__=='__main__': app = wx.App(False) frame = MainFrame() app.SetTopWindow(frame) frame.Show() app.MainLoop()
GUIのレイアウト定義 xml resource.xrc -----
<?xml version="1.0" ?> <resource> <object class="wxFrame" name="MainFrame"> <object class="wxBoxSizer"> <orient>wxVERTICAL</orient> <object class="sizeritem"> <object class="wxPanel"> <object class="wxBoxSizer"> <orient>wxVERTICAL</orient> <object class="sizeritem"> <object class="wxPanel"> <object class="wxStaticText"> <pos>60, 30</pos> <label>hello wrold</label> </object> <size>240, 160</size> </object> <option>1</option> <flag>wxALL|wxEXPAND</flag> <border>0</border> <minsize>240, 160</minsize> </object> <object class="sizeritem"> <object class="wxButton"> <label>OK</label> </object> <flag>wxALIGN_RIGHT</flag> </object> </object> </object> </object> </object> <title>gettext_test</title> </object> </resource>
ja.po -----
# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ORGANIZATION # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # msgid "" msgstr "" "Project-Id-Version: gettext_test 0.0.1\n" "POT-Creation-Date: 2008-06-24 00:04+東京 (標準時)\n" "PO-Revision-Date: 2008-06-24 02:25+0900\n" "Last-Translator: mieki256 <mieki256 atmark 104.net>\n" "Language-Team: japanese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: pygettext.py 1.5\n" "X-Poedit-Language: Japanese\n" "X-Poedit-Country: JAPAN\n" "X-Poedit-SourceCharset: utf-8\n" #: gettext_test.py:53 msgid "Activate!" msgstr "アクティブになりました。" #: resource_xrc.py:103 msgid "hello wrold" msgstr "世界よ、こんにちは" #: resource_xrc.py:104 msgid "OK" msgstr "おけ" #: resource_xrc.py:105 msgid "gettext&test" msgstr "gettextのテスト"
◎ スクリーンショット。 :
◎ アイコン画像を.pyにする手順。 :
念のためにメモ。
img2py -i main_icon.ico main_icon.pymain_icon.ico を、main_icon.py に変換してる。らしい。
[ ツッコむ ]
以上です。