summaryrefslogtreecommitdiff
path: root/repoze/bfg/i18n.py
blob: 7fc4c0f87e2cee644cc8817ae5f1baa541384311 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
##############################################################################
#
# 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.message import Message
from zope.i18nmessageid import MessageFactory

msg = MessageFactory('bfg')

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

TranslationString = Message

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:
        if translator_factory is None:
            try:
                reg = request.registry
            except AttributeError:
                reg = get_current_registry()
            if reg is not None: # pragma: no cover
                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
            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.  Use::

        from repoze.bfg.configuration import Configurator
        from repoze.bfg.i18n import InterpolationOnlyTranslator
        config = Configurator(translator_factory=InterpolationOnlyTranslator)

        """
    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):
    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 interpolate(unicode(default), mapping)
        if not hasattr(text, 'mapping'):
            text = TranslationString(text, domain=domain, default=default,
                                     mapping=mapping)
        return translator(text)
        
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):
    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)