4

I am using Django's i18n mechanisms to translate an app in several languages (Django 1.11).

Situation

I have a Model with a Field takings choices:

from django.utils.translation import ugettext_lazy as _

class Offer(models.Model):
    WEEK = 'week'
    MONTH = 'month'
    YEAR = 'year'
    DURATION_CHOICES = (
        (WEEK, _('week'),
        (MONTH, _('month'),
        (YEAR, _('year'),
    )
    duration = models.CharField(choices=DURATION_CHOICES, max_length=5, blank=True, null=True, \
        verbose_name=_("offer's duration"), help_text=_("Duration for which an offer, once subscribed, stays valid")

If I want to display this field in a template:

{{ counter }} remaining {{ offer.get_duration_display}}{{ counter|pluralize}}

Where counter is an integer, offer an Offer object. Here it works well enough because plural forms for ('week', 'month', 'year') are ('weeks', 'months', 'years'), same words with an 's' ending.


Problem

Now let's say I want to translate my app to French. This gets tricky, because the corresponding translations are ('semaine', 'mois', 'année') and their plural forms are ('semaines', 'mois', 'années'): the translation for 'month' is the same in singular and plural forms.

So what I would like to do is defining custom plural forms for each choice in the duration Field.

Sure enough, I could use if statements, but this is not elegant nor practical. So I tried to find a DRY solution.


What I tried

You can easily give a plural form to a Model's name with verbose_name_plural, but I didn't find anything for Fields or choices.

So I tried to use the ungettext_lazy function, like this:

(Edit: so, it appears one must not choose a custom name other than _ for gettext and consorts)

from django.utils.translation import ungettext_lazy as _

class Offer(models.Model):
    WEEK = 'week'
    MONTH = 'month'
    YEAR = 'year'
    DURATION_CHOICES = (
        (WEEK, _('week', 'weeks')),
        (MONTH, _('month', 'months')),
        (YEAR, _('year', 'years')),
    )

django-admin makemessages -l fr does output those strings in the *.po file, but it doesn't work: in English, the get_duration_display() returns an empty string.

Anyway, I don't know how I would use it, because you can't pass parameters to the get_FOO_display function...


Question

Would there be any way to do it? (without having to use conditional statements in my views or templates).

Do I have to override existing functions or code new ones?

I found this question (Django: override get_FOO_display()), where the answer points in an interesting direction. But this is quite tricky and I'm honestly a bit in over my head.

Can you think of another way to achieve this? Maybe an existing plugin?

  • what is the code for `get_duration_display()` ? – ptr Dec 15 '17 at 15:41
  • It is a Django built-in function, which is generated at runtime, as pointed out in the answer to the last linked question ([Django: override get_FOO_display](https://stackoverflow.com/questions/13924985/django-override-get-foo-display)). The magic happens here in the [`contribute_to_class` function (GitHub)](https://github.com/django/django/blob/master/django/db/models/fields/__init__.py#L704) – TedGrassman Dec 15 '17 at 15:45

0 Answers0