[Fixed]-Override default Django translations

6👍

The easiest way is to collect the .po file found in the django.contrib.admin locale folder and re-compiling it (you can use POEdit for doing so).

You could also override the django.contrib.admin templates by putting them in your projects templates folder (for example: yourproject/templates/admin/change_form.html) then running makemessages from the project root (although this is no longer supported for django 1.4 alpha if i’m correct)

edit: Robert Lujo’s answer is the clean method

42👍

This is what worked for me:

  • create a file in your app folder which will hold django messages for which translations need to be overridden, e.g. django_standard_messages.py

  • in django lib folder or in django.po files find the message (string) that needs to be overridden, e.g. django.forms/fields.py has message _(u"This field is required.") which we want to translate to german differently

  • in django_standard_messages.py add all such messages like this:

# coding: utf-8
_ = lambda s: s
django_standard_messages_to_override = [
_("This field is required."),
...
]
  • Translate the file (makemessages, compilemessages) – makemessages will add added django standard messages in your application .po file, find them and translate, run compilemessages to update .mo file
  • tryout

The logic behind: (I think 😉 ) – when ugettext function searches translation for one message (string), there are several .po/.mo files that needs to be searched through. The first match is used. So, if our local app .po/.mo is first in that order, our translations will override all other (e.g. django default).

Alternative

When you need to translate all or most of django default messages, the other possibility (which I didn’t tried) is to copy default django .po file in our locale or some other special folder, and fix translations and register the folder (if new) in LOCALE_PATHS django settings file as first entry in the list.

The logic behind: is the very similar as noted in previous section.

16👍

Based on Robert Lujo answer, his alternative is totally working. And IMO simpler (keep the overriden locales in a special .po file only). Here are the steps:

  • Add an extra path to the LOCALE_PATHS Django settings.

    • LOCALE_PATHS = (
      # the default one, where the makemessages command will generate the files
      os.path.join(BASE_DIR, ‘myproject’, ‘locale’),
      # our new, extended locale dir
      os.path.join(BASE_DIR, ‘myproject’, ‘locale_extra’),
      )

  • find the original Django (or 3rd party) string to be translated

  • Add the new .po file “myproject/locale_extra/en/LC_MESSAGES/django.po” with the alternative translation :
    • msgid “Recent actions”
      msgstr “Last actions”

  • Compile your messages as usual

0👍

This is another solution we deployed. It involved monkey patching the _add_installed_apps_translations method of the DjangoTranslation class to prioritize the translations of the project apps over the translations of the Django apps.

# patches.py
from __future__ import absolute_import, unicode_literals

import os

from django.apps import apps
from django.core.exceptions import AppRegistryNotReady
from django.utils.translation.trans_real import DjangoTranslation


def patchDjangoTranslation():
    """
    Patch Django to prioritize the project's app translations over
    its own. Fixes GitLab issue #734 for Django 1.11.
    Might needs to be updated for future Django versions.
    """

    def _add_installed_apps_translations_new(self):
        """Merges translations from each installed app."""
        try:
            # Django apps
            app_configs = [
                app for app in apps.get_app_configs() if app.name.startswith('django.')
            ]

            # Non Django apps
            app_configs = [
                app for app in apps.get_app_configs() if not app.name.startswith('django.')
            ]
            app_configs = reversed(app_configs)
        except AppRegistryNotReady:
            raise AppRegistryNotReady(
                "The translation infrastructure cannot be initialized before the "
                "apps registry is ready. Check that you don't make non-lazy "
                "gettext calls at import time.")
        for app_config in app_configs:
            localedir = os.path.join(app_config.path, 'locale')
            if os.path.exists(localedir):
                translation = self._new_gnu_trans(localedir)
                self.merge(translation)

    DjangoTranslation._add_installed_apps_translations = _add_installed_apps_translations_new

Then in the .ready() method of your main app, call patchDjangoTranslation:

from .patches import patchDjangoTranslation

class CommonApp(MayanAppConfig):
    app_namespace = 'common'
    app_url = ''
    has_rest_api = True
    has_tests = True
    name = 'mayan.apps.common'
    verbose_name = _('Common')

    def ready(self):
        super(CommonApp, self).ready()
        patchDjangoTranslation()  # Apply patch

The main change are these lines:

        # Django apps
        app_configs = [
            app for app in apps.get_app_configs() if app.name.startswith('django.')
        ]

        # Non Django apps
        app_configs = [
            app for app in apps.get_app_configs() if not app.name.startswith('django.')
        ]
        app_configs = reversed(app_configs)

The original are:

        app_configs = reversed(list(apps.get_app_configs()))

Instead of interpreting the translations of the apps in the order they appear in the INSTALLED_APPS setting, this block outputs the list of apps placing the project apps before the Django apps. Since this only happens when determining the translation to use, it doesn’t affect any other part of the code and no other changes are necessary.

It works on Django version 1.11 up to 2.2.

Leave a comment