pylons.i18n.translation
Covered: 27 lines
Missed: 115 lines
Skipped 56 lines
Percent: 19 %
  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
 48
def lazify(func):
 49
    """Decorator to return a lazy-evaluated version of the original"""
 50
    def newfunc(*args, **kwargs):
 51
        return LazyString(func, *args, **kwargs)
 52
    newfunc.__name__ = 'lazy_%s' % func.__name__
 53
    newfunc.__doc__ = 'Lazy-evaluated version of the %s function\n\n%s' % \
 54
        (func.__name__, func.__doc__)
 55
    return newfunc
 58
def gettext_noop(value):
 59
    """Mark a string for translation without translating it. Returns
 60
    value.
 62
    Used for global strings, e.g.::
 64
        foo = N_('Hello')
 66
        class Bar:
 67
            def __init__(self):
 68
                self.local_foo = _(foo)
 70
        h.set_lang('fr')
 71
        assert Bar().local_foo == 'Bonjour'
 72
        h.set_lang('es')
 73
        assert Bar().local_foo == 'Hola'
 74
        assert foo == 'Hello'
 76
    """
 77
    return value
 78
N_ = gettext_noop
 81
def gettext(value):
 82
    """Mark a string for translation. Returns the localized string of
 83
    value.
 85
    Mark a string to be localized as follows::
 87
        gettext('This should be in lots of languages')
 89
    """
 90
    return pylons.translator.gettext(value)
 91
lazy_gettext = lazify(gettext)
 94
def ugettext(value):
 95
    """Mark a string for translation. Returns the localized unicode
 96
    string of value.
 98
    Mark a string to be localized as follows::
100
        _('This should be in lots of languages')
102
    """
103
    return pylons.translator.ugettext(value)
104
_ = ugettext
105
lazy_ugettext = lazify(ugettext)
108
def ngettext(singular, plural, n):
109
    """Mark a string for translation. Returns the localized string of
110
    the pluralized value.
112
    This does a plural-forms lookup of a message id. ``singular`` is
113
    used as the message id for purposes of lookup in the catalog, while
114
    ``n`` is used to determine which plural form to use. The returned
115
    message is a string.
117
    Mark a string to be localized as follows::
119
        ngettext('There is %(num)d file here', 'There are %(num)d files here',
120
                 n) % {'num': n}
122
    """
123
    return pylons.translator.ngettext(singular, plural, n)
124
lazy_ngettext = lazify(ngettext)
127
def ungettext(singular, plural, n):
128
    """Mark a string for translation. Returns the localized unicode
129
    string of the pluralized value.
131
    This does a plural-forms lookup of a message id. ``singular`` is
132
    used as the message id for purposes of lookup in the catalog, while
133
    ``n`` is used to determine which plural form to use. The returned
134
    message is a Unicode string.
136
    Mark a string to be localized as follows::
138
        ungettext('There is %(num)d file here', 'There are %(num)d files here',
139
                  n) % {'num': n}
141
    """
142
    return pylons.translator.ungettext(singular, plural, n)
143
lazy_ungettext = lazify(ungettext)
146
def _get_translator(lang, **kwargs):
147
    """Utility method to get a valid translator object from a language
148
    name"""
149
    if not lang:
150
        return NullTranslations()
151
    if 'pylons_config' in kwargs:
152
        conf = kwargs.pop('pylons_config')
153
    else:
154
        conf = pylons.config.current_conf()
155
    localedir = os.path.join(conf['pylons.paths']['root'], 'i18n')
156
    if not isinstance(lang, list):
157
        lang = [lang]
158
    try:
159
        translator = translation(conf['pylons.package'], localedir,
160
                                 languages=lang, **kwargs)
161
    except IOError, ioe:
162
        raise LanguageError('IOError: %s' % ioe)
163
    translator.pylons_lang = lang
164
    return translator
167
def set_lang(lang, **kwargs):
168
    """Set the current language used for translations.
170
    ``lang`` should be a string or a list of strings. If a list of
171
    strings, the first language is set as the main and the subsequent
172
    languages are added as fallbacks.
173
    """
174
    translator = _get_translator(lang, **kwargs)
175
    environ = pylons.request.environ
176
    environ['pylons.pylons'].translator = translator
177
    if 'paste.registry' in environ:
178
        environ['paste.registry'].replace(pylons.translator, translator)
181
def get_lang():
182
    """Return the current i18n language used"""
183
    return getattr(pylons.translator, 'pylons_lang', None)
186
def add_fallback(lang, **kwargs):
187
    """Add a fallback language from which words not matched in other
188
    languages will be translated to.
190
    This fallback will be associated with the currently selected
191
    language -- that is, resetting the language via set_lang() resets
192
    the current fallbacks.
194
    This function can be called multiple times to add multiple
195
    fallbacks.
196
    """
197
    return pylons.translator.add_fallback(_get_translator(lang, **kwargs))