转自:http://thisis.yorven.site/blog/index.php/2017/10/10/wxpython-jiaocheng-shi-guojihua/

国际化和本地化是电脑软件适应非本地环境的方法,尤其是在其他国家和文化环境下。国际化是指确保一个应用能够适应本地的需求,比如保证本地的书写系统可以展示。本地化则是指将应用尽可能的适应特定地域,使用当地语言、当地习惯等等。本节主要讲解 wxPython 国际化 问题。

Unicode

wxPython 有两种构建, ANSI 和 Unicode。如果我们想创建非英语的 wxPython 应用,我们必须使用 Unicode 构建。
Unicode 是一个工业标准,它允许世界范围内的任意电脑可以一致的展现、操作任何文字,它是一个以 16 位存储字符的文本编码标准。传统的 ASCII 编码只使用 8 位。
首先,我们需要得到 Лев Николaевич Толстoй Анна Каренина 的 Unicode 编码。

>>> unicode(u'Лев Николaевич Толстoй Анна Каренина')
u'\u041b\u0435\u0432 \u041d\u0438\u043aa\u0430\u0301\u0435\u0432\u0438\u0447
\u0422\u043e\u043b\u0441o\u0439 \u0410\u043d\u043d\u0430
\u041a\u0430\u0440\u0435\u043d\u0438\u043d\u0430'

我们打开 Python 终端,使用 unicode() 函数调用。注意在下面的例子中,我们使用了回车符将文字划分为两行。

#!/usr/bin/python

import wx

text = u'\u041b\u0435\u0432 \u041d\u0438\u043a\u043e\u043b\u0430\
\u0435\u0432\u0438\u0447 \u0422\u043e\u043b\u0441\u0442\u043e\u0439 \n\
\u0410\u043d\u043d\u0430 \u041a\u0430\u0440\u0435\u043d\u0438\u043d\u0430'


class Unicode(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(250, 150))

self.Bind(wx.EVT_PAINT, self.OnPaint)

self.Centre()
self.Show(True)

def OnPaint(self, event):
dc = wx.PaintDC(self)
dc.DrawText(text, 50, 50)

app = wx.App()
Unicode(None, -1, 'Unicode')
app.MainLoop()

在例子中,我们用俄语西里尔字母绘制了英语的 Anna Karenina。
Unicode
Locale
一个 locale 是一个定义了用户的语言、国家、数字格式、字母格式、货币格式等的对象。一个 local 变量有以下格式:
[language[_territory][.codeset][@modifier]]

比如,_de_AT.utf8_ 是指澳洲使用的德语 local,使用 UTF8 编码集。

#!/usr/bin/python

# locale.py

import wx
import time
import locale

class Locale(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(250, 420))

panel = wx.Panel(self, -1)


tm = time.localtime()

font = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD)
us = wx.StaticText(self, -1, 'United States', (25, 20))
us.SetFont(font)

wx.StaticLine(self, -1, (25, 50), (200 ,1))

locale.setlocale(locale.LC_ALL, '')
date = time.strftime('%x', tm)
time_ = time.strftime('%X', tm)
curr = locale.currency(100000)

wx.StaticText(self, -1, 'date: ', (25, 70))
wx.StaticText(self, -1, 'time: ', (25, 90))
wx.StaticText(self, -1, 'currency: ', (25, 110))

wx.StaticText(self, -1, str(date), (125, 70))
wx.StaticText(self, -1, str(time_), (125, 90))
wx.StaticText(self, -1, str(curr), (125, 110))

de = wx.StaticText(self, -1, 'Germany', (25, 150))
de.SetFont(font)

wx.StaticLine(self, -1, (25, 180), (200,1))

locale.setlocale(locale.LC_ALL, ('de_DE', 'UTF8'))
date = time.strftime('%x', tm)
time_ = time.strftime('%X', tm)
curr = locale.currency(100000)

wx.StaticText(self, -1, 'date: ', (25, 200))
wx.StaticText(self, -1, 'time: ', (25, 220))
wx.StaticText(self, -1, 'currency: ', (25, 240))
wx.StaticText(self, -1, date, (125, 200))
wx.StaticText(self, -1, time_, (125, 220))
wx.StaticText(self, -1, curr, (125, 240))

de = wx.StaticText(self, -1, 'Slovakia', (25, 280))
de.SetFont(font)

wx.StaticLine(self, -1, (25, 310), (200,1))

locale.setlocale(locale.LC_ALL, ('sk_SK', 'UTF8'))
date = time.strftime('%x', tm)
time_ = time.strftime('%X', tm)
curr = locale.currency(100000)

wx.StaticText(self, -1, 'date: ', (25, 330))
wx.StaticText(self, -1, 'time: ', (25, 350))
wx.StaticText(self, -1, 'currency: ', (25, 370))

