import json import os import re ACCEPT_LANGUAGE_HEADER_REGEX = re.compile(r"([a-zA-Z]{2,3})(-[a-zA-Z\-]+)?") def t(*args, **kwargs): return TranslationHandler.translate(*args, **kwargs) class TranslationHandler: translations = {} language = "en" language_available = {"en": True} always_reload_translations = False @classmethod def init(cls, app): cls.app = app cls.always_reload_translations = app.debug app.context_processor(lambda: dict(t=t)) @classmethod def is_language_available(cls, language): if language not in cls.language_available: cls.language_available[language] = os.path.exists( cls.get_language_filepath(language) ) return cls.language_available[language] @classmethod def set_language(cls, language): if not cls.is_language_available(language): cls.app.logger.info(f"Cannot find language {language}. Default to en.") language = "en" cls.language = language if language != "en" and ( cls.always_reload_translations or language not in cls.translations ): cls.translations[language] = json.load( open(cls.get_language_filepath(language), "r") ) @classmethod def translate(cls, string, **interpolation): if cls.language == "en": return string.format(**interpolation) if string not in cls.translations[cls.language]: cls.app.logger.warning( f'Unknown translation to {cls.language} for "{string}"' ) return string.format(**interpolation) return cls.translations[cls.language][string].format(**interpolation) @classmethod def parse_language_header(cls, header): if not header: return None if "*" in header: return None languages = [match[0] for match in ACCEPT_LANGUAGE_HEADER_REGEX.findall(header)] # take the first matching one for language in languages: if cls.is_language_available(language): return language return None @classmethod def get_language_filepath(cls, language): return os.path.join(cls.app.root_path, f"private_static/i18n/{language}.json")