Django translating choice fields

2019-09-14 07:35发布

问题:

I've got some models like this:

class Payment(models.Model):
    class Status(IntEnum):
        open = 0
        balance = 2
        closed = 1
    status = models.IntegerField(choices=enum_to_choices(Status), default=0, verbose_name=_("Status"))

I'm using a enum to denote my choices and to use them throughout other parts of my application. I'm converting these to tuples to use in choice fields with the following code:

from django.utils.translation import ugettext_lazy as _
def enum_to_choices(enum):
    x = tuple([(x.value, _(x.name.replace("_", " "))) for x in enum])
    return x

The conversion part of the code works, i can use these fields as choices but the translation doesn't work, it doesn't show up in my translation files. If i change the parameter to uggettext_lazy with a static string like "open" it does show up.

What's going on here?

回答1:

It seems to me that this is related to the makemessages command, which for some reason struggles with non-static strings.

I couldn't elaborate on the why, but here's how you can solve the problem:

You actually have to manually create the strings in the translation files (django.po):

#: .\polls\models.py:enum_to_choices

msgid "open"
msgstr "ouvert"

msgid "closed"
msgstr "fermé"

msgid "balance"
msgstr "solde"

Don't forget to django-admin compilemessages, and translated strings should appear!
This is not ideal, especially for long enums, but better than nothing.

If someone here knows the inner workings of makemessages (and the xgettext program it uses) and has an explanation, please let us know ^^


Other solution: use recommended choices structure

Another solution, if you don't absolutely need the choices to be in enums, is using the choices structure as shown in the documentation:

from django.utils.translation import ugettext_lazy as _

class Payment(models.Model):
    OPEN = 0
    CLOSED = 1
    BALANCE = 2
    STATUS_CHOICES = (
        (OPEN, _('open')),
        (CLOSED, _('closed')),
        (BALANCE, _('balance')),
    )
    status = models.IntegerField(choices=STATUS_CHOICES, default=OPEN, verbose_name=_("Status"))

And then on django-admin makemessages the strings will be added to the translation files.

You can easily get the value of a choice, as you would with an enum :

from .models import Payment
new_payment = Payment.objects.create(status=Payment.OPEN)