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:
parent
40e764cab5
commit
c6ea8e7dbe
1 changed files with 53 additions and 3 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue