0001"""Translation/Localization functions.
0002
0003Provides ``gettext`` translation functions via an app's ``pylons.translator``
0004and get/set_lang for changing the language translated to.
0005"""
0006import os
0007from gettext import NullTranslations, translation
0008
0009import pylons
0010
0011__all__ = ['_', 'add_fallback', 'get_lang', 'gettext', 'gettext_noop',
0012 'lazy_gettext', 'lazy_ngettext', 'lazy_ugettext', 'lazy_ungettext',
0013 'ngettext', 'set_lang', 'ugettext', 'ungettext', 'LanguageError',
0014 'N_']
0015
0016class LanguageError(Exception):
0017 """Exception raised when a problem occurs with changing languages"""
0018 pass
0019
0020
0021class LazyString(object):
0022 """Has a number of lazily evaluated functions replicating a string. Just
0023 override the eval() method to produce the actual value.
0024
0025 This method copied from TurboGears.
0026 """
0027
0028 def __init__(self, func, *args, **kwargs):
0029 self.func = func
0030 self.args = args
0031 self.kwargs = kwargs
0032
0033 def eval(self):
0034 return self.func(*self.args, **self.kwargs)
0035
0036 def __unicode__(self):
0037 return unicode(self.eval())
0038
0039 def __str__(self):
0040 return str(self.eval())
0041
0042 def __mod__(self, other):
0043 return self.eval() % other
0044
0045
0046def lazify(func):
0047 """Decorator to return a lazy-evaluated version of the original"""
0048 def newfunc(*args, **kwargs):
0049 return LazyString(func, *args, **kwargs)
0050 try:
0051 newfunc.__name__ = 'lazy_%s' % func.__name__
0052 except TypeError: # Python < 2.4
0053 pass
0054 newfunc.__doc__ = 'Lazy-evaluated version of the %s function\n\n%s' % (func.__name__, func.__doc__)
0056 return newfunc
0057
0058
0059def gettext_noop(value):
0060 """Mark a string for translation without translating it. Returns value.
0061
0062 Used for global strings, e.g.:
0063
0064 .. code-block:: Python
0065
0066 foo = N_('Hello')
0067
0068 class Bar:
0069 def __init__(self):
0070 self.local_foo = _(foo)
0071
0072 h.set_lang('fr')
0073 assert Bar().local_foo == 'Bonjour'
0074 h.set_lang('es')
0075 assert Bar().local_foo == 'Hola'
0076 assert foo == 'Hello'
0077 """
0078 return value
0079N_ = gettext_noop
0080
0081
0082def gettext(value):
0083 """Mark a string for translation. Returns the localized string of value.
0084
0085 Mark a string to be localized as follows:
0086
0087 .. code-block:: Python
0088
0089 gettext('This should be in lots of languages')
0090 """
0091 return pylons.translator.gettext(value)
0092lazy_gettext = lazify(gettext)
0093
0094
0095def ugettext(value):
0096 """Mark a string for translation. Returns the localized unicode string of
0097 value.
0098
0099 Mark a string to be localized as follows:
0100
0101 .. code-block:: Python
0102
0103 _('This should be in lots of languages')
0104 """
0105 return pylons.translator.ugettext(value)
0106_ = ugettext
0107lazy_ugettext = lazify(ugettext)
0108
0109
0110def ngettext(singular, plural, n):
0111 """Mark a string for translation. Returns the localized string of the
0112 pluralized value.
0113
0114 This does a plural-forms lookup of a message id. ``singular`` is used as
0115 the message id for purposes of lookup in the catalog, while ``n`` is used
0116 to determine which plural form to use. The returned message is a string.
0117
0118 Mark a string to be localized as follows:
0119
0120 .. code-block:: Python
0121
0122 ngettext('There is %(num)d file here', 'There are %(num)d files here',
0123 n) % {'num': n}
0124 """
0125 return pylons.translator.ngettext(singular, plural, n)
0126lazy_ngettext = lazify(ngettext)
0127
0128
0129def ungettext(singular, plural, n):
0130 """Mark a string for translation. Returns the localized unicode string of
0131 the pluralized value.
0132
0133 This does a plural-forms lookup of a message id. ``singular`` is used as
0134 the message id for purposes of lookup in the catalog, while ``n`` is used
0135 to determine which plural form to use. The returned message is a Unicode
0136 string.
0137
0138 Mark a string to be localized as follows:
0139
0140 .. code-block:: Python
0141
0142 ungettext('There is %(num)d file here', 'There are %(num)d files here',
0143 n) % {'num': n}
0144 """
0145 return pylons.translator.ungettext(singular, plural, n)
0146lazy_ungettext = lazify(ungettext)
0147
0148
0149def _get_translator(lang, **kwargs):
0150 """Utility method to get a valid translator object from a language name"""
0151 conf = pylons.config.current_conf()
0152 # XXX: root_path is deprecated
0153 rootdir = conf['pylons.paths'].get('root',
0154 conf['pylons.paths'].get('root_path'))
0155 localedir = os.path.join(rootdir, 'i18n')
0156 if not isinstance(lang, list):
0157 lang = [lang]
0158 try:
0159 translator = translation(conf['pylons.package'], localedir,
0160 languages=lang, **kwargs)
0161 except IOError, ioe:
0162 raise LanguageError('IOError: %s' % ioe)
0163 translator.pylons_lang = lang
0164 return translator
0165
0166
0167def set_lang(lang, **kwargs):
0168 """Set the i18n language used"""
0169 registry = pylons.request.environ['paste.registry']
0170 if not lang:
0171 registry.replace(pylons.translator, NullTranslations())
0172 else:
0173 translator = _get_translator(lang, **kwargs)
0174 registry.replace(pylons.translator, translator)
0175
0176
0177def get_lang():
0178 """Return the current i18n language used"""
0179 return getattr(pylons.translator, 'pylons_lang', None)
0180
0181
0182def add_fallback(lang, **kwargs):
0183 """Add a fallback language from which words not matched in other languages
0184 will be translated to.
0185 """
0186 return pylons.translator.add_fallback(_get_translator(lang, **kwargs))