The Django Book

Chapter 18: Internationalization

第十八章 国际化

Django was originally developed smack in the middle of the United States (literally; Lawrence, Kansas, is less than 40 miles from the geographic center of the continental United States). Like most open source projects, though, Djangos community grew to include people from all over the globe. As Djangos community became increasingly diverse, internationalization and localization became increasingly important. Since many developers have at best a fuzzy understanding of these terms, well define them briefly.

Django诞生于美国,和许多其他的开源软件一样,Django社区发展中得到了全球范围的支持。所以Djano社区的国际化应用变得非常重要。由于大量开发者对本章内容比较困惑,所以我们将详细介绍。

Internationalization refers to the process of designing programs for the potential use of any locale. This includes marking text (like UI elements and error messages) for future translation, abstracting the display of dates and times so that different local standards may be observed, providing support for differing time zones, and generally making sure that the code contains no assumptions about the location if its users. Youll often see internationalization abbreviated I18N (the number 18 refers to the number of letters omitted between the initial I and the terminal N).

国际化是指为了在任何其它地区使用该软件而进行设计的过程。它包括为了以后的翻译而标记文本(比如用户界面控件和错误信息等),提取出日期和时间的显示以保证显示遵循不同地区的标准,为不同时区提供支持,并且在一般情况下确保代码中不会有关于使用者所在地区的假设。您可以经常看到国际化被缩写为“I18N”(18表示Internationlization这个单词首字母I和结尾字母N之间的字母有18个)。

Localization refers to the process of actually translating an internationalized program for use in a particular locale. Youll sometimes see localization abbreviated as L10N .

本地化是指使一个国际化的程序为了在某个特定地区使用而进行翻译的过程。有时,本地化缩写为 L10N

Django itself is fully internationalized; all strings are marked for translation, and settings control the display of locale-dependent values like dates and times. Django also ships with over 40 different localization files. If youre not a native English speaker, theres a good chance that Django is already is translated into your primary language.

Django本身是完全国际化的;所有的字符串均被标记为需要翻译,而且在选项中可以设置区域选项(如时间和日期)的显示。Django是带着40个不同的本地化文件发行的。即使您不是以英语作为母语的,也很有可能Django已经被翻译为您的母语了。

The same internationalization framework used for these localizations is available for you to use in your own code and templates.

这些本地化文件所使用的国际化框架同样也可以被用在您自己的代码和模板中。

In a nutshell, youll need to add a minimal number of hooks to your Python code and templates. These hooks are called translation strings . They tell Django, This text should be translated into the end users language, if a translation for this text is available in that language.

简要地说,您只需要添加少量的hook到您的Python代码和模板中。这些hook被称为“翻译字符串”。它们告诉Django,如果这段文本可以被翻译为终端用户语言,那么就翻译这段文本。

Django takes care of using these hooks to translate Web applications, on the fly, according to users language preferences.

Django根据用户的语言偏好来使用这些hook去翻译Web应用程序。

Essentially, Django does two things:

本质上来说,Django做这两件事情:

  • It lets developers and template authors specify which parts of their applications should be translatable.

  • 由开发者和模板的作者指定他们的应用程序的哪些部分是需要被翻译的。

  • It uses that information to translate Web applications for particular users according to their language preferences.

  • Django根据用户的语言偏好来翻译Web应用程序。

Note

备注:

Djangos translation machinery uses GNU gettext (http://www.gnu.org/software/gettext/) via the standard gettext module that comes with Python.

Django的翻译机制是使用 GNU gettext (http://www.gnu.org/software/gettext/),具体为Python标准模块 gettext

If You Dont Need Internationalization:

如果您不需要国际化:

Djangos internationalization hooks are enabled by default, which incurs a small bit of overhead. If you dont use internationalization, you should set USE_I18N = False in your settings file. If USE_I18N is set to False , then Django will make some optimizations so as not to load the internationalization machinery.

Django国际化的hook默认是开启的,这可能会给Django增加一点点负担。如果您不需要国际化支持,那么您可以在您的设置文件中设置 USE_I18N = False 。如果 USE_I18N 被设为 False ,那么Django会进行一些优化,而不加载国际化支持机制。

Youll probably also want to remove 'django.core.context_processors.i18n' from your TEMPLATE_CONTEXT_PROCESSORS setting.

您也可以从您的 TEMPLATE_CONTEXT_PROCESSORS 设置中移除 'django.core.context_processors.i18n'

Specifying Translation Strings in Python Code

在Python代码中指定翻译字符串

Translation strings specify This text should be translated. These strings can appear in your Python code and templates. Its your responsibility to mark translatable strings; the system can only translate strings it knows about.

翻译字符串指定这段文本需要被翻译。这些字符串出现在您的Python代码和模板中。您需要做的是标记出这些翻译字符串;而系统只会翻译出它所知道的东西。

Standard Translation Functions

标准的翻译函数

Specify a translation string by using the function _() . (Yes, the name of the function is the underscore character.) This function is available globally (i.e., as a built-in language); you dont have to import it.

ocf07R <a href=”http://qpopfudogomm.com/“>qpopfudogomm</a>, [url=http://zlxzyubhhfhd.com/]zlxzyubhhfhd[/url], [link=http://dbedjhghgiph.com/]dbedjhghgiph[/link], http://opgcbbdwditr.com/

In this example, the text "Welcome to my site." is marked as a translation string:

在下面这个例子中,这段文本 "Welcome to my site" 被标记为翻译字符串:

def my_view(request):
    output = _("Welcome to my site.")
    return HttpResponse(output)

The function django.utils.translation.gettext() is identical to _() . This example is identical to the previous one:

函数 django.utils.translation.gettext()_() 是相同的。下面这个例子与前一个例子没有区别:

from django.utils.translation import gettext
def my_view(request):
    output = gettext("Welcome to my site.")
    return HttpResponse(output)

Most developers prefer to use _() , as its shorter.

大多数开发者喜欢使用 _() , 因为它比较短小.

Translation works on computed values. This example is identical to the previous two:

翻译字符串对于语句同样有效。下面这个例子和前面两个例子相同:

def my_view(request):
    words = ['Welcome', 'to', 'my', 'site.']
    output = _(' '.join(words))
    return HttpResponse(output)

Translation works on variables. Again, heres an identical example:

翻译也可以对变量进行。同样的例子:

def my_view(request):
    sentence = 'Welcome to my site.'
    output = _(sentence)
    return HttpResponse(output)

(The caveat with using variables or computed values, as in the previous two examples, is that Djangos translation-string-detecting utility, make-messages.py , wont be able to find these strings. More on make-messages later.)

(如以上两个例子所示地使用变量或语句,需要注意的一点是Django的翻译字符串检测工具, make-messages.py ,不能找到这些字符串。在后面的内容中会继续讨论这个问题。)

The strings you pass to _() or gettext() can take placeholders, specified with Pythons standard named-string interpolation syntax, for example:

传递给 _()gettext() 的字符串可以接受由Python标准字典型对象的格式化字符串表达式指定的占位符,比如:

def my_view(request, n):
    output = _('%(name)s is my name.') % {'name': n}
    return HttpResponse(output)

This technique lets language-specific translations reorder the placeholder text. For example, an English translation may be "Adrian is my name." , while a Spanish translation may be "Me llamo Adrian." , with the placeholder (the name) placed after the translated text instead of before it.

这项技术使得特定语言的译文可以对这段文本进行重新排序。比如,一段文本的英语翻译为 "Adrian is my name." ,而西班牙语翻译为 "Me llamo Adrian." ,此时,占位符(即name)实在被翻译的文本之前而不是之后。

For this reason, you should use named-string interpolation (e.g., %(name)s ) instead of positional interpolation (e.g., %s or %d ). If you use positional interpolation, translations wont be able to reorder placeholder text.

正因为如此,您应该使用字典型对象的格式化字符串(比如, %(name)s ),而不是针对位置的格式化字符串(比如, %s%d )。如果您使用针对位置的格式化字符串,翻译机制将无法重新安排包含占位符的文本。

Marking Strings As No-op

标记字符串为不操作

Use the function django.utils.translation.gettext_noop() to mark a string as a translation string without actually translating it at that moment. Strings thus marked arent translated until the last possible moment.

Use this approach if you have constant strings that should be stored in the original language such as strings in a database but should be translated at the last possible point in time, such as when the string is presented to the user.

使用这种方法的环境是,有字符串必须以原始语言的形式存储(如储存在数据库中的字符串)而在最后需要被翻译出来,如当其在用户前显示出来时。

Lazy Translation

惰性翻译

Use the function django.utils.translation.gettext_lazy() to translate strings lazily when the value is accessed rather than when the gettext_lazy() function is called.

使用 django.utils.translation.gettext_lazy() 函数,使得其中的值只有在访问时才会被翻译,而不是在 gettext_lazy() 被调用时翻译。

For example, to mark a fieldss help_text attribute as translatable, do the following:

比如,要标记 help_text 列是需要翻译的,可以这么做:

from django.utils.translation import gettext_lazy

class MyThing(models.Model):
    name = models.CharField(help_text=gettext_lazy('This is the help text'))

In this example, gettext_lazy() stores a lazy reference to the string not the actual translation. The translation itself will be done when the string is used in a string context, such as template rendering on the Django admin site.

在这个例子中, gettext_lazy() 将字符串作为惰性翻译字符串存储,此时并没有进行翻译。翻译工作将在字符串在字符串上下文中被用到时进行,比如在Django管理页面提交模板时。

If you dont like the verbose name gettext_lazy , you can just alias it as _ (underscore), like so:

如果觉得 gettext_lazy 太过冗长,可以用 _ (下划线)作为别名,就像这样:

from django.utils.translation import gettext_lazy as _

class MyThing(models.Model):
    name = models.CharField(help_text=_('This is the help text'))

Always use lazy translations in Django models (otherwise they wont be translated correctly on a per-user basis). And its a good idea to add translations for the field names and table names, too. This means writing explicit verbose_name and verbose_name_plural options in the Meta class:

在Django模型中最好一直使用惰性翻译(除非这样翻译的结果无法正确地显示)。同时,对于列名和表名最好也能进行翻译。这需要在 Meta 中明确 verbose_nameverbose_name_plural 的值:

from django.utils.translation import gettext_lazy as _

class MyThing(models.Model):
    name = models.CharField(_('name'), help_text=_('This is the help text'))
    class Meta:
        verbose_name = _('my thing')
        verbose_name_plural = _('mythings')

Pluralization

复数的处理

Use the function django.utils.translation.ngettext() to specify messages that have different singular and plural forms, for example:

使用 django.utils.translation.ngettext() 函数来指定有单数和复数形式之分的信息,比如:

from django.utils.translation import ngettext
def hello_world(request, count):
    page = ngettext(
        'there is %(count)d object',
        'there are %(count)d objects', count
    ) % {'count': count}
    return HttpResponse(page)

ngettext takes three arguments: the singular translation string, the plural translation string, and the number of objects (which is passed to the translation languages as the count variable).

ngettext 函数包括三个参数:单数形式的翻译字符串,复数形式的翻译字符串,和对象的个数(将以 count 变量传递给需要翻译的语言)。

Specifying Translation Strings in Template Code

在模板中指定翻译字符串

Using translations in Django templates uses two template tags and a slightly different syntax than in Python code. To give your template access to these tags, put {% load i18n %} toward the top of your template.

Django模板使用两种模板标签,且语法格式与Python代码有些许不同。为了使得模板访问到标签,需要将 {% load i18n %} 放在模板最前面。

The {% trans %} template tag marks a string for translations:

{% trans %} 模板标签标记需要翻译的字符串:

<title>{% trans "This is the title." %}</title>

If you only want to mark a value for translation, but translate it later, use the noop option:

如果只需要标记字符串而以后再翻译,可以使用 noop 选项:

<title>{% trans "value" noop %}</title>

Its not possible to use template variables in {% trans %} only constant strings, in single or double quotes, are allowed. If your translations require variables (placeholders), use {% blocktrans %} , for example:

{% trans %} 中不允许使用模板中的变量,只能使用单引号或双引号中的字符串。如果翻译时需要用到变量(占位符),可以使用 {% blocktrans %} ,比如:

{% blocktrans %}This will have {{ value }} inside.{% endblocktrans %}

To translate a template expression say, using template filters you need to bind the expression to a local variable for use within the translation block:

使用模板过滤器来翻译一个模板表达式,需要在翻译的这段文本中将表达式绑定到一个本地变量中:

{% blocktrans with value|filter as myvar %}
  This will have {{ myvar }} inside.
{% endblocktrans %}

If you need to bind more than one expression inside a blocktrans tag, separate the pieces with and :

如果需要在 blocktrans 标签内绑定多个表达式,可以用 and 来分隔:

{% blocktrans with book|title as book_t and author|title as author_t %}
  This is {{ book_t }} by {{ author_t }}
{% endblocktrans %}

To pluralize, specify both the singular and plural forms with the {% plural %} tag, which appears within {% blocktrans %} and {% endblocktrans %} , for example:

为了表示单复数相关的内容,需要在 {% blocktrans %}{% endblocktrans %} 之间使用 {% plural %} 标签来指定单复数形式,例如:

{% blocktrans count list|length as counter %}
  There is only one {{ name }} object.
{% plural %}
  There are {{ counter }} {{ name }} objects.
{% endblocktrans %}

Internally, all block and inline translations use the appropriate gettext /ngettext call.

其内在机制是,所有的块和内嵌翻译调用相应的 gettextngettext

When you use RequestContext (see Chapter 10), your templates have access to three translation-specific variables:

使用 RequestContext (见第10章)时,模板可以访问三个针对翻译的变量:

  • {{ LANGUAGES }} is a list of tuples in which the first element is the language code and the second is the language name (in that language).

  • {{ LANGUAGES }} 是一系列元组组成的列表,每个元组的第一个元素是语言代码,第二个元素是用该语言表示的语言名称。

  • {{ LANGUAGE_CODE }} is the current users preferred language, as a string (e.g., en-us ). (See the How Django Discovers Language Preference section for more information.)

  • {{ LANGUAGE_CODE }} 是以字符串表示的当前用户偏好语言(例如, en-us )。(详见 Django 如何确定语言偏好。)

  • {{ LANGUAGE_BIDI }} is the current languages writing system. If True , its a right-to-left language (e.g., Hebrew, Arabic). If False , its a left-to-right language (e.g., English, French, German).

  • {{ LANGUAGE_BIDI }} 是当前语言的书写方式。若设为 True ,则该语言书写方向为从右到左(如希伯来语和阿拉伯语);若设为 False ,则该语言书写方向为从左到右(如英语、法语和德语)。

You can also load these values using template tags:

你也可以通过使用模板标签来加载这些变量:

{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_current_language_bidi as LANGUAGE_BIDI %}

Translation hooks are also available within any template block tag that accepts constant strings. In those cases, just use _() syntax to specify a translation string, for example:

翻译的hook在任何接受常量字符串的模板块标签内也是可以使用的。此时,使用 _() 表达式来指定翻译字符串,例如:

{% some_special_tag _("Page not found") value|yesno:_("yes,no") %}

In this case, both the tag and the filter will see the already-translated string (i.e., the string is translated before being passed to the tag handler functions), so they dont need to be aware of translations.

OQy5ZT <a href=”http://zicjwleradwz.com/“>zicjwleradwz</a>, [url=http://klbbjyqcrtdt.com/]klbbjyqcrtdt[/url], [link=http://adylkqwpfagm.com/]adylkqwpfagm[/link], http://bghiparjlrvr.com/

Creating Language Files

创建语言文件

Once youve tagged your strings for later translation, you need to write (or obtain) the language translations themselves. In this section we explain how that works.

当你标记了翻译字符串,你就需要写出(或获取已有的)对应的语言翻译信息。在这一节中我们将解释如何使它起作用。

Creating Message Files

创建信息文件

The first step is to create a message file for a new language. A message file is a plain-text file representing a single language that contains all available translation strings and how they should be represented in the given language. Message files have a .po file extension.

第一步,就是为一种语言创建一个信息文件。一个信息文件是包含了某一语言翻译字符串和对这些字符串的翻译的一个文本文件。信息文件以 .po 为后缀名。

Django comes with a tool, bin/make-messages.py , that automates the creation and maintenance of these files.

Django中带有一个工具, bin/make-messages.py ,它完成了这些文件的创建和维护工作。

To create or update a message file, run this command:

运行以下命令来创建或更新一个信息文件:

bin/make-messages.py -l de

where de is the language code for the message file you want to create. The language code, in this case, is in locale format. For example, its pt_BR for Brazilian Portuguese and de_AT for Austrian German. Take a look at thelanguage codes in the django/conf/locale/ directory to see which languages are currently supported.

其中 de 是所创建的信息文件的语言代码。在这里,语言代码是以本地格式给出的。例如,巴西地区的葡萄牙语为 pt_BR ,澳大利亚地区的德语为 de_AT 。可查看 django/conf/locale 目录获取Django所支持的语言代码。

The script should be run from one of three places:

这段脚本应该在三处之一运行:

  • The root django directory (not a Subversion checkout, but the one that is linked to via $PYTHONPATH or is located somewhere on that path)

  • django 根目录(不是Subversion检出目录,而是通过 $PYTHONPATH 链接或位于该路径的某处)

  • The root directory of your Django project

  • Django项目根目录

  • The root directory of your Django application

  • Django应用程序根目录

The script runs over the entire tree it is run on and pulls out all strings marked for translation. It creates (or updates) a message file in the directory conf/locale . In the de example, the file will be conf/locale/de/LC_MESSAGES/django.po .

该脚本作用于所在的整个目录树,并且抽取所有被标记的字符串以进行翻译。它在 conf/locale 目录下创建(或更新)了一个信息文件。在上面这个例子中,这个信息文件是 conf/locale/de/LC_MESSAGES/django.po

If run over your project source tree or your application source tree, it will do the same, but the location of the locale directory is locale/LANG/LC_MESSAGES (note the missing conf prefix). The first time you run it on your tree youll need to create the locale directory.

运行于项目源码树或应用程序源码树下时,该脚本完成同样的功能,但是此时locale目录的位置为 locale/LANG/LC_MESSAGES (注意没有``conf``前缀)。在第一次运行时需要创建 locale 目录。

No gettext?

没有gettext?

If you dont have the gettext utilities installed, make-messages.py will create empty files. If thats the case, either install the gettext utilities or just copy the English message file (conf/locale/en/LC_MESSAGES/django.po ) and use it as a starting point; its just an empty translation file.

如果没有安装 gettext 组件, make-messages.py 将会创建空白文件。这种情况下,安装 gettext 组件或只是复制英语信息文件( conf/locale/en/LC_MESSAGES/django.po )来作为一个起点;只是一个空白的翻译信息文件而已。

The format of .po files is straightforward. Each .po file contains a small bit of metadata, such as the translation maintainers contact information, but the bulk of the file is a list of messages simple mappings between translation strings and the actual translated text for the particular language.

.po 文件格式很直观。每个 .po 文件包含一小部分的元数据,比如翻译维护人员的联系信息,而文件的大部分内容是简单的翻译字符串和对应语言翻译结果的映射关系的列表。

For example, if your Django application contains a translation string for the text "Welcome to my site." , like so:

举个例子,如果Django应用程序包括一个 "Welcome to my site." 的翻译字符串,像这样:

_("Welcome to my site.")

then make-messages.py will have created a .po file containing the following snippet a message:

make-message.py 将创建一个包含以下片段的 .po 文件:

#: path/to/python/module.py:23
msgid "Welcome to my site."
msgstr ""

A quick explanation is in order:

按顺序简单解释一下:

  • msgid is the translation string, which appears in the source. Dont change it.

  • msgid 是在源文件中出现的翻译字符串。不要做改动。

  • msgstr is where you put the language-specific translation. It starts out empty, so its your responsibility to change it. Make sure you keep the quotes around your translation.

  • msgstr 是相应语言的翻译结果。刚创建时它只是空字符串,此时就需要你来完成它。注意不要丢掉语句前后的引号。

  • As a convenience, each message includes the file name and line number from which the translation string was gleaned.

  • 方便起见,每一条信息包含了翻译字符串所在文件的文件名和行数。

Long messages are a special case. The first string directly after msgstr (or msgid ) is an empty string. Then the content itself will be written over the next few lines as one string per line. Those strings are directly concatenated. Dont forget trailing spaces within the strings; otherwise, theyll be tacked together without whitespace!

对于比较长的信息也有其处理方法。 msgstr (或 msgid )后紧跟着的字符串为一个空字符串。然后真正的内容在其下面的几行。这些字符串会被直接连在一起。同时,不要忘了字符串末尾的空格,因为它们会不加空格地连到一起。

For example, heres a multiline translation (taken from the Spanish localization that ships with Django):

比如,以下是一个多行翻译(取自随Django发行的西班牙本地化文件):

msgid ""
"There's been an error. It's been reported to the site administrators via e-"
"mail and should be fixed shortly. Thanks for your patience."
msgstr ""
"Ha ocurrido un error. Se ha informado a los administradores del sitio "
"mediante correo electrnico y debera arreglarse en breve. Gracias por su "
"paciencia."

Note the trailing spaces.

注意每一行结尾的空格。

Mind Your Charset

注意字符集

When creating a .po file with your favorite text editor, first edit the charset line (search for "CHARSET" ) and set it to the charset youll be using to edit the content. Generally, UTF-8 should work for most languages, but gettext should handle any charset you throw at it.

当你使用喜爱的文本编辑器创建 .po 文件时,首先请编辑字符集行(搜索 "CHARSET" ),并将其设为你将使用的字符集。一般说来,UTF-8对绝大多数语言有效,不过 gettext 会处理任何你所使用的字符集。

To reexamine all source code and templates for new translation strings and update all message files for all languages, run this:

若要对新创建的翻译字符串校验所有的源代码和模板中,并且更新所有语言的信息文件,可以运行以下命令:

make-messages.py -a

Compiling Message Files

编译信息文件

After you create your message file, and each time you make changes to it, youll need to compile it into a more efficient form, for use by gettext . Do this with the bin/compile-messages.py utility.

创建信息文件之后,每次对其做了修改,都需要将它重新编译成一种更有效率的形式,供 gettext 使用。使用 `` bin/compile-messages.py `` 来完成这项工作。

This tool runs over all available .po files and creates .mo files, which are binary files optimized for use by gettext . In the same directory from which you ran make-messages.py , run compile-messages.py like this:

这个工具作用于所有有效的 .po 文件,创建优化过的二进制 .mo 文件供 gettext 使用。在运行 make-messages.py 的同一目录下,运行 compile-messages.py

bin/compile-messages.py

Thats it. Your translations are ready for use.

就是这样了。你的翻译成果已经可以使用了。

How Django Discovers Language Preference

Django如何处理语言偏好

Once youve prepared your translations or, if you just want to use the translations that are included with Django youll just need to activate translation for your application.

一旦你准备好了翻译,如果希望在Django中使用,那么只需要激活这些翻译即可。

Behind the scenes, Django has a very flexible model of deciding which language should be used installation-wide, for a particular user, or both.

在这些功能背后,Django拥有一个灵活的模型来确定在安装和使用应用程序的过程中选择使用的语言。

To set an installation-wide language preference, set LANGUAGE_CODE in your settings file. Django uses this language as the default translation the final attempt if no other translator finds a translation.

若要在整个安装和使用过程中确定语言偏好,就要在设置文件中设置 LANGUAGE_CODE 。Django将用指定的语言来进行翻译,如果没有其它的翻译器发现要进行翻译的语句,这就是最后一步了。

If all you want to do is run Django with your native language, and a language file is available for your language, simply set LANGUAGE_CODE .

如果你只是想要用本地语言来运行Django,并且该语言的语言文件存在,只需要简单地设置 LANGUAGE_CODE 即可。

If you want to let each individual user specify the language he or she prefers, use LocaleMiddleware . LocaleMiddleware enables language selection based on data from the request. It customizes content for each user.

如果要让每一个使用者各自指定语言偏好,就需要使用 LocaleMiddlewareLocaleMiddleware 使得Django基于请求的数据进行语言选择,从而为每一位用户定制内容。

To use LocaleMiddleware , add 'django.middleware.locale.LocaleMiddleware' to your MIDDLEWARE_CLASSES setting. Because middleware order matters, you should follow these guidelines:

使用 LocaleMiddleware 需要在 MIDDLEWARE_CLASSES 设置中增加 'django.middleware.locale.LocaleMiddleware' 。中间件的顺序是有影响的,最好按照依照以下要求:

  • Make sure its among the first middleware classes installed.

  • 保证它是第一批安装的中间件类。

  • It should come after SessionMiddleware , because LocaleMiddleware makes use of session data.

  • 因为 LocalMiddleware 要用到session数据,所以需要放在 SessionMiddleware 之后。

  • If you use CacheMiddleware , put LocaleMiddleware after it (otherwise users could get cached content from the wrong locale).

  • 如果使用了 CacheMiddleware ,将 LocaleMiddleware 放在 CacheMiddleware 之后(否则用户可能会从错误的本地化文件中取得缓冲数据)。

For example, your MIDDLEWARE_CLASSES might look like this:

例如, MIDDLE_CLASSES 可能会是如此:

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

LocaleMiddleware tries to determine the users language preference by following this algorithm:

LocaleMiddleware 按照如下算法确定用户的语言:

  • First, it looks for a django_language key in the current users session.

  • 首先,在当前用户 session 的中查找键 django_language 的值;

  • Failing that, it looks for a cookie called django_language .

  • 如果失败的话,接着查找名为 django_language 的 cookie ;

  • Failing that, it looks at the Accept-Language HTTP header. This header is sent by your browser and tells the server which language(s) you prefer, in order of priority. Django tries each language in the header until it finds one with available translations.

  • 还是失败的话,就在 HTTP 请求头部查找 Accept-Language 关键字的值,该关键字是你的浏览器发送的,按优先顺序告诉服务器你的语言偏好。Django 会根据这些语言的顺序逐一搜索直到发现可用的翻译;

  • Failing that, it uses the global LANGUAGE_CODE setting.

  • 以上都失败了的话, 就使用全局的 LANGUAGE_CODE 设定值。

In each of these places, the language preference is expected to be in the standard language format, as a string. For example, Brazilian Portuguese is pt-br . If a base language is available but the sub-language specified is not, Django uses the base language. For example, if a user specifies de-at (Austrian German) but Django only has de available, Django uses de .

在上述每一处,语言偏好应作为字符串,以标准的语言格式出现。比如,巴西地区的葡萄牙语表示为 pt-br 。如果Django中只有基本语言而没有其衍生的字语言的话,Django将只是用基本语言。比如,如果用户指定了 de-at (澳式德语)但Django只有针对 de 的翻译,那么 de 会被选用。

Only languages listed in the LANGUAGES setting can be selected. If you want to restrict the language selection to a subset of provided languages (because your application doesnt provide all those languages), set your LANGUAGES setting to a list of languages, for example:

只有在 LANGUAGES 设置中列出的语言才能被选用。若希望将语言限制为所提供语言中的某些(因为应用程序并不提供所有语言的表示),则将 LANGUAGES 设置为所希望提供语言的列表,例如:

LANGUAGES = (
    ('de', _('German')),
    ('en', _('English')),
)

This example restricts languages that are available for automatic selection to German and English (and any sub-language, like de-ch or en-us ).

上面这个例子限制了语言偏好只能是德语和英语(包括它们的子语言,如 de-chen-us )。

If you define a custom LANGUAGES , its OK to mark the languages as translation strings but use a dummy gettext() function, not the one in django.utils.translation . You should never import django.utils.translation from within your settings file, because that module itself depends on the settings, and that would cause a circular import.

如果自定义了 LANGUAGES ,将语言标记为翻译字符串是可以的,但是,请不要使用 django.utils.translation 中的 gettext() (决不要在settings文件中导入 django.utils.translation ,因为这个模块本身是依赖于settings,这样做会导致无限循环),而是使用一个“虚构的” gettext()

The solution is to use a dummy gettext() function. Heres a sample settings file:

解决方案就是使用一个“虚假的” gettext() 。以下是一个settings文件的例子:

_ = lambda s: s

LANGUAGES = (
      ('de', _('German')),
      ('en', _('English')),
)

With this arrangement, make-messages.py will still find and mark these strings for translation, but the translation wont happen at runtime, so youll have to remember to wrap the languages in the real gettext() in any code that uses LANGUAGES at runtime.

这样做的话, make-messages.py 仍会寻找并标记出将要被翻译的这些字符串,但翻译不会再运行时进行,故而需要在任何使用 LANGUAGES 的代码中用“真实的” gettext() 来修饰这些语言。

The LocaleMiddleware can only select languages for which there is a Django-provided base translation. If you want to provide translations for your application that arent already in the set of translations in Djangos source tree, youll want to provide at least basic translations for that language. For example, Django uses technical message IDs to translate date formats and time formats so you will need at least those translations for the system to work correctly.

LocaleMiddleware 只能选择那些Django已经提供了基础翻译的语言。如果想要在应用程序中对Django中还没有基础翻译的语言提供翻译,那么必须至少先提供该语言的基本的翻译。例如,Django使用特定的信息ID来翻译日期和时间格式,故要让系统正常工作,至少要提供这些基本的翻译。

A good starting point is to copy the English .po file and to translate at least the technical messages, and maybe the validator messages, too.

以英语的 .po 文件为基础,翻译其中的技术相关的信息,可能还包括一些使之生效的信息。这会是一个好的开始。

Technical message IDs are easily recognized; theyre all uppercase. You dont translate the message ID as with other messages; rather, you provide the correct local variant on the provided English value. For example, with DATETIME_FORMAT (or DATE_FORMAT or TIME_FORMAT ), this would be the format string that you want to use in your language. The format is identical to the format strings used by the now template tag.

技术相关的信息ID很容易被人出来:它们都是大写的。这些信息ID的翻译与其他信息不同:你需要提供其对应的本地化内容。例如,对于 DATETIME_FORMAT (或 DATE_FORMATTIME_FORMAT ),应该提供希望在该语言中使用的格式化字符串。格式和 now 模板标签中使用的格式化字符串一样。

Once LocaleMiddleware determines the users preference, it makes this preference available as request.LANGUAGE_CODE for each request object. Feel free to read this value in your view code. Heres a simple example:

一旦 LocalMiddleware 确定了用户的使用偏好,就将其以 request.LANGUAGE_CODE 的形式提供给每个请求对象。如此,在视图代码中就可以自由使用了。以下是一个简单的例子:

def hello_world(request, count):
    if request.LANGUAGE_CODE == 'de-at':
        return HttpResponse("You prefer to read Austrian German.")
    else:
        return HttpResponse("You prefer to read another language.")

Note that, with static (i.e. without middleware) translation, the language is in settings.LANGUAGE_CODE , while with dynamic (middleware) translation, its in request.LANGUAGE_CODE .

注意,静态翻译(即不经过中间件)中的语言设置是在 settings.LANGUAGE_CODE 中的,而动态翻译(即使用了中间件)的语言设置实在 request.LANGUAGE_CODE

The set_language Redirect View

set_language重定向视图

As a convenience, Django comes with a view, django.views.i18n.set_language , that sets a users language preference and redirects back to the previous page.

方便起见,Django自带了一个 django.views.i18n.set_language 视图,作用是设置用户语言偏好并重定向返回到前一页面。

Activate this view by adding the following line to your URLconf:

在URLconf中加入下面这行代码来激活这个视图:

(r'^i18n/', include('django.conf.urls.i18n')),

(Note that this example makes the view available at /i18n/setlang/ .)

(注意这个例子使得这个视图在 /i18n/setlang/ 中有效。)

The view expects to be called via the GET method, with a language parameter set in the query string. If session support is enabled, the view saves the language choice in the users session. Otherwise, it saves the language choice in a django_language cookie.

这个视图是通过 GET 方法调用的,在Query String中包含了 language 参数。如果session已启用,这个视图会将语言选择保存在用户的session中。否则,语言选择将被保存在名为 django_language 的cookie中。

After setting the language choice, Django redirects the user, following this algorithm:

保存了语言选择后,Django根据以下算法来重定向页面:

  • Django looks for a next parameter in the query string.

  • Django在提交的Query String中寻找 next 参数。

  • If that doesnt exist or is empty, Django tries the URL in the Referer header.

  • 如果 next 参数不存在或为空,Django尝试重定向页面为HTML头部信息中 Referer 的值。

  • If thats empty say, if a users browser suppresses that header then the user will be redirected to / (the site root) as a fallback.

  • 如果 Referer 也是空的,即该用户的浏览器并不发送 Referer 头信息,则页面将重定向到 / (页面根目录)。

Heres example HTML template code:

这是一个HTML模板代码的例子:

<form action="/i18n/setlang/" method="get">
<input name="next" type="hidden" value="/next/page/" />
<select name="language">
{% for lang in LANGUAGES %}
<option value="{{ lang.0 }}">{{ lang.1 }}</option>
{% endfor %}
</select>
<input type="submit" value="Go" />
</form>

Using Translations in Your Own Projects

在你自己的项目中使用翻译

Django looks for translations by following this algorithm:

Django使用以下算法寻找翻译:

  • First, it looks for a locale directory in the application directory of the view thats being called. If it finds a translation for the selected language, the translation will be installed.

  • 首先,Django在该视图所在的应用程序文件夹中寻找 locale 目录。若找到所选语言的翻译,则加载该翻译。

  • Next, it looks for a locale directory in the project directory. If it finds a translation, the translation will be installed.

  • 第二步,Django在项目目录中寻找 locale 目录。若找到翻译,则加载该翻译。

  • Finally, it checks the base translation in django/conf/locale .

  • 最后,Django使用 django/conf/locale 目录中的基本翻译。

This way, you can write applications that include their own translations, and you can override base translations in your project path. Or, you can just build a big project out of several applications and put all translations into one big project message file. The choice is yours.

以这种方式,你可以创建包含独立翻译的应用程序,可以覆盖项目中的基本翻译。或者,你可以创建一个包含几个应用程序的大项目,并将所有需要的翻译放在一个大的项目信息文件中。决定权在你手中。

Note

注意

If youre using manually configured settings, the locale directory in the project directory will not be examined, since Django loses the ability to work out the location of the project directory. (Django normally uses the location of the settings file to determine this, and a settings file doesnt exist if youre manually configuring your settings.)

如果是使用手动配置的settings文件,因Django无法获取项目目录的位置,所以项目目录下的 locale 目录将不会被检查。(Django一般使用settings文件的位置来确定项目目录,而若手动配置settings文件,则settings文件不会在该目录中。)

All message file repositories are structured the same way:

所有的信息文件库都是以同样方式组织的:

  • $APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)

  • $APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)

  • $PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)

  • $PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)

  • All paths listed in LOCALE_PATHS in your settings file are searched in that order for <language>/LC_MESSAGES/django.(po|mo)

  • 所有在settings文件中 LOCALE_PATHS 中列出的路径以其列出的顺序搜索 <language>/LC_MESSAGES/django.(po|mo)

  • $PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)

  • $PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)