wx.StaticText(self, -1, str(date), (125, 330))
wx.StaticText(self, -1, str(time_), (125, 350))
wx.StaticText(self, -1, str(curr), (125, 370))

self.Centre()
self.Show(True)

app = wx.App()
Locale(None, -1, 'Locale')
app.MainLoop()

我们使用了标准的模块 locale 来进行本地化设置。在例子中,我们使用了多种格式的日期、时间和货币,包括美国、德国和斯洛伐克。

locale.setlocale(locale.LC_ALL, ('de_DE', 'UTF8'))

这里,我们设置了德语的 locale 对象。LC_ALL 是多个本地化设置的组合,比如 LC_TIME, LC_MONETARY 和 LC_NUMERIC。

date = time.strftime('%x', tm)
time_ = time.strftime('%X', tm)
curr = locale.currency(100000)

这些函数调用展示了当前的 locale 对象。
Locale

世界时间

在某一时刻,世界各地的国家时间都不相同,因为地球被划分成了不同的时区。对于程序员来说,处理这样的事情并不稀奇。wxPython 的 wx.DateTime 对象可以用来做这些事情,它代表了一个绝对的时刻(据文档)。

#!/usr/bin/python

import wx
import time

class WorldTime(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(270, 280))

self.panel = wx.Panel(self, -1)
self.panel.SetBackgroundColour('#000000')
font = wx.Font(12, wx.FONTFAMILY_DEFAULT,
wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, 'Georgia')

self.dt = wx.DateTime()

self.tokyo = wx.StaticText(self.panel, -1,
self.dt.FormatTime() , (20, 20))
self.tokyo.SetForegroundColour('#23f002')
self.tokyo.SetFont(font)

self.moscow = wx.StaticText(self.panel, -1,
self.dt.FormatTime() , (20, 70))
self.moscow.SetForegroundColour('#23f002')
self.moscow.SetFont(font)

self.budapest = wx.StaticText(self.panel, -1,
self.dt.FormatTime() , (20, 120))
self.budapest.SetForegroundColour('#23f002')
self.budapest.SetFont(font)

self.london = wx.StaticText(self.panel, -1,
self.dt.FormatTime() , (20, 170))
self.london.SetForegroundColour('#23f002')
self.london.SetFont(font)

self.newyork = wx.StaticText(self.panel, -1,
self.dt.FormatTime() , (20, 220))
self.newyork.SetForegroundColour('#23f002')
self.newyork.SetFont(font)

self.OnTimer(None)

self.timer = wx.Timer(self)
self.timer.Start(1000)
self.Bind(wx.EVT_TIMER, self.OnTimer)

self.Centre()
self.Show(True)

def OnTimer(self, evt):
now = self.dt.Now()
self.tokyo.SetLabel('Tokyo: ' + str(now.Format(('%a %T'),
wx.DateTime.GMT_9)))
self.moscow.SetLabel('Moscow: ' + str(now.Format(('%a %T'),
wx.DateTime.MSD)))
self.budapest.SetLabel('Budapest: ' + str(now.Format(('%a %T'),
wx.DateTime.CEST)))
self.london.SetLabel('London: ' + str(now.Format(('%a %T'),
wx.DateTime.WEST)))
self.newyork.SetLabel('New York: ' + str(now.Format(('%a %T'),
wx.DateTime.EDT)))


app = wx.App()
WorldTime(None, -1, 'World Time')
app.MainLoop()

在上面的例子中,我们显示了东京、莫斯科、布达佩斯、伦敦和纽约的当前时间。

self.dt = wx.DateTime()

我们创建了 wx.DateTime 对象。

now = self.dt.Now()

我们设置了绝对时刻。

self.tokyo.SetLabel('Tokyo: ' + str(now.Format(('%a %T'), 
wx.DateTime.GMT_9)))

这一行设置时间为特定的格式。%a 是指当前 locale 下的 weekday 名称。 %T 是指使用格式 %H:%M:%S 的十进制数字。Format 的第二个参数定义了时间区间, GMT_9 在日本使用,EDT 在纽约使用,等等。

可以通过 timeanddate.com 网站来检查样例的结果。
世界时间

排序

Locale 设置也会影响字符串排序的方式。比如匈牙利语有一些字符是斯洛伐克语和英语里没有的。一些语言有重读字符,有些则没有。

#!/usr/bin/python

# collate.py

import wx
import locale

ID_SORT = 1

words = [u'Sund', u'S\xe4bel', u'S\xfcnde', u'Schl\xe4fe', u'Sabotage']


class Collate(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(300, 220))

panel = wx.Panel(self, -1)
hbox = wx.BoxSizer(wx.HORIZONTAL)

