我希望ChoiceField有这些选择:
choices = [(1, '1 thing'),
(2, '2 things'),
(3, '3 things'),
...]
我希望翻译它。
这不起作用:
choices = [(i, ungettext_lazy('%s thing', '%s things', i) % i) for i in range(1,4)]
因为只要插入了懒惰对象,它就会变成一个unicode对象 - 因为在启动时会评估ChoiceField.choices,它的选择将在Django启动期间处于活动状态。
我可以使用ugettext_lazy('%s things' % i)
,但这需要对每个数字进行翻译,这很愚蠢。这样做的正确方法是什么?
答案 0 :(得分:4)
在Django文档Translation… Working with lazy translation objects中,我看到一条似乎可以解决您关注的问题。
使用
ugettext_lazy()
和ungettext_lazy()
标记模型和实用程序函数中的字符串是一种常见操作。当您在代码中的其他位置处理这些对象时,应确保不会意外地将它们转换为字符串,因为它们应尽可能晚地转换(以便正确的区域设置生效)。这需要使用下面描述的辅助函数。
然后他们出现django.utils.functional.lazy(func, *resultclasses)
,django.utils.functional
module documentation目前尚未涵盖from django.utils import six # Python 3 compatibility
from django.utils.functional import lazy
from django.utils.safestring import mark_safe
choices = [
(i, lazy(
mark_safe(ungettext_lazy('%s thing', '%s things', i) % i),
six.text_type
)# lazy()
for i in range(1,4)
]
。但是,根据django.utils.functional.py
source code,它“将任何可调用的内容转换为懒惰的可评估可调用... ...
每次访问都会评估函数。“
修改他们的example from Other uses of lazy in delayed translations以合并您的代码,以下代码可能适合您。
allow_lazy(func, *resultclasses)
此外,django.utils.functional
module documentation确实提到了函数装饰器lazy(func, *resultclasses)
。这允许您编写自己的函数,该函数将惰性字符串作为参数。 “它修改了函数,因此如果使用延迟转换作为第一个参数调用它,则函数求值会被延迟,直到需要将其转换为字符串。” {{1}}不是装饰器,它会修改可调用的。
N.B。我没有在Django中尝试过这段代码。我只是传递我在文档中找到的内容。希望它会指向你可以使用的东西。
答案 1 :(得分:2)
对于那些遇到这个问题的人。不幸的是,@ Jim DeLaHunt的答案并不完全有效 - 它几乎就在那里,但不完全是需要做的事情。
重要的区别是:
使用lazy
进行变形所需的功能是返回文本值,而不是另一个懒惰的翻译对象,或者您可能会看到奇怪的{{1}而不是实际的文本(IIRC Django不会深入懒惰的对象链)。因此,请使用<django.utils.functional.__proxy__ at ...>
,而不是ungettext
。
您只想在包装函数运行时进行字符串插值。如果你写ungettext_lazy
插值会发生得太早 - 在这种情况下,Python会急切地评估。并且不要忘记变量范围 - 您只能从包装函数中引用迭代器。
在这里,我使用了一个接收数字参数的lazy(f("%d" % 42))
并进行插值。 lambda
中的代码仅在评估惰性对象时执行,即在呈现选择时执行。
所以,工作代码是:
lambda
这将正确地具有与
相同的预期效果choices = [
(
(i, lazy(
lambda cnt: ungettext(u"%(count)d thing",
u"%(count)d things", cnt)
% {"count": cnt},
six.text_type
)(i))
)
for i in [1, 2, 3]
]
但是在翻译数据库中只有一个条目,而不是多个条目。
答案 2 :(得分:0)
这看起来像是一种情况,你可以从Ilian Iliev的博客Django forms ChoiceField with dynamic values…所教授的技巧中受益。
Iliev显示了一个非常相似的初始化者:my_choice_field = forms.ChoiceField(choices=get_my_choices())
他说,“诀窍在于,在这种情况下,my_choice_field选项在服务器(重新)启动时被初始化。或者换句话说,一旦你运行服务器,选择被加载(计算)并且它们将不会改变直到下一步(re) )开始。”听起来像你遇到的困难一样。
他的诀窍是:“幸运的是,表单的类有一个 init 方法,在每个表单加载时调用。大多数时候你在表单定义中跳过它但现在你必须用它。“
以下是他的示例代码,与您的生成器表达式混合:
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['my_choice_field'] = forms.ChoiceField(
choices=(
(i, ungettext_lazy('%s thing', '%s things', i) % i)
for i in range(1,4)
)# choices=
)# __init__
生成器表达式括在括号中,以便将其视为生成器对象,该对象已分配给choices
。
N.B。我没有在Django中尝试过这段代码。我只是在传递Iliev的想法。