Source code for invenio_i18n.babel

# -*- coding: utf-8 -*-
#
# This file is part of Invenio.
# Copyright (C) 2015, 2016 CERN.
#
# Invenio is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# Invenio is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Invenio; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307, USA.
#
# In applying this license, CERN does not
# waive the privileges and immunities granted to it by virtue of its status
# as an Intergovernmental Organization or submit itself to any jurisdiction.

"""Flask-BabelEx domain for merging translations from many directories."""

from __future__ import absolute_import, print_function

import os
from contextlib import contextmanager

from babel.support import NullTranslations, Translations
from flask import _request_ctx_stack, current_app
from flask_babelex import Domain, get_locale
from pkg_resources import iter_entry_points, resource_filename, resource_isdir


@contextmanager
[docs]def set_locale(ln): """Set Babel localization in request context. :param ln: Language identifier. """ ctx = _request_ctx_stack.top if ctx is None: raise RuntimeError('Working outside of request context.') new_locale = current_app.extensions['babel'].load_locale(ln) old_locale = getattr(ctx, 'babel_locale', None) setattr(ctx, 'babel_locale', new_locale) yield setattr(ctx, 'babel_locale', old_locale)
[docs]class MultidirDomain(Domain): """Domain supporting merging translations from many catalogs. The domain contains an internal list of paths that it loads translations from. The translations are merged in order of the list of paths, hence the last path in the list will overwrite strings set by previous paths. Entry points are added to the list of paths before the ``paths``. """ def __init__(self, paths=None, entry_point_group=None, domain='messages'): """Initialize domain. :param paths: List of paths with translations. :param entry_point_group: Name of entry point group. :param domain: Name of message catalog domain. (Default: ``'messages'``) """ self.paths = [] if entry_point_group: self.add_entrypoint(entry_point_group) for p in paths or []: self.add_path(p) super(MultidirDomain, self).__init__(domain=domain)
[docs] def has_paths(self): """Determine if any paths have been specified.""" return bool(self.paths)
[docs] def add_entrypoint(self, entry_point_group): """Load translations from an entry point.""" for ep in iter_entry_points(group=entry_point_group): if not resource_isdir(ep.module_name, 'translations'): continue dirname = resource_filename(ep.module_name, 'translations') self.add_path(dirname)
[docs] def add_path(self, path): """Load translations from an existing path.""" if not os.path.exists(path): raise RuntimeError('Path does not exists: %s.' % path) self.paths.append(path)
def _get_translation_for_locale(self, locale): """Get translation for a specific locale.""" translations = None for dirname in self.paths: # Load a single catalog. catalog = Translations.load(dirname, [locale], domain=self.domain) if translations is None: if isinstance(catalog, Translations): translations = catalog continue try: # Merge catalog into global catalog translations.merge(catalog) except AttributeError: # Translations is probably NullTranslations if isinstance(catalog, NullTranslations): current_app.logger.debug( 'Compiled translations seems to be missing' ' in {0}.'.format(dirname)) continue raise return translations or NullTranslations()
[docs] def get_translations(self): """Return the correct gettext translations for a request. This will never fail and return a dummy translation object if used outside of the request or if a translation cannot be found. """ ctx = _request_ctx_stack.top if ctx is None: return NullTranslations() locale = get_locale() cache = self.get_translations_cache(ctx) translations = cache.get(str(locale)) if translations is None: translations = self._get_translation_for_locale(locale) cache[str(locale)] = translations return translations