============================
Django Step by Step (十五)
============================

:作者: limodou
:联系: limodou@gmail.com
:版本: 0.1
:主页: http://wiki.woodpecker.org.cn/moin/NewEdit
:BLOG: http://www.donews.net/limodou
:版权: FDL

.. contents:: 目录
.. sectnum::

引言
=====

在 Ajax_ 的试验中，你会看到有一些是用英文写的。下面就让我们学习如何将应用改为支持 i18n 处理的吧。在本讲中我会讲述我实现的过程，同时对一些问题进行讨论。
Django_ 中 i18n 的实现过程:
    
.. _Ajax: http://www.ajaxpatterns.org/Main_Page
.. _Django: http://www.djangoproject.com/

在程序和模板中定义翻译字符串
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

在程序中就是使用 ``_()`` 将要翻译的字符串包括起来。这里有几种做法，一种是什么都不导入，这样就使用缺省的方式，另一种是导入 Django 提供的翻译函数。特别是 Django 提供了 Lazy 翻译函数，特别可以用在动态语言的切换。在模板中分几种情况：

* 可以使用 ``{% trans %}`` 标签。它用来翻译一句话，但不能在它中间使用模板变量。

* 如果是大段的文本，或要处理模板变量，可以使用 ``{% blocktrans %}{% endblocktrans %}`` 来处理。

Django 还支持简单的 Javascript 的 i18n 的处理，但有兴趣自已去看吧。

生成 po 文件
~~~~~~~~~~~~~~~

定义好翻译串之后使用 ``bin/make-messages.py`` 来生成 po 文件。

Django 支持多层次的处理。比如在整个 Django 的源码项目，在某一个工程，在某一个应用。在不同层次去实现 i18n 时，需要在不同的层次的根目录去执行 ``make-messages.py`` 。那么可以将 ``make-messages.py`` 拷贝到相应的目录去执行，特别是在你的工程或应用中。在执行 ``make-messasges.py`` 时，需要你预先创建 ``conf/locale`` 或 ``locale`` 目录，而 ``make-messasges.py`` 是不会自动为你创建的。那么 ``conf/locale`` 多用在源码中，象 Django 的源码就是放在 ``conf/locale`` 中的。 **但在运行时，对于自已的项目和应用却是从 ``locale`` 中来找的** 。因此还是建议你创建 ``locale`` 来存放 po 文件。

第一次执行时::

    make-messages.py -l zh_CN

这时会生成 ``locale/zh_CN/LC_MESSAGES/django.po`` 和 ``django.pot`` 两个文件。

.. note:: 在 0.92 版之前需要使用 po 工具将 .pot 合并到 .po 文件中。但 0.92 版则已经自动做好合并了。如果有 .pot 文件，可以：

    用 poEdit 打开 ``django.po`` 后，选择类目->从POT文件更新(我使用的是 poEdit 中文版)。这样内容就更新到 po 中去了。

.. _poEdit: http://www.poedit.org/

然后你就可以开始翻译了。翻译完成之后，首先要执行类目->设置，将缺省的参数修改一下。主要是：项目名称及版本，团队，团队专用电子邮件，字符集(一般为 utf-8)。这些如果不改， poEdit 在保存时会报错。使用 poEdit 的一个好处是，在保存时它会自动将 po 编译成 mo 文件。

以后再更新时::

    make-messasges.py -a

如果已经有多个语言文件，那么执行时会同时更新这些 po 文件。

配置
~~~~~

Django 有一系列的策略来实现 i18n 的功能。基本上分为静态和动态。

静态是指在 ``settings.py`` 中设置 ``LANGUAGE_CODE`` 为你想要的语言。那么这里要注意，中文的语言编码是 ``zh-cn`` ，但 ``locale`` 目录下却是 ``zh_CN`` 。这是为什么：其实一个是 language(zh-cn) ，一个是 locale(zh_CN) ，在 Django 的 ``utils.translation.py`` 中有专门的方法可以进行转换。因此在 Django 的程序中使用的是 language 的形式，在目录中却是使用 locale 的形式。一旦设为静态，则它表示是全局性质的，在所有其它的策略失效后将使用这种策略。

