在Django开发中,常常需要根据物体的单数和复数,而变换不同的表达方式,特别是在英语中,如单复数的显示。如下面的场景:
# 复数表达形式
There are 5 VMs
# 单数表达形式
There is 1 VM
在Django 模板中,有两种方式能解决类似的问题:
pluralize过滤器
blocktrans 块标签搭配plural标签
pluralize 过滤器通过返回后缀的形式以支持单复数形式的显示,默认情况下当为复数时,会添加 “s” 后缀。如:
You have {{ num_messages }} message{{ num_messages|pluralize }}.
如果 num_messages 为1, 输出为“You have 1 message.”,如果为5,则输出“You have 5 messages.”
同样,也可指定复数情况下需要添加的后缀,如:
You have {{ num_walruses }} walrus{{ num_walruses|pluralize:"es" }}.
更深一点,你也可完全同时指定单数和复数形式下的后缀,如
You have {{ num_cherries }} cherr{{ num_cherries|pluralize:"y,ies" }}.
回到上面的问题,那用 pluralize 过滤器就成了下面这样的形式了
There {{ amount|pluralize:"is,are" }} {{ amount }} VM{{ amount|pluralize }}
需要注意的,0 在英语中视作复数。
但需要注意的,上面这种方法虽然使用非常方便,但却无法对其进行国际化了。如果需要支持国际化,则需要用到下面的方法。
和 trans
标签不同,对于 blocktrans
模板标签,通过占位符它支持对复杂句式(同时包含字符串和变量)进行国际化。如下:
{% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %}
另外,如果需要使用模板表达式,即访问 对象的属性 或者使用 过滤器,就需要将这个表达式的值赋给一个局部变量,在blocktrans内部使用。原因见后面的注意说明。
# 访问对象属性
{% blocktrans with amount=article.price %}
That will cost $ {{ amount }}.
{% endblocktrans %}
# 使用过滤器
{% blocktrans with myvar=value|filter %}
This will have {{ myvar }} inside.
{% endblocktrans %}
# 使用多个局部变量
{% blocktrans with book_t=book|title author_t=author|title %}
This is {{ book_t }} by {{ author_t }}
{% endblocktrans %}
而对于django.po
文件里,对应的翻译需要这样写
msgid "That will cost $ %(amount)s."
msgstr "那将花费 $ %(amount)s ."
- 通过
with =
赋值方式是后来的django版本支持,当然新版本仍支持with as
的赋值方式,如:
python
{% blocktrans with book|titleas book_t and author|title as author_t %}
- 在blocktrans块标签内不能再嵌套使用其他的块标签,如{% for %} or {% if %}等。
另外blocktrans
标签也支持复数化(pluralization),使用步骤如下:
count =
指定需要需要判断的数量变量,这个 counter 将用来判断是显示单数形式还是显示复数形式直接上例子说明:
{% 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 %}
注意:blocktrans
内部会调用ungettext
方法以支持国际化,所以同使用ungettext
一样,在blocktrans块标签内只能用单一变量名进行数据的渲染,所以对于表达式和过滤器的使用,需要将先其赋值到一个单一的局部变量,使其能在blocktrans进行数据渲染。
经过上面的探讨后,回到最初的问题,那么用blocktrans
不仅可以解决复数化的问题,还能优雅地支持国际化:
# template
{% blocktrans count amount=amount %}
There is {{ amount }} VM.
{% plural %}
There are {{ amount }} VMs.
{% endblocktrans %}
# django.po
msgid ""
"\n"
" There is %(amount)s VM.\n"
msgid_plural ""
"\n"
" There are %(amount)s VMs.\n"
msgstr[0] ""
"\n"
"有 %(amount)s 台虚拟机\n"
这里尤其要注意在 po 文件中做转译时,排版一定要严格根据 template 中的格式来,尤其要注意换行和空格,要保持绝对的一致,下面再给几个模式以便进行比较分析:
# example 1
# -----template------
{% blocktrans count amount=amount %}
There is {{ amount }} VM.
{% plural %}
There are {{ amount }} VMs.
{% endblocktrans %}
# -----django.po-------
msgid "" # `blocktrans`开始
"\n" # `blocktrans`与转译文本之间的换行
" There is %(amount)s VM.\n" # 上次换行到转译文本结束,包括换行
" " # 上次换行到`plural`标签的字符(空格)`
msgid_plural "" # `plural`开始
"\n" # `plural`与转译文本之间的换行
" There are %(amount)s VMs.\n" #上次换行到转译文本结束,包括换行
" " # 上次换行到`endblocktrans`标签的字符(空格)
msgstr[0] ""
"\n"
"有 %(amount)s 台虚拟机"
# 其实,msgstr[0] 只要保证转译后的文本开关与结尾和msgid中的开头和结尾的"\n"一致就行,所以下面这行也使用于对上面的转译
# msgstr[0] "\n有 %(amount)s 台虚拟机"
# example 2
# -----template------
{% blocktrans count amount=amount %}There is {{ amount }} VM.{% plural %}There are {{ amount }} VMs.{% endblocktrans %}
# -----django.po-------
msgid "There is %(amount)s VM."
msgid_plural "There are %(amount)s VMs."
msgstr[0] "有 %(amount)s 台虚拟机"
从上面的代码分析对比,一般还是建议用第二种方式显示,除非在前台需要预处理格式的排版。
另外关于msgstr[0], msgstr[1],……,以后再深究,可以参考这里
参考
1. Built-in template tags and filters | Django documentation | Django
2. Translation | Django documentation | Django