195be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw"""Internationalization and localization support.
295be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
395be23dc8626684caa268df7ff7749fcb89adddfBarry WarsawThis module provides internationalization (I18N) and localization (L10N)
495be23dc8626684caa268df7ff7749fcb89adddfBarry Warsawsupport for your Python programs by providing an interface to the GNU gettext
595be23dc8626684caa268df7ff7749fcb89adddfBarry Warsawmessage catalog library.
695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
795be23dc8626684caa268df7ff7749fcb89adddfBarry WarsawI18N refers to the operation by which a program is made aware of multiple
895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsawlanguages.  L10N refers to the adaptation of your program, once
933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsawinternationalized, to the local language and cultural habits.
1095be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
1195be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw"""
1295be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
13fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw# This module represents the integration of work, contributions, feedback, and
14fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw# suggestions from the following people:
1595be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw#
1695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# Martin von Loewis, who wrote the initial implementation of the underlying
1795be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# C-based libintlmodule (later renamed _gettext), along with a skeletal
1895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# gettext.py implementation.
1995be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw#
2095be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# Peter Funk, who wrote fintl.py, a fairly complete wrapper around intlmodule,
2195be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# which also included a pure-Python implementation to read .mo files if
2295be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# intlmodule wasn't available.
2395be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw#
2495be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# James Henstridge, who also wrote a gettext.py module, which has some
2595be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# interesting, but currently unsupported experimental features: the notion of
2695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# a Catalog class and instances, and the ability to add to a catalog file via
2795be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# a Python API.
2895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw#
2995be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# Barry Warsaw integrated these modules, wrote the .install() API and code,
3095be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# and conformed all C and Python code to Python's coding standards.
3133d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#
3233d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# Francois Pinard and Marc-Andre Lemburg also contributed valuably to this
3333d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# module.
3433d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#
35a57dccdcd473f3c8187e68348ebcd71c904be9e3Martin v. Löwis# J. David Ibanez implemented plural forms. Bruno Haible fixed some bugs.
36d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis#
3733d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# TODO:
3833d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# - Lazy loading of .mo files.  Currently the entire catalog is loaded into
3933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#   memory, but that's probably bad for large translated programs.  Instead,
4033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#   the lexical sort of original strings in GNU .mo files should be exploited
4133d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#   to do binary searches and lazy initializations.  Or you might want to use
4233d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#   the undocumented double-hash algorithm for .mo files with hash tables, but
4333d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#   you'll need to study the GNU gettext code to do this.
4433d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#
4533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# - Support Solaris .mo file formats.  Unfortunately, we've been unable to
4633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#   find this format documented anywhere.
4795be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
48d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
497bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyerimport locale, copy, os, re, struct, sys
5033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsawfrom errno import ENOENT
5195be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
52d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
53a1ce93f87cb221be9a7466c2f9d2b7dc494f458dBarry Warsaw__all__ = ['NullTranslations', 'GNUTranslations', 'Catalog',
54a1ce93f87cb221be9a7466c2f9d2b7dc494f458dBarry Warsaw           'find', 'translation', 'install', 'textdomain', 'bindtextdomain',
552ca7bb0ba20b6e508625733a75a1c6aae61983bfAndrew Kuchling           'bind_textdomain_codeset',
562ca7bb0ba20b6e508625733a75a1c6aae61983bfAndrew Kuchling           'dgettext', 'dngettext', 'gettext', 'lgettext', 'ldgettext',
572ca7bb0ba20b6e508625733a75a1c6aae61983bfAndrew Kuchling           'ldngettext', 'lngettext', 'ngettext',
58a1ce93f87cb221be9a7466c2f9d2b7dc494f458dBarry Warsaw           ]
592dd4276559658e06eaf8b623c2c47373f1f50688Skip Montanaro
6033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw_default_localedir = os.path.join(sys.prefix, 'share', 'locale')
6195be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
62a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka# Expression parsing for plural form selection.
63a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka#
64a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka# The gettext library supports a small subset of C syntax.  The only
65a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka# incompatible difference is that integer literals starting with zero are
66a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka# decimal.
67a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka#
68a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka# https://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms
69a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka# http://git.savannah.gnu.org/cgit/gettext.git/tree/gettext-runtime/intl/plural.y
70a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
71a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka_token_pattern = re.compile(r"""
72a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        (?P<WHITESPACES>[ \t]+)                    | # spaces and horizontal tabs
73a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        (?P<NUMBER>[0-9]+\b)                       | # decimal integer
74a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        (?P<NAME>n\b)                              | # only n is allowed
75a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        (?P<PARENTHESIS>[()])                      |
76a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        (?P<OPERATOR>[-*/%+?:]|[><!]=?|==|&&|\|\|) | # !, *, /, %, +, -, <, >,
77a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                                                     # <=, >=, ==, !=, &&, ||,
78a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                                                     # ? :
79a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                                                     # unary and bitwise ops
80a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                                                     # not allowed
81a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        (?P<INVALID>\w+|.)                           # invalid token
82a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    """, re.VERBOSE|re.DOTALL)
83a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
84a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchakadef _tokenize(plural):
85a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    for mo in re.finditer(_token_pattern, plural):
86a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        kind = mo.lastgroup
87a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        if kind == 'WHITESPACES':
88a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            continue
89a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        value = mo.group(kind)
90a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        if kind == 'INVALID':
91a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            raise ValueError('invalid token in plural form: %s' % value)
92a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        yield value
93a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    yield ''
94a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
95a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchakadef _error(value):
96a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    if value:
97a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        return ValueError('unexpected token in plural form: %s' % value)
98d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis    else:
99a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        return ValueError('unexpected end of plural form')
100a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
101a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka_binary_ops = (
102a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    ('||',),
103a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    ('&&',),
104a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    ('==', '!='),
105a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    ('<', '>', '<=', '>='),
106a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    ('+', '-'),
107a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    ('*', '/', '%'),
108a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka)
109a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka_binary_ops = {op: i for i, ops in enumerate(_binary_ops, 1) for op in ops}
110a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka_c2py_ops = {'||': 'or', '&&': 'and', '/': '//'}
111a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
112a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchakadef _parse(tokens, priority=-1):
113a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    result = ''
114a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    nexttok = next(tokens)
115a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    while nexttok == '!':
116a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        result += 'not '
117a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        nexttok = next(tokens)
118a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
119a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    if nexttok == '(':
120a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        sub, nexttok = _parse(tokens)
121a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        result = '%s(%s)' % (result, sub)
122a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        if nexttok != ')':
123a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            raise ValueError('unbalanced parenthesis in plural form')
124a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    elif nexttok == 'n':
125a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        result = '%s%s' % (result, nexttok)
126a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    else:
127a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        try:
128a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            value = int(nexttok, 10)
129a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        except ValueError:
130a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            raise _error(nexttok)
131a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        result = '%s%d' % (result, value)
132a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    nexttok = next(tokens)
133a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
134a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    j = 100
135a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    while nexttok in _binary_ops:
136a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        i = _binary_ops[nexttok]
137a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        if i < priority:
138a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            break
139a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        # Break chained comparisons
140a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        if i in (3, 4) and j in (3, 4):  # '==', '!=', '<', '>', '<=', '>='
141a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            result = '(%s)' % result
142a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        # Replace some C operators by their Python equivalents
143a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        op = _c2py_ops.get(nexttok, nexttok)
144a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        right, nexttok = _parse(tokens, i + 1)
145a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        result = '%s %s %s' % (result, op, right)
146a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        j = i
147a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    if j == priority == 4:  # '<', '>', '<=', '>='
148a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        result = '(%s)' % result
149a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
150a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    if nexttok == '?' and priority <= 0:
151a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        if_true, nexttok = _parse(tokens, 0)
152a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        if nexttok != ':':
153a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            raise _error(nexttok)
154a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        if_false, nexttok = _parse(tokens)
155a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        result = '%s if %s else %s' % (if_true, result, if_false)
156a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        if priority == 0:
157a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            result = '(%s)' % result
158a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
159a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    return result, nexttok
160d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
161d3e6c9e4dcf3fd4e97a2526164cc7e28766b8e6dSerhiy Storchakadef _as_int(n):
162d3e6c9e4dcf3fd4e97a2526164cc7e28766b8e6dSerhiy Storchaka    try:
163d3e6c9e4dcf3fd4e97a2526164cc7e28766b8e6dSerhiy Storchaka        i = round(n)
164d3e6c9e4dcf3fd4e97a2526164cc7e28766b8e6dSerhiy Storchaka    except TypeError:
165d3e6c9e4dcf3fd4e97a2526164cc7e28766b8e6dSerhiy Storchaka        raise TypeError('Plural value must be an integer, got %s' %
166d3e6c9e4dcf3fd4e97a2526164cc7e28766b8e6dSerhiy Storchaka                        (n.__class__.__name__,))
167d3e6c9e4dcf3fd4e97a2526164cc7e28766b8e6dSerhiy Storchaka    return n
168d3e6c9e4dcf3fd4e97a2526164cc7e28766b8e6dSerhiy Storchaka
169d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwisdef c2py(plural):
170c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw    """Gets a C expression as used in PO files for plural forms and returns a
171a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    Python function that implements an equivalent expression.
172d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis    """
173d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
174a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    if len(plural) > 1000:
175a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        raise ValueError('plural form expression is too long')
176a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    try:
177a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        result, nexttok = _parse(_tokenize(plural))
178a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        if nexttok:
179a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            raise _error(nexttok)
180a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
181a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        depth = 0
182a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        for c in result:
183a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            if c == '(':
184a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                depth += 1
185a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                if depth > 20:
186a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                    # Python compiler limit is about 90.
187a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                    # The most complex example has 2.
188a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                    raise ValueError('plural form expression is too complex')
189a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            elif c == ')':
190a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                depth -= 1
191a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka
192d3e6c9e4dcf3fd4e97a2526164cc7e28766b8e6dSerhiy Storchaka        ns = {'_as_int': _as_int}
193a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        exec('''if 1:
194a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            def func(n):
195a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                if not isinstance(n, int):
196d3e6c9e4dcf3fd4e97a2526164cc7e28766b8e6dSerhiy Storchaka                    n = _as_int(n)
197a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka                return int(%s)
198a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka            ''' % result, ns)
199a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        return ns['func']
200a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka    except RuntimeError:
201a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        # Recursion error can be raised in _parse() or exec().
202a8760275bd59fb8d8be1f1bf05313fed31c08321Serhiy Storchaka        raise ValueError('plural form expression is too complex')
203d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
20407e99cb77406e1bc84606f49b743e41b0de8a6d5Tim Peters
205fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsawdef _expand_lang(locale):
206fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    from locale import normalize
207fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    locale = normalize(locale)
208fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    COMPONENT_CODESET   = 1 << 0
209fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    COMPONENT_TERRITORY = 1 << 1
210fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    COMPONENT_MODIFIER  = 1 << 2
211fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    # split up the locale into its base components
212fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    mask = 0
213fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    pos = locale.find('@')
214fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    if pos >= 0:
215fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        modifier = locale[pos:]
216fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        locale = locale[:pos]
217fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        mask |= COMPONENT_MODIFIER
218fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    else:
219fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        modifier = ''
220fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    pos = locale.find('.')
221fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    if pos >= 0:
222fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        codeset = locale[pos:]
223fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        locale = locale[:pos]
224fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        mask |= COMPONENT_CODESET
225fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    else:
226fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        codeset = ''
227fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    pos = locale.find('_')
228fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    if pos >= 0:
229fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        territory = locale[pos:]
230fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        locale = locale[:pos]
231fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        mask |= COMPONENT_TERRITORY
232fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    else:
233fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        territory = ''
234fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    language = locale
235fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    ret = []
236fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    for i in range(mask+1):
237fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        if not (i & ~mask):  # if all components for this combo exist ...
238fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw            val = language
239fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw            if i & COMPONENT_TERRITORY: val += territory
240fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw            if i & COMPONENT_CODESET:   val += codeset
241fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw            if i & COMPONENT_MODIFIER:  val += modifier
242fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw            ret.append(val)
243fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    ret.reverse()
244fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    return ret
245fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw
246fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw
24707e99cb77406e1bc84606f49b743e41b0de8a6d5Tim Peters
24833d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsawclass NullTranslations:
24933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    def __init__(self, fp=None):
25033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        self._info = {}
2516008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw        self._charset = None
2527bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        self._output_charset = None
253a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        self._fallback = None
254094662a16542d1cc56713b35dde39089490dec35Raymond Hettinger        if fp is not None:
25533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw            self._parse(fp)
25695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
25733d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    def _parse(self, fp):
25833d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        pass
25933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
260a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis    def add_fallback(self, fallback):
261a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        if self._fallback:
262a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            self._fallback.add_fallback(fallback)
263a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        else:
264a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            self._fallback = fallback
265a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis
26633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    def gettext(self, message):
267a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        if self._fallback:
268a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            return self._fallback.gettext(message)
26933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        return message
27033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
2717bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    def lgettext(self, message):
2727bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        if self._fallback:
2737bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            return self._fallback.lgettext(message)
2747bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        return message
2757bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer
276d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis    def ngettext(self, msgid1, msgid2, n):
277d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        if self._fallback:
278d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            return self._fallback.ngettext(msgid1, msgid2, n)
279d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        if n == 1:
280d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            return msgid1
281d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        else:
282d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            return msgid2
283d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
2847bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    def lngettext(self, msgid1, msgid2, n):
2857bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        if self._fallback:
2867bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            return self._fallback.lngettext(msgid1, msgid2, n)
2877bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        if n == 1:
2887bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            return msgid1
2897bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        else:
2907bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            return msgid2
2917bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer
29233d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    def ugettext(self, message):
293a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        if self._fallback:
294a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            return self._fallback.ugettext(message)
29533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        return unicode(message)
29633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
297d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis    def ungettext(self, msgid1, msgid2, n):
298d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        if self._fallback:
299d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            return self._fallback.ungettext(msgid1, msgid2, n)
300d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        if n == 1:
301d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            return unicode(msgid1)
302d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        else:
303d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            return unicode(msgid2)
304d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
30533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    def info(self):
30633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        return self._info
30733d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
30833d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    def charset(self):
30933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        return self._charset
31033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
3117bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    def output_charset(self):
3127bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        return self._output_charset
3137bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer
3147bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    def set_output_charset(self, charset):
3157bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        self._output_charset = charset
3167bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer
317602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl    def install(self, unicode=False, names=None):
31833d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        import __builtin__
31933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        __builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext
320602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl        if hasattr(names, "__contains__"):
321602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl            if "gettext" in names:
322602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl                __builtin__.__dict__['gettext'] = __builtin__.__dict__['_']
323602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl            if "ngettext" in names:
324602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl                __builtin__.__dict__['ngettext'] = (unicode and self.ungettext
325602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl                                                             or self.ngettext)
326602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl            if "lgettext" in names:
327602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl                __builtin__.__dict__['lgettext'] = self.lgettext
328602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl            if "lngettext" in names:
329602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl                __builtin__.__dict__['lngettext'] = self.lngettext
33033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
33133d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
33233d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsawclass GNUTranslations(NullTranslations):
33333d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    # Magic number of .mo files
33409707e363723fa24ba53e4e5d77cc26d4dea724fBarry Warsaw    LE_MAGIC = 0x950412deL
33509707e363723fa24ba53e4e5d77cc26d4dea724fBarry Warsaw    BE_MAGIC = 0xde120495L
33695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
33795be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw    def _parse(self, fp):
33895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        """Override this method to support alternative .mo formats."""
33995be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        unpack = struct.unpack
34095be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        filename = getattr(fp, 'name', '')
34195be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        # Parse the .mo file header, which consists of 5 little endian 32
34295be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        # bit words.
34333d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        self._catalog = catalog = {}
344a57dccdcd473f3c8187e68348ebcd71c904be9e3Martin v. Löwis        self.plural = lambda n: int(n != 1) # germanic plural by default
34595be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        buf = fp.read()
3469a2d9d7f04f2ed08574c670bb5ee8ead2a4048ceBarry Warsaw        buflen = len(buf)
34733d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        # Are we big endian or little endian?
34809707e363723fa24ba53e4e5d77cc26d4dea724fBarry Warsaw        magic = unpack('<I', buf[:4])[0]
34933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        if magic == self.LE_MAGIC:
35009707e363723fa24ba53e4e5d77cc26d4dea724fBarry Warsaw            version, msgcount, masteridx, transidx = unpack('<4I', buf[4:20])
35109707e363723fa24ba53e4e5d77cc26d4dea724fBarry Warsaw            ii = '<II'
35233d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        elif magic == self.BE_MAGIC:
35309707e363723fa24ba53e4e5d77cc26d4dea724fBarry Warsaw            version, msgcount, masteridx, transidx = unpack('>4I', buf[4:20])
35409707e363723fa24ba53e4e5d77cc26d4dea724fBarry Warsaw            ii = '>II'
35533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        else:
35695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw            raise IOError(0, 'Bad magic number', filename)
35795be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        # Now put all messages from the .mo file buffer into the catalog
35895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        # dictionary.
35995be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        for i in xrange(0, msgcount):
36033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw            mlen, moff = unpack(ii, buf[masteridx:masteridx+8])
36109707e363723fa24ba53e4e5d77cc26d4dea724fBarry Warsaw            mend = moff + mlen
36233d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw            tlen, toff = unpack(ii, buf[transidx:transidx+8])
36309707e363723fa24ba53e4e5d77cc26d4dea724fBarry Warsaw            tend = toff + tlen
3649a2d9d7f04f2ed08574c670bb5ee8ead2a4048ceBarry Warsaw            if mend < buflen and tend < buflen:
365d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis                msg = buf[moff:mend]
36633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw                tmsg = buf[toff:tend]
36795be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw            else:
36895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw                raise IOError(0, 'File is corrupt', filename)
36933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw            # See if we're looking at GNU .mo conventions for metadata
3706008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw            if mlen == 0:
37133d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw                # Catalog description
372270b0586b0c13588ed6ffd86581f11235bb4d22aAndrew Kuchling                lastk = None
373a1ce93f87cb221be9a7466c2f9d2b7dc494f458dBarry Warsaw                for item in tmsg.splitlines():
37433d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw                    item = item.strip()
37533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw                    if not item:
37633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw                        continue
377270b0586b0c13588ed6ffd86581f11235bb4d22aAndrew Kuchling                    k = v = None
3787de63f57c881cb94ba89c2a1dfaf79af1bba52bbBarry Warsaw                    if ':' in item:
3797de63f57c881cb94ba89c2a1dfaf79af1bba52bbBarry Warsaw                        k, v = item.split(':', 1)
3807de63f57c881cb94ba89c2a1dfaf79af1bba52bbBarry Warsaw                        k = k.strip().lower()
3817de63f57c881cb94ba89c2a1dfaf79af1bba52bbBarry Warsaw                        v = v.strip()
3827de63f57c881cb94ba89c2a1dfaf79af1bba52bbBarry Warsaw                        self._info[k] = v
3837de63f57c881cb94ba89c2a1dfaf79af1bba52bbBarry Warsaw                        lastk = k
3847de63f57c881cb94ba89c2a1dfaf79af1bba52bbBarry Warsaw                    elif lastk:
3857de63f57c881cb94ba89c2a1dfaf79af1bba52bbBarry Warsaw                        self._info[lastk] += '\n' + item
38633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw                    if k == 'content-type':
38733d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw                        self._charset = v.split('charset=')[1]
388d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis                    elif k == 'plural-forms':
389d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis                        v = v.split(';')
390d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis                        plural = v[1].split('plural=')[1]
391d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis                        self.plural = c2py(plural)
392c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            # Note: we unconditionally convert both msgids and msgstrs to
393c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            # Unicode using the character encoding specified in the charset
394c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            # parameter of the Content-Type header.  The gettext documentation
39524b07bcba350bb86c4d6ca446e1564647a199868Ezio Melotti            # strongly encourages msgids to be us-ascii, but some applications
396c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            # require alternative encodings (e.g. Zope's ZCML and ZPT).  For
397c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            # traditional gettext applications, the msgid conversion will
398c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            # cause no problems since us-ascii should always be a subset of
399c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            # the charset encoding.  We may want to fall back to 8-bit msgids
400c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            # if the Unicode conversion fails.
401bac788a3cd348203a5fdabba52e5faf65bf35c5eRaymond Hettinger            if '\x00' in msg:
4026008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw                # Plural forms
4036008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw                msgid1, msgid2 = msg.split('\x00')
4046008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw                tmsg = tmsg.split('\x00')
405c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw                if self._charset:
4066008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw                    msgid1 = unicode(msgid1, self._charset)
4076008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw                    tmsg = [unicode(x, self._charset) for x in tmsg]
4086008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw                for i in range(len(tmsg)):
4096008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw                    catalog[(msgid1, i)] = tmsg[i]
4106008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw            else:
411c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw                if self._charset:
4126008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw                    msg = unicode(msg, self._charset)
4136008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw                    tmsg = unicode(tmsg, self._charset)
4146008cbd4c2b5b4f5e9941f312804e12c88aa6efeBarry Warsaw                catalog[msg] = tmsg
41595be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw            # advance to next entry in the seek tables
416fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw            masteridx += 8
417fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw            transidx += 8
41895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
41933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    def gettext(self, message):
420c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw        missing = object()
421c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw        tmsg = self._catalog.get(message, missing)
422c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw        if tmsg is missing:
423a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            if self._fallback:
424a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis                return self._fallback.gettext(message)
425a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            return message
426c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw        # Encode the Unicode tmsg back to an 8-bit string, if possible
4277bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        if self._output_charset:
4287bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            return tmsg.encode(self._output_charset)
4297bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        elif self._charset:
430c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            return tmsg.encode(self._charset)
431c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw        return tmsg
43233d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
4337bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    def lgettext(self, message):
4347bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        missing = object()
4357bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        tmsg = self._catalog.get(message, missing)
4367bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        if tmsg is missing:
4377bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            if self._fallback:
4387bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                return self._fallback.lgettext(message)
4397bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            return message
4407bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        if self._output_charset:
4417bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            return tmsg.encode(self._output_charset)
4427bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        return tmsg.encode(locale.getpreferredencoding())
4437bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer
444d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis    def ngettext(self, msgid1, msgid2, n):
445d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        try:
446c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            tmsg = self._catalog[(msgid1, self.plural(n))]
4477bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            if self._output_charset:
4487bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                return tmsg.encode(self._output_charset)
4497bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            elif self._charset:
450c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw                return tmsg.encode(self._charset)
451c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            return tmsg
452d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        except KeyError:
453d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            if self._fallback:
454d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis                return self._fallback.ngettext(msgid1, msgid2, n)
455d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            if n == 1:
456d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis                return msgid1
457d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            else:
458d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis                return msgid2
459d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
4607bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    def lngettext(self, msgid1, msgid2, n):
4617bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        try:
4627bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            tmsg = self._catalog[(msgid1, self.plural(n))]
4637bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            if self._output_charset:
4647bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                return tmsg.encode(self._output_charset)
4657bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            return tmsg.encode(locale.getpreferredencoding())
4667bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        except KeyError:
4677bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            if self._fallback:
4687bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                return self._fallback.lngettext(msgid1, msgid2, n)
4697bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            if n == 1:
4707bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                return msgid1
4717bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            else:
4727bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                return msgid2
4737bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer
47433d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    def ugettext(self, message):
475a1ce93f87cb221be9a7466c2f9d2b7dc494f458dBarry Warsaw        missing = object()
476a1ce93f87cb221be9a7466c2f9d2b7dc494f458dBarry Warsaw        tmsg = self._catalog.get(message, missing)
477a1ce93f87cb221be9a7466c2f9d2b7dc494f458dBarry Warsaw        if tmsg is missing:
478a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            if self._fallback:
479a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis                return self._fallback.ugettext(message)
480c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw            return unicode(message)
481a1ce93f87cb221be9a7466c2f9d2b7dc494f458dBarry Warsaw        return tmsg
48295be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
483d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis    def ungettext(self, msgid1, msgid2, n):
484d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        try:
485d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            tmsg = self._catalog[(msgid1, self.plural(n))]
486d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        except KeyError:
487d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            if self._fallback:
488d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis                return self._fallback.ungettext(msgid1, msgid2, n)
489d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            if n == 1:
490c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw                tmsg = unicode(msgid1)
491d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            else:
492c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw                tmsg = unicode(msgid2)
493a1ce93f87cb221be9a7466c2f9d2b7dc494f458dBarry Warsaw        return tmsg
494d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
49507e99cb77406e1bc84606f49b743e41b0de8a6d5Tim Peters
49695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw# Locate a .mo file using the gettext strategy
497a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwisdef find(domain, localedir=None, languages=None, all=0):
49895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw    # Get some reasonable defaults for arguments that were not supplied
49995be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw    if localedir is None:
50033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        localedir = _default_localedir
50195be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw    if languages is None:
50295be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        languages = []
50395be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
50495be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw            val = os.environ.get(envar)
50595be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw            if val:
50695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw                languages = val.split(':')
50795be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw                break
50895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        if 'C' not in languages:
50995be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw            languages.append('C')
510fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    # now normalize and expand the languages
51175f8101c429e132472bba92f3972b7eac68f35f8Barry Warsaw    nelangs = []
512fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw    for lang in languages:
513fa488ec2a0c60dc6a90d1be7b58d0a277db97b41Barry Warsaw        for nelang in _expand_lang(lang):
51475f8101c429e132472bba92f3972b7eac68f35f8Barry Warsaw            if nelang not in nelangs:
51575f8101c429e132472bba92f3972b7eac68f35f8Barry Warsaw                nelangs.append(nelang)
51695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw    # select a language
517a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis    if all:
518a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        result = []
519a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis    else:
520a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        result = None
52175f8101c429e132472bba92f3972b7eac68f35f8Barry Warsaw    for lang in nelangs:
52295be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        if lang == 'C':
52395be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw            break
52484314b72d65471b0ae68432aefe36a4691e3b04eBarry Warsaw        mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain)
52533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        if os.path.exists(mofile):
526a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            if all:
527a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis                result.append(mofile)
528a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            else:
529a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis                return mofile
530a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis    return result
53195be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
53295be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
53307e99cb77406e1bc84606f49b743e41b0de8a6d5Tim Peters
53433d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# a mapping between absolute .mo file path and Translation object
53533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw_translations = {}
53633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
5371be641987145f88d61e52d5b4714a8cc6c7e6da8Martin v. Löwisdef translation(domain, localedir=None, languages=None,
5387bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                class_=None, fallback=False, codeset=None):
53933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    if class_ is None:
54033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        class_ = GNUTranslations
541a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis    mofiles = find(domain, localedir, languages, all=1)
542c4acc2bd32f14c22f9cd7a95021184809e096ebeBarry Warsaw    if not mofiles:
5431be641987145f88d61e52d5b4714a8cc6c7e6da8Martin v. Löwis        if fallback:
5441be641987145f88d61e52d5b4714a8cc6c7e6da8Martin v. Löwis            return NullTranslations()
54533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        raise IOError(ENOENT, 'No translation file found for domain', domain)
546293b03f73f5ad48719a0f07fb45857182dfb1d51Barry Warsaw    # Avoid opening, reading, and parsing the .mo file after it's been done
547293b03f73f5ad48719a0f07fb45857182dfb1d51Barry Warsaw    # once.
548a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis    result = None
549a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis    for mofile in mofiles:
550c17776f96e8654dc9510daf55d90987be2e4dec2Éric Araujo        key = (class_, os.path.abspath(mofile))
551a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        t = _translations.get(key)
552a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        if t is None:
55314c7bc2ad22f4fffdcf2b3d58b1358846a53079fBenjamin Peterson            with open(mofile, 'rb') as fp:
55414c7bc2ad22f4fffdcf2b3d58b1358846a53079fBenjamin Peterson                t = _translations.setdefault(key, class_(fp))
5557bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        # Copy the translation object to allow setting fallbacks and
5567bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        # output charset. All other instance data is shared with the
5577bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        # cached object.
558a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        t = copy.copy(t)
5597bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        if codeset:
5607bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            t.set_output_charset(codeset)
561a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        if result is None:
562a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            result = t
563a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis        else:
564a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis            result.add_fallback(t)
565a55ffaeee9eb2fba62f280c364b3460fafd9efc9Martin v. Löwis    return result
56633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
56707e99cb77406e1bc84606f49b743e41b0de8a6d5Tim Peters
568602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandldef install(domain, localedir=None, unicode=False, codeset=None, names=None):
5697bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    t = translation(domain, localedir, fallback=True, codeset=codeset)
570602b9ba6b37c4ac4ed445f8c9e9dccd68d631899Georg Brandl    t.install(unicode, names)
57133d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
57233d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw
57307e99cb77406e1bc84606f49b743e41b0de8a6d5Tim Peters
57433d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# a mapping b/w domains and locale directories
57533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw_localedirs = {}
5767bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer# a mapping b/w domains and codesets
5777bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer_localecodesets = {}
57833d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# current global domain, `messages' used for compatibility w/ GNU gettext
57933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw_current_domain = 'messages'
58095be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
58195be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
58295be23dc8626684caa268df7ff7749fcb89adddfBarry Warsawdef textdomain(domain=None):
58395be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw    global _current_domain
58433d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    if domain is not None:
58595be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw        _current_domain = domain
58633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    return _current_domain
58795be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
58895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
58933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsawdef bindtextdomain(domain, localedir=None):
59033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    global _localedirs
59133d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    if localedir is not None:
59233d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        _localedirs[domain] = localedir
59333d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    return _localedirs.get(domain, _default_localedir)
59495be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
59595be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
5967bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyerdef bind_textdomain_codeset(domain, codeset=None):
5977bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    global _localecodesets
5987bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    if codeset is not None:
5997bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        _localecodesets[domain] = codeset
6007bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    return _localecodesets.get(domain)
6017bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer
6027bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer
60395be23dc8626684caa268df7ff7749fcb89adddfBarry Warsawdef dgettext(domain, message):
60433d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    try:
6057bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        t = translation(domain, _localedirs.get(domain, None),
6067bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                        codeset=_localecodesets.get(domain))
60733d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    except IOError:
60833d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw        return message
60933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    return t.gettext(message)
61007e99cb77406e1bc84606f49b743e41b0de8a6d5Tim Peters
6117bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyerdef ldgettext(domain, message):
6127bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    try:
6137bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        t = translation(domain, _localedirs.get(domain, None),
6147bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                        codeset=_localecodesets.get(domain))
6157bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    except IOError:
6167bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        return message
6177bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    return t.lgettext(message)
61895be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
619d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwisdef dngettext(domain, msgid1, msgid2, n):
620d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis    try:
6217bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        t = translation(domain, _localedirs.get(domain, None),
6227bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                        codeset=_localecodesets.get(domain))
623d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis    except IOError:
624d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        if n == 1:
625d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            return msgid1
626d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis        else:
627d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis            return msgid2
628d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis    return t.ngettext(msgid1, msgid2, n)
629d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
6307bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyerdef ldngettext(domain, msgid1, msgid2, n):
6317bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    try:
6327bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        t = translation(domain, _localedirs.get(domain, None),
6337bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer                        codeset=_localecodesets.get(domain))
6347bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    except IOError:
6357bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        if n == 1:
6367bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            return msgid1
6377bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer        else:
6387bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer            return msgid2
6397bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    return t.lngettext(msgid1, msgid2, n)
640d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
64133d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsawdef gettext(message):
64233d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw    return dgettext(_current_domain, message)
64395be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
6447bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyerdef lgettext(message):
6457bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    return ldgettext(_current_domain, message)
64695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
647d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwisdef ngettext(msgid1, msgid2, n):
648d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis    return dngettext(_current_domain, msgid1, msgid2, n)
649d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
6507bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyerdef lngettext(msgid1, msgid2, n):
6517bd33c5e22c67e06042fb7ab1186f7587d78153dGustavo Niemeyer    return ldngettext(_current_domain, msgid1, msgid2, n)
652d899605e30eef8e77f70184eac15fad1bf770586Martin v. Löwis
65333d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# dcgettext() has been deemed unnecessary and is not implemented.
65495be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
65533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# James Henstridge's Catalog constructor from GNOME gettext.  Documented usage
65633d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# was:
65733d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#
65833d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#    import gettext
65933d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#    cat = gettext.Catalog(PACKAGE, localedir=LOCALEDIR)
66033d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#    _ = cat.gettext
66133d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw#    print _('Hello World')
66295be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
66333d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# The resulting catalog object currently don't support access through a
66433d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# dictionary API, which was supported (but apparently unused) in GNOME
66533d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry Warsaw# gettext.
66695be23dc8626684caa268df7ff7749fcb89adddfBarry Warsaw
66733d8d705b88ca6fb227d75d9b7f5bf8efda1d0a7Barry WarsawCatalog = translation
668