self.listbox = wx.ListBox(panel, -1)
for i in words:
self.listbox.Append(i)
hbox.Add(self.listbox, 1, wx.EXPAND | wx.ALL, 20)

btnPanel = wx.Panel(panel, -1)
vbox = wx.BoxSizer(wx.VERTICAL)
new = wx.Button(btnPanel, ID_SORT, 'Sort', size=(90, 30))

self.Bind(wx.EVT_BUTTON, self.OnSort, id=ID_SORT)

vbox.Add((-1, 20))
vbox.Add(new)

btnPanel.SetSizer(vbox)
hbox.Add(btnPanel, 0.6, wx.EXPAND | wx.RIGHT, 20)
panel.SetSizer(hbox)

locale.setlocale(locale.LC_COLLATE, ('de_DE', 'UTF8'))

self.Centre()
self.Show(True)

def OnSort(self, event):
self.listbox.Clear()
words.sort( lambda a,b: locale.strcoll(a, b) )
for i in words:
self.listbox.Append(i)


app = wx.App()
Collate(None, -1, 'Collate')
app.MainLoop()

在这个例子中,我们从字典中找了 5 个德语单词。默认的 sort() 函数的排序结果是:Sabotage, Schläfe, Sund, Säbel, Sünde。然而这是错的,因为在德语中 ä 比字母要优先。为了得到正确的排序结果,我们必须使用 locale 函数。

locale.setlocale(locale.LC_COLLATE, ('de_DE', 'UTF8'))

这里,我们使用了 LC_COLLATE,当然也可以直接使用 LC_ALL 选项。

words.sort( lambda a,b: locale.strcoll(a, b) )

技巧在于这里在 sort() 函数中使用了新的比较方法。我们定义了一个 lambda 函数, strcoll() 函数比较两个字符串,和默认一样返回 -1,0和1,但它会考虑 locale 设置,这样我们才会得到正确的单词排序。
排序

简单翻译

在下面的例子中,我们展示一个非常基础的翻译。

对于翻译,编程者可以有两个选择,一个是使用 GNU 的 gettext,另一个是使用 wxPython 的 catalogs,这两个是兼容的。

wxPython 有一个类叫 wx.Locale,它是使用信息 catalogs 的基础。每个翻译有一个 catalog。如果我们想把一个字符串翻译成德语,首先,我们必须确保我们系统中拥有对德语的语言支持。‘’

$ locale -a
C
de_AT.utf8
de_BE.utf8
de_CH.utf8
de_DE.utf8
de_LU.utf8
en_AU.utf8
en_BW.utf8
en_CA.utf8
en_DK.utf8
en_GB.utf8
en_HK.utf8
en_IE.utf8
en_IN
en_NZ.utf8
en_PH.utf8
en_SG.utf8
en_US.utf8
en_ZA.utf8
en_ZW.utf8
POSIX
sk_SK.utf8

使用 locale 命令可以检查系统支持什么语言。在原作者的系统中,支持英语、德语和斯洛伐克语。英语和德语有不同地方的版本,所有会有很多种语言支持。注意到 utf8,这意味着系统对字符串使用的是 utf8 编码支持。
现在我们进行编码。我们把需要翻译的字符串放到 _(),或者可以使用 wx.GetTranslation()。

#!/usr/bin/python

import wx

class Translation(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(220, 100))

panel = wx.Panel(self, -1)

mylocale = wx.Locale()
mylocale.AddCatalogLookupPathPrefix('.')
mylocale.AddCatalog('simple_de')

_ = wx.GetTranslation

wx.StaticText(panel, -1, _("hello"), (10, 10))
#wx.StaticText(panel, -1, wx.GetTranslation('hello'), (10, 10))

self.Centre()
self.Show(True)

app = wx.App()
Translation(None, -1, 'Translation')
app.MainLoop()

现在我们创建一个 PO 文件,它是一个简单的文本文件,翻译器使用它来翻译字符串。

pygettext -o simple_de.po simple.py

我们使用 pygettext 命令是创建 po 文件,为了完整的理解 po 文件,可以参考 GNU 的 gettext 手册

"Content-Type: text/plain; charset=utf-8\n"

我们编辑 simple_de.po 文件,必须定义字符集,这里我们设置为 utf-8。

#: simple.py:17
msgid "hello"
msgstr "Grüß Gott"

这里,我们提供了一个对 ‘hello’ 字符串的翻译。

最后我们要做的事情是创建一个二进制的信息 catalog。

msgfmt --output-file simple_de.mo simple_de.po

我们使用了 msgfmt 命令来创建 mo 文件。

简单翻译
在本节中,我们主要讲解 Unicode 字符。