Django ugettext_lazy,interpolation和ChoiceField

时间:2012-12-03 18:49:05

标签: django internationalization

我希望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),但这需要对每个数字进行翻译,这很愚蠢。这样做的正确方法是什么?

3 个答案:

答案 0 :(得分:4)

在Django文档TranslationWorking 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的答案并不完全有效 - 它几乎就在那里,但不完全是需要做的事情。

重要的区别是:

  1. 使用lazy进行变形所需的功能是返回文本值,而不是另一个懒惰的翻译对象,或者您可能会看到奇怪的{{1}而不是实际的文本(IIRC Django不会深入懒惰的对象链)。因此,请使用<django.utils.functional.__proxy__ at ...>,而不是ungettext

  2. 您只想在包装函数运行时进行字符串插值。如果你写ungettext_lazy插值会发生得太早 - 在这种情况下,Python会急切地评估。并且不要忘记变量范围 - 您只能从包装函数中引用迭代器。

  3. 在这里,我使用了一个接收数字参数的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的想法。