而动态是指在运行中对于不同的用户，不同的浏览器的支持的语言可以有不同的语言翻译文件被使用。这种方式需要在 ``settings.py`` 中安装 ``django.middleware.locale.LocaleMiddleware`` 到 ``MIDDLEWARE_CLASSES`` 中去。同时如果你想在实现应用中的翻译文件被使用，也要采用这种方式。

在一个请求发送到 Django 之后，如果安装了 ``LocaleMiddleware`` ，它会采用下面的策略：

    * 在当前用户的 session 中查找 ``django_language`` 键字。
    * 如果没有找到则在 cookie 中查找叫 ``django_language`` 的值。
    * 如果没有找到，则查看 ``Accept-Language`` HTTP 头。这个头是由浏览器发送给服务器的。
    * 如果没有找到，则使用全局的 ``LANGUAGE_CODE`` 设置。

如果你使用 FireFox 可以在 Tools->Options->Advanced->Eidt Languages 设置你所接受的语言，并且将 ``zh-cn`` 放在最前面。

上面讲述得还是有些粗，建议你好好阅读 i18n 的文档。

.. note:: 国际化处理的文档请参阅： Internationalization_ 文档

.. _Internationalization: http://www.djangoproject.com/documentation/i18n/

下面开始我们的试验。

修改 ajax/views.py
==========================

::

    #coding=utf-8
    # Create your views here.
    from django.http import HttpResponse
    
    def input(request):
        input = request.REQUEST["input"]
        return HttpResponse(_('<p>You input is "%s"</p>') % input)
        
    def json(request):
        a = {'head':(unicode(_('Name'), 'utf-8'), unicode(_('Telphone'), 'utf-8')), 
            'body':[(u'张三', '1111'), (u'李四', '2222')]}
        import simplejson
        return HttpResponse(simplejson.dumps(a))

这里对所有英文都使用 ``_()`` 进行了封装。但对于 Json 方法，这里我使用 ``unicode(_('Name'), 'utf-8')`` 进行了转换。

.. note:: 目前来说， Django 内部使用 utf-8 编码。因此从 ``_()`` 返回的并不象我以前认为的是 unicode ，而是 utf-8 编码。那么关于缺省编码是在 ``django.conf.global_settings.py`` 中有一个 ``DEFAULT_CHARSET`` 的值。缺省情况下它是 utf-8 。但现在的问题是 simplejson 需要 unicode 来正确处理汉字。那么我只有将 ``_()`` 返回的值转化为 unicode 。

.. _simplejson: http://undefined.org/python/#simplejson

.. note:: 在 Django 的邮件列表中已经开始讨论 Django 的核心是否全部采用 unicode 的问题。

修改 settings.py
=====================

增加 ``LocaleMiddleware``

::

    MIDDLEWARE_CLASSES = (
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.locale.LocaleMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.doc.XViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
    )

这里在文档中对于 ``LocaleMiddleware`` 的顺序有要求，要求排在 ``SessionMiddleware`` 之后，但在其它的 Middleware 之前。

.. note:: 话虽如此，但我感觉目前顺序影响不大，也许只是个人感觉吧。

创建 ajax/locale 目录
============================

拷贝 make-messasges.py 到 ajax 目录下
=======================================

执行 make-messasges.py
========================

::

    cd ajax
    make-message.py -l zh_CN

使用 poEdit 翻译 django.po 文件
================================

按上面说的先更新 pot 文件，然后修改缺省的参数，再保存。

.. note:: 如果你没有 poEdit，或不在 Windows 平台下，那么只好自已去想办法了。同时这里 make-message.py 还需要 Windows 下的 xgettext 工具。可以在 http://code.djangoproject.com/wiki/Localization 找到说明。

这里我没有演示模板的处理。因为 Ajax 所用到的模板没有放在 ``ajax`` 目录下，而是放在 ``templates`` 目录下。因此，如果想支持 i18n 的话，目录的布置是一个问题。所以不再试验了。

启动 server 测试
==================

是不是都是中文了呢？如果不是，看一看是否浏览器没有设置成接受 ``zh-cn`` 。