如何在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程序里支持多国语言的主要内容,如果未能解决你的问题,请参考以下文章

.net网站如何实现多国语言切换?

使用VB6资源文件开发多国语言应用攻略

XAF应用开发教程 汉化与多国语言支持

求Enigma Virtual Box(单文件制作工具) V9.20 多国语言官方版网盘资源

iOS应用内切换多国语言

详解升讯威在线客服系统前端多国语言实现技术:原生支持葡文印尼文土耳其文俄文