I want a ChoiceField with these choices:
choices = [(1, '1 thing'),
(2, '2 things'),
(3, '3 things'),
...]
and I want to have it translated.
This does not work:
choices = [(i, ungettext_lazy('%s thing', '%s things', i) % i) for i in range(1,4)]
because as soon as the lazy object is interpolated, it becomes a unicode object - since ChoiceField.choices is evaluated at startup, its choices will be in the language active during Django's startup.
I could use ugettext_lazy('%s things' % i)
, but that would require a translation for each numeral, which is silly. What is the right way to do this?
In the Django documentation, Translation… Working with lazy translation objects, I see a remark which seems to address your concern here.
Using
ugettext_lazy()
andungettext_lazy()
to mark strings in models and utility functions is a common operation. When you're working with these objects elsewhere in your code, you should ensure that you don't accidentally convert them to strings, because they should be converted as late as possible (so that the correct locale is in effect). This necessitates the use of the helper function described next.
Then they present django.utils.functional.lazy(func, *resultclasses)
, which is not presently covered by the django.utils.functional
module documentation. However, according to the django.utils.functional.py
source code, it "Turns any callable into a lazy evaluated callable.… the
function is evaluated on every access."
Modifying their example from Other uses of lazy in delayed translations to incorporate your code, the following code might work for you.
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)
]
Also, the django.utils.functional
module documentation does mention a function decorator allow_lazy(func, *resultclasses)
. This lets you write your own function which takes a lazy string as arguments. "It modifies the function so that if it's called with a lazy translation as the first argument, the function evaluation is delayed until it needs to be converted to a string." lazy(func, *resultclasses)
is not a decorator, it modifies a callable.
N.B. I haven't tried this code in Django. I'm just passing along what I found in the documentation. Hopefully it will point you to something you can use.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With