diff options
| author | Chris McDonough <chrism@plope.com> | 2010-10-25 10:29:31 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2010-10-25 10:29:31 -0400 |
| commit | 64372401084889a440c9d990a0febc221e3e4b5c (patch) | |
| tree | c8939a341505d19f19fa6918d264b4e1d95326f8 /repoze/bfg/i18n.py | |
| parent | c8e78c2037806f3e5dab57de635bf80865b7061d (diff) | |
| download | pyramid-64372401084889a440c9d990a0febc221e3e4b5c.tar.gz pyramid-64372401084889a440c9d990a0febc221e3e4b5c.tar.bz2 pyramid-64372401084889a440c9d990a0febc221e3e4b5c.zip | |
first pass at converting bfg to pyramid namespace
Diffstat (limited to 'repoze/bfg/i18n.py')
| -rw-r--r-- | repoze/bfg/i18n.py | 322 |
1 files changed, 0 insertions, 322 deletions
diff --git a/repoze/bfg/i18n.py b/repoze/bfg/i18n.py deleted file mode 100644 index c3ba7efad..000000000 --- a/repoze/bfg/i18n.py +++ /dev/null @@ -1,322 +0,0 @@ -import gettext -import os - -from translationstring import Translator -from translationstring import Pluralizer -from translationstring import TranslationString # API -from translationstring import TranslationStringFactory # API - -TranslationString = TranslationString # PyFlakes -TranslationStringFactory = TranslationStringFactory # PyFlakes - -from repoze.bfg.interfaces import ILocalizer -from repoze.bfg.interfaces import ITranslationDirectories -from repoze.bfg.interfaces import ILocaleNegotiator - -from repoze.bfg.settings import get_settings -from repoze.bfg.threadlocal import get_current_registry - -class Localizer(object): - """ - An object providing translation and pluralizations related to - the current request's locale name. A - :class:`repoze.bfg.i18n.Localizer` object is created using the - :func:`repoze.bfg.i18n.get_localizer` function. - """ - def __init__(self, locale_name, translations): - self.locale_name = locale_name - self.translations = translations - self.pluralizer = None - self.translator = None - - def translate(self, tstring, domain=None, mapping=None): - """ - Translate a :term:`translation string` to the current language - and interpolate any *replacement markers* in the result. The - ``translate`` method accepts three arguments: ``tstring`` - (required), ``domain`` (optional) and ``mapping`` (optional). - When called, it will translate the ``tstring`` translation - string to a ``unicode`` object using the current locale. If - the current locale could not be determined, the result of - interpolation of the default value is returned. The optional - ``domain`` argument can be used to specify or override the - domain of the ``tstring`` (useful when ``tstring`` is a normal - string rather than a translation string). The optional - ``mapping`` argument can specify or override the ``tstring`` - interpolation mapping, useful when the ``tstring`` argument is - a simple string instead of a translation string. - - Example:: - - from repoze.bfg.18n import TranslationString - ts = TranslationString('Add ${item}', domain='mypackage', - mapping={'item':'Item'}) - translated = localizer.translate(ts) - - Example:: - - translated = localizer.translate('Add ${item}', domain='mypackage', - mapping={'item':'Item'}) - - """ - if self.translator is None: - self.translator = Translator(self.translations) - return self.translator(tstring, domain=domain, mapping=mapping) - - def pluralize(self, singular, plural, n, domain=None, mapping=None): - """ - Return a Unicode string translation by using two - :term:`message identifier` objects as a singular/plural pair - and an ``n`` value representing the number that appears in the - message using gettext plural forms support. The ``singular`` - and ``plural`` objects passed may be translation strings or - unicode strings. ``n`` represents the number of elements. - ``domain`` is the translation domain to use to do the - pluralization, and ``mapping`` is the interpolation mapping - that should be used on the result. Note that if the objects - passed are translation strings, their domains and mappings are - ignored. The domain and mapping arguments must be used - instead. If the ``domain`` is not supplied, a default domain - is used (usually ``messages``). - - Example:: - - num = 1 - translated = localizer.pluralize('Add ${num} item', - 'Add ${num} items', - num, - mapping={'num':num}) - - - """ - if self.pluralizer is None: - self.pluralizer = Pluralizer(self.translations) - return self.pluralizer(singular, plural, n, domain=domain, - mapping=mapping) - - -def default_locale_negotiator(request): - """ The default :term:`locale negotiator`. Returns a locale name - or ``None``. - - - First, the negotiator looks for the ``_LOCALE_`` attribute of - the request object (possibly set by a view or a listener for an - :term:`event`). - - - Then it looks for the ``request.params['_LOCALE_']`` value. - - - Then it looks for the ``request.cookies['_LOCALE_']`` value. - - - Finally, the negotiator returns ``None`` if the locale could not - be determined via any of the previous checks (when a locale - negotiator returns ``None``, it signifies that the - :term:`default locale name` should be used.) - """ - name = '_LOCALE_' - locale_name = getattr(request, name, None) - if locale_name is None: - locale_name = request.params.get(name) - if locale_name is None: - locale_name = request.cookies.get(name) - return locale_name - -def negotiate_locale_name(request): - """ Negotiate and return the :term:`locale name` associated with - the current request (never cached).""" - try: - registry = request.registry - except AttributeError: - registry = get_current_registry() - negotiator = registry.queryUtility(ILocaleNegotiator, - default=default_locale_negotiator) - locale_name = negotiator(request) - - if locale_name is None: - settings = get_settings() or {} - locale_name = settings.get('default_locale_name', 'en') - - return locale_name - -def get_locale_name(request): - """ Return the :term:`locale name` associated with the current - request (possibly cached).""" - locale_name = getattr(request, 'bfg_locale_name', None) - if locale_name is None: - locale_name = negotiate_locale_name(request) - request.bfg_locale_name = locale_name - return locale_name - -def get_localizer(request): - """ Retrieve a :class:`repoze.bfg.i18n.Localizer` object - corresponding to the current request's locale name. """ - localizer = getattr(request, 'bfg_localizer', None) - - if localizer is None: - # no locale object cached on request - try: - registry = request.registry - except AttributeError: - registry = get_current_registry() - - current_locale_name = get_locale_name(request) - localizer = registry.queryUtility(ILocalizer, name=current_locale_name) - - if localizer is None: - # no localizer utility registered yet - translations = Translations() - translations._catalog = {} - tdirs = registry.queryUtility(ITranslationDirectories, default=[]) - for tdir in tdirs: - locale_dirs = [ (lname, os.path.join(tdir, lname)) for lname in - os.listdir(tdir) ] - for locale_name, locale_dir in locale_dirs: - if locale_name != current_locale_name: - continue - messages_dir = os.path.join(locale_dir, 'LC_MESSAGES') - if not os.path.isdir(os.path.realpath(messages_dir)): - continue - for mofile in os.listdir(messages_dir): - mopath = os.path.realpath(os.path.join(messages_dir, - mofile)) - if mofile.endswith('.mo') and os.path.isfile(mopath): - mofp = open(mopath, 'rb') - domain = mofile[:-3] - dtrans = Translations(mofp, domain) - translations.add(dtrans) - - localizer = Localizer(locale_name=current_locale_name, - translations=translations) - registry.registerUtility(localizer, ILocalizer, - name=current_locale_name) - request.bfg_localizer = localizer - - return localizer - -class Translations(gettext.GNUTranslations, object): - """An extended translation catalog class (ripped off from Babel) """ - - DEFAULT_DOMAIN = 'messages' - - def __init__(self, fileobj=None, domain=DEFAULT_DOMAIN): - """Initialize the translations catalog. - - :param fileobj: the file-like object the translation should be read - from - """ - gettext.GNUTranslations.__init__(self, fp=fileobj) - self.files = filter(None, [getattr(fileobj, 'name', None)]) - self.domain = domain - self._domains = {} - - @classmethod - 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 locales is not None: - if not isinstance(locales, (list, tuple)): - locales = [locales] - locales = [str(l) for l in locales] - if not domain: - domain = cls.DEFAULT_DOMAIN - filename = gettext.find(domain, dirname, locales) - if not filename: - return gettext.NullTranslations() - return cls(fileobj=open(filename, 'rb'), domain=domain) - - def __repr__(self): - return '<%s: "%s">' % (type(self).__name__, - self._info.get('project-id-version')) - - def add(self, translations, merge=True): - """Add the given translations to the catalog. - - If the domain of the translations is different than that of the - current catalog, they are added as a catalog that is only accessible - by the various ``d*gettext`` functions. - - :param translations: the `Translations` instance with the messages to - add - :param merge: whether translations for message domains that have - already been added should be merged with the existing - translations - :return: the `Translations` instance (``self``) so that `merge` calls - can be easily chained - :rtype: `Translations` - """ - domain = getattr(translations, 'domain', self.DEFAULT_DOMAIN) - if merge and domain == self.domain: - return self.merge(translations) - - existing = self._domains.get(domain) - if merge and existing is not None: - existing.merge(translations) - else: - translations.add_fallback(self) - self._domains[domain] = translations - - return self - - def merge(self, translations): - """Merge the given translations into the catalog. - - Message translations in the specified 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, gettext.GNUTranslations): - self._catalog.update(translations._catalog) - if isinstance(translations, Translations): - self.files.extend(translations.files) - - return self - - def dgettext(self, domain, message): - """Like ``gettext()``, but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).gettext(message) - - def ldgettext(self, domain, message): - """Like ``lgettext()``, but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).lgettext(message) - - def dugettext(self, domain, message): - """Like ``ugettext()``, but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).ugettext(message) - - def dngettext(self, domain, singular, plural, num): - """Like ``ngettext()``, but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).ngettext(singular, plural, num) - - def ldngettext(self, domain, singular, plural, num): - """Like ``lngettext()``, but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).lngettext(singular, plural, num) - - def dungettext(self, domain, singular, plural, num): - """Like ``ungettext()`` but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).ungettext(singular, plural, num) - |
