--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://babel.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://babel.edgewall.org/log/.
+
+"""Several classes and functions that help with integrating and using Babel
+in applications.
+
+.. note: the code in this module is not used by Babel itself
+"""
+
+import gettext
+
+__all__ = ['LazyProxy', 'Translations']
+__docformat__ = 'restructuredtext en'
+
+
+class LazyProxy(object):
+ """Class for proxy objects that delegate to a specified function to evaluate
+ the actual object.
+
+ >>> def greeting(name='world'):
+ ... return 'Hello, %s!' % name
+ >>> lazy_greeting = LazyProxy(greeting, name='Joe')
+ >>> print lazy_greeting
+ Hello, Joe!
+ >>> u' ' + lazy_greeting
+ u' Hello, Joe!'
+ >>> u'(%s)' % lazy_greeting
+ u'(Hello, Joe!)'
+
+ This can be used, for example, to implement lazy translation functions that
+ delay the actual translation until the string is actually used. The
+ rationale for such behavior is that the locale of the user may not always
+ be available. In web applications, you only know the locale when processing
+ a request.
+
+ The proxy implementation attempts to be as complete as possible, so that
+ the lazy objects should mostly work as expected, for example for sorting:
+
+ >>> greetings = [
+ ... LazyProxy(greeting, 'world'),
+ ... LazyProxy(greeting, 'Joe'),
+ ... LazyProxy(greeting, 'universe'),
+ ... ]
+ >>> greetings.sort()
+ >>> for greeting in greetings:
+ ... print greeting
+ Hello, Joe!
+ Hello, universe!
+ Hello, world!
+ """
+ __slots__ = ['_func', '_args', '_kwargs', '_value']
+
+ def __init__(self, func, *args, **kwargs):
+ # Avoid triggering our own __setattr__ implementation
+ object.__setattr__(self, '_func', func)
+ object.__setattr__(self, '_args', args)
+ object.__setattr__(self, '_kwargs', kwargs)
+ object.__setattr__(self, '_value', None)
+
+ def value(self):
+ if self._value is None:
+ value = self._func(*self._args, **self._kwargs)
+ object.__setattr__(self, '_value', value)
+ return self._value
+ value = property(value)
+
+ def __contains__(self, key):
+ return key in self.value
+
+ def __nonzero__(self):
+ return bool(self.value)
+
+ def __dir__(self):
+ return dir(self.value)
+
+ def __iter__(self):
+ return iter(self.value)
+
+ def __len__(self):
+ return len(self.value)
+
+ def __str__(self):
+ return str(self.value)
+
+ def __unicode__(self):
+ return unicode(self.value)
+
+ def __add__(self, other):
+ return self.value + other
+
+ def __radd__(self, other):
+ return other + self.value
+
+ def __mod__(self, other):
+ return self.value % other
+
+ def __rmod__(self, other):
+ return other % self.value
+
+ def __mul__(self, other):
+ return self.value * other
+
+ def __rmul__(self, other):
+ return other * self.value
+
+ def __call__(self, *args, **kwargs):
+ return self.value(*args, **kwargs)
+
+ def __lt__(self, other):
+ return self.value < other
+
+ def __le__(self, other):
+ return self.value <= other
+
+ def __eq__(self, other):
+ return self.value == other
+
+ def __ne__(self, other):
+ return self.value != other
+
+ def __gt__(self, other):
+ return self.value > other
+
+ def __ge__(self, other):
+ return self.value >= other
+
+ def __delattr__(self, name):
+ delattr(self.value, name)
+
+ def __getattr__(self, name):
+ return getattr(self.value, name)
+
+ def __setattr__(self, key, value):
+ setattr(self.value, name, value)
+
+ def __delitem__(self, key):
+ del self.value[key]
+
+ def __getitem__(self, key):
+ return self.value[key]
+
+ def __setitem__(self, key, value):
+ self.value[name] = value
+
+
+class Translations(gettext.GNUTranslations):
+ """An extended translation catalog class."""
+
+ DEFAULT_DOMAIN = 'messages'
+
+ def __init__(self, fileobj=None):
+ """Initialize the translations catalog.
+
+ :param fileobj: the file-like object the translation should be read
+ from
+ """
+ gettext.GNUTranslations.__init__(self, fp=fileobj)
+ self.files = [getattr(fileobj, 'name')]
+
+ def load(cls, dirname=None, locales=None, domain=DEFAULT_DOMAIN):
+ """Load translations from the given directory.
+
+ :param dirname: the directory containing the ``MO`` files
+ :param locales: the list of locales in order of preference (items in
+ this list can be either `Locale` objects or locale
+ strings)
+ :param domain: the message domain
+ :return: the loaded catalog, or a ``NullTranslations`` instance if no
+ matching translations were found
+ :rtype: `Translations`
+ """
+ if not isinstance(locales, (list, tuple)):
+ locales = [locales]
+ locales = [str(locale) for locale in locales]
+ filename = gettext.find(domain or self.DEFAULT_DOMAIN, dirname, locales)
+ if not filename:
+ return gettext.NullTranslations()
+ return cls(fileobj=open(filename, 'rb'))
+ load = classmethod(load)
+
+ def merge(self, translations):
+ """Merge the given translations into the catalog.
+
+ Message translations in the specfied catalog override any messages with
+ the same identifier in the existing catalog.
+
+ :param translations: the `Translations` instance with the messages to
+ merge
+ :return: the `Translations` instance (``self``) so that `merge` calls
+ can be easily chained
+ :rtype: `Translations`
+ """
+ if isinstance(translations, Translations):
+ self._catalog.update(translations._catalog)
+ self.files.extend(translations.files)
+ return self
+
+ def __repr__(self):
+ return "<%s %r>" % (type(self).__name__)