mieki256's diary



2008/06/23(月) [n年前の日記]

#1 [python] wxPythonでメッセージ文字列を国際化する方法を勉強

wxPython なのか Python なのか gettext なのか wx.Locale なのか、よーわからんけど。

英語圏用に書かれたアプリを国際化することを、i18nとか、L10Nとか言うらしい。数字の18とか10とかは何を表してるのか、というと、省略された文字数なのだとか。
i18nはinternationalizationを省略した記述法。ジャーゴン。

国際化と地域化 - Wikipedia より

一般的な学習者用の辞書などにはあまり掲載されていませんが、L10N というのは Localization の慣例的な略記法です。これは地域化、具体的には英語のソフトを日本語化することなどを意味する言葉です。Localization は長い単語であるため、L と N の間に 10 文字あるという意味で L10N と表記されます。l10n とするとアルファベットの l と数字の 1 とが区別しにくいため L10n と大文字にします。ローカライズセンターでは n も大文字で表記していますが、これは単にデザイン上の理由によるものです。

L10N FAQ - ローカライズセンターについて より


さておき、かなりジタバタしつつ、何とかなってくれそうな気配が。忘れそうなので手順をメモ。

実験時の環境。 :

  • 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 ファイル中に、
_("&File")
_("&Open")
_("E&xit")
といった形で、gettext用(?)の文字列が列挙された部分ができる。

.xrc から直接、文字列を抽出することもできるらしい。pywxrc を使う。
pywxrc -g resource.xrc > resource.txt

メッセージカタログファイル(.mo)を格納するディレクトリを作成。 :

locale/ja/LC_MESSAGES といった感じのディレクトリを、本体スクリプトと同階層に作成する。
mkdir locale\ja\LC_MESSAGES
ja ってのが、「ここに日本語のソレが入ってるよ」ということらしく。英語の場合は en とかになるのかしら。たぶん。

本来は、というか *NIX文化圏では、/usr/share/locale/ja/LC_MESSAGES/ といった感じで作って、その中にいろいろなアプリの .mo を入れるらしいのだけど。今回実験したのはWindows環境下なので、そのようなディレクトリをルートに作ってええのやろかと。なので、該当アプリの入ってるディレクトリに locale/(以下略) を作っておく。

メッセージカタログファイル(.pot)を作成。 :

例えば、
  • gettext_test.py (本体スクリプト)
  • resource_xrc.py (XRCedで出力したスクリプト)
てな具合に2つのファイルがある場合は、
[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 はエディタで開いて訳を追加したもの。上書きしちゃダメ。手修正すること。
と分類したかったので、.pot で。

メッセージカタログファイル(.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 で行う。
# 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ヘルパーモジュールの作成 の 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.py
main_icon.ico を、main_icon.py に変換してる。らしい。

以上です。

過去ログ表示

Prev - 2008/06 - Next
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project