1

I wish to find a simple way to override the gettext method use by Django.

I wish to create my own method and tell Django to use it everywhere (.py, Template …). In my .py it's simple, I can use my new method directly but in the Django engine I do not know how to do it ?

My aim is to use a database of translations + Google Cloud Translation.

I do not find a way to do it … but Django si perfect, so I suppose there is a way ? :)

Vivak P
  • 110
  • 6

2 Answers2

2

you need to write your custom template context_preprocessor to make your custom translation function available globally for use in all templates, refer to https://docs.djangoproject.com/en/3.0/ref/templates/api/#writing-your-own-context-processors

Custom context processors can live anywhere in your code base, but since your function is about translation it does make sens to make app-independent, meaning the code should live in the root project:

under root project create context_processors.py:

my_gettext(request):
    #  put your logic here ..
    # ..

then under settings.py:

# ..
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',

                'context_processors.my_gettext',  # register your custom context preprocessor

            ],
        },
    },
]
# ..

cizario
  • 3,995
  • 3
  • 13
  • 27
0

Here how I have done it.

Create a class to save in the database the translations :

class Cache(models.Model):
    source: str = models.TextField(verbose_name=_('Source'), null=False, blank=False)
    result: str = models.TextField(verbose_name=_('Result'), null=False, blank=False)
    language_code: str = models.CharField(verbose_name=_('Language Code'), max_length=10, null=False, blank=False)
    md5: str = models.CharField(verbose_name=_('MD5'), max_length=512, null=False, blank=False, db_index=True)

    def save(self):
        self.md5 = hashlib.md5( (self.language_code + '__' + self.source).encode() ).digest()
        obj: Cache = super(Cache, self).save()
        return obj

Create a function to translate a text from Google Cloud API

# -*- coding: utf-8 -*-
import hashlib
from google.cloud import translate_v2 as translate

def GoogleTranslationGetText(msg, language_code):
    from .models import Cache
    from . import translate_client, cache_local
    md5 = hashlib.md5( (language_code + '__' + msg).encode()).digest()

    result = cache_local.get(md5, None)
    if not result:
        caches: Cache = Cache.objects.filter(md5 = md5).defer('source', 'language_code', 'md5')
        result = msg
        if caches:
            result = caches[0].result
        else:
            if not translate_client:
                translate_client = translate.Client()
            result = translate_client.translate(msg, target_language=language_code)
            result = result['translatedText']

            #Save cache
            cache = Cache()
            cache.source = msg
            cache.result = result
            cache.language_code = language_code
            cache.save()
        
        cache_local[md5] = result

    return result

init.py

translate_client  = None
cache_local = {}

Modify the method gettext() of django/utils/translation/trans_real.py :

def gettext(message):
    """
    Translate the 'message' string. It uses the current thread to find the
    translation object to use. If no current translation is activated, the
    message will be run through the default translation object.
    """
    global _default

    eol_message = message.replace('\r\n', '\n').replace('\r', '\n')

    if eol_message:
        _default = _default or translation(settings.LANGUAGE_CODE)
        translation_object = getattr(_active, "value", _default)

        ##CANICOMPET
        if type(translation_object) == DjangoTranslation and translation_object.language() in settings.LANGUAGE_TO_GOOGLE_CODES:
            result = settings.LANGUAGE_TO_GOOGLE_FCT(eol_message, translation_object.language())
        else:
            ## original Django
            result = translation_object.gettext(eol_message)


    else:
        # Return an empty value of the corresponding type if an empty message
        # is given, instead of metadata, which is the default gettext behavior.
        result = type(message)('')

    if isinstance(message, SafeData):
        return mark_safe(result)

    return result

Finnaly in settings.py add :

os.environ.setdefault("GOOGLE_APPLICATION_CREDENTIALS", "pth/to/google/api/json/key/file.json")
LANGUAGE_TO_GOOGLE_CODES = ['de', ] #list of languages translated by Google
from GoogleTranslation.functions import GoogleTranslationGetText
LANGUAGE_TO_GOOGLE_FCT = GoogleTranslationGetText