from typing import Optional import logging from contextvars import ContextVar from collections import defaultdict from enum import Enum import gettext from discord.app_commands import Translator, locale_str from discord.enums import Locale logger = logging.getLogger(__name__) SOURCE_LOCALE = 'en_GB' ctx_locale: ContextVar[str] = ContextVar('locale', default=SOURCE_LOCALE) ctx_translator: ContextVar['LeoBabel'] = ContextVar('translator', default=None) # type: ignore null = gettext.NullTranslations() class LeoBabel(Translator): def __init__(self): self.supported_locales = {loc.name for loc in Locale} self.supported_domains = {} self.translators = defaultdict(dict) # locale -> domain -> GNUTranslator async def load(self): pass async def unload(self): self.translators.clear() def get_translator(self, locale: Optional[str], domain): return null def t(self, lazystr, locale=None): return lazystr._translate_with(null) async def translate(self, string: locale_str, locale: Locale, context): if not isinstance(string, LazyStr): return string else: return string.message ctx_translator.set(LeoBabel()) class Method(Enum): GETTEXT = 'gettext' NGETTEXT = 'ngettext' PGETTEXT = 'pgettext' NPGETTEXT = 'npgettext' class LocalBabel: def __init__(self, domain): self.domain = domain @property def methods(self): return (self._, self._n, self._p, self._np) def _(self, message): return LazyStr(Method.GETTEXT, message, domain=self.domain) def _n(self, singular, plural, n): return LazyStr(Method.NGETTEXT, singular, plural, n, domain=self.domain) def _p(self, context, message): return LazyStr(Method.PGETTEXT, context, message, domain=self.domain) def _np(self, context, singular, plural, n): return LazyStr(Method.NPGETTEXT, context, singular, plural, n, domain=self.domain) class LazyStr(locale_str): __slots__ = ('method', 'args', 'domain', 'locale') def __init__(self, method, *args, locale=None, domain=None): self.method = method self.args = args self.domain = domain self.locale = locale @property def message(self): return self._translate_with(null) @property def extras(self): return {'locale': self.locale, 'domain': self.domain} def __str__(self): return self.message def _translate_with(self, translator: gettext.GNUTranslations): method = getattr(translator, self.method.value) return method(*self.args) def __repr__(self) -> str: return f'{self.__class__.__name__}({self.method}, {self.args!r}, locale={self.locale}, domain={self.domain})' def __eq__(self, obj: object) -> bool: return isinstance(obj, locale_str) and self.message == obj.message def __hash__(self) -> int: return hash(self.args)