From cdae91d72b7bfa5edb26e12e721891a3ce2f2aa7 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 19 Apr 2010 08:58:54 +0000 Subject: Pass along translate function to templates. --- repoze/bfg/chameleon_text.py | 8 ++- repoze/bfg/chameleon_zpt.py | 8 ++- repoze/bfg/configuration.py | 4 ++ repoze/bfg/i18n.py | 96 ++++++++++++++++++++++----------- repoze/bfg/interfaces.py | 12 +++-- repoze/bfg/tests/test_chameleon_text.py | 20 +++++++ repoze/bfg/tests/test_chameleon_zpt.py | 20 +++++++ repoze/bfg/tests/test_configuration.py | 4 ++ repoze/bfg/tests/test_i18n.py | 63 +++++++++++++++++----- 9 files changed, 186 insertions(+), 49 deletions(-) (limited to 'repoze') diff --git a/repoze/bfg/chameleon_text.py b/repoze/bfg/chameleon_text.py index f1dc8e3aa..deb1cc43d 100644 --- a/repoze/bfg/chameleon_text.py +++ b/repoze/bfg/chameleon_text.py @@ -22,6 +22,7 @@ except ImportError: # pragma: no cover from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import ITemplateRenderer +from repoze.bfg.interfaces import IChameleonTranslate from repoze.bfg.decorator import reify from repoze.bfg.renderers import template_renderer_factory @@ -50,7 +51,12 @@ class TextTemplateRenderer(object): def template(self): settings = get_settings() auto_reload = settings and settings['reload_templates'] - return TextTemplateFile(self.path, auto_reload=auto_reload) + reg = get_current_registry() + translate = None + if reg is not None: + translate = reg.queryUtility(IChameleonTranslate) + return TextTemplateFile(self.path, auto_reload=auto_reload, + translate=translate) def implementation(self): return self.template diff --git a/repoze/bfg/chameleon_zpt.py b/repoze/bfg/chameleon_zpt.py index f597ebd5f..687a11305 100644 --- a/repoze/bfg/chameleon_zpt.py +++ b/repoze/bfg/chameleon_zpt.py @@ -13,6 +13,7 @@ except ImportError: # pragma: no cover def __init__(self, *arg, **kw): raise ImportError, exc, tb +from repoze.bfg.interfaces import IChameleonTranslate from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import ITemplateRenderer @@ -33,7 +34,12 @@ class ZPTTemplateRenderer(object): def template(self): settings = get_settings() auto_reload = settings and settings['reload_templates'] - return PageTemplateFile(self.path, auto_reload=auto_reload) + reg = get_current_registry() + translate = None + if reg is not None: + translate = reg.queryUtility(IChameleonTranslate) + return PageTemplateFile(self.path, auto_reload=auto_reload, + translate=translate) def implementation(self): return self.template diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py index 862f8b3a6..a98b3e7d6 100644 --- a/repoze/bfg/configuration.py +++ b/repoze/bfg/configuration.py @@ -17,6 +17,7 @@ from zope.interface import implements from repoze.bfg.interfaces import IAuthenticationPolicy from repoze.bfg.interfaces import IAuthorizationPolicy +from repoze.bfg.interfaces import IChameleonTranslate from repoze.bfg.interfaces import IDebugLogger from repoze.bfg.interfaces import IDefaultRootFactory from repoze.bfg.interfaces import IExceptionViewClassifier @@ -46,6 +47,7 @@ from repoze.bfg.events import WSGIApplicationCreatedEvent from repoze.bfg.exceptions import Forbidden from repoze.bfg.exceptions import NotFound from repoze.bfg.exceptions import ConfigurationError +from repoze.bfg.i18n import ChameleonTranslate from repoze.bfg.log import make_stream_logger from repoze.bfg.path import caller_package from repoze.bfg.registry import Registry @@ -1313,6 +1315,8 @@ class Configurator(object): """ Set ``factory`` up as the current application :term:`translator factory` (for internationalization)""" self.registry.registerUtility(factory, ITranslatorFactory) + ctranslate = ChameleonTranslate(factory) + self.registry.registerUtility(ctranslate, IChameleonTranslate) def add_static_view(self, name, path, cache_max_age=3600, _info=u''): """ Add a view used to render static resources to the current diff --git a/repoze/bfg/i18n.py b/repoze/bfg/i18n.py index bc0ae4423..6fa809bf6 100644 --- a/repoze/bfg/i18n.py +++ b/repoze/bfg/i18n.py @@ -1,32 +1,48 @@ +import re + from zope.interface import implements from zope.interface import classProvides from repoze.bfg.interfaces import ITranslator from repoze.bfg.interfaces import ITranslatorFactory +from repoze.bfg.interfaces import IChameleonTranslate + from repoze.bfg.threadlocal import get_current_registry from repoze.bfg.threadlocal import get_current_request -def get_translator(request): - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() - - if reg is None: # pragma: no cover - return None # only in insane circumstances +def get_translator(request, translator_factory=None): + """ Return a :term:`translator` for the given request based on the + :term:`translator factory` registered for the current application + and the :term:`request` passed in as the request object. If no + translator factory was sent to the + :class:`repoze.bfg.configuration.Configurator` constructor at + application startup, this function will return ``None``. + Note that the translation factory will only be called once per + request instance. + """ + translator = getattr(request, '_bfg_translator', None) if translator is False: return None if translator is None: - translator_factory = reg.queryUtility(ITranslatorFactory) + if translator_factory is None: + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() + if reg is None: # pragma: no cover + return None # only in insane circumstances + translator_factory = reg.queryUtility(ITranslatorFactory) + if translator_factory is None: request_value = False else: translator = translator_factory(request) request_value = translator + try: request._bfg_translator = request_value except AttributeError: # pragma: no cover @@ -41,8 +57,8 @@ class InterpolationOnlyTranslator(object): self.request = request def __call__(self, message): - mapping = getattr(message, 'mapping', {}) #should be a TranslationString - return message % mapping + mapping = getattr(message, 'mapping', None) + return interpolate(message, mapping) class TranslationString(unicode): __slots__ = ('msgid', 'domain', 'mapping') @@ -61,27 +77,43 @@ class TranslationString(unicode): def __getstate__(self): return unicode(self), self.msgid, self.domain, self.mapping -def chameleon_translate(text, domain=None, mapping=None, context=None, - target_language=None, default=None): - if text is None: - return None - translator = None - request = get_current_request() - if request is not None: - request.chameleon_target_language = target_language - translator = get_translator(request) - if default is None: - default = text - if mapping is None: - mapping = {} - if translator is None: - return unicode(default) % mapping - if not isinstance(text, TranslationString): - text = TranslationString(default, msgid=text, domain=domain, - mapping=mapping) - return translator(text) +class ChameleonTranslate(object): + implements(IChameleonTranslate) + def __init__(self, translator_factory): + self.translator_factory = translator_factory + + def __call__(self, text, domain=None, mapping=None, context=None, + target_language=None, default=None): + if text is None: + return None + translator = None + request = get_current_request() + if request is not None: + request.chameleon_target_language = target_language + translator = get_translator(request, self.translator_factory) + if default is None: + default = text + if mapping is None: + mapping = {} + if translator is None: + return unicode(default) % mapping + if not isinstance(text, TranslationString): + text = TranslationString(default, msgid=text, domain=domain, + mapping=mapping) + return translator(text) +NAME_RE = r"[a-zA-Z][-a-zA-Z0-9_]*" + +_interp_regex = re.compile(r'(?