To create message files, you use the same make-messages.py tool as with the Django message files. You only need to be in the right place in the directory where either the conf/locale (in case of the source tree) or the locale/ (in case of application messages or project messages) directory is located. And you use the same compile-messages.py to produce the binary django.mo files that are used by gettext .

要创建信息文件,也是使用 make-messages.py 工具,和Django信息文件一样。需要做的就是改变到正确的目录下—— conf/locale (在源码树的情况下)或者 locale/ (在应用程序信息或项目信息的情况下)所在的目录下。同样地,使用 compile-messages.py 生成 gettext 需要使用的二进制 django.mo 文件。

Application message files are a bit complicated to discover they need the LocaleMiddleware . If you dont use the middleware, only the Django message files and project message files will be processed.

应用程序信息文件稍微难以发现——因为它们需要 LocaleMiddle 。如果不使用中间件,Django只会处理Django的信息文件和项目的信息文件。

Finally, you should give some thought to the structure of your translation files. If your applications need to be delivered to other users and will be used in other projects, you might want to use application-specific translations. But using application-specific translations and project translations could produce weird problems with make-messages . make-messages will traverse all directories below the current path and so might put message IDs into the project message file that are already in application message files.

最后,需要考虑一下翻译文件的结构。若应用程序要发放给其他用户,应用到其它项目中,可能需要使用应用程序相关的翻译。但是,使用应用程序相关的翻译和项目翻译在使用 make-messages 时会产生古怪的问题。 make-messages 会遍历当前路径下的所有目录,所以可能会将应用程序信息文件已有的信息ID放在项目信息文件中。

