James Bryant

【转】python下wxpython程序国际化的实践(中文英文切换)

0
阅读(1418)

一、什么是python的国际化(I18N)

有关I18N,百度上解释一大堆,个人比较喜欢这个说法。

i18n是 Internationalization 这个英文的简写,因为Internationalization这个单词去掉头尾的i和n刚好还剩下18个字符,意思是国际化。

再通俗讲就是程序的多语言:程序提供多语言功能,用户选择中文,则切换到中文界面,选择英文,则切换到英文界面,甚至是俄文、西班牙文、繁体等等。

具体到本文的python下的wxpython国际化,是指在python开发环境下,实现windows程序(基于wxpython开发)多语言功能。

很简单也很普世的需求吧,但在实现的过程中间其实遇到了很多困难,主要原因还是两个:1、百度资料太少,2、英文水平又一般。

经过几天的研究,各种google,结合wxpython下的Editra示例程序(在C:\Python27\Lib\site-packages\wx-3.0-msw\wx\tools\Editra目录下),最后总结出来一个如下的简化用法。

特别说明:本方案适合小白用户,只需要实现,不需要了解国际化的具体实现方法。

二、版本说明

Python    : 2.7.11rc1 (v2.7.11rc1:82dd9545bd93, Nov 21 2015, 23:25:27) [MSC v.1500 64 bit (AMD64)]

pyOpenSSL : 16.0.0 (OpenSSL 1.0.2g  1 Mar 2016)

Platform  : Windows-10-10.0.10586

wx.VERSION_STRING:3.0.2.0

三、wxpython程序代码说明

程序如下:上面是英文界面,下面是中文界面。

1、主程序 eUpdateMainFrame.py

程序很简单,全部是使用wx的配套界面工具wxFormBuilder自动生成,红色字体部分就是用来实现多语言功能的代码。

a.修改__builtin__.__dict__,引入_()函数到系统内建模块中,使_()等同于wx.GetTranslation()。

b.调用wx.locale(wx.LANGUAGE_ENGLISH),声明使用英语界面,若是要改成简体中文,则是wx.LANGUAGE_CHINESE_SIMPLIFIED

c.调用wx.locale().AddCatalogLookupPathPrefix('local'),声明语言包放在程序目录下local子目录下。

d.调用wx.locale().AddCatalog('autoupdate') 声明语言文件名字autoupdate,注意这个函数一定得是返回True才是调用成功。

复制代码

"""Subclass of MainFrame, which is generated by wxFormBuilder."""

import wx
import eupdate

#把_()变成系统方法
import __builtin__
__builtin__.__dict__['_'] = wx.GetTranslation

# Implementing MainFrame    
class eUpdateMainFrame( eupdate.MainFrame ):
    def __init__( self, parent ):
        locale = None
        locale = wx.Locale(wx.LANGUAGE_ENGLISH)
        if locale.IsOk():
            locale.AddCatalogLookupPathPrefix('locale')
            ibRet = locale.AddCatalog('autoupdate')    
            
        eupdate.MainFrame.__init__( self, parent )
    

# init the programe
#实例化APP,因为wxformbuilder只提供界面布局,所以需要我们自己对代码进行构架
app = wx.App() 
#frame的实例
frame = eUpdateMainFrame(None) 
frame.Show();
#wxpython的启动函数
app.MainLoop() 

复制代码

2、窗口布局代码 eupdate.py(用wxFormBuilder画的。)

a.注意所有需要自动翻译的文本都需要用前文定义的_()函数扩起来。

譬如:title=_(u"自动升级"), 本来写法是:title=u"自动升级"。

复制代码

# -*- coding: utf-8 -*- 

###########################################################################
## Python code generated with wxFormBuilder (version Jun 17 2015)
## http://www.wxformbuilder.org/
##
## PLEASE DO "NOT" EDIT THIS FILE!
###########################################################################

import wx
import wx.xrc
import wx.richtext


###########################################################################
## Class MainFrame
###########################################################################

