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).
This commit is contained in:
Goffi 2017-05-21 15:59:47 +02:00
commit c6ea8e7dbe

View file

@ -28,6 +28,9 @@ import os.path
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 @@ except:
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 @@ class Renderer(object):
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 @@ class Renderer(object):
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 @@ class Renderer(object):
# 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 @@ class Renderer(object):
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)