重写django翻译方法

Override the django translation method

我希望找到一种简单的方法来覆盖 Django 使用的 gettext 方法。

我希望创建自己的方法并告诉 Django 在任何地方使用它(.py、模板……)。在我的 .py 中很简单,我可以直接使用我的新方法,但在 Django 引擎中我不知道该怎么做?

我的目标是使用翻译数据库 + Google Cloud Translation。

我没有找到办法...但是 Django 非常完美,所以我想有办法吗? :)

您需要编写您的自定义模板context_preprocessor才能使您的自定义翻译功能全局在所有模板中使用,参考https://docs.djangoproject.com/en/3.0/ref/templates/api/#writing-your-own-context-processors

自定义上下文处理器可以存在于您的代码库中的任何位置,但由于您的函数是关于翻译的,因此制作 app-independent 确实有意义,这意味着代码应该存在于根项目中:

在根项目下创建context_processors.py:

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

然后在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

            ],
        },
    },
]
# ..

这是我的做法。

创建一个 class 以将翻译保存在数据库中:

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

创建一个函数来翻译来自 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 = {}

修改django/utils/translation/trans_real.py的gettext()方法:

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

最后在 settings.py 添加:

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