5

I've been using django-modeltranslation to translate models in django for a while. It is really straightforward and it works really well on apps I've been developing, where all model translated content gets inserted with forms by the final user.

eg: inputs: content, content_en, content_pt, ...

I have to build an application where I need to translate 'built-in' model strings that are generated by django, like 'auth.permission.name' or 'contenttypes.contenttype.name' and add them to translation django.po files.

I came up with a solution that works fine,

which uses post_migration signals that create a file with lists of ugettext_lazy elements, so new strings, like a new contenttype.name for example, are added to 'django.po' dynamically and loaded to the database.

Yet, is a bit weird having to create a file with ugettext calls

in order to register the strings, but I didn't find another way of registering and adding them dynamically to the django.po file, so I need your help


Here's what I have done:

1. I created an app named 'tools', that is the last one on INSTALLED_APPS, so its migrations are naturally the last ones to be called. This app does not have any models, it just runs migrations, has the django-modeltranslation translation.py file and an application config with a post_migration signal call.

# translations.py

from modeltranslation.translator import translator, TranslationOptions
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType

class PermissionTranslationOptions(TranslationOptions):
    fields = ('name',)

class ContentTypeTranslationOptions(TranslationOptions):
    fields = ('name',)

translator.register(Permission, PermissionTranslationOptions)
translator.register(ContentType, ContentTypeTranslationOptions)

2. Running 'manage.py makemigrations' creates the migrations on the 'auth' and 'contenttypes' applications with the extra 'name_*' fields.

3. the app has an application config that has a post_migrate signal

# __init__.py

default_app_config = 'apps.tools.config.SystemConfig'


# config.py

from django.apps import AppConfig
from django.db.models.signals import post_migrate
from apps.tools.translations.exporter import make_translations
from apps.tools.translations.importer import load_translations


def run_translations(sender, **kwargs):

    # This creates the translations
    make_translations()

    # This loads the the translations to the db
    load_translations()


class SystemConfig(AppConfig):

    name = 'apps.tools'
    verbose_name = 'Tools'

    def ready(self):
        # Call post migration operations
        post_migrate.connect(run_translations, sender=self)

4. make_translations() is called after migrations and generates a file with lists of uggettext_lazy calls.

This is the bit I would like to change. Do I really need to create a file?

# exporter

import os
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.utils import translation
from django.contrib.contenttypes.management import update_all_contenttypes


# TODO 
# It has got to be another way

def make_translations():
    # lets go default
    translation.activate("en")
    update_all_contenttypes()

    try:
        f = open(os.path.join(os.path.realpath(os.path.dirname(__file__)), 'translations.py'), 'w')

        # Write file
        f.write("from django.utils.translation import ugettext_lazy as _\n\n")

        # All Permissions to lazy text
        f.write('permissions = {\n')
        for perm in Permission.objects.all().order_by('id'):
            f.write('    "'+str(perm.id)+'": _("'+perm.name+'"),\n')
        f.write('}\n\n')

        # All Content types to lazy text
        f.write('content_types = {\n')
        for content in ContentType.objects.all().order_by('id'):
            f.write('    "'+str(content.id)+'": _("'+content.name+'"),\n')
        f.write('}\n\n')

        # Closing file
        f.close()

        # Importing file to get it registered with ugettext_lazy
        try:
            from apps.tools.translations import translations
        except:
            print('Could not import file')
            pass
    except:
        print('Could not create file')
        pass

The above results in a file like this:

from django.utils.translation import ugettext_lazy as _

permissions = {
    "1": _("Can add permission"),
    "2": _("Can change permission"),
    "3": _("Can delete permission"),
    "4": _("Can add group"),
  ...
}

content_types = {
    "1": _("group"),
    "2": _("user"),
    "3": _("permission"),
    "4": _("content type"),
    "5": _("session"),
  ...
}

5. Running 'makemessages' would add this strings to 'django.po' files, yet, the post_migration signal does not stop here, and loads the existing compiled strings in the database

# importer

from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.conf import settings
from django.utils import translation


def load_translations():
    try:
        from apps.tools.translations.translations import permissions, content_types
    except:
        # File does not exists
        print('Translations could not be loaded')
        return

    # For each language
    for lang in settings.LANGUAGES:

        # Activate language
        translation.activate(lang[0])

        # Loading translated permissions
        all_permissions = Permission.objects.all()
        for permission in all_permissions:
            permission.name = unicode(permissions[str(permission.id)])
            permission.save()

        # Loading translated content_types
        all_contenttypes = ContentType.objects.all()
        for contenttype in all_contenttypes:
            contenttype.name = unicode(content_types[str(contenttype.id)])
            contenttype.save()

How can I replace 'make_translations()' without creating a file and register those strings with ugettext_lazy?

Thanks for your help

brunofitas
  • 2,983
  • 1
  • 20
  • 26
  • Can you elaborate more about you solution in the first part. It might help to understand what you are doing. – gabn88 Sep 24 '15 at 14:46

1 Answers1

0

I've read your post and also somehow I had the same problem with translation for permissions, I've found a very short way to solve the problem:

  • I wouldn't recommend to do this way neither regret that.

but the solution is: edit the app_labeled_name function decorated as property of this path: .pyenv/Lib/site-packages/django/contrib/contenttypes/models.py of ContentType class, to become like this:

@property
def app_labeled_name(self):
    model = self.model_class()
    if not model:
        return self.model
    return '%s | %s' % (apps.get_app_config(model._meta.app_label).verbose_name,
                        model._meta.verbose_name)

the trick is to use apps.get_app_config(model._meta.app_label).verbose_name instead of model._meta.app_label, so it would use the same verbose_name as whatever use set for your app in the AppConfig subclass of your app.

mh-firouzjah
  • 834
  • 1
  • 6
  • 15