diff options
| -rw-r--r-- | CHANGES.txt | 9 | ||||
| -rw-r--r-- | TODO.txt | 2 | ||||
| -rw-r--r-- | docs/api.rst | 1 | ||||
| -rw-r--r-- | docs/api/i18n.rst | 23 | ||||
| -rw-r--r-- | docs/glossary.rst | 24 | ||||
| -rw-r--r-- | docs/index.rst | 1 | ||||
| -rw-r--r-- | docs/latexindex.rst | 1 | ||||
| -rw-r--r-- | docs/narr/i18n.rst | 265 | ||||
| -rw-r--r-- | docs/narr/views.rst | 5 | ||||
| -rw-r--r-- | repoze/bfg/chameleon_text.py | 8 | ||||
| -rw-r--r-- | repoze/bfg/chameleon_zpt.py | 8 | ||||
| -rw-r--r-- | repoze/bfg/configuration.py | 35 | ||||
| -rw-r--r-- | repoze/bfg/i18n.py | 202 | ||||
| -rw-r--r-- | repoze/bfg/interfaces.py | 16 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_chameleon_text.py | 20 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_chameleon_zpt.py | 20 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_configuration.py | 21 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_i18n.py | 219 | ||||
| -rw-r--r-- | setup.py | 3 |
19 files changed, 12 insertions, 871 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index ea6e17f90..fdc67b191 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -22,7 +22,7 @@ Features returned. Other normal view predicates can also be used in combination with an - exception view registration:: + exception view registration: from repoze.bfg.view import bfg_view from repoze.bfg.exceptions import NotFound @@ -74,13 +74,6 @@ Dependencies - A new install-time dependency on the ``venusian`` distribution was added. -- Chameleon 1.2.3 or better is now required (internationalization and - per-template debug settings). - -- Add an explicit direct dependency on ``zope.i18nmessageid``. This - distribution was already a transitive dependency, but now we're - relying on it directly within ``repoze.bfg.i18n``. - Internal -------- @@ -17,5 +17,3 @@ - Provide a webob.Response class facade for forward compat. -- Replace default_notfound_view and default_forbidden_view with better - exception view candidates. diff --git a/docs/api.rst b/docs/api.rst index a97c79fa9..2bd5fca01 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -15,7 +15,6 @@ documentation is organized alphabetically by module name. api/configuration api/events api/exceptions - api/i18n api/interfaces api/location api/paster diff --git a/docs/api/i18n.rst b/docs/api/i18n.rst deleted file mode 100644 index a67350b64..000000000 --- a/docs/api/i18n.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _i18n_module: - -:mod:`repoze.bfg.i18n` ----------------------- - -.. automodule:: repoze.bfg.i18n - - .. autoclass:: TranslationString - - .. autoclass:: TranslationStringFactory - - .. autoinstance:: bfg_tstr - - .. autofunction:: get_translator(request) - - .. autofunction:: interpolate - - .. autoclass:: InterpolationOnlyTranslator - -See :ref:`i18n_chapter` for more information about using -:mod:`repoze.bfg` internationalization services within an application. - - diff --git a/docs/glossary.rst b/docs/glossary.rst index 537af06b9..14bd4fc44 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -667,27 +667,3 @@ Glossary at import time, the action usually taken by the decorator is deferred until a separate "scan" phase. :mod:`repoze.bfg` relies on Venusian to provide a basis for its :term:`scan` feature. - - Translation String - An instance of the :class:`repoze.bfg.i18n.TranslationString`, - which is a class that behaves like a Unicode string, but has - several extra attributes such as ``domain``, ``msgid``, and - ``mapping`` for use during translation. Translation strings are - usually created by hand within software, but are sometimes - created on the behalf of the system for automatic template - translation. For more information, see :ref:`i18n_chapter`. - - Translator - A callable which receives a :term:`translation string` and - returns a translated Unicode object for the purposes of - internationalization. A translator may be suppled to a - :mod:`repoze.bfg` application at startup time indirectly via the - ``translator_factory`` function, which is a :term:`translator - factory`. - - Translator Factory - A callable which receives a :term:`request` and returns a - :term:`translator` for the purposes of internationalization. A - translator factory may be suppled to a :mod:`repoze.bfg` - application at startup time via the ``translator_factory`` - function. diff --git a/docs/index.rst b/docs/index.rst index c1b185352..ee32cf691 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -53,7 +53,6 @@ Narrative documentation in chapter form explaining how to use narr/templates narr/models narr/security - narr/i18n narr/vhosting narr/events narr/environment diff --git a/docs/latexindex.rst b/docs/latexindex.rst index 5850ee06c..45438e939 100644 --- a/docs/latexindex.rst +++ b/docs/latexindex.rst @@ -42,7 +42,6 @@ Narrative Documentation narr/templates narr/models narr/security - narr/i18n narr/vhosting narr/events narr/environment diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst deleted file mode 100644 index 45bbab342..000000000 --- a/docs/narr/i18n.rst +++ /dev/null @@ -1,265 +0,0 @@ -.. index:: - single: i18n - single: internationalization - -.. _i18n_chapter: - -Using Internationalization -========================== - -:mod:`repoze.bfg` offers an internationalization (i18n) subsystem that -can be used to translate the text of buttons, the text of error -messages and other software-defined values into the native language of -aq user of your :mod:`repoze-bfg` driven website. - -Activating Translation ----------------------- - -By default, a :mod:`repoze.bfg` application performs no translation -without explicitly configuring a :term:`translator factory`. To make -any translation at all happen, you must pass a translator factory -object to your application's -:mod:`repoze.bfg.configuration.Configurator` by supplying it with a -``translator_factory`` argument. For example: - -.. code-block:: python - :linenos: - - from repoze.bfg.configuration import Configurator - from repoze.bfg.i18n import InterpolationOnlyTranslator - config = Configurator(translator_factory=InterpolationOnlyTranslator) - -.. note:: At the time of this writing, only one (very weak) translator - factory named :class:`repoze.bfg.i18n.InterpolationOnlyTranslator` - ships as part of the :mod:`repoze.bfg` software. This class only - does basic interpolation of mapping values; it does not actually do - any language translation. - -Creating a Translation String ------------------------------ - -While you write your software, you can insert specialized markup into -your Python code that makes it possible for the system to translate -text values into the languages used by your application's users. This -markup generates a :term:`translation string`. A translation string -is an object that behave mostly like a normal Unicode object, except -that it also carries around extra information related to its job as -part of :mod:`repoze.bfg` the translation machinery. - -Using The ``TranslationString`` Class -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -One way to create a translation string is to use the -:class:`repoze.bfg.i18n.TranslationString` callable: - -.. code-block:: python - :linenos: - - from repoze.bfg.i18n import TranslationString - ts = TranslationString('Add') - -This creates a Unicode-like object that is a TranslationString. - -The first argument to :class:`repoze.bfg.i18n.TranslationString` is -the ``text``; it is required. The ``text`` value acts as a default -value for the translation string if a translation to the user's -language cannot be found at translation time. The ``text`` argument -must be a Unicode object or an ASCII string. The text may optionally -contain *replacement markers*. For instance: - -.. code-block:: python - :linenos: - - from repoze.bfg.i18n import TranslationString - ts = TranslationString('Add ${number}') - -Within the string above, ``${stuff}`` is a replacement marker. It -will be replaced by whatever is in the *mapping* for a translation -string. The mapping may be supplied at the same time as the -replacement marker: - -.. code-block:: python - :linenos: - - from repoze.bfg.i18n import TranslationString - ts = TranslationString('Add ${number}', mapping={'number':1}) - -Any number of replacement markers can be present in th text value, any -number of times. Only markers which can be replaced by the values in -the *mapping* will be replaced at translation time. The others will -not be interpolated and will be output literally. - -A translation string should also usually carry a *domain*. The domain -represents a translation category to disambiguate it from other -translations of the same msgid, in case they conflict. - -.. code-block:: python - :linenos: - - from repoze.bfg.i18n import TranslationString - ts = TranslationString('Add ${number}', mapping={'number':1}, - domain='form') - -The above translation string named a domain of "form". A -:term:`translator` function will often use the domain to locate a file -on the filesystem which contains translations for a given context. In -this case, if it were trying to translate to our msgid to German, it -might try to find a translation from a :term:`gettext` file like this -one:: - - locale/de/LC_MESSAGES/form.mo - -In other words, it would want to take translations from the "form.mo" -translation file in the German language. - -Domain translation support is dependent upon the :term:`translator -factory` in use. Not all translator factories use domain information -that is associated with a translation string. However, it is always -safe to associate a given translation string with a domain; the -information is ignored by translators that don't support it. - -Finally, the TranslationString constructor accepts a ``msgid`` -argument. If a ``msgid`` argument is supplied, it is used as the -*message identifier* for the translation string. When ``msgid`` is -``None``, the ``text`` value passed to a TranslationString is used as -an implicit message identifier. Message identifiers are matched with -translations in translation files, so it is often useful to create -translation strings with "opaque" message identifiers unrelated to -their default text: - -.. code-block:: python - :linenos: - - from repoze.bfg.i18n import TranslationString - ts = TranslationString('Add ${number}', msgid='add-number', - domain='form', mapping={'number':1}) - -Using the ``bfg_tstr`` Translation String Factory -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Another way to generate a translation string is to use the -:attr:`repoze.bfg.i18n.bfg_tstr` object. This object is a -*translation string factory*. Basically a translation string factory -presets the ``domain`` value of any :term:`translation string` -generated by using it. For example: - -.. code-block:: python - :linenos: - - from repoze.bfg.i18n import bfg_tstr as _ - ts = _('Add ${number}', msgid='add-number', mapping={'number':1}) - -.. note:: We imported ``bfg_tstr`` as the name ``_``. This is a - convention which will be supported by translation file generation - tools. - -The result of calling ``bfg_tstr`` is a -:class:`repoze.bfg.i18n.TranslationString` instance. Even though a -``domain`` value was not passed to bfg_tstr (as would have been -necessary if the :class:`repoze.bfg.i18n.TranslationString` -constructor were used instead of a translation string factory), the -``domain`` attribute of the resulting translation string will be -``bfg``. As a result, the previous code example is completely -equivalent (except for spelling) to: - -.. code-block:: python - :linenos: - - from repoze.bfg.i18n import TranslationString as _ - ts = _('Add ${number}', msgid='add-number', mapping={'number':1}, - domain='form') - -Using the ``TranslationStringFactory`` Class -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can set up your own translation string factory much like the one -provided as :mod:`repoze.bfg.i18n.bfg_tstr` by using the -:class:`repoze.bfg.i18n.TranslationStringFactory` class. For example, -if you'd like to create a translation string factory which presets the -``domain`` value of generated translation strings to ``form``, you'd -do something like this: - -.. code-block:: python - :linenos: - - from repoze.bfg.i18n import TranslationStringFactory - _ = TranslationStringFactory('form') - ts = _('Add ${number}', msgid='add-number', mapping={'number':1}) - -.. note:: We created this factory with the name ``_``. This is a - convention which will be supported by translation file generation - tools. - -Performing a Translation by Hand --------------------------------- - -If you need to perform translation "by hand" in an application, use -the :func:`repoze.bfg.i18n.get_translator` function to obtain a -:term:`translator` . :func:`repoze.bfg.i18n.get_translator` will -return either the current translator defined by the -``translator_factory`` passed to the Configurator at startup or a -default translator if no explicit translator factory has been -registered. - -Remember that a translator is a callable which accepts either a -:term:`translation string` and which returns a Unicode object -representing the translation. So, generating a translation in a view -component of your application might look like so: - -.. code-block:: python - :linenos: - - from repoze.bfg.i18n import get_translator - - from repoze.bfg.i18n import bfg_tstr as _ - ts = _('Add ${number}', mapping={'number':1}) - - def aview(request): - translator = get_translator(request) - translated = translator(ts) - -A translator may be called any number of times after being retrieved -from the ``get_translator`` function. - -Defining A Translator Factory ------------------------------ - -A translator factory is an object which accepts a :term:`request` and -which returns a :term:`translator` callable. - -The :term:`translator` callable returned by a translator factory must -accept a single positional argument which represents a -:term:`translation string` and should return a fully localized and -expanded translation of the translation string. - -A simplistic implementation of both a translator factory and a -translator (via its constructor and ``__call__`` methods respecively) -named :class:`repoze.bfg.i18n.InterpolationOnlyTranslator` is defined. -Here it is: - -.. code-block:: python - :linenos: - - from repoze.bfg.i18n import interpolate - - class InterpolationOnlyTranslator(object): - def __init__(self, request): - self.request = request - - def __call__(self, message): - mapping = getattr(message, 'mapping', None) - return interpolate(message, mapping) - -The exact operation of a translator is left to the implementor of a -particular translator factory. You can define and use your own -translator factory by passing it as the ``translator_factory`` -argument to the :class:`repoze.bfg.configuration.Configurator` -constructor. - -.. code-block:: python - :linenos: - - from repoze.bfg.configuration import Configurator - from repoze.bfg.i18n import InterpolationOnlyTranslator - config = Configurator(translator_factory=InterpolationOnlyTranslator, ...) - diff --git a/docs/narr/views.rst b/docs/narr/views.rst index eebaa63de..a24e4b7b5 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -840,11 +840,6 @@ In all cases, the message provided to the exception constructor is made available to the view which :mod:`repoze.bfg` invokes as ``request.exception.args[0]``. -.. index:: - single: exception views - -.. _exception_views: - Exception Views ~~~~~~~~~~~~~~~~ diff --git a/repoze/bfg/chameleon_text.py b/repoze/bfg/chameleon_text.py index deb1cc43d..f1dc8e3aa 100644 --- a/repoze/bfg/chameleon_text.py +++ b/repoze/bfg/chameleon_text.py @@ -22,7 +22,6 @@ 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 @@ -51,12 +50,7 @@ class TextTemplateRenderer(object): def template(self): settings = get_settings() auto_reload = settings and settings['reload_templates'] - 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) + return TextTemplateFile(self.path, auto_reload=auto_reload) def implementation(self): return self.template diff --git a/repoze/bfg/chameleon_zpt.py b/repoze/bfg/chameleon_zpt.py index 687a11305..f597ebd5f 100644 --- a/repoze/bfg/chameleon_zpt.py +++ b/repoze/bfg/chameleon_zpt.py @@ -13,7 +13,6 @@ 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 @@ -34,12 +33,7 @@ class ZPTTemplateRenderer(object): def template(self): settings = get_settings() auto_reload = settings and settings['reload_templates'] - 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) + return PageTemplateFile(self.path, auto_reload=auto_reload) def implementation(self): return self.template diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py index a98b3e7d6..4673479da 100644 --- a/repoze/bfg/configuration.py +++ b/repoze/bfg/configuration.py @@ -17,7 +17,6 @@ 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 @@ -32,7 +31,6 @@ from repoze.bfg.interfaces import IRoutesMapper from repoze.bfg.interfaces import ISecuredView from repoze.bfg.interfaces import ISettings from repoze.bfg.interfaces import ITemplateRenderer -from repoze.bfg.interfaces import ITranslatorFactory from repoze.bfg.interfaces import ITraverser from repoze.bfg.interfaces import IView from repoze.bfg.interfaces import IViewClassifier @@ -47,7 +45,6 @@ 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 @@ -138,23 +135,14 @@ class Configurator(object): logs to stderr will be used. If it is passed, it should be an instance of the :class:`logging.Logger` (PEP 282) standard library class. The debug logger is used by :mod:`repoze.bfg` itself to - log warnings and authorization debugging information. - - If ``translator_factory`` is passed, it should be a - :term:`translator factory` object. A translator factory is an - object which accepts a request and which returns a translation - function. The translation function accepts a :term:`translation - string` and returns a Unicode object representing the translated - string. The default for ``translator_factory`` is ``None``, - meaning that no translation is performed during template - rendering. """ + log warnings and authorization debugging information. """ manager = manager # for testing injection venusian = venusian # for testing injection def __init__(self, registry=None, package=None, settings=None, root_factory=None, authentication_policy=None, authorization_policy=None, renderers=DEFAULT_RENDERERS, - debug_logger=None, translator_factory=None): + debug_logger=None): self.package = package or caller_package() self.registry = registry if registry is None: @@ -166,8 +154,7 @@ class Configurator(object): authentication_policy=authentication_policy, authorization_policy=authorization_policy, renderers=renderers, - debug_logger=debug_logger, - translator_factory=translator_factory) + debug_logger=debug_logger) def _set_settings(self, mapping): settings = Settings(mapping or {}) @@ -283,8 +270,7 @@ class Configurator(object): def setup_registry(self, settings=None, root_factory=None, authentication_policy=None, authorization_policy=None, - renderers=DEFAULT_RENDERERS, debug_logger=None, - translator_factory=None): + renderers=DEFAULT_RENDERERS, debug_logger=None): """ When you pass a non-``None`` ``registry`` argument to the :term:`Configurator` constructor, no initial 'setup' is performed against the registry. This is because the registry @@ -318,8 +304,6 @@ class Configurator(object): self.add_renderer(name, renderer) self.set_notfound_view(default_notfound_view) self.set_forbidden_view(default_forbidden_view) - if translator_factory is not None: - self.set_translator_factory(translator_factory) # getSiteManager is a unit testing dep injection def hook_zca(self, getSiteManager=None): @@ -1241,6 +1225,10 @@ class Configurator(object): found via context-finding or ``None`` if no context could be found. The exception causing the registered view to be called is however still available as ``request.exception``. + .. warning:: This method has been deprecated in + :mod:`repoze.bfg` 1.3. See + :ref:`changing_the_forbidden_view` to see how a not found + view should be registered in :mod:`repoze.bfg` 1.3+. The ``view`` argument should be a :term:`view callable`. @@ -1311,13 +1299,6 @@ class Configurator(object): return self.add_view(bwcompat_view, context=NotFound, wrapper=wrapper, _info=_info) - def set_translator_factory(self, factory): - """ 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 configuration state. diff --git a/repoze/bfg/i18n.py b/repoze/bfg/i18n.py deleted file mode 100644 index adbdb1db6..000000000 --- a/repoze/bfg/i18n.py +++ /dev/null @@ -1,202 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Corporation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## - -import re - -from zope.interface import implements -from zope.interface import classProvides - -from zope.i18nmessageid import Message - -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 - -class TranslationString(Message): - """ The constructor for a :term:`translation string`. This - constructor accepts one required argument named ``text``. - ``text`` must be the default text of the translation string, - optionally including replacement markers such as ``${foo}``. - - Optional keyword arguments to the TranslationString constructor - include ``msgid``, ``mapping`` and ``domain``. - - ``mapping``, if supplied, must be a dictionarylike object which - represents the replacement values for any replacement markers - found within the ``text`` value of this - - ``msgid`` represents an explicit :term:`message identifier` for - this translation string. Usually, the ``text`` of a translation - string serves as its message identifier. However, using this - option you can pass an explicit message identifier, usually a - simple string. This is useful when the ``text`` of a translation - string is too complicated or too long to be used as a translation - key. If ``msgid`` is ``None`` (the default), the ``msgid`` value - used by this translation string will be assumed to be the value of - ``text``. - - ``domain`` represents the :term:`translation domain`. By default, - the translation domain is ``None``, indicating that this - translation string is associated with no translation domain. - - After a translation string is constructed, its ``text`` value is - available as the ``default`` attribute of the object, the - ``msgid`` is available as the ``msgid`` attribute of the object, - the ``domain`` is available as the ``domain`` attribute, and the - ``mapping`` is available as the ``mapping`` attribute. - """ - def __new__(cls, text, mapping=None, msgid=None, domain=None): - if msgid is None: - msgid = text - return Message.__new__(cls, msgid, domain=domain, default=text, - mapping=mapping) - -class TranslationStringFactory(object): - """ Create a factory which will generate translation strings - without requiring that each call to the factory be passed a - ``domain`` value. The ``domain`` value passed to this class' - constructor will be used as the ``domain`` values of - :class:`repoze.bfg.i18n.TranslationString` objects generated by - the ``__call__`` of this class. The ``text``, ``mapping``, and - ``msgid`` values provided to ``__call__`` have the meaning as - described by the constructor of the - :class:`repoze.bfg.i18n.TranslationString`""" - def __init__(self, domain): - self.domain = domain - - def __call__(self, text, mapping=None, msgid=None): - return TranslationString(text, mapping=mapping, msgid=msgid, - domain=self.domain) - -bfg_tstr = TranslationStringFactory('bfg') -bfg_tstr.__doc__ = """\ - A :class:`repoze.bfg.i18n.TranslationStringFactory` instance with - a default ``domain`` value of ``bfg``. This object may be called - with the values ``text``, ``mapping``, and ``msgid`` as per the - documentation of the - :class:`repoze.bfg.i18n.TranslationStringFactory` class.""" - -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 a very simple - default 'interpolation only' translator. - - Note that the translation factory will only be constructed once - per request instance. - """ - - translator = getattr(request, '_bfg_translator', None) - - if translator is None: - - if translator_factory is None: - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() - translator_factory = reg.queryUtility( - ITranslatorFactory, - default=InterpolationOnlyTranslator) - - translator = translator_factory(request) - - try: - request._bfg_translator = translator - except AttributeError: # pragma: no cover - pass # it's only a cache - - return translator - -class InterpolationOnlyTranslator(object): - - """ A class implementing the :term:`translator factory` interface - as its constructor and the :term:`translator` interface as its - ``__call__`` method. Useful as a minimal translator factory, this - class only does basic interpolation of mapping values; it does not - actually do any language translations and ignores all - :term:`translation domain` information. To use it explicitly:: - - from repoze.bfg.configuration import Configurator - from repoze.bfg.i18n import InterpolationOnlyTranslator - config = Configurator(translator_factory=InterpolationOnlyTranslator) - - An instance of this class is returned by - :func:`repoze.bfg.i18n.get_translator` if no explicit translator - factory is registered. - """ - classProvides(ITranslatorFactory) - implements(ITranslator) - def __init__(self, request): - self.request = request - - def __call__(self, message): - mapping = getattr(message, 'mapping', None) - return interpolate(message, mapping) - -class ChameleonTranslate(object): - """ Registered as a Chameleon translate function 'under the hood' - to allow our ITranslator and ITranslatorFactory to drive template - translation.""" - 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 - if default is None: - default = text - if mapping is None: - mapping = {} - if not hasattr(text, 'mapping'): - text = TranslationString(default, mapping=mapping, msgid=text, - domain=domain) - translator = self.make_translator(target_language) - return translator(text) - - def make_translator(self, target_language): - translator = None - request = get_current_request() - if request is not None: - translator = get_translator(request, self.translator_factory) - if translator is None: - translator = InterpolationOnlyTranslator(request) - return translator - -NAME_RE = r"[a-zA-Z][-a-zA-Z0-9_]*" - -_interp_regex = re.compile(r'(?<!\$)(\$(?:(%(n)s)|{(%(n)s)}))' - % ({'n': NAME_RE})) - -def interpolate(text, mapping=None): - """ Interpolate a string with one or more *replacement markers* - (``${foo}`` or ``${bar}``). Note that if a :term:`translation - string` is passed to this function, it will be implicitly - converted back to the Unicode object.""" - def replace(match): - whole, param1, param2 = match.groups() - return unicode(mapping.get(param1 or param2, whole)) - - if not text or not mapping: - return text - - return _interp_regex.sub(replace, text) diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py index f09ad7e36..40d29348c 100644 --- a/repoze/bfg/interfaces.py +++ b/repoze/bfg/interfaces.py @@ -229,19 +229,3 @@ class IPackageOverrides(Interface): # VH_ROOT_KEY is an interface; its imported from other packages (e.g. # traversalwrapper) VH_ROOT_KEY = 'HTTP_X_VHM_ROOT' - -class ITranslatorFactory(Interface): - """ Internal interface representing an i18n translator factory """ - def __call__(request): - """ Return a translator """ - -class ITranslator(Interface): - def __call__(tstr): - """ Return a translation based on the translation string tstr """ - -class IChameleonTranslate(Interface): - """ Internal interface representing a chameleon translate function """ - def __call__(msgid, domain=None, mapping=None, context=None, - target_language=None, default=None): - """ Translate a mess of arguments to a Unicode object """ - diff --git a/repoze/bfg/tests/test_chameleon_text.py b/repoze/bfg/tests/test_chameleon_text.py index 204805adb..007c102e3 100644 --- a/repoze/bfg/tests/test_chameleon_text.py +++ b/repoze/bfg/tests/test_chameleon_text.py @@ -28,16 +28,6 @@ class Base: class TextTemplateRendererTests(Base, unittest.TestCase): - def setUp(self): - from repoze.bfg.configuration import Configurator - from repoze.bfg.registry import Registry - registry = Registry() - self.config = Configurator(registry=registry) - self.config.begin() - - def tearDown(self): - self.config.end() - def _getTargetClass(self): from repoze.bfg.chameleon_text import TextTemplateRenderer return TextTemplateRenderer @@ -64,16 +54,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template, instance.__dict__['template']) - def test_template_with_ichameleon_translate(self): - from repoze.bfg.interfaces import IChameleonTranslate - def ct(): pass - self.config.registry.registerUtility(ct, IChameleonTranslate) - minimal = self._getTemplatePath('minimal.txt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template.translate, ct) - def test_call(self): minimal = self._getTemplatePath('minimal.txt') instance = self._makeOne(minimal) diff --git a/repoze/bfg/tests/test_chameleon_zpt.py b/repoze/bfg/tests/test_chameleon_zpt.py index cbf9dd10b..e4bf8f766 100644 --- a/repoze/bfg/tests/test_chameleon_zpt.py +++ b/repoze/bfg/tests/test_chameleon_zpt.py @@ -21,16 +21,6 @@ class Base(object): return reg class ZPTTemplateRendererTests(Base, unittest.TestCase): - def setUp(self): - from repoze.bfg.configuration import Configurator - from repoze.bfg.registry import Registry - registry = Registry() - self.config = Configurator(registry=registry) - self.config.begin() - - def tearDown(self): - self.config.end() - def _getTargetClass(self): from repoze.bfg.chameleon_zpt import ZPTTemplateRenderer return ZPTTemplateRenderer @@ -65,16 +55,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template, instance.__dict__['template']) - def test_template_with_ichameleon_translate(self): - from repoze.bfg.interfaces import IChameleonTranslate - def ct(): pass - self.config.registry.registerUtility(ct, IChameleonTranslate) - minimal = self._getTemplatePath('minimal.pt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template.translate, ct) - def test_call_with_nondict_value(self): minimal = self._getTemplatePath('minimal.pt') instance = self._makeOne(minimal) diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py index efd58139f..99d564b91 100644 --- a/repoze/bfg/tests/test_configuration.py +++ b/repoze/bfg/tests/test_configuration.py @@ -265,15 +265,6 @@ class ConfiguratorTests(unittest.TestCase): self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'), renderer) - def test_setup_registry_translator_factory(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import ITranslatorFactory - factory = object() - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(translator_factory=factory) - self.assertEqual(reg.getUtility(ITranslatorFactory), factory) - def test_add_settings_settings_already_registered(self): from repoze.bfg.registry import Registry from repoze.bfg.interfaces import ISettings @@ -1736,18 +1727,6 @@ class ConfiguratorTests(unittest.TestCase): request = self._makeRequest(config) self.assertEqual(wrapped(None, request).__class__, StaticURLParser) - def test_set_translator_factory(self): - from repoze.bfg.interfaces import ITranslatorFactory - from repoze.bfg.interfaces import IChameleonTranslate - def factory(): pass - config = self._makeOne() - config.set_translator_factory(factory) - self.assertEqual(config.registry.getUtility(ITranslatorFactory), - factory) - self.assertEqual( - config.registry.getUtility(IChameleonTranslate).translator_factory, - factory) - def test_set_notfound_view(self): from zope.interface import implementedBy from repoze.bfg.interfaces import IRequest diff --git a/repoze/bfg/tests/test_i18n.py b/repoze/bfg/tests/test_i18n.py deleted file mode 100644 index 7d17b4551..000000000 --- a/repoze/bfg/tests/test_i18n.py +++ /dev/null @@ -1,219 +0,0 @@ -import unittest - -class TestTranslationString(unittest.TestCase): - def _makeOne(self, text, **kw): - from repoze.bfg.i18n import TranslationString - return TranslationString(text, **kw) - - def test_msgid_None(self): - inst = self._makeOne('text') - self.assertEqual(inst, 'text') - self.assertEqual(inst.default, 'text') - - def test_msgid_not_None(self): - inst = self._makeOne('text', msgid='msgid') - self.assertEqual(inst, 'msgid') - self.assertEqual(inst.default, 'text') - - def test_allargs(self): - inst = self._makeOne('text', msgid='msgid', mapping='mapping', - domain='domain') - self.assertEqual(inst, 'msgid') - self.assertEqual(inst.default, 'text') - self.assertEqual(inst.mapping, 'mapping') - self.assertEqual(inst.domain, 'domain') - -class TestTranslationStringFactory(unittest.TestCase): - def _makeOne(self, domain): - from repoze.bfg.i18n import TranslationStringFactory - return TranslationStringFactory(domain) - - def test_allargs(self): - factory = self._makeOne('budge') - inst = factory('text', mapping='mapping', msgid='msgid') - self.assertEqual(inst, 'msgid') - self.assertEqual(inst.domain, 'budge') - self.assertEqual(inst.mapping, 'mapping') - self.assertEqual(inst.default, 'text') - -class Test_bfg_tstr(unittest.TestCase): - def _callFUT(self, text, **kw): - from repoze.bfg.i18n import bfg_tstr - return bfg_tstr(text, **kw) - - def test_allargs(self): - inst = self._callFUT('text', mapping='mapping', msgid='msgid') - self.assertEqual(inst, 'msgid') - self.assertEqual(inst.domain, 'bfg') - self.assertEqual(inst.mapping, 'mapping') - self.assertEqual(inst.default, 'text') - -class Test_get_translator(unittest.TestCase): - def _callFUT(self, request): - from repoze.bfg.i18n import get_translator - return get_translator(request) - - def test_no_ITranslatorFactory(self): - from repoze.bfg.i18n import InterpolationOnlyTranslator - request = DummyRequest() - request.registry = DummyRegistry() - translator = self._callFUT(request) - self.assertEqual(translator.__class__, InterpolationOnlyTranslator) - - def test_no_registry_on_request(self): - from repoze.bfg.i18n import InterpolationOnlyTranslator - request = DummyRequest() - translator = self._callFUT(request) - self.assertEqual(translator.__class__, InterpolationOnlyTranslator) - - def test_with_ITranslatorFactory_from_registry(self): - request = DummyRequest() - tfactory = DummyTranslatorFactory() - request.registry = DummyRegistry(tfactory) - translator = self._callFUT(request) - self.assertEqual(translator.request, request) - - def test_with_ITranslatorFactory_from_request_cache(self): - request = DummyRequest() - request.registry = DummyRegistry() - request._bfg_translator = 'abc' - translator = self._callFUT(request) - self.assertEqual(translator, 'abc') - -class TestInterpolationOnlyTranslator(unittest.TestCase): - def _makeOne(self, request): - from repoze.bfg.i18n import InterpolationOnlyTranslator - return InterpolationOnlyTranslator(request) - - def test_it(self): - message = DummyMessage('text ${a}', mapping={'a':'1'}) - translator = self._makeOne(None) - result = translator(message) - self.assertEqual(result, u'text 1') - -class TestChameleonTranslate(unittest.TestCase): - def setUp(self): - request = DummyRequest() - from repoze.bfg.configuration import Configurator - self.config = Configurator() - self.config.begin(request=request) - self.request = request - - def tearDown(self): - self.config.end() - - def _makeOne(self, factory): - from repoze.bfg.i18n import ChameleonTranslate - return ChameleonTranslate(factory) - - def test_text_None(self): - trans = self._makeOne(None) - result = trans(None) - self.assertEqual(result, None) - - def test_no_current_request(self): - self.config.manager.pop() - trans = self._makeOne(None) - result = trans('text') - self.assertEqual(result, 'text') - - def test_with_current_request_no_translator(self): - trans = self._makeOne(None) - result = trans('text') - self.assertEqual(result, 'text') - - def test_with_current_request_and_translator(self): - from repoze.bfg.interfaces import ITranslatorFactory - translator = DummyTranslator() - factory = DummyTranslatorFactory(translator) - self.config.registry.registerUtility(factory, ITranslatorFactory) - trans = self._makeOne(None) - result = trans('text') - self.assertEqual(result, 'text') - self.assertEqual(result.domain, None) - self.assertEqual(result.default, 'text') - self.assertEqual(result.mapping, {}) - - def test_with_allargs(self): - from repoze.bfg.interfaces import ITranslatorFactory - translator = DummyTranslator() - factory = DummyTranslatorFactory(translator) - self.config.registry.registerUtility(factory, ITranslatorFactory) - trans = self._makeOne(None) - result = trans('text', domain='domain', mapping={'a':'1'}, - context=None, target_language='lang', - default='default') - self.assertEqual(result, 'text') - self.assertEqual(result.domain, 'domain') - self.assertEqual(result.default, 'default') - self.assertEqual(result.mapping, {'a':'1'}) - -class Test_interpolate(unittest.TestCase): - def _callFUT(self, text, mapping=None): - from repoze.bfg.i18n import interpolate - return interpolate(text, mapping) - - def test_substitution(self): - mapping = {"name": "Zope", "version": 3} - result = self._callFUT(u"This is $name version ${version}.", mapping) - self.assertEqual(result, u'This is Zope version 3.') - - def test_subsitution_more_than_once(self): - mapping = {"name": "Zope", "version": 3} - result = self._callFUT( - u"This is $name version ${version}. ${name} $version!", - mapping) - self.assertEqual(result, u'This is Zope version 3. Zope 3!') - - def test_double_dollar_escape(self): - mapping = {"name": "Zope", "version": 3} - result = self._callFUT('$$name', mapping) - self.assertEqual(result, u'$$name') - - def test_missing_not_interpolated(self): - mapping = {"name": "Zope", "version": 3} - result = self._callFUT( - u"This is $name $version. $unknown $$name $${version}.", - mapping) - self.assertEqual(result, - u'This is Zope 3. $unknown $$name $${version}.') - - def test_missing_mapping(self): - result = self._callFUT(u"This is ${name}") - self.assertEqual(result, u'This is ${name}') - -class DummyMessage(unicode): - def __new__(cls, text, msgid=None, domain=None, mapping=None): - self = unicode.__new__(cls, text) - if msgid is None: - msgid = unicode(text) - self.msgid = msgid - self.domain = domain - self.mapping = mapping or {} - return self - -class DummyRequest(object): - pass - -class DummyRegistry(object): - def __init__(self, tfactory=None): - self.tfactory = tfactory - - def queryUtility(self, iface, default=None): - return self.tfactory or default - -class DummyTranslator(object): - def __call__(self, message): - return message - -class DummyTranslatorFactory(object): - def __init__(self, translator=None): - self.translator = translator - - def __call__(self, request): - self.request = request - if self.translator is None: - return self - return self.translator - - @@ -27,7 +27,7 @@ except IOError: README = CHANGES = '' install_requires=[ - 'Chameleon >= 1.2.3', + 'Chameleon', 'Paste > 1.7', # temp version pin to prevent PyPi install failure :-( 'PasteDeploy', 'PasteScript', @@ -39,7 +39,6 @@ install_requires=[ 'zope.deprecation', 'zope.interface >= 3.5.1', # 3.5.0 comment: "allow to bootstrap on jython" 'venusian >= 0.2', - 'zope.i18nmessageid', ] if sys.version_info[:2] < (2, 6): |
