Search code examples
pythongettext

Python, gettext: How to get right locale for module- and class-level variables?


I have some troubles when i use gettext for module- and class-level variables.

First of all, at import time i use my default locale and install it. After it I get specific locale when I start my project by parsing settings and use gettext.translation.install for set right locale. All work well, but in my code all module- and class-level variables stay translated with default locale, not right.

For example I have project like this:

├── foo.py
├── i18t
│   └── __init__.py
├── __init__.py
└── run.py

i18t/__init__.py contains:

import gettext
from os import path

SUPPORTED_LANGUAGES = ('en', 'fr')
DEFAULT_LANGUAGE = SUPPORTED_LANGUAGES[0]

locale_path = path.abspath(path.join(path.dirname(__file__), 'locale'))

TRANSLATIONS = {
    lang: gettext.translation(
        'domain',
        locale_path,
        languages=[lang]
    ) for lang in SUPPORTED_LANGUAGES
}


def install_language(language):
    lang = TRANSLATIONS.get(language)
    if lang is None:
        lang = TRANSLATIONS[DEFAULT_LANGUAGE]
    lang.install()

__init__.py:

from i18n import DEFAULT_LANGUAGE, install_language

install_language(DEFAULT_LANGUAGE)

foo.py:

foo = _('Bar')

run.py:

from i18t import install_language
import foo


def main():
    ...
    read config and get right locale
    ...
    install_language(right_locale)
    bar = _('Bazz')
    print(foo.foo)
    print(bar)

I execute run.py and I get translated foo.foo with default locale and bar with right locale. I know that foo.foo run _(gettext) only once when I import module but I need to get right translated string. Which better way to do it?


Solution

  • Problem of this case is desire to use run-time function gettext for strings which evaluated only once in import-time. I think it's better not to do this.

    But I resolved this problem with using string-like object like in speaklater package or inherit object UserSting and make your own gettext function:

    from collections import UserString
    
    
    class RuntimeTranslatedString(UserString):
    
        def __init__(self, message):
            self._data = message
    
        @property
        def data(self):
            return _(self._data)
    
    
    def N_(message):
        return RuntimeTranslatedString(message)
    

    In your code you can use it like _(gettext):

    from .i18n import N_
    
    foo = N_('Bar')
    

    Don't forget add it to your extract_message command. In my setup.cfg it looks like:

    [extract_messages]
    output_file = my-package/i18n/locale/translation.pot
    keywords = _ N_
    

    I'm not sure that this is the good solution but it works.