class MainFrame ( wx.Frame ):
    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = _(u"自动升级"), pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.CAPTION|wx.CLOSE_BOX|wx.STAY_ON_TOP|wx.TAB_TRAVERSAL )
        
        self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
        self.SetForegroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNTEXT ) )
        self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
        self.SetToolTipString( _(u"自动升级程序") )
        
        sizer = wx.BoxSizer( wx.VERTICAL )
        
        self.m_tip = wx.StaticText( self, wx.ID_ANY, _(u"MyLabel"), wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_LEFT )
        self.m_tip.Wrap( -1 )
        self.m_tip.SetForegroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_CAPTIONTEXT ) )
        self.m_tip.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
        
        sizer.Add( self.m_tip, 1, wx.ALIGN_TOP|wx.ALL|wx.EXPAND, 5 )
        
        self.m_desc = wx.richtext.RichTextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_AUTO_URL|wx.TE_READONLY|wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER|wx.WANTS_CHARS )
        sizer.Add( self.m_desc, 2, wx.ALIGN_TOP|wx.ALL|wx.EXPAND, 5 )
        
        self.m_gauage_process = wx.Gauge( self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL|wx.GA_SMOOTH )
        self.m_gauage_process.SetValue( 0 ) 
        self.m_gauage_process.SetMinSize( wx.Size( -1,25 ) )
        
        sizer.Add( self.m_gauage_process, 0, wx.ALL|wx.EXPAND, 5 )
        
        sizebtn = wx.BoxSizer( wx.HORIZONTAL )       
        
        sizebtn.AddSpacer( ( 0, 0), 1, wx.EXPAND, 5 )
        
        self.m_btnok = wx.Button( self, wx.ID_ANY, _(u"升级&O"), wx.DefaultPosition, wx.DefaultSize, wx.BU_BOTTOM )
        self.m_btnok.SetDefault() 
        self.m_btnok.SetMinSize( wx.Size( -1,25 ) )
        
        sizebtn.Add( self.m_btnok, 0, wx.ALIGN_BOTTOM|wx.ALIGN_RIGHT|wx.ALL|wx.EXPAND, 2 )
        
        self.m_btncacel = wx.Button( self, wx.ID_ANY, _(u"取消升级&C"), wx.DefaultPosition, wx.DefaultSize, wx.BU_BOTTOM )
        self.m_btncacel.SetMinSize( wx.Size( -1,25 ) )
        
        sizebtn.Add( self.m_btncacel, 0, wx.ALIGN_BOTTOM|wx.ALIGN_RIGHT|wx.ALL|wx.EXPAND, 2 )
        
        
        sizer.Add( sizebtn, 0, wx.ALL|wx.EXPAND|wx.ALIGN_RIGHT, 5 )
        
        
        self.SetSizer( sizer )
        self.Layout()
        
        self.Centre( wx.BOTH )
        
        # Connect Events
        self.m_btncacel.Bind( wx.EVT_BUTTON, self.close )
    
    def __del__( self ):
        pass
    
    
    # Virtual event handlers, overide them in your derived class
    def close( self, event ):
        pass
    

复制代码


四、I18N多语言原理

1、多语言原理

前面的多语言功能代码,提到_()函数等同于wx.translation方法,其核心原理:

读取指定的语言文件,依据原始文本查找对应的翻译文本。譬如_('自动升级')翻译成英文就是'auto update'。

2、多语言文件

语言文件主要是指三个文件:.mo文件,.po文件,.pot文件,百度上资料也很多,具体可以查看这个网址:http://shandian.biz/278.html

这里简单说明如下:

1、pot文件,原始的语言文件,提取自源代码,读取所有的_()括弧内的文本,按一定规则生成这个文件。

2、po文件,是从原始pot文件对应的翻译文件,生成这个文件需要用到一个poedit软件。

3、mo文件,是编译后的po文件,程序可以直接读取,个人理解这么设置的目的是提高加载效率。

这里说明一点,这个pot、po、mo文件是一个通用规则,不是python首创的,很多著名的开发语言都支持上述i18n解决方案。

五、python下语言文件生成方法

如前文所述,语言文件主要有三步:

1.提取文本,生成pot文件,代码如下:

python C:\Python27\Tools\i18n\pygettext.py -a -d eupdate -o eupdate.pot -p d:\python\eupdate\locale d:\python\eupdate

a、调用C:\Python27\Tools\i18n\下的pygettext.py脚本;

b、提取源码目录d:\python\eupdate中,所有的py中的_()中文本,生成pot文件。

2.使用poEdit生成po文件。

a、这里提到的外部工具poEDIT有中文版本,请自行到https://poedit.net去下载.

b、读取pot文件,翻译生成po文件,这个需要手动逐条完成 。

3.生产mo文件

