Mercurial > libervia-backend
diff src/tools/common/template.py @ 2249:e572482f6cbd
core (tools/common/template): i18n support
- babel has been added as a new dependencies, and should replace gettext in core in the future
- added i18n support in template rendered
- current locale is available as babel Locale in templates through "locale" variable
- locales can be changed before rendering using setLocale
- for now, all locales translations are loaded on init, and stay in cache. A more complex cache system may be needed in the future (e.g. keeping only most used and load others from files when needed).
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 21 May 2017 15:59:47 +0200 |
parents | e09048cb7595 |
children | 322694543225 |
line wrap: on
line diff
--- a/src/tools/common/template.py Sun May 21 15:52:23 2017 +0200 +++ b/src/tools/common/template.py Sun May 21 15:59:47 2017 +0200 @@ -28,6 +28,9 @@ from collections import OrderedDict from xml.sax.saxutils import quoteattr import time +from babel import support +from babel import Locale +from babel.core import UnknownLocaleError try: import sat_templates except ImportError: @@ -41,6 +44,7 @@ raise exceptions.MissingModule(u'Missing module jinja2, please install it from http://jinja.pocoo.org or with pip install jinja2') HTML_EXT = ('html', 'xhtml') +DEFAULT_LOCALE = u'en' # TODO: handle external path (an additional search path for templates should be settable by user # TODO: handle absolute URL (should be used for trusted use cases) only (e.g. jp) for security reason @@ -172,7 +176,11 @@ autoescape=jinja2.select_autoescape(['html', 'xhtml', 'xml']), trim_blocks=True, lstrip_blocks=True, + extensions=['jinja2.ext.i18n'], ) + self._locale_str = DEFAULT_LOCALE + self._locale = Locale.parse(self._locale_str) + self.installTranslations() # we want to have access to SàT constants in templates self.env.globals[u'C'] = C # custom filters @@ -180,6 +188,47 @@ self.env.filters['cur_gidx'] = self._cur_gidx self.env.filters['blog_date'] = self._blog_date + def installTranslations(self): + i18n_dir = os.path.join(self.base_dir, 'i18n') + self.translations = {} + for lang_dir in os.listdir(i18n_dir): + lang_path = os.path.join(i18n_dir, lang_dir) + if not os.path.isdir(lang_path): + continue + po_path = os.path.join(lang_path, 'LC_MESSAGES/sat.mo') + try: + with open(po_path, 'rb') as f: + self.translations[Locale.parse(lang_dir)] = support.Translations(f, 'sat') + except EnvironmentError: + log.error(_(u"Can't find template translation at {path}").format(path = po_path)) + except UnknownLocaleError as e: + log.error(_(u"Invalid locale name: {msg}").format(msg=e)) + else: + log.info(_(u'loaded {lang} templates translations').format(lang=lang_dir)) + self.env.install_null_translations(True) + + def setLocale(self, locale_str): + if locale_str == self._locale_str: + return + locale = Locale.parse(locale_str) + locale_str = unicode(locale) + if locale_str != DEFAULT_LOCALE: + try: + translations = self.translations[locale] + except KeyError: + log.warning(_(u"Can't find locale {locale}".format(locale=locale))) + locale_str = DEFAULT_LOCALE + locale = Locale.parse(self._locale_str) + else: + self.env.install_gettext_translations(translations, True) + log.debug(_(u'Switched to {lang}').format(lang=locale.english_name)) + + if locale_str == DEFAULT_LOCALE: + self.env.install_null_translations(True) + + self._locale = locale + self._locale_str = locale_str + def getThemeAndRoot(self, template): """retrieve theme and root dir of a given tempalte @@ -265,7 +314,7 @@ # FIXME: Q&D, need to be done properly return unicode(int(time.time() - int(timestamp))/(3600*24)) + u" days ago" - def render(self, template, theme=None, root_path=u'', css_files=None, css_inline=False, **kwargs): + def render(self, template, theme=None, locale=DEFAULT_LOCALE, root_path=u'', css_files=None, css_inline=False, **kwargs): """render a template @param template(unicode): template to render (e.g. blog/articles.html) @@ -309,6 +358,7 @@ kwargs['css_content'] = '\n'.join(css_contents) scripts_handler = ScriptsHandler(self, template_path, template_root_dir) + self.setLocale(locale) # XXX: theme used in template arguments is the requested theme, which may differ from actual theme # if the template doesn't exist in the requested theme. - return template_source.render(theme=theme, root_path=root_path, css_files=css_files, gidx=Indexer(), scripts_handler=scripts_handler, **kwargs) + return template_source.render(theme=theme, root_path=root_path, css_files=css_files, locale=locale, gidx=Indexer(), scripts_handler=scripts_handler, **kwargs)