""" ``i18n`` module.
"""
import gettext
import os
import os.path
from collections import defaultdict
null_translations = gettext.NullTranslations()
[docs]class TranslationsManager(object):
"""Manages several languages and translation domains."""
def __init__(self, directories=None, default_lang="en"):
"""
>>> curdir = os.path.dirname(__file__)
>>> localedir = os.path.join(curdir, 'tests', 'i18n')
>>> tm = TranslationsManager(directories=[localedir])
>>> sorted(tm.translations.keys()) # doctest: +ELLIPSIS
[...]
"""
self.default_lang = default_lang
self.fallbacks = {}
self.translations = defaultdict(
lambda: defaultdict(lambda: null_translations)
)
self.domains = defaultdict(
lambda: defaultdict(lambda: null_translations)
)
if directories:
for localedir in directories:
self.load(localedir)
[docs] def add_fallback(self, languages):
"""Adds fallback languages.
>>> tm = TranslationsManager()
>>> tm.add_fallback(('uk', 'ru'))
>>> tm.fallbacks
{'uk': ('uk', 'ru', 'en')}
"""
if self.default_lang not in languages:
languages = list(languages)
languages.append(self.default_lang)
self.fallbacks[languages[0]] = tuple(languages)
[docs] def load(self, localedir):
"""Load all available languages and domains from the
given directory.
{localedir}/{lang}/LC_MESSAGES/{domain}.mo
In order to generate .mo file from .po file:
$ msgfmt domain.po
>>> curdir = os.path.dirname(__file__)
>>> localedir = os.path.join(curdir, 'tests', 'i18n')
>>> tm = TranslationsManager()
>>> tm.load(localedir)
>>> sorted(tm.translations.keys()) # doctest: +ELLIPSIS
[...]
Assess by language:
>>> lang = tm['en']
>>> m = lang['messages']
>>> m.gettext('hello') # doctest: +ELLIPSIS
'...ello'
>>> lang = tm['de']
>>> m = lang['messages']
>>> m.gettext('hello') # doctest: +ELLIPSIS
'...'
Assess by translation domain:
>>> messages = tm.domains['messages']
>>> m = messages['en']
>>> m.gettext('hello') # doctest: +ELLIPSIS
'...ello'
Fallback to English:
>>> m.gettext('world') # doctest: +ELLIPSIS
'...orld'
If translation is unknown ``key`` returned
>>> m.gettext('test')
'test'
"""
for lang in os.listdir(localedir):
if lang.startswith("."): # pragma: nocover
continue
domaindir = os.path.join(localedir, lang, "LC_MESSAGES")
if not os.path.isdir(domaindir): # pragma: nocover
continue
for domain in os.listdir(domaindir):
if not domain.endswith(".mo"):
continue
domain = domain[:-3]
if lang not in self.fallbacks:
self.add_fallback((lang,))
translation = gettext.translation(
domain, localedir, languages=self.fallbacks[lang]
)
self.translations[lang][domain] = translation
self.domains[domain][lang] = translation
def __getitem__(self, lang):
"""Returns language ``defaultdict`` with translation domains."""
return self.translations[lang]