Guide: Models

This module provides an in depth knowledge of the Translations models.

Make models translatable

To make a model translatable inherit it from the Translatable abstract model.

from translations.models import Translatable
class Continent(Translatable):
    name = models.CharField(
        verbose_name=_('name'),
        help_text=_('the name of the continent'),
        max_length=64,
    )
    denonym = models.CharField(
        verbose_name=_('denonym'),
        help_text=_('the denonym of the continent'),
        max_length=64,
        blank=True,
    )
    code = models.CharField(
        verbose_name=_('code'),
        help_text=_('the code of the continent'),
        max_length=2,
        unique=True,
        primary_key=True,
    )

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = _('continent')
        verbose_name_plural = _('continents')

Note

Since Translatable is an abstract model there is no need to migrate afterwards.

Warning

Care not to inherit the Translation model accidentally instead of the Translatable model.

Specify models’ translatable fields

To specify the model’s translatable fields specify the fields attribute of the TranslatableMeta class declared inside the Translatable model.

class Continent(Translatable):
    name = models.CharField(
        verbose_name=_('name'),
        help_text=_('the name of the continent'),
        max_length=64,
    )
    denonym = models.CharField(
        verbose_name=_('denonym'),
        help_text=_('the denonym of the continent'),
        max_length=64,
        blank=True,
    )
    code = models.CharField(
        verbose_name=_('code'),
        help_text=_('the code of the continent'),
        max_length=2,
        unique=True,
        primary_key=True,
    )

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = _('continent')
        verbose_name_plural = _('continents')

    class TranslatableMeta:
        fields = ['name', 'denonym']

By default the fields attribute is set to None. This means the translation will use the text based fields automatically. (like CharField and TextField - this does not include EmailField or the fields with choices)

class City(Translatable):
    name = models.CharField(
        verbose_name=_('name'),
        help_text=_('the name of the city'),
        max_length=64,
    )
    denonym = models.CharField(
        verbose_name=_('denonym'),
        help_text=_('the denonym of the city'),
        max_length=64,
        blank=True,
    )
    country = models.ForeignKey(
        verbose_name=_('country'),
        help_text=_('the country of the city'),
        to=Country,
        on_delete=models.CASCADE,
        related_name='cities',
    )

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = _('city')
        verbose_name_plural = _('cities')

If needed, the fields attribute can be set to nothing. This can be done by explicitly setting it to [].

class Timezone(Translatable):
    name = models.CharField(
        verbose_name=_('name'),
        help_text=_('the name of the timezone'),
        max_length=32,
    )

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = _('timezone')
        verbose_name_plural = _('timezones')

    class TranslatableMeta:
        fields = []

Changing the models and fields configurations

To synchronize the translations with the apps models configurations run the synctranslations command.

$ python manage.py synctranslations

This is useful in the cases when you decide to change the configurations of the models and fields later on, but you have already defined some translations for them and the translations are obsolete now.

Note

Since this command deletes the obsolete translations which are not useful any more based on the current models and fields configurations, it has to make sure that you are aware of the risks.

So you either have to run it in a TTY environment and respond yes when it asks you if you are sure, or you have to declare that you are sure explicitly while calling the command using the --no-input option.