如何在wxPython程序里支持多国语言
Posted 悠然红茶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在wxPython程序里支持多国语言相关的知识,希望对你有一定的参考价值。
最近笔者在用wxPython写一些小工具,其中不免涉及到对多国语言的支持,于是翻查了一些资料,解决了相关的技术点。今天特别整理出一份文档,总结一二。
基本上来说,要支持多国语言,我们需要以下几个步骤:
1)将代码中需要支持多国语言的字符串部分,用_()形式来表达;
2)利用pygettext工具,将相关字符串ID抽取出来,并汇总成.pot文件;
3)利用类似Poedit这样的工具,打开.pot文件,并生成不同语言对应的.po、.mo文件。
4)将.po、.mo文件拷贝到特定子目录。
5)在主代码模块中,执行gettext.translation(),得到一个gettext.GNUTranslations对象,并调用其install()函数。
下面我们会用一个简单的例子来说明问题。在例子中我们有两个.py脚本,一个放到工程根目录,另一个放到工程子目录,这主要是为了便于大家搞明白,该如何应对在多个文件里切换语言的情况。例子大体的初始目录结构如下:
1. 在代码中调用_()
Python语言实现多国语言机制时,要求使用一个特殊的_()函数来处理需变动的字符串。比如在我们的main_frame.py代码里,在创建一个按钮控件时,为了使按钮上的文字可以调整为不同国家的文字,就需要这样设定label:
button = wx.Button(self.panel, label=_("STR_ID_CLICK_ME"), pos=(50,50))
为了明确表示传入的是一个有id意义的字符串,我专门用“STR_ID”来打头。同样,在about_dlg.py里,我们也会使用_(),比如:
txt = wx.StaticText(panel, label=_("STR_ID_HAVE_A_TRY"), pos = (15, 15))
因为代码量比较少,所以我们把main_frame.py和about_dlg.py的代码都贴一下:
【main_frame.py】
import wx
import os
import gettext
from aid.about_dlg import *
es = gettext.translation('str_res', localedir='locale', languages=['zh_CN'])
es.install()
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(None, title="The Main Frame")
self.SetTopWindow(self.frame)
self.frame.Show()
return True
class MyFrame(wx.Frame):
def __init__(self, parent, id=wx.ID_ANY, title="",
pos=wx.DefaultPosition, size=wx.DefaultSize,
style=wx.DEFAULT_FRAME_STYLE,
name="MyFrame"):
super(MyFrame, self).__init__(parent, id, title, pos, size, style, name)
self.panel = wx.Panel(self)
self.panel.SetBackgroundColour(wx.BLACK)
button = wx.Button(self.panel, label=_("STR_ID_CLICK_ME"), pos=(50,50))
self.btn_1 = button
self.Bind(wx.EVT_BUTTON, self.OnButton, button)
button = wx.Button(self.panel, label=_("STR_ID_HELLO"), pos=(50,100))
self.btn_2 = button
self.Bind(wx.EVT_BUTTON, self.OnButton2, button)
def OnButton(self, event):
dlg = AboutDialog()
dlg.ShowModal()
dlg.Destroy()
def OnButton2(self, event):
global es
es = gettext.translation('str_res', localedir='locale', languages=['en_US'])
es.install()
self.btn_1.SetLabel(_("STR_ID_CLICK_ME"))
self.btn_2.SetLabel(_("STR_ID_HELLO"))
if __name__=="__main__":
app = MyApp(False)
app.MainLoop()
【aid/about_dlg.py】
import wx
class AboutDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, "About", size=(300, 160))
panel = wx.Panel(self, -1)
txt = wx.StaticText(panel, label=_("STR_ID_HAVE_A_TRY"), pos = (15, 15))
okButton = wx.Button(panel, wx.ID_OK, "Ok", pos = (15, 80), size=(70, 20))
okButton.SetDefault()
2. 生成.pot文件
在写好main_frame.py和about_dlg.py之后,我们就可以利用python自带的抽取工具(pygettext.py)来抽取字符串id信息了。这个工具位于:
【python安装目录】\\python3.8\\Tools\\i18n\\pygettext.py
因此我们可以在进入例子目录后,执行如下的句子:
python 【python安装目录】\\python3.8\\Tools\\i18n\\pygettext.py -d str_res main_frame.py aid\\about_dlg.py
抽取的结果会生成一个.pot文件,上面的-d str_res参数指明了文件名,也就是说会生成str_res.pot文件。可以看到,我是把两个py文件的字符串信息汇总抽取到一个文件里的。
生成的.pot文件的内容如下:
【test/str_res.pot】
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\\n"
"POT-Creation-Date: 2020-11-15 14:57+0800\\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=utf-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Generated-By: pygettext.py 1.5\\n"
#: aid\\about_dlg.py:14
msgid "STR_ID_HAVE_A_TRY"
msgstr ""
#: main_frame.py:44
msgid "STR_ID_CLICK_ME"
msgstr ""
#: main_frame.py:45
msgid "STR_ID_HELLO"
msgstr ""
3. 生成.po、.mo文件
接下来我们可以下载安装并使用PoEdit工具。选择菜单“文件”->“从POT/PO文件新建”,然后选择刚刚生成的str_res.pot文件。
此时,会弹出一个对话框,询问是要创建哪个语言的字符串资源:
比如我选择“英文(美国)”后,会显示下面界面,我们需要为每个词条填写对应的“翻译”文字。
然后另存为str_res.po文件,此时会同时自动生成str_res.mo文件。str_res.po的内容如下:
【locale/en_US/LC_MESSAGES/str_res.po】
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \\n"
"POT-Creation-Date: 2020-11-16 00:21+0800\\n"
"PO-Revision-Date: 2020-11-16 11:20+0800\\n"
"Language-Team: \\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-Generator: Poedit 2.4.2\\n"
"Last-Translator: \\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\\n"
"Language: en_US\\n"
#: aid\\about_dlg.py:14
msgid "STR_ID_HAVE_A_TRY"
msgstr "have a try"
#: main_frame.py:27 main_frame.py:44
msgid "STR_ID_CLICK_ME"
msgstr "click me"
#: main_frame.py:31 main_frame.py:45
msgid "STR_ID_HELLO"
msgstr "hello"
可以看到,基本上和str_res.pot是一致的,而且填写了内部的msgstr部分。
依照类似的步骤,我们还可以生成中文词条对应的str_res.po、str_res.mo文件。现在我们把这些文件放到下图所示的位置:
这样,基本上就可以了。现在有必要再说一下前面main_frame.py文件里的两个句子:
es = gettext.translation('str_res', localedir='locale', languages=['zh_CN'])
es.install()
其中translation()函数的第一个参数,基本上对应着最终要用的.mo文件。第二个参数则指明了本地化资源位于哪个子目录,第三个参数表示要安装哪个语言对应的字符串资源。
对于我们的demo来说,起始的资源是zh_CN,也就是说会显示中文界面,当我们执行python main_frame.py时,会弹出demo的主界面,截图如下:
点击第一个按钮后,会弹出About对话框:
而如果点击第二个按钮的话,则会立即转换成英文界面:
大家可以从代码中看到,点击第二个按钮时,我们不但重新install了en_US资源,而且还需要显式给按钮控件重新设置label,这样才能立即改变界面。
def OnButton2(self, event):
global es
es = gettext.translation('str_res', localedir='locale', languages=['en_US'])
es.install()
self.btn_1.SetLabel(_("STR_ID_CLICK_ME"))
self.btn_2.SetLabel(_("STR_ID_HELLO"))
4. 结尾
好了,有关wxPython程序的多国语言展示问题,我们就先说这么多,希望可以帮到一些新同学。本文并没有深入解析pygettext的细节,大家如有兴趣,可以自行研究。
以上是关于如何在wxPython程序里支持多国语言的主要内容,如果未能解决你的问题,请参考以下文章