13257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel"""Internationalization and localization support.
23257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
33257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielThis module provides internationalization (I18N) and localization (L10N)
43257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielsupport for your Python programs by providing an interface to the GNU gettext
53257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielmessage catalog library.
63257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
73257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielI18N refers to the operation by which a program is made aware of multiple
83257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniellanguages.  L10N refers to the adaptation of your program, once
93257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielinternationalized, to the local language and cultural habits.
103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel"""
123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# This module represents the integration of work, contributions, feedback, and
143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# suggestions from the following people:
153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#
163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# Martin von Loewis, who wrote the initial implementation of the underlying
173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# C-based libintlmodule (later renamed _gettext), along with a skeletal
183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# gettext.py implementation.
193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#
203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# Peter Funk, who wrote fintl.py, a fairly complete wrapper around intlmodule,
213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# which also included a pure-Python implementation to read .mo files if
223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# intlmodule wasn't available.
233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#
243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# James Henstridge, who also wrote a gettext.py module, which has some
253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# interesting, but currently unsupported experimental features: the notion of
263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# a Catalog class and instances, and the ability to add to a catalog file via
273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# a Python API.
283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#
293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# Barry Warsaw integrated these modules, wrote the .install() API and code,
303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# and conformed all C and Python code to Python's coding standards.
313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#
323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# Francois Pinard and Marc-Andre Lemburg also contributed valuably to this
333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# module.
343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#
353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# J. David Ibanez implemented plural forms. Bruno Haible fixed some bugs.
363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#
373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# TODO:
383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# - Lazy loading of .mo files.  Currently the entire catalog is loaded into
393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#   memory, but that's probably bad for large translated programs.  Instead,
403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#   the lexical sort of original strings in GNU .mo files should be exploited
413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#   to do binary searches and lazy initializations.  Or you might want to use
423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#   the undocumented double-hash algorithm for .mo files with hash tables, but
433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#   you'll need to study the GNU gettext code to do this.
443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#
453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# - Support Solaris .mo file formats.  Unfortunately, we've been unable to
463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#   find this format documented anywhere.
473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielimport locale, copy, os, re, struct, sys
503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielfrom errno import ENOENT
513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel__all__ = ['NullTranslations', 'GNUTranslations', 'Catalog',
543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel           'find', 'translation', 'install', 'textdomain', 'bindtextdomain',
553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel           'bind_textdomain_codeset',
563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel           'dgettext', 'dngettext', 'gettext', 'lgettext', 'ldgettext',
573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel           'ldngettext', 'lngettext', 'ngettext',
583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel           ]
593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel_default_localedir = os.path.join(sys.prefix, 'share', 'locale')
613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef test(condition, true, false):
643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """
653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    Implements the C expression:
663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel      condition ? true : false
683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    Required to correctly interpret plural forms.
703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """
713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if condition:
723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return true
733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    else:
743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return false
753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef c2py(plural):
783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """Gets a C expression as used in PO files for plural forms and returns a
793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    Python lambda function that implements an equivalent expression.
803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """
813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # Security check, allow only the "n" identifier
823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    try:
833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        from cStringIO import StringIO
843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    except ImportError:
853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        from StringIO import StringIO
863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    import token, tokenize
873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    tokens = tokenize.generate_tokens(StringIO(plural).readline)
883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    try:
893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        danger = [x for x in tokens if x[0] == token.NAME and x[1] != 'n']
903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    except tokenize.TokenError:
913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        raise ValueError, \
923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel              'plural forms expression error, maybe unbalanced parenthesis'
933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    else:
943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if danger:
953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            raise ValueError, 'plural forms expression could be dangerous'
963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # Replace some C operators by their Python equivalents
983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    plural = plural.replace('&&', ' and ')
993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    plural = plural.replace('||', ' or ')
1003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    expr = re.compile(r'\!([^=])')
1023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    plural = expr.sub(' not \\1', plural)
1033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # Regular expression and replacement function used to transform
1053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # "a?b:c" to "test(a,b,c)".
1063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    expr = re.compile(r'(.*?)\?(.*?):(.*)')
1073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def repl(x):
1083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return "test(%s, %s, %s)" % (x.group(1), x.group(2),
1093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                                     expr.sub(repl, x.group(3)))
1103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # Code to transform the plural expression, taking care of parentheses
1123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    stack = ['']
1133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    for c in plural:
1143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if c == '(':
1153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            stack.append('')
1163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        elif c == ')':
1173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if len(stack) == 1:
1183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                # Actually, we never reach this code, because unbalanced
1193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                # parentheses get caught in the security check at the
1203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                # beginning.
1213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                raise ValueError, 'unbalanced parenthesis in plural form'
1223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            s = expr.sub(repl, stack.pop())
1233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            stack[-1] += '(%s)' % s
1243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
1253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            stack[-1] += c
1263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    plural = expr.sub(repl, stack.pop())
1273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return eval('lambda n: int(%s)' % plural)
1293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef _expand_lang(locale):
1333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    from locale import normalize
1343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    locale = normalize(locale)
1353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    COMPONENT_CODESET   = 1 << 0
1363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    COMPONENT_TERRITORY = 1 << 1
1373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    COMPONENT_MODIFIER  = 1 << 2
1383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # split up the locale into its base components
1393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    mask = 0
1403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    pos = locale.find('@')
1413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if pos >= 0:
1423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        modifier = locale[pos:]
1433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        locale = locale[:pos]
1443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        mask |= COMPONENT_MODIFIER
1453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    else:
1463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        modifier = ''
1473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    pos = locale.find('.')
1483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if pos >= 0:
1493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        codeset = locale[pos:]
1503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        locale = locale[:pos]
1513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        mask |= COMPONENT_CODESET
1523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    else:
1533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        codeset = ''
1543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    pos = locale.find('_')
1553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if pos >= 0:
1563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        territory = locale[pos:]
1573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        locale = locale[:pos]
1583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        mask |= COMPONENT_TERRITORY
1593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    else:
1603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        territory = ''
1613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    language = locale
1623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    ret = []
1633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    for i in range(mask+1):
1643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not (i & ~mask):  # if all components for this combo exist ...
1653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            val = language
1663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if i & COMPONENT_TERRITORY: val += territory
1673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if i & COMPONENT_CODESET:   val += codeset
1683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if i & COMPONENT_MODIFIER:  val += modifier
1693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            ret.append(val)
1703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    ret.reverse()
1713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return ret
1723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielclass NullTranslations:
1763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def __init__(self, fp=None):
1773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self._info = {}
1783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self._charset = None
1793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self._output_charset = None
1803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self._fallback = None
1813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if fp is not None:
1823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self._parse(fp)
1833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def _parse(self, fp):
1853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        pass
1863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_fallback(self, fallback):
1883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self._fallback:
1893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self._fallback.add_fallback(fallback)
1903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
1913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self._fallback = fallback
1923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def gettext(self, message):
1943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self._fallback:
1953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return self._fallback.gettext(message)
1963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return message
1973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def lgettext(self, message):
1993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self._fallback:
2003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return self._fallback.lgettext(message)
2013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return message
2023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def ngettext(self, msgid1, msgid2, n):
2043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self._fallback:
2053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return self._fallback.ngettext(msgid1, msgid2, n)
2063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if n == 1:
2073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return msgid1
2083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
2093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return msgid2
2103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def lngettext(self, msgid1, msgid2, n):
2123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self._fallback:
2133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return self._fallback.lngettext(msgid1, msgid2, n)
2143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if n == 1:
2153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return msgid1
2163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
2173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return msgid2
2183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def ugettext(self, message):
2203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self._fallback:
2213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return self._fallback.ugettext(message)
2223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return unicode(message)
2233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def ungettext(self, msgid1, msgid2, n):
2253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self._fallback:
2263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return self._fallback.ungettext(msgid1, msgid2, n)
2273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if n == 1:
2283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return unicode(msgid1)
2293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
2303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return unicode(msgid2)
2313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def info(self):
2333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return self._info
2343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def charset(self):
2363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return self._charset
2373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def output_charset(self):
2393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return self._output_charset
2403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def set_output_charset(self, charset):
2423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self._output_charset = charset
2433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def install(self, unicode=False, names=None):
2453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        import __builtin__
2463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        __builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext
2473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if hasattr(names, "__contains__"):
2483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if "gettext" in names:
2493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                __builtin__.__dict__['gettext'] = __builtin__.__dict__['_']
2503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if "ngettext" in names:
2513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                __builtin__.__dict__['ngettext'] = (unicode and self.ungettext
2523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                                                             or self.ngettext)
2533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if "lgettext" in names:
2543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                __builtin__.__dict__['lgettext'] = self.lgettext
2553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if "lngettext" in names:
2563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                __builtin__.__dict__['lngettext'] = self.lngettext
2573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielclass GNUTranslations(NullTranslations):
2603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # Magic number of .mo files
2613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    LE_MAGIC = 0x950412deL
2623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    BE_MAGIC = 0xde120495L
2633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def _parse(self, fp):
2653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Override this method to support alternative .mo formats."""
2663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        unpack = struct.unpack
2673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        filename = getattr(fp, 'name', '')
2683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # Parse the .mo file header, which consists of 5 little endian 32
2693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # bit words.
2703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self._catalog = catalog = {}
2713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.plural = lambda n: int(n != 1) # germanic plural by default
2723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        buf = fp.read()
2733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        buflen = len(buf)
2743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # Are we big endian or little endian?
2753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        magic = unpack('<I', buf[:4])[0]
2763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if magic == self.LE_MAGIC:
2773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            version, msgcount, masteridx, transidx = unpack('<4I', buf[4:20])
2783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            ii = '<II'
2793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        elif magic == self.BE_MAGIC:
2803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            version, msgcount, masteridx, transidx = unpack('>4I', buf[4:20])
2813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            ii = '>II'
2823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
2833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            raise IOError(0, 'Bad magic number', filename)
2843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # Now put all messages from the .mo file buffer into the catalog
2853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # dictionary.
2863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        for i in xrange(0, msgcount):
2873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            mlen, moff = unpack(ii, buf[masteridx:masteridx+8])
2883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            mend = moff + mlen
2893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            tlen, toff = unpack(ii, buf[transidx:transidx+8])
2903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            tend = toff + tlen
2913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if mend < buflen and tend < buflen:
2923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                msg = buf[moff:mend]
2933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                tmsg = buf[toff:tend]
2943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            else:
2953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                raise IOError(0, 'File is corrupt', filename)
2963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # See if we're looking at GNU .mo conventions for metadata
2973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if mlen == 0:
2983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                # Catalog description
2993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                lastk = None
3003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                for item in tmsg.splitlines():
3013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    item = item.strip()
3023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    if not item:
3033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        continue
3043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    k = v = None
3053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    if ':' in item:
3063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        k, v = item.split(':', 1)
3073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        k = k.strip().lower()
3083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        v = v.strip()
3093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        self._info[k] = v
3103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        lastk = k
3113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    elif lastk:
3123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        self._info[lastk] += '\n' + item
3133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    if k == 'content-type':
3143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        self._charset = v.split('charset=')[1]
3153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    elif k == 'plural-forms':
3163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        v = v.split(';')
3173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        plural = v[1].split('plural=')[1]
3183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        self.plural = c2py(plural)
3193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # Note: we unconditionally convert both msgids and msgstrs to
3203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # Unicode using the character encoding specified in the charset
3213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # parameter of the Content-Type header.  The gettext documentation
3223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # strongly encourages msgids to be us-ascii, but some applications
3233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # require alternative encodings (e.g. Zope's ZCML and ZPT).  For
3243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # traditional gettext applications, the msgid conversion will
3253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # cause no problems since us-ascii should always be a subset of
3263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # the charset encoding.  We may want to fall back to 8-bit msgids
3273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # if the Unicode conversion fails.
3283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if '\x00' in msg:
3293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                # Plural forms
3303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                msgid1, msgid2 = msg.split('\x00')
3313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                tmsg = tmsg.split('\x00')
3323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                if self._charset:
3333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    msgid1 = unicode(msgid1, self._charset)
3343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    tmsg = [unicode(x, self._charset) for x in tmsg]
3353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                for i in range(len(tmsg)):
3363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    catalog[(msgid1, i)] = tmsg[i]
3373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            else:
3383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                if self._charset:
3393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    msg = unicode(msg, self._charset)
3403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    tmsg = unicode(tmsg, self._charset)
3413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                catalog[msg] = tmsg
3423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # advance to next entry in the seek tables
3433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            masteridx += 8
3443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            transidx += 8
3453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def gettext(self, message):
3473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        missing = object()
3483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        tmsg = self._catalog.get(message, missing)
3493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if tmsg is missing:
3503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if self._fallback:
3513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return self._fallback.gettext(message)
3523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return message
3533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # Encode the Unicode tmsg back to an 8-bit string, if possible
3543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self._output_charset:
3553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return tmsg.encode(self._output_charset)
3563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        elif self._charset:
3573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return tmsg.encode(self._charset)
3583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return tmsg
3593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def lgettext(self, message):
3613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        missing = object()
3623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        tmsg = self._catalog.get(message, missing)
3633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if tmsg is missing:
3643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if self._fallback:
3653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return self._fallback.lgettext(message)
3663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return message
3673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self._output_charset:
3683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return tmsg.encode(self._output_charset)
3693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return tmsg.encode(locale.getpreferredencoding())
3703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def ngettext(self, msgid1, msgid2, n):
3723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        try:
3733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            tmsg = self._catalog[(msgid1, self.plural(n))]
3743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if self._output_charset:
3753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return tmsg.encode(self._output_charset)
3763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            elif self._charset:
3773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return tmsg.encode(self._charset)
3783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return tmsg
3793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        except KeyError:
3803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if self._fallback:
3813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return self._fallback.ngettext(msgid1, msgid2, n)
3823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if n == 1:
3833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return msgid1
3843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            else:
3853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return msgid2
3863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def lngettext(self, msgid1, msgid2, n):
3883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        try:
3893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            tmsg = self._catalog[(msgid1, self.plural(n))]
3903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if self._output_charset:
3913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return tmsg.encode(self._output_charset)
3923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return tmsg.encode(locale.getpreferredencoding())
3933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        except KeyError:
3943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if self._fallback:
3953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return self._fallback.lngettext(msgid1, msgid2, n)
3963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if n == 1:
3973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return msgid1
3983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            else:
3993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return msgid2
4003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def ugettext(self, message):
4023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        missing = object()
4033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        tmsg = self._catalog.get(message, missing)
4043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if tmsg is missing:
4053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if self._fallback:
4063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return self._fallback.ugettext(message)
4073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return unicode(message)
4083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return tmsg
4093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def ungettext(self, msgid1, msgid2, n):
4113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        try:
4123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            tmsg = self._catalog[(msgid1, self.plural(n))]
4133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        except KeyError:
4143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if self._fallback:
4153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return self._fallback.ungettext(msgid1, msgid2, n)
4163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if n == 1:
4173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                tmsg = unicode(msgid1)
4183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            else:
4193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                tmsg = unicode(msgid2)
4203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return tmsg
4213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# Locate a .mo file using the gettext strategy
4243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef find(domain, localedir=None, languages=None, all=0):
4253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # Get some reasonable defaults for arguments that were not supplied
4263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if localedir is None:
4273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        localedir = _default_localedir
4283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if languages is None:
4293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        languages = []
4303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
4313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            val = os.environ.get(envar)
4323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if val:
4333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                languages = val.split(':')
4343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                break
4353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if 'C' not in languages:
4363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            languages.append('C')
4373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # now normalize and expand the languages
4383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    nelangs = []
4393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    for lang in languages:
4403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        for nelang in _expand_lang(lang):
4413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if nelang not in nelangs:
4423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                nelangs.append(nelang)
4433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # select a language
4443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if all:
4453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        result = []
4463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    else:
4473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        result = None
4483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    for lang in nelangs:
4493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if lang == 'C':
4503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            break
4513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain)
4523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if os.path.exists(mofile):
4533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if all:
4543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                result.append(mofile)
4553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            else:
4563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return mofile
4573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return result
4583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# a mapping between absolute .mo file path and Translation object
4623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel_translations = {}
4633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef translation(domain, localedir=None, languages=None,
4653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                class_=None, fallback=False, codeset=None):
4663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if class_ is None:
4673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        class_ = GNUTranslations
4683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    mofiles = find(domain, localedir, languages, all=1)
4693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if not mofiles:
4703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if fallback:
4713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return NullTranslations()
4723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        raise IOError(ENOENT, 'No translation file found for domain', domain)
4733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # Avoid opening, reading, and parsing the .mo file after it's been done
4743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    # once.
4753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    result = None
4763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    for mofile in mofiles:
4773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        key = (class_, os.path.abspath(mofile))
4783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        t = _translations.get(key)
4793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if t is None:
4803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            with open(mofile, 'rb') as fp:
4813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                t = _translations.setdefault(key, class_(fp))
4823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # Copy the translation object to allow setting fallbacks and
4833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # output charset. All other instance data is shared with the
4843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # cached object.
4853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        t = copy.copy(t)
4863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if codeset:
4873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            t.set_output_charset(codeset)
4883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if result is None:
4893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            result = t
4903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
4913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            result.add_fallback(t)
4923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return result
4933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef install(domain, localedir=None, unicode=False, codeset=None, names=None):
4963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    t = translation(domain, localedir, fallback=True, codeset=codeset)
4973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    t.install(unicode, names)
4983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# a mapping b/w domains and locale directories
5023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel_localedirs = {}
5033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# a mapping b/w domains and codesets
5043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel_localecodesets = {}
5053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# current global domain, `messages' used for compatibility w/ GNU gettext
5063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel_current_domain = 'messages'
5073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef textdomain(domain=None):
5103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    global _current_domain
5113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if domain is not None:
5123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _current_domain = domain
5133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return _current_domain
5143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef bindtextdomain(domain, localedir=None):
5173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    global _localedirs
5183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if localedir is not None:
5193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _localedirs[domain] = localedir
5203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return _localedirs.get(domain, _default_localedir)
5213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef bind_textdomain_codeset(domain, codeset=None):
5243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    global _localecodesets
5253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if codeset is not None:
5263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _localecodesets[domain] = codeset
5273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return _localecodesets.get(domain)
5283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef dgettext(domain, message):
5313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    try:
5323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        t = translation(domain, _localedirs.get(domain, None),
5333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        codeset=_localecodesets.get(domain))
5343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    except IOError:
5353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return message
5363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return t.gettext(message)
5373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef ldgettext(domain, message):
5393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    try:
5403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        t = translation(domain, _localedirs.get(domain, None),
5413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        codeset=_localecodesets.get(domain))
5423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    except IOError:
5433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return message
5443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return t.lgettext(message)
5453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef dngettext(domain, msgid1, msgid2, n):
5473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    try:
5483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        t = translation(domain, _localedirs.get(domain, None),
5493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        codeset=_localecodesets.get(domain))
5503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    except IOError:
5513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if n == 1:
5523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return msgid1
5533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
5543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return msgid2
5553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return t.ngettext(msgid1, msgid2, n)
5563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef ldngettext(domain, msgid1, msgid2, n):
5583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    try:
5593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        t = translation(domain, _localedirs.get(domain, None),
5603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                        codeset=_localecodesets.get(domain))
5613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    except IOError:
5623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if n == 1:
5633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return msgid1
5643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
5653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return msgid2
5663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return t.lngettext(msgid1, msgid2, n)
5673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef gettext(message):
5693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return dgettext(_current_domain, message)
5703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef lgettext(message):
5723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return ldgettext(_current_domain, message)
5733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef ngettext(msgid1, msgid2, n):
5753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return dngettext(_current_domain, msgid1, msgid2, n)
5763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef lngettext(msgid1, msgid2, n):
5783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    return ldngettext(_current_domain, msgid1, msgid2, n)
5793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# dcgettext() has been deemed unnecessary and is not implemented.
5813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# James Henstridge's Catalog constructor from GNOME gettext.  Documented usage
5833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# was:
5843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#
5853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#    import gettext
5863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#    cat = gettext.Catalog(PACKAGE, localedir=LOCALEDIR)
5873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#    _ = cat.gettext
5883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel#    print _('Hello World')
5893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# The resulting catalog object currently don't support access through a
5913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# dictionary API, which was supported (but apparently unused) in GNOME
5923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# gettext.
5933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
5943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielCatalog = translation
595