The easiest way out is to store applications that are not part of the project (and so carry their own translations) outside the project tree. That way, make-messages on the project level will only translate strings that are connected to your explicit project and not strings that are distributed independently.

最容易的解决方法就是将不属于项目的应用程序(因此附带着本身的翻译)存储在项目树之外。这样做的话,项目级的 make-messages 将只会翻译与项目精确相关的,而不包括那些独立发布的应用程序中的字符串。

Translations and JavaScript

翻译与JavaScript

Adding translations to JavaScript poses some problems:

将翻译添加到JavaScript会引起一些问题:

  • JavaScript code doesnt have access to a gettext implementation.

  • JavaScript代码无法访问一个 gettext 的实现。

  • JavaScript code doesnt have access to .po or .mo files; they need to be delivered by the server.

  • JavaScript代码无法访问 .po.mo 文件,它们需要由服务器分发。

  • The translation catalogs for JavaScript should be kept as small as possible.

  • 针对JavaScript的翻译目录应尽量小。

Django provides an integrated solution for these problems: it passes the translations into JavaScript, so you can call gettext and friends from within JavaScript.

Django已经提供了一个集成解决方案:它会将翻译传递给JavaScript,因此就可以在JavaScript中调用 gettext 之类的代码。

The javascript_catalog View

