Source code for invenio_i18n.ext

# -*- coding: utf-8 -*-
# This file is part of Invenio.
# Copyright (C) 2015-2018 CERN.
# Invenio is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Invenio internationalization module."""

from __future__ import absolute_import, print_function

import os.path

from flask import current_app
from flask_babelex import Babel
from flask_babelex import get_locale as get_current_locale
from flask_babelex import get_timezone as get_current_timezone
from werkzeug.local import LocalProxy

from . import config
from ._compat import text_type
from .babel import MultidirDomain
from .jinja2 import filter_language_name, filter_language_name_local, \
    filter_to_user_timezone, filter_to_utc
from .selectors import get_locale, get_timezone
from .views import create_blueprint

current_i18n = LocalProxy(lambda: current_app.extensions['invenio-i18n'])

[docs]def get_lazystring_encoder(app): """Return a JSONEncoder for handling lazy strings from Babel. Installed on Flask application by default by :class:`InvenioI18N`. """ from speaklater import _LazyString class JSONEncoder(app.json_encoder): def default(self, o): if isinstance(o, _LazyString): return text_type(o) return super(JSONEncoder, self).default(o) return JSONEncoder
[docs]class InvenioI18N(object): """Invenio I18N extension.""" def __init__(self, app=None, date_formats=None, localeselector=None, timezoneselector=None, entry_point_group='invenio_i18n.translations'): """Initialize extension. :param app: Flask application. :param data_formats: Override default date/time formatting. :param localeselector: Callback function used for locale selection. (Default: :func:`invenio_i18n.selectors.get_locale()`) :param timezoneselector: Callback function used for timezone selection. (Default: ``BABEL_DEFAULT_TIMEZONE``) :param entry_point_group: Entrypoint used to load translations from. Set to ``None`` to not load translations from entry points. """ self.domain = MultidirDomain() self.babel = Babel( date_formats=date_formats, configure_jinja=True, default_domain=self.domain, ) self.localeselector = localeselector self.timezoneselector = timezoneselector self.entry_point_group = entry_point_group self._locales_cache = None self._languages_cache = None if app: self.init_app(app)
[docs] def init_app(self, app): """Flask application initialization. The initialization will: * Set default values for the configuration variables. * Load translations from paths specified in ``I18N_TRANSLATIONS_PATHS``. * Load translations from ``app.root_path>/translations`` if it exists. * Load translations from a specified entry point. * Add ``toutc`` and ``tousertimezone`` template filters. * Install a custom JSON encoder on app. """ self.init_config(app) # Initialize Flask-BabelEx self.babel.init_app(app) self.babel.localeselector(self.localeselector or get_locale) self.babel.timezoneselector(self.timezoneselector or get_timezone) # 1. Paths listed in I18N_TRANSLATIONS_PATHS for p in app.config.get('I18N_TRANSLATIONS_PATHS', []): self.domain.add_path(p) # 2. <app.root_path>/translations app_translations = os.path.join(app.root_path, 'translations') if os.path.exists(app_translations): self.domain.add_path(app_translations) # 3. Entrypoints if self.entry_point_group: self.domain.add_entrypoint(self.entry_point_group) # Register default routes if URL is set. register_default_routes = app.config['I18N_SET_LANGUAGE_URL'] \ and app.config['I18N_LANGUAGES'] app.register_blueprint( create_blueprint(register_default_routes=register_default_routes), url_prefix=app.config['I18N_SET_LANGUAGE_URL'] ) # Register Jinja2 template filters for date formatting (Flask-Babel # already installs other filters). app.add_template_filter(filter_to_utc, name='toutc') app.add_template_filter(filter_to_user_timezone, name='tousertimezone') app.add_template_filter(filter_language_name, name='language_name') app.add_template_filter( filter_language_name_local, name='language_name_local') app.add_template_global(current_i18n, name='current_i18n') # Lazy string aware JSON encoder. app.json_encoder = get_lazystring_encoder(app) app.extensions['invenio-i18n'] = self
[docs] def init_config(self, app): """Initialize configuration.""" for k in dir(config): if k.startswith('I18N_'): app.config.setdefault(k, getattr(config, k))
[docs] def iter_languages(self): """Iterate over list of languages.""" default_lang = self.babel.default_locale.language default_title = self.babel.default_locale.get_display_name( default_lang) yield (default_lang, default_title) for l, title in current_app.config.get('I18N_LANGUAGES', []): yield l, title
[docs] def get_languages(self): """Get list of languages.""" if self._languages_cache is None: self._languages_cache = list(self.iter_languages()) return self._languages_cache
[docs] def get_locales(self): """Get a list of supported locales. Computes the list using ``I18N_LANGUAGES`` configuration variable. """ if self._locales_cache is None: langs = [self.babel.default_locale] for l, dummy_title in current_app.config.get('I18N_LANGUAGES', []): langs.append(self.babel.load_locale(l)) self._locales_cache = langs return self._locales_cache
@property def locale(self): """Get current locale.""" return get_current_locale() @property def language(self): """Get current language code.""" return get_current_locale().language @property def timezone(self): """Get current timezone.""" return get_current_timezone()