你寫了一個Python 3程序,還想要它適用於其他語言。你能復制全部代碼庫,然后刻意地檢查每個.py文件,替換掉所有找到的文本字符串。但這意味着你有兩份你代碼的獨立副本,每當你要做出個改動或修復個bug,你的工作量會加倍。而且如果你想要程序還適用於其他語言,就更糟了。
幸運的是,Python給了一個解決辦法,就是用gettext模塊。
一個Hack解法
你應該把你自己的解決辦法統一改變。例如,你可以把你程序中的每個字符串替換為一個函數調用(函數名簡單些,比如像_()一樣),這會返回被翻譯為該正確語言的字符串。舉個例子,如果你的程序原本是:
1
|
print
(
'Hello world!'
)
|
……你可以將它改為:
1
|
print
(_(
'Hello world!'
))
|
……函數_()會返回'Hello world!'的翻譯,它基於程序設置有的語言。比如,如果這個語言設置之前被存在一個叫LANGUAGE的全局變量中,函數_()看起來像這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def
_(s):
spanishStrings
=
{
'Hello world!'
:
'Hola Mundo!'
}
frenchStrings
=
{
'Hello world!'
:
'Bonjour le monde!'
}
germanStrings
=
{
'Hello world!'
:
'Hallo Welt!'
}
if
LANGUAGE
=
=
'English'
:
return
s
if
LANGUAGE
=
=
'Spanish'
:
return
spanishStrings[s]
if
LANGUAGE
=
=
'French'
:
return
frenchStrings[s]
if
LANGUAGE
=
=
'German'
:
return
germanStrings[s]
|
這可以,但是你這是在重復造輪子。Python的gettext模塊可以做更多。gettext是一系列工具,文件格式在20世紀90年代被發明出來,來規范軟件國際化(也叫I18N)。gettext是個作為對於所有編程語言的系統化的設計,但是我們會在本篇文章中只專注於Python。
程序例子
設想你有個想要翻譯的用Python3寫的簡單“猜數字”游戲。程序的源代碼在這里。有四步來使這個程序國際化:
調整這個.py文件的源代碼,這樣使字符串輸入進一個名為_()的函數。
用和Python一起安裝的pygettext.py文本,從源代碼創建一個”pot”文件。
用這個免費的跨平台Poedit軟件,從pot文件創建.po和.mo文件。
再次調整你的.py文件源代碼導入gettext模塊的代碼,設置語言。
第一步:添加 _() 函數
首先,檢查你程序中的所有需要被翻譯和用_()的調用來替代的字符串。針對Python使用的gettext系統用_()作為得到翻譯了的字符串的通用名,因為它是個短名。
注意:用格式型字符串而不是連接型字符串會是你的程序翻譯起來更簡單。例如,用連接型字符串你的程序會像這樣:
1
2
|
print
(
'Good job, '
+
myName
+
'! You guessed my number in '
+
guessesTaken
+
' guesses!'
)
print
(_(
'Good job, '
)
+
myName
+
_(
'! You guessed my number in '
)
+
guessesTaken
+
_(
' guesses!'
))
|
This results in three separate strings that need to be translated, as opposed to the single string needed in the string formatting approach:
這會導致三個獨立的字符串都需要翻譯,然而相反的是在格式型的字符串中,只需翻譯一個字符串:
print('Good job, %s! You guessed my number in %s guesses!' % (myName, guessesTaken))
print(_('Good job, %s! You guessed my number in %s guesses!') % (myName, guessesTaken))
當你改完“猜數字”源代碼后,它會像這樣。你並不能運行它,因為_()函數還沒定義。這個變化只是讓pygettext.py文本可以找到所有需要翻譯的字符串。
第二步:用pygettext.py提取字符串
在你Python安裝(Windows上的C:Python34Toolsi18n)中的Tools/i18n就是pygettext.py文本。對於可譯字符串普通 gettext unix 命令解析 C/C++ 源碼並且 xgettext unix 命令可以解析其他語言,而pygettext.py則知道怎樣去解析Python源碼。它會找到所有字符串並產生個”pot”文件。
在Windows上我已經運行了這個文本像這樣:
1
|
C:>py
-
3.4
C:Python34Toolsi18npygettext.py
-
d guess guess.py
|
這創建了一個pot文件,叫guess.pot。這只是個普通純文本文件,它列出來了全部的在源碼中尋找_()的調用的要翻譯的字符串。你可以在這兒看guess.pot文件.
第三步:用Poedit翻譯字符串
你可以用文本編輯器填寫翻譯但是免費的Poedit軟件會更容易從這兒下載http://poedit.net. 選擇 > New from POT/PO file… 然后選擇你的guess.po文件。
Poedit會問你想要翻譯成什么語言。我們舉例用西班牙語:
填寫翻譯吧。(我用 http://translate.google.com,所以對於真的使用西班牙語的人會感覺有點奇怪。)
現在儲存文件在它的gettext形式的文件夾里。保存會創建.po文件(一個人類可讀的文本文件不同於原始.pot文件,除了是有西語翻譯的)和一個.mo文件(一個gettext會讀取的機器可讀版本。這些文件會存在一個特定的文件夾內,為的是讓gettext能夠找到他們。他們看起來像這樣(比如西語文件中的”es”和德語文件中”de”):
1
2
3
4
5
6
|
.
/
guess.py
.
/
guess.pot
.
/
locale
/
es
/
LC_MESSAGES
/
guess.mo
.
/
locale
/
es
/
LC_MESSAGES
/
guess.po
.
/
locale
/
de
/
LC_MESSAGES
/
guess.mo
.
/
locale
/
de
/
LC_MESSAGES
/
guess.po
|
這些兩種性質的語言像西語中的”es”和德語中的 ”de” 被稱作ISO 639-1 codes 是語言的標准縮寫。你不一定要用他們,但是遵循標准是有道理的。
第四步:給你程序加上gettext代碼
現在你有包含翻譯的.mo文件,調整你的Python代碼去用它。在你的程序中加上下面的:
1
2
3
|
import
gettext
es
=
gettext.translation(
'guess'
, localedir
=
'locale'
, languages
=
[
'es'
])
es.install()
|
第一個 'guess' 是”定義域”,這其實是意味着guess.mo文件名中“猜”的部分。 localedir是你創建的locale文件夾的目錄地址。這會是相對或絕對的路徑。'es'描述在locale文件夾下面的文件。LC_MESSAGES文件夾是個標准名
install()方法會導致調用_()返回翻譯為西語的字符串。如果你想回到原始的英語只需要分配一個lambda函數值給_,這會返回當時輸入的字符串:
1
2
3
4
5
|
import
gettext
es
=
gettext.translation(
'guess'
, localedir
=
'locale'
, languages
=
[
'es'
])
print
(_(
'Hello! What is your name?'
))
# prints Spanish
_
=
lambda
s: s
|
你可以檢查准備翻譯的”Guess the Number”源碼。如果你想要運行此程序,下載並解壓這個壓縮文件和它的locale文件夾和.mo安裝文件。