pylons.i18n.translation
Covered: 27 lines
Missed: 117 lines
Skipped 57 lines
Percent: 18 %
  1
"""Translation/Localization functions.
  3
Provides :mod:`gettext` translation functions via an app's
  4
``pylons.translator`` and get/set_lang for changing the language
  5
translated to.
  7
"""
  8
import os
  9
from gettext import NullTranslations, translation
 11
import pylons
 13
__all__ = ['_', 'add_fallback', 'get_lang', 'gettext', 'gettext_noop',
 14
           'lazy_gettext', 'lazy_ngettext', 'lazy_ugettext', 'lazy_ungettext',
 15
           'ngettext', 'set_lang', 'ugettext', 'ungettext', 'LanguageError',
 16
           'N_']
 18
class LanguageError(Exception):
 19
    """Exception raised when a problem occurs with changing languages"""
 20
    pass
 23
class LazyString(object):
 24
    """Has a number of lazily evaluated functions replicating a 
 25
    string. Just override the eval() method to produce the actual value.
 27
    This method copied from TurboGears.
 29
    """
 30
    def __init__(self, func, *args, **kwargs):
 31
        self.func = func
 32
        self.args = args
 33
        self.kwargs = kwargs
 35
    def eval(self):
 36
        return self.func(*self.args, **self.kwargs)
 38
    def __unicode__(self):
 39
        return unicode(self.eval())
 41
    def __str__(self):
 42
        return str(self.eval())
 44
    def __mod__(self, other):
 45
        return self.eval() % other
 47
    def format(self, other):
 48
        return self.eval().format(other)
 51
def lazify(func):
 52
    """Decorator to return a lazy-evaluated version of the original"""
 53
    def newfunc(*args, **kwargs):
 54
        return LazyString(func, *args, **kwargs)
 55
    newfunc.__name__ = 'lazy_%s' % func.__name__
 56
    newfunc.__doc__ = 'Lazy-evaluated version of the %s function\n\n%s' % \
 57
        (func.__name__, func.__doc__)
 58
    return newfunc
 61
def gettext_noop(value):
 62
    """Mark a string for translation without translating it. Returns
 63
    value.
 65
    Used for global strings, e.g.::
 67
        foo = N_('Hello')
 69
        class Bar:
 70
            def __init__(self):
 71
                self.local_foo = _(foo)
 73
        h.set_lang('fr')
 74
        assert Bar().local_foo == 'Bonjour'
 75
        h.set_lang('es')
 76
        assert Bar().local_foo == 'Hola'
 77
        assert foo == 'Hello'
 79
    """
 80
    return value
 81
N_ = gettext_noop
 84
def gettext(value):
 85
    """Mark a string for translation. Returns the localized string of
 86
    value.
 88
    Mark a string to be localized as follows::
 90
        gettext('This should be in lots of languages')
 92
    """
 93
    return pylons.translator.gettext(value)
 94
lazy_gettext = lazify(gettext)
 97
def ugettext(value):
 98
    """Mark a string for translation. Returns the localized unicode
 99
    string of value.
101
    Mark a string to be localized as follows::
103
        _('This should be in lots of languages')
105
    """
106
    return pylons.translator.ugettext(value)
107
_ = ugettext
108
lazy_ugettext = lazify(ugettext)
111
def ngettext(singular, plural, n):
112
    """Mark a string for translation. Returns the localized string of
113
    the pluralized value.
115
    This does a plural-forms lookup of a message id. ``singular`` is
116
    used as the message id for purposes of lookup in the catalog, while
117
    ``n`` is used to determine which plural form to use. The returned
118
    message is a string.
120
    Mark a string to be localized as follows::
122
        ngettext('There is %(num)d file here', 'There are %(num)d files here',
123
                 n) % {'num': n}
125
    """
126
    return pylons.translator.ngettext(singular, plural, n)
127
lazy_ngettext = lazify(ngettext)
130
def ungettext(singular, plural, n):
131
    """Mark a string for translation. Returns the localized unicode
132
    string of the pluralized value.
134
    This does a plural-forms lookup of a message id. ``singular`` is
135
    used as the message id for purposes of lookup in the catalog, while
136
    ``n`` is used to determine which plural form to use. The returned
137
    message is a Unicode string.
139
    Mark a string to be localized as follows::
141
        ungettext('There is %(num)d file here', 'There are %(num)d files here',
142
                  n) % {'num': n}
144
    """
145
    return pylons.translator.ungettext(singular, plural, n)
146
lazy_ungettext = lazify(ungettext)
149
def _get_translator(lang, **kwargs):
150
    """Utility method to get a valid translator object from a language
151
    name"""
152
    if not lang:
153
        return NullTranslations()
154
    if 'pylons_config' in kwargs:
155
        conf = kwargs.pop('pylons_config')
156
    else:
157
        conf = pylons.config.current_conf()
158
    localedir = os.path.join(conf['pylons.paths']['root'], 'i18n')
159
    if not isinstance(lang, list):
160
        lang = [lang]
161
    try:
162
        translator = translation(conf['pylons.package'], localedir,
163
                                 languages=lang, **kwargs)
164
    except IOError, ioe:
165
        raise LanguageError('IOError: %s' % ioe)
166
    translator.pylons_lang = lang
167
    return translator
170
def set_lang(lang, **kwargs):
171
    """Set the current language used for translations.
173
    ``lang`` should be a string or a list of strings. If a list of
174
    strings, the first language is set as the main and the subsequent
175
    languages are added as fallbacks.
176
    """
177
    translator = _get_translator(lang, **kwargs)
178
    environ = pylons.request.environ
179
    environ['pylons.pylons'].translator = translator
180
    if 'paste.registry' in environ:
181
        environ['paste.registry'].replace(pylons.translator, translator)
184
def get_lang():
185
    """Return the current i18n language used"""
186
    return getattr(pylons.translator, 'pylons_lang', None)
189
def add_fallback(lang, **kwargs):
190
    """Add a fallback language from which words not matched in other
191
    languages will be translated to.
193
    This fallback will be associated with the currently selected
194
    language -- that is, resetting the language via set_lang() resets
195
    the current fallbacks.
197
    This function can be called multiple times to add multiple
198
    fallbacks.
199
    """
200
    return pylons.translator.add_fallback(_get_translator(lang, **kwargs))