有关anaconda的汉化

谈到汉化,那就是我们在整个系统的安装过程中,几乎所有的步骤都是显示中文的,而在我所读到的anaconda源代码中没有任何的有关汉字的踪影,那么它显示汉字到底是怎么一回事了呢?还好因为有源代码,虽然走了些弯路,但还是值得的!读了这么多的代码,在每个设计到显示给用户的程序中都会有如此一个导入的语句:from rhpl.translate import _, N_凡是导入此模块的程序,涉及到输出到终端的语句或词语,都会调用到_或N_ 在此以anaconda/iw/language_gui.py为例分析:~~~~~~~~~from rhpl.translate import _, N_class LanguageWindow (InstallWindow):    windowTitle = N_("Language Selection")~~~~~~~~~~~~~~~~那个N_到底是什么意思了呢?我们在安装系统的时候,看到的选择语言的界面:“语言选择”,此时你会恍然大悟,原来如此。
那么这个_和N_到底是怎么实现的了呢?
下载rhpl软件包,从中找到translate.py和translate.c,往下分析:
先来看translate.c,这是一个python的扩展,用C语言实现。代码不长,所以
全部罗列于此:
/*
* translate.c - simple bindings for various disk related functions.
* blatently stolen from anaconda/isys/isys.c
*
* Copyright 1999-2002 Red Hat, Inc.
*/
#include
#include
#include
#include
#include
static PyObject * py_bind_textdomain_codeset(PyObject * o, PyObject * args);
/*包装函数py_bind_textdomain_codeset定义*/
static PyMethodDef translateModuleMethods[] = {
    { "bind_textdomain_codeset", (PyCFunction) py_bind_textdomain_codeset,
      METH_VARARGS, NULL},
    { NULL }
};
/*ModuleMethods[]数组,罗列了被包装的函数bind_textdomain_codeset,以便于python解释器能够导入并
调用它们*/
static PyObject *
py_bind_textdomain_codeset(PyObject * o, PyObject * args) {
    char *domain, *codeset, *ret;
   
    if (!PyArg_ParseTuple(args, "ss", &domain, &codeset))
    return NULL;
    ret = bind_textdomain_codeset(domain, codeset);
    if (ret)
    return PyString_FromString(ret);
    PyErr_SetFromErrno(PyExc_SystemError);
    return NULL;
}
/*包装函数py_bind_textdomain_codeset的实现,参数为三个字符串,调用
bind_textdomain_codeset函数*/
void init_translate(void) {
    PyObject * m, * d;
    m = Py_InitModule("_translate", translateModuleMethods);/*增加模块化函数,
_translate,以便于在模块导入时被解释器调用*/
    d = PyModule_GetDict(m);
}
(注:各位看官(include me),关于python的扩展,以后肯定会多起来的,
我不会给自己定多个起点的)
再来看translate.py,此程序很长,在此就不贴出,截取关键的片段分析:
import gettext #来自python官方的注释:  he gettext module defines the following
API, which is very similar to the GNU gettext API. If you usethis API you will affect
the translation of your entire application globally. Often this is what you want if
yourapplication is monolingual, with the choice of language dependent on the locale
of your user. If you are localizinga Python module, or if your application needs
to switch languages on the fly, you probably want to use the class-based API instead.
import _translate#这就是刚才上面所分析的translate.c编译后的可使python导入的模块
import iconv#和codec相关的模块,亦有python扩展用C实现。
def _expandLang(str):
def getDefaultLangs():#此两个函数应该合起来一起看,获得系统环境变量,然后用字典
对字符串进行了处理.
class i18n:#这才是真正的主角:i18n的类
def __init__(self, langs=None, conversion=0, domain=None, paths=None):
#构造方法,类的默认行为,需传入4个参数。语言、编码、要翻译的程序、路径。
以下是属于i18n类所定义的方法:
    def setDomain(self, domain):    def updateCachedCatPaths(self):
           catalog = gettext.GNUTranslations(mofile) #找到mo文件,调用gettext模块的
GNUTranslations方法做出翻译。
      def setDomainCodeset(self, domain, codeset):
    def setCodeset(self, codeset):
    def setconversion(self, value):
    def getconversion(self, value)
    def getlangs(self)
     def setlangs(self, langs)
    def convert(self, string):
       def gettext(self, string)
以下为translate.py的函数,
def N_(str):      #呵呵,也不过是返回一字符串而已!
    return str
def textdomain_codeset(domain, codeset):
    global cat
    cat.setDomainCodeset(domain, codeset)
def textdomain(domain):
    global cat
    cat.setDomain(domain)
def addPoPath(path):
    global cat
    cat.addPoPath(path)
def py_bind_textdomain_codeset(domain, codeset):
    _translate.bind_textdomain_codeset(domain, codeset)
全局变量:
cat = i18n()
_ = cat.gettext  #呵呵,真正起作用的输出在此隐藏。。。。
utf8 = cat.convert
convert = cat.convert
关于路径的问题;在主程序anaconda中定义了翻译文件的路径:def setupTranslations():    if os.path.isdir("/mnt/source/RHupdates/po"):        log.info("adding RHupdates/po")        addPoPath("/mnt/source/RHupdates/po")    if os.path.isdir("/tmp/updates/po"):        log.info("adding /tmp/updates/po")        addPoPath("/tmp/updates/po")    textdomain("anaconda")~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
分析完文件之后,我还是没有明白最后的意义,那就是gettext的工作原理:
gettext是GNU的一个项目:
gettext的意义

如果你使用的是linux,那么你的系统已经有了gettext套件,对于我们非C程序
员来说,有两个指令很有用,那就是扩展名为po,mo文件的转换。不信的化你
可以到你的系统/usr/share/locale/zh_CN/LC_MESSAGES下作如下操作:
msgunfmt xxx.mo -o xxx.po
cat xxx.po,会看到什么,是不是类于下面的default.po文件?
如果想还原的话,msgfmt xxx.po -o xxx.mo
gettext是用来作国际化编程的,尽管有很多不如意的地方,这也是python的缺憾。
就象是php一样(猜得)。。
以下为验证上面的代码分析,所写的一个小程序,只是小了点、短了点而已:

poedit
新建一个名为default.po的文件,poedit会自动随着保存而生成一个default.mo的文件,具体内容如下:lee@lee-laptop:~/桌面/python汉化$ cat default.pomsgid ""msgstr """Project-Id-Version: lang1.0\n""POT-Creation-Date: \n""PO-Revision-Date: 2008-07-13 17:54+0800\n""Last-Translator: lijiansheng \n""Language-Team: no.1 \n""MIME-Version: 1.0\n""Content-Type: text/plain; charset=utf-8\n""Content-Transfer-Encoding: 8bit\n""X-Poedit-Language: Chinese\n""X-Poedit-Country: CHINA\n""X-Poedit-SourceCharset: utf-8\n"# #这就是个试验而已msgid "hello,python world"msgstr "你好!python的世界"然后进入python的交互模式,>>> import gettext>>> cat = gettext.GNUTranslations(open("default.mo"))>>> _= cat.gettext>>> print _("hello,python world")你好!python的世界似乎看起来是很简单的事情。但就是害怕多,害怕烦。