python C:\Python27\Tools\i18n\msgfmt.py d:\python\eupdate\locale\en\LC_MESSAGES\eupdate.po

a、调用C:\Python27\Tools\i18n\下的msgfmt.py脚本;

b、读取步骤2生成的d:\python\eupdate\locale\en\LC_MESSAGES\eupdate.po文件,生成mo文件。

但是,但是,经过研究发现poEDIT最新版本,集成了上述三个功能,而且实测该功能支持python代码。

具体步骤请各位自行琢磨,中文菜单相对还是比较简单的。

六、语言文件目录及命名规则

前面介绍了语言文件原理,其实语言文件还有一个特定的命名、存放目录规则,下面实例说明:

复制代码

#1、locale目录默认在源码目录下,名字可以自定义。
D:\python\eupdate\locale>dir /S 驱动器 D 中的卷没有标签。 卷的序列号是 36D9-CDC7

#2、en目录默认在locale下,名字有规则,与源码中配置对应。 D:\python\eupdate\locale 的目录 2016-10-03 21:00 <DIR> . 2016-10-03 21:00 <DIR> .. 2016-10-03 21:01 <DIR> en 2016-10-03 20:58 995 eupdate.pot 1 个文件 995 字节
#3、en目录下的LC_MESSAGES,名字不能改 D:\python\eupdate\locale\en 的目录 2016-10-03 21:01 <DIR> . 2016-10-03 21:01 <DIR> .. 2016-10-03 21:03 <DIR> LC_MESSAGES 0 个文件 0 字节
#4、mo、po文件必须放在LC_MESSAGES目录下,名字可以改,也需与代码配合 D:\python\eupdate\locale\en\LC_MESSAGES 的目录 2016-10-03 21:03 <DIR> . 2016-10-03 21:03 <DIR> .. 2016-10-03 21:03 832 autoupdate.mo 2016-10-03 21:03 1,136 autoupdate.po 2 个文件 1,968 字节 所列文件总数: 3 个文件 2,963 字节 8 个目录 116,639,555,584 可用字节

复制代码

1、在源码目录下有locale子目录,对应代码wx.locale().AddCatalogLookupPathPrefix('locale'),目录名名可以修改。

2、locale下子目录en,对应代码wx.Locale(wx.LANGUAGE_ENGLISH),目录名与语言有一个对照关系规则(附一个对照关系表)。

View Code

3、en子目录下需要有个LC_MESSAGES子目录,这个不能改。

4、LC_MESSAGES目录下存放autoupdate.mo语言文件,对照代码wx.locale().AddCatalog('autoupdate'),文件名字可以修改。

其他

1、以上内容都是自己读代码琢磨以及google出来的,已经使用正常,唯一的问题:

语言文件的目录规则(譬如en与wx.LANGUAGE_ENGLISHT想对应)的具体机制,没看到实现源码(貌似被封装wx的dll里了),若有高手知情,请不吝赐教。

2、另外,i18n的机制不是python的首创,还多语言都有类似的实现,不过mo文件能否通用没试过。

3、脑洞大开,python的开发工具wing,wxpython包等等,是否也支持国际化,有待研究,不过确确实实在安装目录里看到了语言文件目录。

4、与cxfreeze的结合,可能还是会存在文件目录的问题,估计要判断frozen了。

经验证cxfreeze支持wx.locale,没问题,赞 ^_^

5、wxpython3.0.2下有个很强大的例子程序Editra,有关这个Edittra:

首先它本身是一个功能很强大的开源产品,功能有实用性(网上有编译好的版本供下载)。

其次它的源码结构性很好,用了很多wx里不太被人知道,但很强大的机制,譬如pubsub,很值得研究:

a、editra一开始会编译出错,需要修改ed_xml.py文件的import部分。

#import extern.dexml as dexml

#from extern.dexml.fields

import * import dexml as dexml

from dexml.fields import *

把extern.dexml换成dexml,至于dexml哪里来的?当然是用pip install dexml安装了。

b、editra目录下有个ed_i18n.py,这个小程序实现了两个很好的功能:

1)、GetAvailLocales()获取当前目录下已经安装的mo文件。

2)、GetLocaleDict()获取当前已安装的mo文件,对应的wx.LANGUAGE_XXX的值,这个值就是wx.locale()的参数。

c、editra实现了不基于esky的update,个人觉得可以学习。

以上内容,都是个人理解,错误之处请指正。