javascript_catalog视图

The main solution to these problems is the javascript_catalog view, which generates a JavaScript code library with functions that mimic the gettext interface, plus an array of translation strings. Those translation strings are taken from the application, project, or Django core, according to what you specify in either the info_dict or the URL.

这些问题的主要解决方案就是 javascript_catalog 视图。该视图生成一个JavaScript代码库,包括模仿 gettext 接口的函数,和翻译字符串的数组。这些翻译字符串来自应用程序,项目,或者Django核心,具体由 info_dict 或URL来确定。

You hook it up like this:

像这样使用:

js_info_dict = {
    'packages': ('your.app.package',),
}

urlpatterns = patterns('',
    (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
)

Each string in packages should be in Python dotted-package syntax (the same format as the strings in INSTALLED_APPS ) and should refer to a package that contains a locale directory. If you specify multiple packages, all those catalogs are merged into one catalog. This is useful if youre depending upon JavaScript that uses strings from different applications.

packages 里的每个字符串应该是Python中的点分割的包的表达式形式(和在 INSTALLED_APPS 中的字符串相同的格式),而且应指向包含 locale 目录的包。如果指定了多个包,所有的目录会合并成一个目录。如果有用到来自不同应用程序的字符串的JavaScript,这种机制会很有帮助。

You can make the view dynamic by putting the packages into the URL pattern:

你可以动态使用视图,将包放在urlpatterns里:

urlpatterns = patterns('',
    (r'^jsi18n/(?P<packages>\S+?)/$, 'django.views.i18n.javascript_catalog'),
)

With this, you specify the packages as a list of package names delimited by plus signs (+ ) in the URL. This is especially useful if your pages use code from different applications, and this changes often and you dont want to pull in one big catalog file. As a security measure, these values can only be either django.conf or any package from the INSTALLED_APPS setting.

这样的话,就可以在URL中指定由加号( + )分隔包名的包了。如果页面使用来自不同应用程序的代码,且经常改变,还不想将其放在一个大的目录文件中,对于这些情况,显然这是很有用的。出于安全考虑,这些值只能是 django.confINSTALLED_APPS 设置中的包。

Using the JavaScript Translation Catalog

使用JavaScript翻译目录

To use the catalog, just pull in the dynamically generated script like this:

要使用这个目录,只要这样引入动态生成的脚本:

<script type="text/javascript" src="/path/to/jsi18n/"></script>

This is how the admin site fetches the translation catalog from the server. When the catalog is loaded, your JavaScript code can use the standard gettext interface to access it:

这就是管理页面如何从服务器获取翻译目录。当目录加载后,JavaScript代码就能通过标准的 gettext 接口进行访问:

document.write(gettext('this is to be translated'));

There even is a ngettext interface and a string interpolation function:

甚至有一个 ngettext 接口和一个字符串查补函数:

d = {
    count: 10
};
s = interpolate(ngettext('this is %(count)s object', 'this are %(count)s objects', d.count), d);

The interpolate function supports both positional interpolation and named interpolation. So the preceding code could have been written as follows:

interpolate 函数支持位置插补和名字查补。因此前面的代码也可以写成这样:

s = interpolate(ngettext('this is %s object', 'this are %s objects', 11), [11]);

The interpolation syntax is borrowed from Python. You shouldnt go over the top with string interpolation, though this is still JavaScript, so the code will have to do repeated regular-expression substitutions. This isnt as fast as string interpolation in Python, so keep it to those cases where you really need it (e.g., in conjunction with ngettext to produce proper pluralization).

插补的语法是借鉴了Python。但不应该超过字符串插补的能力,这仍然还是JavaScript,因此代码将不得不做重复的正则替换。它不会和Python中的字符串插补一样快,因此只有真正需要的时候再使用它(例如,利用 ngettext 生成合适的复数形式)。

Creating JavaScript Translation Catalogs

创建JavaScript翻译目录

You create and update the translation catalogs the same way as the other Django translation catalogs: with the `make-messages.py` tool. The only difference is you need to provide a -d djangojs parameter, like this:

用和其它Django翻译目录相同的方法来创建和更新JavaScript翻译目录:用 `make-messages.py` 工具。唯一的差别是需要提供一个 -d djangojs 的参数,就像这样:

make-messages.py -d djangojs -l de

This creates or updates the translation catalog for JavaScript for German. After updating translation catalogs, just run compile-messages.py the same way as you do with normal Django translation catalogs.

这样来创建或更新JavaScript的德语翻译目录。和普通的Django翻译目录一样,更新了翻译目录后,运行 compile-messages.py 即可。

Notes for Users Familiar with gettext

熟悉 gettext 用户的注意事项

If you know gettext , you might note these special things in the way Django does translation:

如果你了解 gettext ,你可能会发现Django进行翻译时的一些特殊的东西:

  • The string domain is django or djangojs . The string domain is used to differentiate between different programs that store their data in a common message-file library (usually /usr/share/locale/ ). The django domain is used for Python and template translation strings, and is loaded into the global translation catalogs. The djangojs domain is only used for JavaScript translation catalogs to make sure that those are as small as possible.

  • 字符串域为 djangodjangojs 。字符串域是用来区别将数据存储在同一信息文件库(一般是 /usr/share/locale/ )的不同程序。 django 域是为Python和模板翻译字符串服务的,被加载到全局翻译目录。 djangojs 域用在JavaScript翻译目录中,以确保其足够小。

  • Django only uses gettext and gettext_noop . Thats because Django always uses DEFAULT_CHARSET strings internally. There isnt much benefit to using ugettext , because youll always need to produce UTF-8 anyway.

  • Django仅适用 gettextgettext_noop 。这是因为Django总是内在地使用 DEFAULT_CHARSET 字符串。使用 ugettext 并没有什么好处,因为总是需要生成UTF-8。

  • Django doesnt use xgettext alone. It uses Python wrappers around xgettext and msgfmt . Thats mostly for convenience.

  • Django不单独使用 xgettext ,而是经过Python包装后的 xgettextmsgfmt 。这主要是为了方便。

Whats Next?

下一章

This chapter mostly concludes our coverage of Djangos features. You should now know enough to start producing your own Django sites.

这一章基本上已经结束了我们对于Django特性的介绍。你应该已经掌握了创建你自己Django页面的知识。

However, writing the code is only the first step in deploying a successful Web site. The next two chapters cover the things youll need to know if you want your site to survive in the real world. Chapter 19 discuses how you can secure your sites and your users from malicious attackers, and Chapter 20 details how to deploy a Django application onto one or many servers.

然而,编码工作仅仅是部署一个成功网站的第一步。接下来的两章包括了你的网站在网络世界的生存之道。第19章讨论了如何防范恶意攻击,以增强站点的安全性,保护使用者的安全;第20章详述了如何将一个Django应用程序部署到一个或多个服务器上。

Copyright 2006 Adrian Holovaty and Jacob Kaplan-Moss.
This work is licensed under the GNU Free Document License.
Hosting graciously provided by media temple
Chinese translate hosting by py3k.cn. 粤ICP备16122281号-1