为了让Django项目可翻译,你必须添加一些钩子到你的Python 代码和模板中。 这些钩子叫做translation strings。 他们告诉Django:“这个文本应该被翻译成最终用户的语言,如果这个文本的翻译是可用的那种语言。”你有责任标记可翻译的字符串;系统只能翻译它知道的字符串。
Django 提供一些工具用于提取翻译字符串到message file中。 这个文件方便翻译人员提供翻译字符串的目标语言。 翻译人员填充完消息文件后,必须编译它。 这个过程依赖GNU gettext 工具集。
完成这些事情之后,Django 将负责根据用户的语言偏好将网页翻译成对应的语言。
Django 的国际化钩子默认是打开的,这表示框架的某些地方已经有相关的I18N 钩子。 如果你不需要使用国际化,你可以花两秒钟将在设置文件中设置USE_I18N = False
。 这样的话,Django 将做一些优化而不加载国际化的机制。
注
还有一个独立但是相关的设置USE_L10N
,它控制Django 是否应该实现本地化格式。 更多信息参见Format localization。
注
确保您已经为您的项目激活了翻译(最快的方法是检查MIDDLEWARE
是否包含django.middleware.locale.LocaleMiddleware
)。 如果还没有,请参见How Django discovers language preference。
函数ugettext()
用于指定标准的翻译。 习惯上会将它导入成一个别名 _
以节省打字。
注
Python 的标准库_()
模块将gettext
安装进全局命名空间中,并作为gettext()
的别名。 在Django 中,我们选择不遵守这个实践,原因有:
ugettext()
比gettext()
更有用。 有时候,对于特定的文件,你应该使用ugettext_lazy()
作为默认的翻译方法。 如果_()
不在全局命名空间中,开发人员必须想清楚哪一个是最合适的翻译函数。_
)在Python 的交互式shell 和doctest 测试中,用于表示“前一个结果”。 安装全局的_()
函数会引起混乱。 显式地导入_()
为ugettext()
将避免这个问题。什么功能可以被别名为_
?
由于xgettext
(由makemessages
使用)的工作原理,只有具有单个字符串参数的函数才能导入为_
:
在下面的示例中,文本"Welcome to my site."
标记为一个翻译字符串:
from django.utils.translation import ugettext as _
from django.http import HttpResponse
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)
很明显,你可以不用别名来编写这段代码。 下面的例子与前面完全一样:
from django.utils.translation import ugettext
from django.http import HttpResponse
def my_view(request):
output = ugettext("Welcome to my site.")
return HttpResponse(output)
翻译在计算生成的值上进行。 下面的例子与前面两个完全一样:
def my_view(request):
words = ['Welcome', 'to', 'my', 'site.']
output = _(' '.join(words))
return HttpResponse(output)
翻译可以在变量上进行。 下面同样是一个完全一样的示例:
def my_view(request):
sentence = 'Welcome to my site.'
output = _(sentence)
return HttpResponse(output)
(告诫使用变量或计算的值,如在前面的两个例子, 是Django的翻译字符串检测工具, django-admin makemessages
, 将不能够找到这些字符串。 后面有makemessages
的更多信息)。
传递给ugettext()
或_()
的字符串可以通过Python 标准的命名字符串插值语法接收占位符。 例如:
def my_view(request, m, d):
output = _('Today is %(month)s %(day)s.') % {'month': m, 'day': d}
return HttpResponse(output)
这种技术可以让语言相关的翻译重新排序。 例如,英语翻译可能是"Today is November 26."
,而西班牙语可能是"Hoy es 26 de Noviembre."
—— 月份和天数的占位符交换位置了。
由于这个原因,每当有多个参数的时候,你都应该使用命名的字符串插值(例如,%s
)而不是位置插值(例如,%(day)s
或%d
)。 如果使用位置插值,翻译将不能重新排序占位符。
如果你想给翻译人员一些提示,可以添加一个以Translators
为前缀的注释,例如:
def my_view(request):
# Translators: This message appears on the home page only
output = ugettext("Welcome to my site.")
这个注释会在生成的.po
文件中出现在可翻译的结构上方,而且可以在大部分翻译工具中显示出来。
注
只是为了完整性,下面是生成的.po
文件片段:
#. Translators: This message appears on the home page only
# path/to/python/file.py:123
msgid "Welcome to my site."
msgstr ""
它在模板中也可以工作。 更多细节参见Comments for translators in templates。
django.utils.translation.ugettext_noop()
函数用于标记字符串为一个翻译字符串但是不用翻译它。 该字符串将在后面依据一个变量翻译。
如果你的常量字符串需要在不同的系统和用户之间交互 —— 例如数据库中的字符串,它们应该保存在源语言中,但是需要在最后例如呈现给用户的时刻翻译,可以使用它。
函数django.utils.translation.ungettext()
用于指定多元化的消息。
ungettext
接收三个参数:单数形式的翻译字符串、复数形式的翻译字符串和对象的个数。
这个函数用于当你的Django 应用所要本地化的语言中,复数形式 比英语中的要复杂时(‘object’ 表示单数,‘objects’ 表示所有count
不等于一的情形,无论具体的值是多少)。
像这样:
from django.utils.translation import ungettext
from django.http import HttpResponse
def hello_world(request, count):
page = ungettext(
'there is %(count)d object',
'there are %(count)d objects',
count) % {
'count': count,
}
return HttpResponse(page)
这个例子中对象的数字作为count
变量传递给翻译语言
需要注意的是多元化是复杂的,在每种语言的工作方式不同。
count
计数到1的比较并不总是正确的规则。 此代码看起来复杂,但会产生某些语言不正确的结果:
from django.utils.translation import ungettext
from myapp.models import Report
count = Report.objects.count()
if count == 1:
name = Report._meta.verbose_name
else:
name = Report._meta.verbose_name_plural
text = ungettext(
'There is %(count)d %(name)s available.',
'There are %(count)d %(name)s available.',
count
) % {
'count': count,
'name': name
}
不要尝试自己去实现单复数逻辑, 这样会出错。 这种情况下, 可以考虑这么做:
text = ungettext(
'There is %(count)d %(name)s object available.',
'There are %(count)d %(name)s objects available.',
count
) % {
'count': count,
'name': Report._meta.verbose_name,
}
注
在使用 ungettext()
的时候, 确保你你使用在每一个占位符中使用同一个名称。 在上面的例子中, 你可以注意到我们是怎么在两种翻译中使用name
这个变量的。 下面这个例子中, 除了在某些语言中会表达不正确之外, 还可能会造成错误:
text = ungettext(
'There is %(count)d %(name)s available.',
'There are %(count)d %(plural_name)s available.',
count
) % {
'count': Report.objects.count(),
'name': Report._meta.verbose_name,
'plural_name': Report._meta.verbose_name_plural
}
运行 django-admin
compilemessages
时,你会得到一个错误:
a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgid'
注
复数形式和po文件
Django不支持在po文件中的自定义复数方程。 当所有翻译目录被合并时,仅考虑主Django po文件的复数形式(在django/conf/locale/<lang_code>/LC_MESSAGES/django.po
)。 所有其他po文件中的多个表单将被忽略。 因此,您不应在项目或应用程序po文件中使用不同的复数方程。
有时候,词语有几种含义,例如英语中的"May"
,指的是月份名称和动词。 要使翻译人员可以在不同的上下文中正确地翻译这些单词,您可以使用django.utils.translation.pgettext()
函数,或django.utils.translation.npgettext()
两者都将上下文字符串作为第一个变量。
在所得到的msgctxt
文件中,字符串将随着同一字符串存在不同的上下文标记而频繁出现(上下文将出现在.po
行)翻译给他们每个不同的翻译。
像这样:
from django.utils.translation import pgettext
month = pgettext("month name", "May")
要么:
from django.db import models
from django.utils.translation import pgettext_lazy
class MyThing(models.Model):
name = models.CharField(help_text=pgettext_lazy(
'help text for MyThing model', 'This is the help text'))
将出现在.po
文件中:
msgctxt "month name"
msgid "May"
msgstr ""
上下文标记也受trans
和blocktrans
模板标记支持。
使用django.utils.translation
中的翻译函数的惰性版本(可以通过名称中的lazy
后缀轻松识别)来平移字符串 - 当访问该值而不是当他们被叫。
这些函数存储对字符串的惰性引用 - 而不是实际的翻译。 当字符串在字符串上下文中使用时,例如在模板呈现中,翻译本身将被完成。
当对这些函数的调用位于在模块加载时执行的代码路径中时,这是至关重要的。
这在定义模型,表单和模型表单时很容易发生,因为Django实现了这些,使得它们的字段实际上是类级别的属性。 因此,请确保在以下情况下使用延迟翻译:
verbose_name
和help_text
选项值¶例如,要翻译以下模型中名称字段的帮助文本,请执行以下操作:
from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_('This is the help text'))
您可以使用verbose_name
选项将ForeignKey
,ManyToManyField
或OneToOneField
关系标记为可翻译的名称:
class MyThing(models.Model):
kind = models.ForeignKey(
ThingKind,
on_delete=models.CASCADE,
related_name='kinds',
verbose_name=_('kind'),
)
就像你在verbose_name
中所做的那样,你应该为关系提供一个小写的详细名称文本,因为Django会在需要时自动定义它。
建议始终提供显式的verbose_name
和verbose_name_plural
选项,而不是依赖于以英语为中心的回退和有些朴素的确定django通过查看模型的类名执行的详细名称:
from django.db import models
from django.utils.translation import ugettext_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 = _('my things')
short_description
属性值¶对于模型方法,您可以使用short_description
属性向Django和管理网站提供翻译:
from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
kind = models.ForeignKey(
ThingKind,
on_delete=models.CASCADE,
related_name='kinds',
verbose_name=_('kind'),
)
def is_mouse(self):
return self.kind.type == MOUSE_TYPE
is_mouse.short_description = _('Is it a mouse?')
无论在其他Django代码中使用unicode字符串(str
对象),可以使用ugettext_lazy()
调用的结果,但它可能无法与任意Python码。 例如,以下内容将不起作用,因为请求库不处理ugettext_lazy
对象:
body = ugettext_lazy("I \u2764 Django") # (unicode :heart:)
requests.post('https://example.com/send', data={'body': body})
您可以通过将ugettext_lazy()
对象转换为文本字符串,然后传递给非Django代码来避免此类问题:
requests.post('https://example.com/send', data={'body': str(body)})
使用unicode
代替Python 2上的str
,或six.text_type
来支持Python 2和3。
如果您尝试使用ugettext_lazy()
结果,其中会出现一个bytestring(一个bytes
对象),事情将无法正常工作,因为ugettext_lazy()
对象不知道如何将自身转换为bytestring。
你不能在一个bytestring中使用一个unicode字符串,所以这是正常的Python行为。 例如,将unicode代理放入unicode字符串是很好的:
"Hello %s" % ugettext_lazy("people")
但是您不能将unicode对象插入到bytestring中,也不能在其中插入unicode代理:
b"Hello %s" % ugettext_lazy("people")
如果您看到像“hello &lt; django.utils.functional ...&gt;”
的输出,将ugettext_lazy()
的结果转换为字节。 这是你的代码中的一个错误。
如果你不喜欢长的_
名称,可以将其命名为ugettext_lazy
(下划线),如下所示:
from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_('This is the help text'))
使用ungettext_lazy()
和ugettext_lazy()
在模型和效用函数中标记字符串是一种常见的操作。 当你在代码中的其他地方使用这些对象时,你应该确保你不会意外地将它们转换为字符串,因为它们应该尽可能晚地转换(以便正确的区域设置生效)。 这需要使用下面描述的辅助函数。
当对多个字符串(number
)使用延迟转换时,通常不知道字符串定义时的[u]n[p]gettext_lazy
参数。 因此,您有权将number
参数传递一个键名称,而不是整数。 然后在字符串插值期间,在该键下的字典中查找number
。 这里的例子:
from django import forms
from django.utils.translation import ungettext_lazy
class MyForm(forms.Form):
error_message = ungettext_lazy("You only provided %(num)d argument",
"You only provided %(num)d arguments", 'num')
def clean(self):
# ...
if error:
raise forms.ValidationError(self.error_message % {'num': number})
如果字符串只包含一个未命名的占位符,则可以直接使用number
参数进行插值:
class MyForm(forms.Form):
error_message = ungettext_lazy(
"You provided %d argument",
"You provided %d arguments",
)
def clean(self):
# ...
if error:
raise forms.ValidationError(self.error_message % number)
format_lazy()
¶当format_string
或str.format()
的任何参数包含延迟翻译对象时,Python的str.format()
方法将无法正常工作。 相反,您可以使用django.utils.text.format_lazy()
,只有当结果包含在...中时,它才会创建一个运行str.format()
方法的延迟对象一个字符串 像这样:
from django.utils.text import format_lazy
from django.utils.translation import ugettext_lazy
...
name = ugettext_lazy('John Lennon')
instrument = ugettext_lazy('guitar')
result = format_lazy('{name}: {instrument}', name=name, instrument=instrument)
在这种情况下,result
中的懒惰翻译只有当result
本身用于字符串(通常在模板渲染时间)时才会转换为字符串。
对于任何其他情况下,你想延迟翻译,但必须将可翻译字符串作为参数传递给另一个函数,你可以将这个函数包装在一个懒惰的调用自己。 像这样:
from django.utils import six # Python 3 compatibility
from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
mark_safe_lazy = lazy(mark_safe, six.text_type)
然后稍后:
lazy_string = mark_safe_lazy(_("<p>My <strong>string!</strong></p>"))
get_language_info()
函数提供有关语言的详细信息:
>>> from django.utils.translation import activate, get_language_info
>>> activate('fr')
>>> li = get_language_info('de')
>>> print(li['name'], li['name_local'], li['name_translated'], li['bidi'])
German Deutsch Allemand False
字典中的name
,name_local
和name_translated
属性包含英文中的语言名称,语言本身以及当前的主动语言分别。 bidi
属性仅对双向语言为True。
语言信息的来源是django.conf.locale
模块。
类似的访问此信息可用于模板代码。 见下文。
Django templates 中的翻译使用两个模板标签,语法与Python 代码中使用的语法有稍许不同。 为了让你的模板能够访问这些标签,需要将{% load i18n %}
放置在模板的顶部。
和所有模板标签一样,这个标签需要在所有使用翻译的模板中加载,即使这些模板扩展自已经加载i18n
标签的模板。
trans
模板标签¶{% trans %}
模板标签翻译一个常量字符串(位于单引号或双引号中)或变量:
<title>{% trans "This is the title." %}</title>
<title>{% trans myvar %}</title>
如果带有noop
选项,变量的查找仍然继续但是会忽略翻译。 这对于需要在未来进行翻译的内容非常有用:
<title>{% trans "myvar" noop %}</title>
在内部,内联的翻译使用ugettext()
调用。
当一个模板变量(上面的myvar
)传递给该标签时, 该标签会首先在运行时刻将变量解析成一个字符串,然后在消息目录中查找这个字符串。
{% trans %}
不可以将模板标签嵌入到字符串中。 如果你的翻译字符串需要带有变量(占位符),可以使用{% blocktrans %}
。
如果你想提前翻译字符串但是不显示出来,你可以使用以下语法:
{% trans "This is the title" as the_title %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">
在实践中,您将使用它来获取可以在模板中的多个位置使用的字符串,或者可以将输出用作其他模板标记或过滤器的参数:
{% 反式 "starting point" as start %}
{% 反式 "end point" as end %}
{% 反式 "La Grande Boucle" as race %}
<h1>
<a href="/" title="{% blocktrans %}Back to '{{ race }}' homepage{% endblocktrans %}">{{ race }}</a>
</h1>
<p>
{% for stage in tour_stages %}
{% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}<br />{% else %}, {% endif %}
{% endfor %}
</p>
利用context
关键字,{% trans %}
还支持contextual markers:
{% trans "May" context "month name" %}
blocktrans
模板标签¶与trans
标签相反,blocktrans
标签允许您通过使用占位符来标记由文字和可变内容组成的复杂句子进行翻译:
{% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %}
要翻译模板表达式,例如访问对象属性或使用模板过滤器,您需要将表达式绑定到本地变量,以在翻译块中使用。 例子:
{% blocktrans with amount=article.price %}
That will cost $ {{ amount }}.
{% endblocktrans %}
{% blocktrans with myvar=value|filter %}
This will have {{ myvar }} inside.
{% endblocktrans %}
您可以在单个blocktrans
标记中使用多个表达式:
{% blocktrans with book_t=book|title author_t=author|title %}
This is {{ book_t }} by {{ author_t }}
{% endblocktrans %}
注
仍然支持以前更详细的格式:{% blocktrans 与 book | title 作为 book_t 和 作者|标题 as author_t %} T11> T0>
其他块标签(例如{% %}
或{% t5> 如果 %}
)不允许在blocktrans
标签内。
如果解析其中一个块参数失败,则通过使用deactivate_all()
函数暂时停用当前活动的语言,blocktrans将会回退到默认语言。
该标签还提供了复数。 使用它:
count
的计数器值。 该值将用于选择正确的复数形式。{% plural %}
tag within the {% blocktrans %}
and
{% endblocktrans %}
tags.一个例子:
{% blocktrans count counter=list|length %}
There is only one {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
{% endblocktrans %}
一个更复杂的例子:
{% blocktrans with amount=article.price count years=i.length %}
That will cost $ {{ amount }} per year.
{% plural %}
That will cost $ {{ amount }} per {{ years }} years.
{% endblocktrans %}
除了计数器值之外,当您同时使用复数特性和绑定值到局部变量时,请记住ungettext
结构在内部转换为blocktrans
调用。 这意味着相同的notes regarding ungettext variables适用。
反向URL查找不能在blocktrans
中执行,应该预先检索(并存储):
{% url 'path.to.view' arg arg2 as the_url %}
{% blocktrans %}
This is a URL: {{ the_url }}
{% endblocktrans %}
如果你想提前翻译字符串但是不显示出来,你可以使用以下语法:
{% blocktrans asvar the_title %}The title is {{ title }}.{% endblocktrans %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">
实际上,您将使用它来获取可以在模板中的多个位置使用的字符串,或者可以将输出用作其他模板标记或过滤器的参数。
{% blocktrans %}
also supports contextual
markers using the context
keyword:
{% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %}
{% blocktrans %}
支持的另一个功能是trimmed
选项。 此选项将从{% blocktrans %}
的内容的开头和结尾删除换行符。标签,在行的开头和结尾替换任何空格,并使用空格字符将所有行合并为一个,以将它们分隔开。 这对缩进{% blocktrans %}
标记而不缩进缩进字符的内容非常有用在PO文件中的相应条目中,这使得翻译过程更容易。
例如,以下{% blocktrans %}
标记:
{% blocktrans trimmed %}
First sentence.
Second paragraph.
{% endblocktrans %}
将导致进入 “第一 句子。 第二 段。”
在PO文件中,与“\ n 第一 句子相比。\ n 第二个 句子。\ n“
,如果未指定trimmed
选项。
您可以使用熟悉的_()
语法将作为参数传递的字符串文字转换为标记和过滤器:
{% some_tag _("Page not found") value|yesno:_("yes,no") %}
在这种情况下,标记和过滤器都将看到翻译的字符串,因此他们不需要知道翻译。
注
在此示例中,转换基础结构将传递字符串"yes"
,而不是单个字符串"yes,no"
和"no"
翻译后的字符串需要包含逗号,以便过滤器解析代码知道如何拆分参数。 例如,德语翻译者可以将字符串"ja,nein"
翻译为"yes,no"
(保持逗号不变)。
与Python code一样,可以使用注释指定这些笔记的注释,可以使用comment
标签:
{% comment %}Translators: View verb{% endcomment %}
{% trans "View" %}
{% comment %}Translators: Short intro blurb{% endcomment %}
<p>{% blocktrans %}A multiline translatable
literal.{% endblocktrans %}</p>
或使用{#
... #}
one-line comment constructs:
{# Translators: Label of a button that triggers search #}
<button type="submit">{% trans "Go" %}</button>
{# Translators: This is a text of the base template #}
{% blocktrans %}Ambiguous translatable block of text{% endblocktrans %}
注
为了完整性,这些是所得到的.po
文件的相应片段:
#. Translators: View verb
# path/to/template/file.html:10
msgid "View"
msgstr ""
#. Translators: Short intro blurb
# path/to/template/file.html:13
msgid ""
"A multiline translatable"
"literal."
msgstr ""
# ...
#. Translators: Label of a button that triggers search
# path/to/template/file.html:100
msgid "Go"
msgstr ""
#. Translators: This is a text of the base template
# path/to/template/file.html:103
msgid "Ambiguous translatable block of text"
msgstr ""
如果您要在模板中选择语言,可以使用language
模板标记:
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<p>{% 反式 "Welcome to our page" %}</p>
{% language 'en' %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<p>{% 反式 "Welcome to our page" %}</p>
{% endlanguage %}
虽然第一次出现的“欢迎来到我们的页面”使用当前语言,第二次将始终是英语。
这些标签还需要{% load i18n %}
。
get_available_languages
¶{% get_available_languages as LANGUAGES %}
第一个元素是language code的元组列表,第二个是语言名称(翻译成当前活动的语言环境)。
get_current_language
¶{% get_current_language as LANGUAGE_CODE %}
返回当前用户的首选语言为字符串。 示例:en-us
。 请参阅How Django discovers language preference。
get_current_language_bidi
¶{% get_current_language_bidi as LANGUAGE_BIDI %} 当前语言环境的方向。
如果True
,则是从右至左的语言,例如希伯来语,阿拉伯语 如果False
,这是一种从左到右的语言,例如英文,法文,德文等
If you enable the django.template.context_processors.i18n
context processor
then each RequestContext
will have access to LANGUAGES
,
LANGUAGE_CODE
, and LANGUAGE_BIDI
as defined above.
get_language_info
¶您还可以使用提供的模板标记和过滤器检索有关任何可用语言的信息。 要获取有关单一语言的信息,请使用标签: get_language_info %}
{% get_language_info for LANGUAGE_CODE as lang %}
{% get_language_info for "pl" as lang %}
然后,您可以访问信息:
Language code: {{ lang.code }}<br />
Name of language: {{ lang.name_local }}<br />
Name in English: {{ lang.name }}<br />
Bi-directional: {{ lang.bidi }}
Name in the active language: {{ lang.name_translated }}
get_language_info_list
¶您还可以使用{% get_language_info_list %}
模板标记来检索语言列表在LANGUAGES
中指定的活动语言)。 有关如何使用{% get_language_info_list %的语言选择器的示例,请参见the section about the set_language redirect
view的部分} T5> T2>。
除了LANGUAGES
元组的样式列表,{% get_language_info_list %}
简单的语言代码清单。
如果你在你的观点:
context = {'available_languages': ['en', 'es', 'fr']}
return render(request, 'mytemplate.html', context)
您可以在模板中迭代这些语言:
{% get_language_info_list for available_languages as langs %}
{% for lang in langs %} ... {% endfor %}
还有简单的过滤器为了方便:
{{ LANGUAGE_CODE | language_name }}
(“German”){{ LANGUAGE_CODE | language_name_local }}
(“Deutsch”){{ LANGUAGE_CODE | language_bidi }}
(False){{ LANGUAGE_CODE | language_name_translated }}
(“německy”,当活动语言为捷克语时)将翻译添加到JavaScript会带来一些问题:
gettext
实现。.po
或.mo
文件;他们需要由服务器交付。Django为这些问题提供了一个集成的解决方案:它将翻译传递给JavaScript,因此您可以在JavaScript中调用gettext
等。
这些问题的主要解决方案是以下JavaScriptCatalog
视图,它生成一个具有模拟gettext
接口的函数的JavaScript代码库,以及一个翻译字符串数组。
JavaScriptCatalog
视图¶JavaScriptCatalog
[source]¶一个生成具有模拟gettext
接口的函数的JavaScript代码库以及一个翻译字符串数组的视图。
属性
域 T0> ¶ T1>
翻译域包含要添加到视图输出中的字符串。
默认为'djangojs'
。
包 T0> ¶ T1>
已安装应用程序中的application names
的列表。 那些应用程序应该包含一个locale
目录。 所有这些目录以及LOCALE_PATHS
(始终包含在内)中的所有目录都会合并到一个目录中。 默认为None
,这意味着所有INSTALLED_APPS
中的所有可用翻译都在JavaScript输出中提供。
默认值示例:
from django.views.i18n import JavaScriptCatalog
urlpatterns = [
url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'),
]
使用自定义软件包的示例:
urlpatterns = [
url(r'^jsi18n/myapp/$',
JavaScriptCatalog.as_view(packages=['your.app.label']),
name='javascript-catalog'),
]
如果您的根URLconf使用i18n_patterns()
,JavaScriptCatalog
也必须由i18n_patterns()
包装,以使目录正确生成。
i18n_patterns()
:
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns(
url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'),
)
翻译的优先级使得packages
参数稍后出现的包的优先级高于开头出现的包。 在翻译相同文字的情况下,这一点非常重要。
如果您在一个站点上使用多个JavaScriptCatalog
视图,并且其中一些定义了相同的字符串,则最后加载的目录中的字符串将优先。
javascript_catalog
视图¶自1.10版以来已弃用 javascript_catalog()
不利于JavaScriptCatalog
,并将在Django 2.0中删除。
这些问题的主要解决方案是django.views.i18n.javascript_catalog()
视图,它发送一个JavaScript代码库,其函数模仿gettext
接口,翻译字符串数组。 这些翻译字符串取自应用程序或Django core,根据您在info_dict
或URL中指定的内容。 还包括LOCALE_PATHS
中列出的路径。
你这样挂钩:
from django.views.i18n import javascript_catalog
js_info_dict = {
'packages': ('your.app.package',),
}
urlpatterns = [
url(r'^jsi18n/$', javascript_catalog, js_info_dict, name='javascript-catalog'),
]
locale
中的每个字符串都应采用Python点分包格式(与INSTALLED_APPS
中的字符串格式相同),并且应该指向包含packages
如果指定多个包,则所有这些目录将合并到一个目录中。 如果您的JavaScript使用来自不同应用程序的字符串,这将非常有用。
翻译的优先级是使得稍后在packages
参数中出现的包比在开始处出现的包具有更高的优先级,这在针对相同文字冲突翻译的情况下是重要的。
默认情况下,视图使用djangojs
gettext域。 这可以通过更改domain
参数来更改。
您可以通过将包放入URL模式来使视图动态:
urlpatterns = [
url(r'^jsi18n/(?P<packages>\S+?)/$', javascript_catalog, name='javascript-catalog'),
]
这样,您可以将包指定为由URL中的“+”号分隔的包名称列表。 如果您的网页使用来自不同应用程式的程式码,且这项变更经常发生,而且您不想提取一个大型目录档案,这项功能就特别实用。 作为安全措施,这些值只能是django.conf
或来自INSTALLED_APPS
设置的任何包。
您还可以在多个URL中拆分目录,并根据需要在站点中加载它们:
js_info_dict_app = {
'packages': ('your.app.package',),
}
js_info_dict_other_app = {
'packages': ('your.other.app.package',),
}
urlpatterns = [
url(r'^jsi18n/app/$', javascript_catalog, js_info_dict_app),
url(r'^jsi18n/other_app/$', javascript_catalog, js_info_dict_other_app),
]
如果在站点上使用多个javascript_catalog
,并且其中一些定义了相同的字符串,则最后加载的目录中的字符串将优先。
在LOCALE_PATHS
设置中列出的路径中找到的JavaScript翻译也始终包括在内。 为了与用于Python和模板的翻译查找顺序算法保持一致,LOCALE_PATHS
中列出的目录具有最高优先级,首先出现的优先级高于稍后出现的优先级。
要使用目录,只需拉入动态生成的脚本,如下所示:
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
这使用反向URL查找来查找JavaScript目录视图的URL。 加载目录后,您的JavaScript代码可以使用以下方法:
gettext
ngettext
插
get_format
gettext_noop
pgettext
npgettext
复数
gettext
¶gettext
函数的行为与您的Python代码中的标准gettext
接口类似:
document.write(gettext('this is to be translated'));
ngettext
¶ngettext
函数提供了一个多元化单词和短语的接口:
var object_count = 1 // or 0, or 2, or 3, ...
s = ngettext('literal for the singular case',
'literal for the plural case', object_count);
interpolate
¶interpolate
函数支持动态填充格式字符串。
插值语法是从Python借用的,因此interpolate
函数支持位置和命名插值:
位置插值:fmt
包含一个JavaScript Array对象,其元素值然后按照它们出现的相同顺序在其相应的obj
占位符中顺序插值。
像这样:
fmts = ngettext('There is %s object. Remaining: %s',
'There are %s objects. Remaining: %s', 11);
s = interpolate(fmts, [11, 20]);
// s is 'There are 11 objects. Remaining: 20'
Named interpolation: This mode is selected by passing the optional
boolean named
parameter as true
. obj
包含JavaScript对象或关联数组。 像这样:
d = {
count: 10,
total: 50
};
fmts = ngettext('Total: %(total)s, there is %(count)s object',
'there are %(count)s of a total of %(total)s objects', d.count);
s = interpolate(fmts, d, true);
你不应该使用字符串插值,但是这仍然是JavaScript,所以代码必须重复正则表达式替换。
这不像Python中的字符串插值那么快,所以保持它在那些你真正需要它的情况下(例如,与ngettext
一起产生正确的复数)。
get_format
¶get_format
函数可以访问配置的i18n格式设置,并可以检索给定设置名称的格式字符串:
document.write(get_format('DATE_FORMAT'));
// 'N j, Y'
它可以访问以下设置:
日期格式
DATE_INPUT_FORMATS
DATETIME_FORMAT
DATETIME_INPUT_FORMATS
DECIMAL_SEPARATOR
FIRST_DAY_OF_WEEK
MONTH_DAY_FORMAT
NUMBER_GROUPING
SHORT_DATE_FORMAT
SHORT_DATETIME_FORMAT
THOUSAND_SEPARATOR
时间格式
TIME_INPUT_FORMATS
YEAR_MONTH_FORMAT
这对于保持与Python呈现的值的格式一致性很有用。
gettext_noop
¶这模拟gettext
函数,但什么都不做,返回任何传递给它的:
document.write(gettext_noop('this will not be translated'));
这对于剔除将来需要翻译的代码部分很有用。
pgettext
¶pgettext
函数的行为类似于Python变体(pgettext()
),提供上下文翻译词:
document.write(pgettext('month name', 'May'));
npgettext
¶npgettext
函数的行为类似于Python变体(npgettext()
),提供了一个多元化
document.write(npgettext('group', 'party', 1));
// party
document.write(npgettext('group', 'party', 2));
// parties
pluralidx
¶count
函数以与pluralize
模板过滤器类似的方式工作,确定给定的pluralidx
是否应使用字的复数形式:
document.write(pluralidx(0));
// true
document.write(pluralidx(1));
// false
document.write(pluralidx(2));
// true
在最简单的情况下,如果不需要自定义复数,则对于所有其他数字,对于整数1
和false
返回true
。
然而,在所有语言中,复数并不是这么简单。 如果语言不支持复数,则提供空值。
此外,如果围绕复数有复杂的规则,目录视图将呈现条件表达式。 这将评估false
(应为pluralize)或true
(应不 pluralize)值。
JSONCatalog
视图¶JSONCatalog
[source]¶为了使用另一个客户端库来处理翻译,您可能希望利用JSONCatalog
视图。 它类似于JavaScriptCatalog
,但返回JSON响应。
请参阅JavaScriptCatalog
的文档,了解可能的值和使用domain
和packages
属性。
答复格式如下:
{
"catalog": {
# Translations catalog
},
"formats": {
# Language formats for date, time, etc.
},
"plural": "..." # Expression for plural forms, or null.
}
json_catalog
视图¶自1.10版以来已弃用 json_catalog()
不利于JSONCatalog
,并将在Django 2.0中删除。
为了使用另一个客户端库来处理翻译,您可能希望利用json_catalog()
视图。 它类似于javascript_catalog()
,但返回JSON响应。
JSON对象包含i18n格式设置(可用于get_format)的格式设置,复数规则(作为GNU gettext Plural-Forms
的plural
部分表达式)和翻译字符串。 根据通过urlpatterns
参数或请求参数指定的内容,翻译字符串取自应用程序或Django自己的翻译。 还包括LOCALE_PATHS
中列出的路径。
该视图与您的应用程序挂钩,并以与javascript_catalog()
相同的方式进行配置(即domain
和packages
参数的行为相同) :
from django.views.i18n import json_catalog
js_info_dict = {
'packages': ('your.app.package',),
}
urlpatterns = [
url(r'^jsoni18n/$', json_catalog, js_info_dict),
]
答复格式如下:
{
"catalog": {
# Translations catalog
},
"formats": {
# Language formats for date, time, etc.
},
"plural": "..." # Expression for plural forms, or null.
}
各种JavaScript / JSON i18n视图从每个请求的.mo
文件生成目录。 由于其输出不变,至少对于给定版本的站点,它是缓存的良好候选者。
服务器端缓存将减少CPU负载。 它很容易用cache_page()
装饰器实现。 要在您的翻译更改时触发高速缓存无效,请提供与版本相关的密钥前缀,如下面的示例所示,或者将视图映射到与版本相关的URL:
from django.views.decorators.cache import cache_page
from django.views.i18n import JavaScriptCatalog
# The value returned by get_version() must change when translations change.
urlpatterns = [
url(r'^jsi18n/$',
cache_page(86400, key_prefix='js18n-%s' % get_version())(JavaScriptCatalog.as_view()),
name='javascript-catalog'),
]
客户端缓存可以节省带宽,并加快网站加载速度。 如果您使用的是ETag(ConditionalGetMiddleware
),那么您已经被覆盖了。 否则,您可以应用conditional decorators。 在以下示例中,无论何时重新启动应用程序服务器,缓存都将失效:
from django.utils import timezone
from django.views.decorators.http import last_modified
from django.views.i18n import JavaScriptCatalog
last_modified_date = timezone.now()
urlpatterns = [
url(r'^jsi18n/$',
last_modified(lambda req, **kw: last_modified_date)(JavaScriptCatalog.as_view()),
name='javascript-catalog'),
]
您甚至可以预先生成JavaScript目录作为部署过程的一部分,并将其作为静态文件提供。 这种激进的技术在django-statici18n中实现。
Django提供了两种机制来国际化URL模式:
LocaleMiddleware
可以从请求的网址中检测要激活的语言。django.utils.translation.ugettext_lazy()
函数进行翻译。警告
使用这些功能之一要求为每个请求设置一个活动语言;换句话说,您需要在MIDDLEWARE
设置中设置django.middleware.locale.LocaleMiddleware
。
该功能可以在根URLconf中使用,Django将自动将当前的活动语言代码添加到i18n_patterns()
中定义的所有URL模式。
将prefix_default_language
设置为False
从默认语言(LANGUAGE_CODE
)中删除前缀。 当将翻译添加到现有站点时,这可能很有用,以便当前URL不会更改。
网址格式示例:
from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from about import views as about_views
from news import views as news_views
from sitemap.views import sitemap
urlpatterns = [
url(r'^sitemap\.xml$', sitemap, name='sitemap-xml'),
]
news_patterns = ([
url(r'^$', news_views.index, name='index'),
url(r'^category/(?P<slug>[\w-]+)/$', news_views.category, name='category'),
url(r'^(?P<slug>[\w-]+)/$', news_views.details, name='detail'),
], 'news')
urlpatterns += i18n_patterns(
url(r'^about/$', about_views.main, name='about'),
url(r'^news/', include(news_patterns, namespace='news')),
)
定义这些网址格式后,Django会自动将语言前缀添加到由i18n_patterns
函数添加的网址格式。 例如:
>>> from django.urls import reverse
>>> from django.utils.translation import activate
>>> activate('en')
>>> reverse('sitemap-xml')
'/sitemap.xml'
>>> reverse('news:index')
'/en/news/'
>>> activate('nl')
>>> reverse('news:detail', kwargs={'slug': 'news-slug'})
'/nl/news/news-slug/'
通过prefix_default_language=False
和LANGUAGE_CODE='en'
,URL将为:
>>> activate('en')
>>> reverse('news:index')
'/news/'
>>> activate('nl')
>>> reverse('news:index')
'/nl/news/'
添加了prefix_default_language
参数。
警告
i18n_patterns()
。 在包含的URLconf中使用它会引发ImproperlyConfigured
异常。
在旧版本中,通过设置request.urlconf
不支持使用i18n_patterns
在不同于ROOT_URLCONF
的根URLconf中。
警告
确保您没有可能与自动添加的语言前缀相冲突的非前缀网址格式。
网址格式也可以使用ugettext_lazy()
函数标记为可翻译。 例如:
from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from django.utils.translation import ugettext_lazy as _
from about import views as about_views
from news import views as news_views
from sitemaps.views import sitemap
urlpatterns = [
url(r'^sitemap\.xml$', sitemap, name='sitemap-xml'),
]
news_patterns = ([
url(r'^$', news_views.index, name='index'),
url(_(r'^category/(?P<slug>[\w-]+)/$'), news_views.category, name='category'),
url(r'^(?P<slug>[\w-]+)/$', news_views.details, name='detail'),
], 'news')
urlpatterns += i18n_patterns(
url(_(r'^about/$'), about_views.main, name='about'),
url(_(r'^news/'), include(news_patterns, namespace='news')),
)
创建翻译后,reverse()
函数将返回活动语言的URL。 例如:
>>> from django.urls import reverse
>>> from django.utils.translation import activate
>>> activate('en')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/en/news/category/recent/'
>>> activate('nl')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/nl/nieuws/categorie/recent/'
警告
在大多数情况下,最好只在语言代码前缀的模式块中使用已翻译的URL(使用i18n_patterns()
),以避免粗心大意的翻译URL导致与非翻译的网址格式。
如果在模板中反转本地化URL,它们总是使用当前语言。 要链接到其他语言的网址,请使用language
模板标记。 它在所包含的模板部分中启用给定的语言:
{% load i18n %}
{% get_available_languages as languages %}
{% trans "View this category in:" %}
{% for lang_code, lang_name in languages %}
{% language lang_code %}
<a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
{% endlanguage %}
{% endfor %}
language
标签需要将语言代码作为唯一的参数。
一旦应用程序的字符串文字已经标记为以后翻译,翻译本身需要被写入(或获得)。 这是如何工作。
第一步是为新语言创建message file。 消息文件是纯文本文件,表示单一语言,包含所有可用的翻译字符串以及如何以给定语言表示它们。 消息文件具有.po
文件扩展名。
Django提供了一个工具,django-admin makemessages
,自动创建和维护这些文件。
Gettext实用程序
The makemessages
command (and compilemessages
discussed later) use
commands from the GNU gettext toolset: xgettext
, msgfmt
,
msgmerge
and msguniq
.
支持的gettext
实用程序的最低版本为0.15。
要创建或更新消息文件,请运行以下命令:
django-admin makemessages -l de
...其中de
是要创建的消息文件的locale name。 例如,巴西葡萄牙语为de_AT
,奥地利德语为pt_BR
,印尼语为id
。
脚本应该从两个地方之一运行:
manage.py
的目录)。脚本运行在项目源代码树或应用程序源代码树中,并拉出所有标记为翻译的字符串(请参阅How Django discovers translations,并确保LOCALE_PATHS
已正确配置)。 它在目录locale/LANG/LC_MESSAGES
中创建(或更新)消息文件。 在locale/de/LC_MESSAGES/django.po
示例中,文件将为de
。
当从项目的根目录运行makemessages
时,提取的字符串将自动分发到正确的消息文件。
也就是说,从包含locale
目录的应用的文件中提取的字符串将放在该目录下的消息文件中。 从没有任何locale
目录的应用的文件中提取的字符串将进入LOCALE_PATHS
中列出的目录下的消息文件,否则会生成错误,如果LOCALE_PATHS
为空。
By default django-admin makemessages
examines every
file that has the .html
, .txt
or .py
file extension. 如果要覆盖该默认值,请使用--extension
或-e
选项指定要检查的文件扩展名:
django-admin makemessages -l de -e txt
使用逗号分隔多个扩展程序和/或使用--extension
或-e
多次:
django-admin makemessages -l de -e html,txt -e xml
警告
当creating message files from JavaScript source code创建消息文件时,您需要使用特殊的'djangojs'域,而不是 -e js
。
使用Jinja2模板?
makemessages
不能理解Jinja2模板的语法。
要从包含Jinja2模板的项目中提取字符串,请改用Babel中的消息提取。
下面是一个示例babel.cfg
的配置文件:
# Extraction from Python source files
[python: **.py]
# Extraction from Jinja2 templates
[jinja2: **.jinja]
extensions = jinja2.ext.with_
请确保您列出了您使用的所有扩展程序! 否则Babel将无法识别这些扩展定义的标签,并会忽略包含它们的Jinja2模板。
Babel提供与makemessages
类似的功能,可以替换它,而不依赖于gettext
。 有关详细信息,请阅读有关处理消息目录的文档。
没有gettext?
如果您没有安装gettext
实用程序,makemessages
将创建空文件。 如果是这样,请安装gettext
实用程序,或者只是复制英文消息文件(locale/en/LC_MESSAGES/django.po
)(如果可用),并将其用作开始点;它只是一个空的翻译文件。
在Windows上工作?
如果您使用Windows并需要安装GNU gettext实用程序,因此makemessages
可以工作,有关详细信息,请参阅gettext on Windows。
.po
文件的格式很简单。 每个.po
文件包含一小部分元数据,例如翻译维护者的联系信息,但文件的大部分是消息的列表 - 翻译字符串之间的简单映射和特定语言的实际翻译文本。
例如,如果您的Django应用程序包含文本“欢迎 到 我的 网站的翻译字符串” t4>
,像这样:
_("Welcome to my site.")
...然后django-admin makemessages
将创建一个包含以下代码段的.po
文件 - 一条消息:
#: path/to/python/module.py:23
msgid "Welcome to my site."
msgstr ""
快速解释:
msgid
是翻译字符串,它显示在源中。 不要改变它。msgstr
是您放置语言特定翻译的位置。 它开始是空的,所以你有责任改变它。 确保您在翻译周围保留引号。msgid
为前缀并位于#
之上的注释行的形式包括翻译字符串所从的文件名和行号收集。长消息是一种特殊情况。 在那里,紧跟msgid
(或msgstr
)之后的第一个字符串是空字符串。 然后内容本身将被写在下几行作为每行一个字符串。 这些字符串是直接连接的。 不要忘记字符串中的尾随空格;否则,他们将在一起没有空白!
注意你的字符集
由于gettext
工具的内部工作方式,因为我们要在Django的核心和应用程序中允许非ASCII源字符串,您必须使用UTF-8作为编码为您的PO文件(创建PO文件时的默认值)。 这意味着每个人都将使用相同的编码,这在Django处理PO文件时很重要。
要对所有源代码和模板重新审核新的翻译字符串,并更新所有所有语言的邮件文件,请运行:
django-admin makemessages -a
创建消息文件后 - 每次对其进行更改时,都需要将其编译为更有效的形式,以供gettext
使用。 使用django-admin compilemessages
实用程序执行此操作。
此工具运行所有可用的.mo
文件,并创建.po
文件,这是优化为gettext
使用的二进制文件。 在运行django-admin makemessages
的同一目录中,运行django-admin compilemessages
像这样:
django-admin compilemessages
而已。 您的翻译已准备就绪。
在Windows上工作?
如果您使用Windows并需要安装GNU gettext实用程序,因此django-admin compilemessages
可以在Windows上看到gettext on Windows
.po文件:编码和BOM使用。
Django只支持以UTF-8编码并且没有任何BOM(字节顺序标记)的.po
文件,因此如果您的文本编辑器在默认情况下将这样的标记添加到文件的开头,则需要重新配置它。
ugettext()
在具有百分号¶的字符串中检测不到python-format
在某些情况下,例如具有百分号的字符串后跟空格和string conversion type(例如_(“10% 兴趣”)
),ugettext()
使用python-format
错误地标记字符串。
If you try to compile message files with incorrectly flagged strings, you’ll
get an error message like number of format specifications in 'msgid' and
'msgstr' does not match
or 'msgstr' is not a valid Python format string,
unlike 'msgid'
.
要解决这个问题,您可以通过添加第二个百分号来逃避百分号:
from django.utils.translation import ugettext as _
output = _("10%% interest)
或者您可以使用no-python-format
,以便所有百分号都被视为文字:
# xgettext:no-python-format
output = _("10% interest)
您可以使用django-admin makemessages
工具,以与其他Django邮件文件相同的方式创建和更新邮件文件。
唯一的区别是,通过提供-d ,您需要明确指定gettext语法在这种情况下称为域(
参数,如下所示:djangojs
域) djangojs
django-admin makemessages -d djangojs -l de
这将创建或更新用于德语的JavaScript的消息文件。 更新邮件文件后,只需以与对普通Django邮件文件相同的方式运行django-admin compilemessages
即可。
gettext
在Windows ¶这只适用于要提取消息ID或编译消息文件(.po
)的人员。 翻译工作本身只涉及编辑此类型的现有文件,但如果要创建自己的消息文件,或想要测试或编译已更改的消息文件,请下载预编译的二进制安装程序。
只要xgettext - version
命令正常工作,您还可以使用您在其他地方获得的gettext
二进制。 如果在命令xgettext - version
中输入命令,则不要尝试使用gettext
Windows命令提示符导致弹出窗口说“xgettext.exe生成错误,将被Windows关闭”。
makemessages
命令¶如果要向xgettext_options
传递附加参数,则需要创建自定义makemessages
命令并覆盖其xgettext
属性:
from django.core.management.commands import makemessages
class Command(makemessages.Command):
xgettext_options = makemessages.Command.xgettext_options + ['--keyword=mytrans']
如果您需要更多灵活性,还可以向自定义makemessages
命令中添加一个新参数:
from django.core.management.commands import makemessages
class Command(makemessages.Command):
def add_arguments(self, parser):
super(Command, self).add_arguments(parser)
parser.add_argument(
'--extra-keyword',
dest='xgettext_keywords',
action='append',
)
def handle(self, *args, **options):
xgettext_keywords = options.pop('xgettext_keywords')
if xgettext_keywords:
self.xgettext_options = (
makemessages.Command.xgettext_options[:] +
['--keyword=%s' % kwd for kwd in xgettext_keywords]
)
super(Command, self).handle(*args, **options)
set_language
重定向视图¶方便起见, Django自带了一个, django.views.i18n.set_language()
视图, 作用是设置用户语言偏好并重定向返回到前一页面
在URLconf中加入下面这行代码来激活这个视图:
url(r'^i18n/', include('django.conf.urls.i18n')),
(注意这个例子使得这个视图在/i18n/setlang/
中有效.)
警告
请确保您不要在i18n_patterns()
中包含上述网址 - 它需要与语言无关才能正常工作。
该视图需要通过language
方法调用,并在请求中设置POST
参数。 如果启用了会话支持,则视图会在用户的会话中保存语言选择。 否则,它会将语言选项保存在默认名为django_language
的Cookie中。
(名称可以通过LANGUAGE_COOKIE_NAME
设置更改。)
在设置语言选择后,Django在POST
或GET
数据中查找next
参数。 如果找到并且Django认为它是一个安全的URL(即它不指向不同的主机并使用安全的方案),将重定向该URL。 否则,Django可能会拒绝将用户重定向到Referer
标题中的URL,或者如果未设置为/
,则取决于请求的性质:
next
参数时,才能执行回退。 否则将返回204状态代码(无内容)。当没有指定重定向时,返回AJAX请求的204状态代码是新的。
这里是HTML模板代码的示例:
{% load i18n %}
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select name="language">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
<input type="submit" value="Go" />
</form>
在此示例中,Django在redirect_to
上下文变量中查找用户将被重定向到的网页的网址。
您可能需要显式设置当前会话的活动语言。 例如,可能从另一个系统检索用户的语言偏好。
您已介绍过django.utils.translation.activate()
。 这只适用于当前线程。 要为整个会话持久保存语言,还需修改会话中的LANGUAGE_SESSION_KEY
:
from django.utils import translation
user_language = 'fr'
translation.activate(user_language)
request.session[translation.LANGUAGE_SESSION_KEY] = user_language
您通常希望同时使用:django.utils.translation.activate()
将更改此线程的语言,并修改会话使此首选项在未来的请求中保持不变。
如果您没有使用会话,该语言将保留在Cookie中,其名称在LANGUAGE_COOKIE_NAME
中配置。 像这样:
from django.utils import translation
from django import http
from django.conf import settings
user_language = 'fr'
translation.activate(user_language)
response = http.HttpResponse(...)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)
虽然Django提供了一组丰富的i18n工具以用于视图和模板,但它并不限制对Django特定代码的使用。 Django翻译机制可以用于将任意文本翻译成Django支持的任何语言(当然,只要存在适当的翻译目录)。 您可以加载翻译目录,将其激活并将文本翻译为您选择的语言,但请记住切换回原始语言,因为激活翻译目录是基于每个线程完成的,这样的更改将影响在同一个线程中运行的代码。
像这样:
from django.utils import translation
def welcome_translated(language):
cur_language = translation.get_language()
try:
translation.activate(language)
text = translation.ugettext('welcome')
finally:
translation.activate(cur_language)
return text
无论LANGUAGE_CODE
和中间件设置的语言如何,使用值“de”调用此函数将给予"Willkommen"
特别感兴趣的函数是django.utils.translation.get_language()
,它返回当前线程中使用的语言django.utils.translation.activate()
,它激活一个当前线程的翻译目录,以及django.utils.translation.check_for_language()
,它检查Django是否支持给定的语言。
为了帮助编写更简洁的代码,还有一个上下文管理器django.utils.translation.override()
,用于在输入时存储当前语言,并在退出时恢复。 有了它,上面的例子变成:
from django.utils import translation
def welcome_translated(language):
with translation.override(language):
return translation.ugettext('welcome')
可以使用多种设置来调整语言Cookie选项:
Django 的翻译机制使用Python 自带的gettext
模块。 如果你了解gettext
,你应该注意到Django 翻译方式的这些特点:
djangojs
或django
。 这个字符串域用于区分不同的而程序,这些程序将它们的数据保存在共同的消息文件库中(通常是/usr/share/locale/
)。 django
域用于Python和模板翻译字符串,并加载到全球翻译目录中。 djangojs
域只用于JavaScript 的翻译目录,以确保它尽可能的小。xgettext
。 它使用Python 对msgfmt
和 xgettext
进行的封装。 最主要是为了方便。一旦你准备好翻译,或者你只是想使用Django 自带的翻译,你需要为你的应用启用翻译。
在后台,Django 有一个非常灵活的模型决定应该使用哪种语言 —— 所有用户还是特定的用户,或者两种都可以。
要设置安装范围的语言首选项,请设置LANGUAGE_CODE
。
Django使用这种语言作为默认翻译 - 如果没有找到更好的匹配翻译通过场所中间件(见下文)使用的方法之一的最终尝试。
如果你想要的是用你的母语运行Django,你只需设置LANGUAGE_CODE
并确保相应的message
files及其编译版本(.mo
)。
如果你想让每个用户指定首选语言,那么你还要 使用 LocaleMiddleware
.
LocaleMiddleware
会打开基于请求数据的语言选择。
它为每个人用户的内容进行个性化。
要使用LocaleMiddleware
,请将'django.middleware.locale.LocaleMiddleware'
添加到您的MIDDLEWARE
设置中。 因为中间件顺序很重要,请遵循以下准则:
LocaleMiddleware
之后,因为SessionMiddleware
使用会话数据。 它应该在CommonMiddleware
之前,因为CommonMiddleware
需要一个激活的语言才能解析请求的URL。LocaleMiddleware
,请在其后放置CacheMiddleware
。例如,您的MIDDLEWARE
可能如下所示:
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
]
(有关中间件的详情,请参阅middleware documentation。)
LocaleMiddleware
尝试通过以下算法确定用户的语言首选项:
首先,它在请求的URL中查找语言前缀。 这仅在您在根URLconf中使用i18n_patterns
函数时才会执行。 有关语言前缀以及如何将网址格式国际化的详细信息,请参见Internationalization: in URL patterns中。
否则,它会在当前用户的会话中查找LANGUAGE_SESSION_KEY
键。
如果没有,它寻找一个cookie。
所使用的Cookie名称由LANGUAGE_COOKIE_NAME
设置设置。 (默认名称为django_language
。)
如果没有,它会查看Accept-Language
HTTP标头。 此标题由您的浏览器发送,并按优先级顺序告诉服务器您喜欢哪种语言。 Django尝试标题中的每种语言,直到找到一个可用的翻译。
否则,它使用全局LANGUAGE_CODE
设置。
笔记:
在每个位置,语言首选项应为标准language format,作为字符串。 例如,巴西葡萄牙语是pt-br
。
如果基本语言可用,但指定的子语言不是,Django使用基本语言。 例如,如果用户指定de
(奥地利德语),但Django只有de-at
可用,则Django使用de
。
只能选择LANGUAGES
设置中列出的语言。
如果要将语言选择限制为所提供语言的一个子集(因为您的应用程序不提供所有这些语言),请将LANGUAGES
设置为语言列表。 像这样:
LANGUAGES = [
('de', _('German')),
('en', _('English')),
]
此示例将可用于自动选择的语言限制为德语和英语(以及任何子语言,如de-ch或en-us)。
如果您定义自定义LANGUAGES
设置(如上一个项目符号所述),则可以将语言名称标记为翻译字符串 - 但使用ugettext_lazy()
而不是ugettext()
以避免循环导入。
下面是一个示例设置文件:
from django.utils.translation import ugettext_lazy as _
LANGUAGES = [
('de', _('German')),
('en', _('English')),
]
一旦LocaleMiddleware
确定用户的首选项,它使每个HttpRequest
的此首选项可用作request.LANGUAGE_CODE
。 随意在您的视图代码中读取此值。 这里有一个简单的例子:
from django.http import HttpResponse
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.")
请注意,使用静态(无中间件)转换,语言在settings.LANGUAGE_CODE
中,而在动态(中间件)转换时,它位于request.LANGUAGE_CODE
中。
在运行时,Django构建一个内存中的文字 - 翻译目录。
为了实现这一点,它通过遵循该算法关于其检查不同文件路径以加载编译的message files(.mo
)和优先级的顺序来寻找翻译多个翻译为同一个字面量:
LOCALE_PATHS
中列出的目录具有最高优先级,首先出现的优先级高于稍后出现的优先级。INSTALLED_APPS
中列出的每个已安装应用程序中是否存在locale
目录。 首先出现的优先级高于稍后出现的优先级。django/conf/locale
中提供的Django提供的基本翻译用作后备。请参见
JavaScript资源中包含的文字的翻译是根据类似但不完全相同的算法查找的。 有关详细信息,请参阅javascript_catalog view documentation。
在所有情况下,包含翻译的目录的名称应使用locale name符号命名。 例如。 pt_BR
,de
,es_AR
等。
这样,您可以编写包含自己的翻译的应用程序,并且可以覆盖项目中的基本翻译。 或者,您可以从几个应用程序构建一个大项目,并将所有翻译成一个大的通用消息文件,特定于您正在撰写的项目。 这是你的选择。
所有消息文件存储库的结构都是相同的。 他们是:
LOCALE_PATHS
中列出的所有路径 / LC_MESSAGES / django的。(PO | MO)
$ APPPATH /区域/ / LC_MESSAGES / django的。(PO | MO)
$ PYTHONPATH / django的/ CONF /区域/ / LC_MESSAGES / django的。(PO | MO)
要创建邮件文件,请使用django-admin makemessages
工具。 And you use django-admin compilemessages
to produce the binary .mo
files that are used by gettext
.
您还可以运行django-admin compilemessages
--settings=path.to.settings
您的LOCALE_PATHS
设置中的所有目录。
Django通常假设可翻译项目中的原始字符串用英语写成。 您可以选择其他语言,但您必须了解某些限制:
gettext
仅为原始邮件提供两种复数形式,因此,如果基础语言的复数规则与英语不同,您还需要为基础语言提供翻译以包含所有复数形式。LANGUAGE_CODE
,而是原始字符串。 例如,访问西班牙语作为默认语言的网站的英文用户和以俄语写成的原始字符串将回溯到俄语,而不是西班牙语。2017年9月6日