Thewebframeworkforperfectionistswithdeadlines.
Oncethisisdone,Djangotakescareoftranslatingwebappsontheflyineachavailablelanguage,accordingtousers'languagepreferences.
备注
Python标准库gettext模块在全局命名空间里安装了_()。在Django里,我们没有遵循这个做法,出于下面两个原因:
哪些函数能以_为别名?
由于xgettext的工作方式,只有带有单一字符串参数的函数才能当做_引入:
在这个例子里,文本"Welcometomysite."被标记为翻译字符串:
传递到_()orgettext()的字符串可以使用占位符,这是Python标准命名字符串插值语法指定的。例如:
所以,当你有多个参数的时候,你应该使用命名字符串插值(比如%(day)s)而不是位置插值(比如%s或%d)。如果你使用位置插值,翻译时就不会对占位符字段重新排序。
如果你想为翻译者提供一些翻译字段的提示,你可以添加以Translators为前缀的注释。例如:
为了完整起见,这是.po文件的相应片段:
ngettext()带有三个参数:单数翻译字符串,复数翻译字符串和一些对象。
当你需要Django应用本地化为复数形式的数量和复杂度大于英语中使用的两种形式的语言时,该函数非常有用。('object'表示单数,'objects'表示count不同于1时的所有情况,不论其值如何)
例如:
要注意复数形式是复杂的,并且在每种语言中工作方式不同。比较count和1并不总是正确的规则。该代码看着很复杂,但会对某些语言产生不正确的结果:
当使用ngettext()时,要确保为每一个变量使用一个名称。上面的例子中,请注意我们在两个翻译字符串中如何使用namePython变量。该例子除了上面提到的错误外,还会遇到下面的问题:
在生成的.po文件中,字符串将会频繁出现,因为相同字符串(上下文将在msgctxt行上显示)有不同的上下文标记,所以允许翻译者为每个字符串给出不同翻译。
fromdjango.utils.translationimportpgettextmonth=pgettext("monthname","May")或者:
这些函数存储对字符串的惰性引用——并非真实的翻译。当在字符串上下文里使用字符串时,才会翻译。比如模板渲染。
当对这些函数的调用位于模块加载时执行的代码路径中时,这一点非常重要。
在定义模型、表单和模型表单的时候,很容易遇见这种情况,因为Django已经实现了这些,所以它们的字段实际是类级别的属性。因此,请确保以下情况下使用惰性翻译:
比如下面这个模型,为了翻译其中name字段的帮助提示,可以这么做:
当为复数字符串(n[p]gettext_lazy)使用惰性翻译时,通常不知道字符串定义时number参数值。因此,你可以传递一个关键字名称来作为number参数而不是整数。然后,在插值期间,会在字典里查找number。例如:
对于“你想延迟翻译,但不得不传递要翻译的字符串作为参数到其他函数”的情况,你可以在惰性调用中包装这个函数。比如:
fromdjango.utils.functionalimportlazyfromdjango.utils.safestringimportmark_safefromdjango.utils.translationimportgettext_lazyas_mark_safe_lazy=lazy(mark_safe,str)然后:
警告
在渲染模板的时候,翻译后的字符串不会被转义。这使你在翻译中包含HTML,比如为了强调,但潜在的危险字符(比如")也将保持不变。
{%translate%}模板标签可以翻译常量字符串(用单引号或双引号括起来)或变量内容:
如果模板变量(如上面的myvar)传递给了一个标签,该标签将在运行时首先将这些变量解析为字符串,然后在消息目录里查找变量。
如果你想检索翻译的字符串而不显示它,那你可以使用下面的语法:
{%translate"Thisisthetitle"asthe_title%}
{%blocktranslate%}Thisstringwillhave{{value}}inside.{%endblocktranslate%}要翻译模板表达式(比如,访问对象属性或使用模板过滤器),你需要绑定表达式到本地变量,以便在翻译块中使用。例如:
{%blocktranslatewithamount=article.price%}Thatwillcost${{amount}}.{%endblocktranslate%}{%blocktranslatewithmyvar=value|filter%}Thiswillhave{{myvar}}inside.{%endblocktranslate%}你可以在一个blocktranslate标签内使用多个表达式:
{%blocktranslatewithbook_t=book|titleauthor_t=author|title%}Thisis{{book_t}}by{{author_t}}{%endblocktranslate%}备注
仍然支持以前的更简洁的格式:{%blocktranslatewithbook|titleasbook_tandauthor|titleasauthor_t%}。
其他区块标签(例如{%for%}或{%if%})不允许放在blocktranslate标签内。
该标签也提供复数形式。要使用它:
一个例子:
{%blocktranslatecountcounter=list|length%}Thereisonlyone{{name}}object.{%plural%}Thereare{{counter}}{{name}}objects.{%endblocktranslate%}一个更复杂的例子:
反向URL查询不能在blocktranslate内进行,应事先检索(并存储):
{%blocktranslateasvarthe_title%}Thetitleis{{title}}.{%endblocktranslate%}
{%blocktranslatewithname=user.usernamecontext"greeting"%}Hi{{name}}{%endblocktranslate%}Anotherfeature{%blocktranslate%}supportsisthetrimmedoption.Thisoptionwillremovenewlinecharactersfromthebeginningandtheendofthecontentofthe{%blocktranslate%}tag,replaceanywhitespaceatthebeginningandendofalineandmergealllinesintooneusingaspacecharactertoseparatethem.Thisisquiteusefulforindentingthecontentofa{%blocktranslate%}tagwithouthavingtheindentationcharactersendupinthecorrespondingentryinthe.pofile,whichmakesthetranslationprocesseasier.
例如,以下{%blocktranslate%}标签:
可以使用常见的_()语法将翻译的字符串作为参数传递给标签和过滤器:
{%some_tag_("Pagenotfound")value|yesno:_("yes,no")%}在这种情况下,标签和过滤器将看到翻译后的字符串,因此它们不需要知道翻译。
在这个例子里,翻译基础结构将被传递字符串"yes,no",而不是单独的字符"yes"和"no"。翻译后的字符串需要包含逗号,以便过滤器解析代码时知道如何分割参数。比如,一名德国翻译者可以将"yes,no"翻译为"ja,nein"(保持逗号不变)。
{%comment%}Translators:Viewverb{%endcomment%}{%translate"View"%}{%comment%}Translators:Shortintroblurb{%endcomment%}
{%blocktranslate%}Amultilinetranslatableliteral.{%endblocktranslate%}
或者使用{#...#}单行注释:{#Translators:Labelofabuttonthattriggerssearch#}
为了完整起见,这些是生成.po文件的相应片段:
下面这些标签同样需要先引入{%loadi18n%}.
{%get_available_languagesasLANGUAGES%}返回一个元组列表,其中第一个元素是:term:languagecode,第二个元素是语言名称(翻译成当前活动的语言环境)。
{%get_current_language_bidiasLANGUAGE_BIDI%}返回当前语言文字的阅读方向。如果是True,则是从右向左阅读的语言,比如希伯来语,阿拉伯语。如果是False,则是从左向右阅读的语言,比如英语,法语,德语等。
你也可以查找关于任何使用支持模板标签和过滤器的可用语言的信息。为了得到单一语言的信息,可以使用{%get_language_info%}标签:
{%get_language_infoforLANGUAGE_CODEaslang%}{%get_language_infofor"pl"aslang%}然后,可以访问以下信息:
向JavaScript添加翻译会带来一些问题:
Django为这三个问题提供了解决方案:它将翻译传递给Javascript,因此你可以从JavaScript内部调用gettext等。
解决这三个问题的主要解决办法是下面的JavaScriptCatalog视图,该视图生成一个JavaScript库,该库具有模仿gettext接口的函数,外加一个翻译字符串数组。
一个视图,该视图生成一个JavaScript代码库,该库带有一个模仿gettext界面的函数,外加一个翻译字符串的数组。
属性
翻译域包含要添加到视图输出中的字符串。默认为'djangojs'。
默认值实例:
i18n_patterns()实例:
如果你使用多个JavaScriptCatalog视图,并且它们中的某些视图定义了相同的字符串,那么最后加载的目录中的字符串拥有优先级。
要使用目录,请像这样引入动态生成的js:
gettext函数的行为类似于Python代码中的标准gettext接口:
尽管如此,你不应该使用字符串插值:这仍然是JavaScript,因此代码必须进行重复的正则表达式替换。这并不像Python中的字符串插值那么快,所以请确保你真的需要这么做(比如,与ngettext结合产生合适的复数形式)。
get_format函数可以访问已配置的i18n格式化设置,而且可以检索指定配置名称的格式字符串:
这对于保持与Python呈现的值的格式一致性非常有用。
这模拟了gettext函数,但不执行任何操作,返回那传递给它的所有内容:
document.write(pluralidx(0));//truedocument.write(pluralidx(1));//falsedocument.write(pluralidx(2));//true在这种最简单的情况下,如果不需要自定义复数,那么会对整数1返回false,其他数返回true。
然而,在所有语言中复数化并不是这么简单。如果语言不支持复数化,那么就需要提供一个空值。
此外,如果复数的规则复杂,目录视图将渲染一个条件表达式。它将返回true(如果是复数)或false(如果不是复数)的值。
响应格式如下:
Django提供两种方式来国际化URL模式:
URL模式举例:
确保没有可能与自动添加的语言前缀冲突的非前缀URL模式。
一旦标记了应用程序的字符串文字以供以后翻译,就需要写入(或获取)翻译。这里介绍一下方法。
Gettext实用程序
makemessages命令(和稍后讨论的compilemessages)使用来自GNU文字工具集的命令行:xgettext,msgfmt,msgmerge和msguniq。
gettext实用工具集支持的最低版本是0.15。
要创建或更新消息文件,需执行这个命令:
脚本应该从以下两个位置之一来运行:
django-adminmakemessages-lde-etxt使用逗号和(或)多次使用-e或--extension来分隔多个扩展名:
django-adminmakemessages-lde-ehtml,txt-exml警告
使用Jinja2模板?
babel.cfg配置文件示例:
#ExtractionfromPythonsourcefiles[python:**.py]#ExtractionfromJinja2templates[jinja2:**.jinja]extensions=jinja2.ext.with_确保列出所有正在使用的扩展名。否则Babel不会识别这些扩展名定义的标签,并会完全忽略包含它们的Jinja2模板。
没有gettext?
在Windows上工作?
每个.po文件包含少量的元数据(例如翻译维护者的联系方式等等)以及大量的翻译文件——要翻译的字符串以及实际翻译的字段之间的映射。
例如,如果Djanog程序包含一段"Welcometomysite."的翻译字符串,像这样:
#:path/to/python/module.py:23msgid"Welcometomysite."msgstr""快速解释:
长消息是特殊的情况。这里,在msgstr(ormsgid)后的第一个字符串是空字符串。然后内容本身就会在接下来的几行中以每行一串的形式写出来。这些字符串被直接连接起来。不要忘了字符串中尾部的空格,否则,它们会被粘贴在一起而不留空白!
注意你的字符集
Duetothewaythegettexttoolsworkinternallyandbecausewewanttoallownon-ASCIIsourcestringsinDjango'scoreandyourapplications,youmustuseUTF-8astheencodingforyour.pofiles(thedefaultwhen.pofilesarecreated).Thismeansthateverybodywillbeusingthesameencoding,whichisimportantwhenDjangoprocessesthe.pofiles.
模糊条目
要重新检查新翻译字符串的源代码和模板并更新所有语言的所有消息,运行这行:
该工具运行所有可用的.po文件并创建.mo文件,这些文件是为gettext使用而优化的二进制文件。
django-admincompilemessages这就行了。可以使用你的翻译文件了。
.pofiles:EncodingandBOMusage.
Django只支持使用UTF-8编码的.po文件并且没有任何BOM(字节顺序标记),因此如果文本编辑器默认在文件开头添加这个标记,那么你需要重新配置它.
如果试着使用已被错误标记的字符串来编译消息文件,会得到一个消息,例如"'msgid'和'msgstr'格式数量不匹配"(numberofformatspecificationsin'msgid'and'msgstr'doesnotmatch),或者"'msgstr'不是有效的Python格式字符串,与'msgid'不同"('msgstr'isnotavalidPythonformatstring,unlike'msgid').
要解决这个问题,可以通过添加第二个百分号来转义百分号:
fromdjango.utils.translationimportgettextas_output=_("10%%interest")或者可以使用非python格式(no-python-format),这样所有百分号会被视为文字:
Youmayalsousegettextbinariesyouhaveobtainedelsewhere,solongasthexgettext--versioncommandworksproperly.DonotattempttouseDjangotranslationutilitieswithagettextpackageifthecommandxgettext--versionenteredataWindowscommandpromptcausesapopupwindowsaying"xgettext.exehasgeneratederrorsandwillbeclosedbyWindows".
要激活这个视图,需要在你的URLconf中添加下面这行代码:
在设置语言选择后,Django会检查POST或GET数据中的next参数。如果找到这个参数并且Django认为它是一个安全的链接(也就是说它不指向其他主机并使用安全模式),将会重定向到这个链接。否则,Django可能会重定向到Refererheader里的URL,如果没设置这个URL,则会跳转到/,这取决于请求的性质。
这里是一个模板代码的例子:
虽然Django提供了一套丰富的i18n工具用于视图和模板,但它并不会限制Django特定代码的使用。Django翻译机制可用于将任何文本翻译成Django支持的语言(当然,需要存在合适的翻译目录)。你可以加载翻译目录,激活它并翻译你选择的语言,但记住要切换回原始语言,因为激活语言目录是在每个线程基础上完成的,这样的改变将影响在同一线程中运行的代码。
Django翻译机制使用Python自带的标准gettext模块。如果你知道gettext,你可能注意到了Django翻译的这些特性:
一旦你准备好了翻译——或者,如果你想使用Django自带的翻译——那么你需要激活你的项目的翻译。
在后台,Django有一个非常灵活的模型来决定使用哪一种语言——在全局使用,还是特定用户使用,还是二者都有。
如果你想让每个独立用户指定他们想要的语言,则还需要使用LocaleMiddleware。LocaleMiddleware可以基于请求中的数据启用语言选择。它为每个用户定制内容。
LocaleMiddleware试图通过以下算法来确定用户的语言偏好。
注意:
参见
这样,你可以编写包含自己翻译的应用程序,你可以在你的项目中覆盖基础翻译。或者,你可以用几个应用程序构建一个大项目,并将所有翻译放到一个大的通用消息文件中,具体到你正在编写的项目。这是你的选择。
所有的消息文件库的结构都是一样的。它们是:
Django一般假设可翻译项目中的原始字符串是用英语编写的。你可以选择其他语言,但你必须意识到某些限制。