xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/gettext.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""Internationalization and localization support.
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard WorkerThis module provides internationalization (I18N) and localization (L10N)
4*cda5da8dSAndroid Build Coastguard Workersupport for your Python programs by providing an interface to the GNU gettext
5*cda5da8dSAndroid Build Coastguard Workermessage catalog library.
6*cda5da8dSAndroid Build Coastguard Worker
7*cda5da8dSAndroid Build Coastguard WorkerI18N refers to the operation by which a program is made aware of multiple
8*cda5da8dSAndroid Build Coastguard Workerlanguages.  L10N refers to the adaptation of your program, once
9*cda5da8dSAndroid Build Coastguard Workerinternationalized, to the local language and cultural habits.
10*cda5da8dSAndroid Build Coastguard Worker
11*cda5da8dSAndroid Build Coastguard Worker"""
12*cda5da8dSAndroid Build Coastguard Worker
13*cda5da8dSAndroid Build Coastguard Worker# This module represents the integration of work, contributions, feedback, and
14*cda5da8dSAndroid Build Coastguard Worker# suggestions from the following people:
15*cda5da8dSAndroid Build Coastguard Worker#
16*cda5da8dSAndroid Build Coastguard Worker# Martin von Loewis, who wrote the initial implementation of the underlying
17*cda5da8dSAndroid Build Coastguard Worker# C-based libintlmodule (later renamed _gettext), along with a skeletal
18*cda5da8dSAndroid Build Coastguard Worker# gettext.py implementation.
19*cda5da8dSAndroid Build Coastguard Worker#
20*cda5da8dSAndroid Build Coastguard Worker# Peter Funk, who wrote fintl.py, a fairly complete wrapper around intlmodule,
21*cda5da8dSAndroid Build Coastguard Worker# which also included a pure-Python implementation to read .mo files if
22*cda5da8dSAndroid Build Coastguard Worker# intlmodule wasn't available.
23*cda5da8dSAndroid Build Coastguard Worker#
24*cda5da8dSAndroid Build Coastguard Worker# James Henstridge, who also wrote a gettext.py module, which has some
25*cda5da8dSAndroid Build Coastguard Worker# interesting, but currently unsupported experimental features: the notion of
26*cda5da8dSAndroid Build Coastguard Worker# a Catalog class and instances, and the ability to add to a catalog file via
27*cda5da8dSAndroid Build Coastguard Worker# a Python API.
28*cda5da8dSAndroid Build Coastguard Worker#
29*cda5da8dSAndroid Build Coastguard Worker# Barry Warsaw integrated these modules, wrote the .install() API and code,
30*cda5da8dSAndroid Build Coastguard Worker# and conformed all C and Python code to Python's coding standards.
31*cda5da8dSAndroid Build Coastguard Worker#
32*cda5da8dSAndroid Build Coastguard Worker# Francois Pinard and Marc-Andre Lemburg also contributed valuably to this
33*cda5da8dSAndroid Build Coastguard Worker# module.
34*cda5da8dSAndroid Build Coastguard Worker#
35*cda5da8dSAndroid Build Coastguard Worker# J. David Ibanez implemented plural forms. Bruno Haible fixed some bugs.
36*cda5da8dSAndroid Build Coastguard Worker#
37*cda5da8dSAndroid Build Coastguard Worker# TODO:
38*cda5da8dSAndroid Build Coastguard Worker# - Lazy loading of .mo files.  Currently the entire catalog is loaded into
39*cda5da8dSAndroid Build Coastguard Worker#   memory, but that's probably bad for large translated programs.  Instead,
40*cda5da8dSAndroid Build Coastguard Worker#   the lexical sort of original strings in GNU .mo files should be exploited
41*cda5da8dSAndroid Build Coastguard Worker#   to do binary searches and lazy initializations.  Or you might want to use
42*cda5da8dSAndroid Build Coastguard Worker#   the undocumented double-hash algorithm for .mo files with hash tables, but
43*cda5da8dSAndroid Build Coastguard Worker#   you'll need to study the GNU gettext code to do this.
44*cda5da8dSAndroid Build Coastguard Worker#
45*cda5da8dSAndroid Build Coastguard Worker# - Support Solaris .mo file formats.  Unfortunately, we've been unable to
46*cda5da8dSAndroid Build Coastguard Worker#   find this format documented anywhere.
47*cda5da8dSAndroid Build Coastguard Worker
48*cda5da8dSAndroid Build Coastguard Worker
49*cda5da8dSAndroid Build Coastguard Workerimport os
50*cda5da8dSAndroid Build Coastguard Workerimport re
51*cda5da8dSAndroid Build Coastguard Workerimport sys
52*cda5da8dSAndroid Build Coastguard Worker
53*cda5da8dSAndroid Build Coastguard Worker
54*cda5da8dSAndroid Build Coastguard Worker__all__ = ['NullTranslations', 'GNUTranslations', 'Catalog',
55*cda5da8dSAndroid Build Coastguard Worker           'bindtextdomain', 'find', 'translation', 'install',
56*cda5da8dSAndroid Build Coastguard Worker           'textdomain', 'dgettext', 'dngettext', 'gettext',
57*cda5da8dSAndroid Build Coastguard Worker           'ngettext', 'pgettext', 'dpgettext', 'npgettext',
58*cda5da8dSAndroid Build Coastguard Worker           'dnpgettext'
59*cda5da8dSAndroid Build Coastguard Worker           ]
60*cda5da8dSAndroid Build Coastguard Worker
61*cda5da8dSAndroid Build Coastguard Worker_default_localedir = os.path.join(sys.base_prefix, 'share', 'locale')
62*cda5da8dSAndroid Build Coastguard Worker
63*cda5da8dSAndroid Build Coastguard Worker# Expression parsing for plural form selection.
64*cda5da8dSAndroid Build Coastguard Worker#
65*cda5da8dSAndroid Build Coastguard Worker# The gettext library supports a small subset of C syntax.  The only
66*cda5da8dSAndroid Build Coastguard Worker# incompatible difference is that integer literals starting with zero are
67*cda5da8dSAndroid Build Coastguard Worker# decimal.
68*cda5da8dSAndroid Build Coastguard Worker#
69*cda5da8dSAndroid Build Coastguard Worker# https://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms
70*cda5da8dSAndroid Build Coastguard Worker# http://git.savannah.gnu.org/cgit/gettext.git/tree/gettext-runtime/intl/plural.y
71*cda5da8dSAndroid Build Coastguard Worker
72*cda5da8dSAndroid Build Coastguard Worker_token_pattern = re.compile(r"""
73*cda5da8dSAndroid Build Coastguard Worker        (?P<WHITESPACES>[ \t]+)                    | # spaces and horizontal tabs
74*cda5da8dSAndroid Build Coastguard Worker        (?P<NUMBER>[0-9]+\b)                       | # decimal integer
75*cda5da8dSAndroid Build Coastguard Worker        (?P<NAME>n\b)                              | # only n is allowed
76*cda5da8dSAndroid Build Coastguard Worker        (?P<PARENTHESIS>[()])                      |
77*cda5da8dSAndroid Build Coastguard Worker        (?P<OPERATOR>[-*/%+?:]|[><!]=?|==|&&|\|\|) | # !, *, /, %, +, -, <, >,
78*cda5da8dSAndroid Build Coastguard Worker                                                     # <=, >=, ==, !=, &&, ||,
79*cda5da8dSAndroid Build Coastguard Worker                                                     # ? :
80*cda5da8dSAndroid Build Coastguard Worker                                                     # unary and bitwise ops
81*cda5da8dSAndroid Build Coastguard Worker                                                     # not allowed
82*cda5da8dSAndroid Build Coastguard Worker        (?P<INVALID>\w+|.)                           # invalid token
83*cda5da8dSAndroid Build Coastguard Worker    """, re.VERBOSE|re.DOTALL)
84*cda5da8dSAndroid Build Coastguard Worker
85*cda5da8dSAndroid Build Coastguard Worker
86*cda5da8dSAndroid Build Coastguard Workerdef _tokenize(plural):
87*cda5da8dSAndroid Build Coastguard Worker    for mo in re.finditer(_token_pattern, plural):
88*cda5da8dSAndroid Build Coastguard Worker        kind = mo.lastgroup
89*cda5da8dSAndroid Build Coastguard Worker        if kind == 'WHITESPACES':
90*cda5da8dSAndroid Build Coastguard Worker            continue
91*cda5da8dSAndroid Build Coastguard Worker        value = mo.group(kind)
92*cda5da8dSAndroid Build Coastguard Worker        if kind == 'INVALID':
93*cda5da8dSAndroid Build Coastguard Worker            raise ValueError('invalid token in plural form: %s' % value)
94*cda5da8dSAndroid Build Coastguard Worker        yield value
95*cda5da8dSAndroid Build Coastguard Worker    yield ''
96*cda5da8dSAndroid Build Coastguard Worker
97*cda5da8dSAndroid Build Coastguard Worker
98*cda5da8dSAndroid Build Coastguard Workerdef _error(value):
99*cda5da8dSAndroid Build Coastguard Worker    if value:
100*cda5da8dSAndroid Build Coastguard Worker        return ValueError('unexpected token in plural form: %s' % value)
101*cda5da8dSAndroid Build Coastguard Worker    else:
102*cda5da8dSAndroid Build Coastguard Worker        return ValueError('unexpected end of plural form')
103*cda5da8dSAndroid Build Coastguard Worker
104*cda5da8dSAndroid Build Coastguard Worker
105*cda5da8dSAndroid Build Coastguard Worker_binary_ops = (
106*cda5da8dSAndroid Build Coastguard Worker    ('||',),
107*cda5da8dSAndroid Build Coastguard Worker    ('&&',),
108*cda5da8dSAndroid Build Coastguard Worker    ('==', '!='),
109*cda5da8dSAndroid Build Coastguard Worker    ('<', '>', '<=', '>='),
110*cda5da8dSAndroid Build Coastguard Worker    ('+', '-'),
111*cda5da8dSAndroid Build Coastguard Worker    ('*', '/', '%'),
112*cda5da8dSAndroid Build Coastguard Worker)
113*cda5da8dSAndroid Build Coastguard Worker_binary_ops = {op: i for i, ops in enumerate(_binary_ops, 1) for op in ops}
114*cda5da8dSAndroid Build Coastguard Worker_c2py_ops = {'||': 'or', '&&': 'and', '/': '//'}
115*cda5da8dSAndroid Build Coastguard Worker
116*cda5da8dSAndroid Build Coastguard Worker
117*cda5da8dSAndroid Build Coastguard Workerdef _parse(tokens, priority=-1):
118*cda5da8dSAndroid Build Coastguard Worker    result = ''
119*cda5da8dSAndroid Build Coastguard Worker    nexttok = next(tokens)
120*cda5da8dSAndroid Build Coastguard Worker    while nexttok == '!':
121*cda5da8dSAndroid Build Coastguard Worker        result += 'not '
122*cda5da8dSAndroid Build Coastguard Worker        nexttok = next(tokens)
123*cda5da8dSAndroid Build Coastguard Worker
124*cda5da8dSAndroid Build Coastguard Worker    if nexttok == '(':
125*cda5da8dSAndroid Build Coastguard Worker        sub, nexttok = _parse(tokens)
126*cda5da8dSAndroid Build Coastguard Worker        result = '%s(%s)' % (result, sub)
127*cda5da8dSAndroid Build Coastguard Worker        if nexttok != ')':
128*cda5da8dSAndroid Build Coastguard Worker            raise ValueError('unbalanced parenthesis in plural form')
129*cda5da8dSAndroid Build Coastguard Worker    elif nexttok == 'n':
130*cda5da8dSAndroid Build Coastguard Worker        result = '%s%s' % (result, nexttok)
131*cda5da8dSAndroid Build Coastguard Worker    else:
132*cda5da8dSAndroid Build Coastguard Worker        try:
133*cda5da8dSAndroid Build Coastguard Worker            value = int(nexttok, 10)
134*cda5da8dSAndroid Build Coastguard Worker        except ValueError:
135*cda5da8dSAndroid Build Coastguard Worker            raise _error(nexttok) from None
136*cda5da8dSAndroid Build Coastguard Worker        result = '%s%d' % (result, value)
137*cda5da8dSAndroid Build Coastguard Worker    nexttok = next(tokens)
138*cda5da8dSAndroid Build Coastguard Worker
139*cda5da8dSAndroid Build Coastguard Worker    j = 100
140*cda5da8dSAndroid Build Coastguard Worker    while nexttok in _binary_ops:
141*cda5da8dSAndroid Build Coastguard Worker        i = _binary_ops[nexttok]
142*cda5da8dSAndroid Build Coastguard Worker        if i < priority:
143*cda5da8dSAndroid Build Coastguard Worker            break
144*cda5da8dSAndroid Build Coastguard Worker        # Break chained comparisons
145*cda5da8dSAndroid Build Coastguard Worker        if i in (3, 4) and j in (3, 4):  # '==', '!=', '<', '>', '<=', '>='
146*cda5da8dSAndroid Build Coastguard Worker            result = '(%s)' % result
147*cda5da8dSAndroid Build Coastguard Worker        # Replace some C operators by their Python equivalents
148*cda5da8dSAndroid Build Coastguard Worker        op = _c2py_ops.get(nexttok, nexttok)
149*cda5da8dSAndroid Build Coastguard Worker        right, nexttok = _parse(tokens, i + 1)
150*cda5da8dSAndroid Build Coastguard Worker        result = '%s %s %s' % (result, op, right)
151*cda5da8dSAndroid Build Coastguard Worker        j = i
152*cda5da8dSAndroid Build Coastguard Worker    if j == priority == 4:  # '<', '>', '<=', '>='
153*cda5da8dSAndroid Build Coastguard Worker        result = '(%s)' % result
154*cda5da8dSAndroid Build Coastguard Worker
155*cda5da8dSAndroid Build Coastguard Worker    if nexttok == '?' and priority <= 0:
156*cda5da8dSAndroid Build Coastguard Worker        if_true, nexttok = _parse(tokens, 0)
157*cda5da8dSAndroid Build Coastguard Worker        if nexttok != ':':
158*cda5da8dSAndroid Build Coastguard Worker            raise _error(nexttok)
159*cda5da8dSAndroid Build Coastguard Worker        if_false, nexttok = _parse(tokens)
160*cda5da8dSAndroid Build Coastguard Worker        result = '%s if %s else %s' % (if_true, result, if_false)
161*cda5da8dSAndroid Build Coastguard Worker        if priority == 0:
162*cda5da8dSAndroid Build Coastguard Worker            result = '(%s)' % result
163*cda5da8dSAndroid Build Coastguard Worker
164*cda5da8dSAndroid Build Coastguard Worker    return result, nexttok
165*cda5da8dSAndroid Build Coastguard Worker
166*cda5da8dSAndroid Build Coastguard Worker
167*cda5da8dSAndroid Build Coastguard Workerdef _as_int(n):
168*cda5da8dSAndroid Build Coastguard Worker    try:
169*cda5da8dSAndroid Build Coastguard Worker        i = round(n)
170*cda5da8dSAndroid Build Coastguard Worker    except TypeError:
171*cda5da8dSAndroid Build Coastguard Worker        raise TypeError('Plural value must be an integer, got %s' %
172*cda5da8dSAndroid Build Coastguard Worker                        (n.__class__.__name__,)) from None
173*cda5da8dSAndroid Build Coastguard Worker    import warnings
174*cda5da8dSAndroid Build Coastguard Worker    warnings.warn('Plural value must be an integer, got %s' %
175*cda5da8dSAndroid Build Coastguard Worker                  (n.__class__.__name__,),
176*cda5da8dSAndroid Build Coastguard Worker                  DeprecationWarning, 4)
177*cda5da8dSAndroid Build Coastguard Worker    return n
178*cda5da8dSAndroid Build Coastguard Worker
179*cda5da8dSAndroid Build Coastguard Worker
180*cda5da8dSAndroid Build Coastguard Workerdef c2py(plural):
181*cda5da8dSAndroid Build Coastguard Worker    """Gets a C expression as used in PO files for plural forms and returns a
182*cda5da8dSAndroid Build Coastguard Worker    Python function that implements an equivalent expression.
183*cda5da8dSAndroid Build Coastguard Worker    """
184*cda5da8dSAndroid Build Coastguard Worker
185*cda5da8dSAndroid Build Coastguard Worker    if len(plural) > 1000:
186*cda5da8dSAndroid Build Coastguard Worker        raise ValueError('plural form expression is too long')
187*cda5da8dSAndroid Build Coastguard Worker    try:
188*cda5da8dSAndroid Build Coastguard Worker        result, nexttok = _parse(_tokenize(plural))
189*cda5da8dSAndroid Build Coastguard Worker        if nexttok:
190*cda5da8dSAndroid Build Coastguard Worker            raise _error(nexttok)
191*cda5da8dSAndroid Build Coastguard Worker
192*cda5da8dSAndroid Build Coastguard Worker        depth = 0
193*cda5da8dSAndroid Build Coastguard Worker        for c in result:
194*cda5da8dSAndroid Build Coastguard Worker            if c == '(':
195*cda5da8dSAndroid Build Coastguard Worker                depth += 1
196*cda5da8dSAndroid Build Coastguard Worker                if depth > 20:
197*cda5da8dSAndroid Build Coastguard Worker                    # Python compiler limit is about 90.
198*cda5da8dSAndroid Build Coastguard Worker                    # The most complex example has 2.
199*cda5da8dSAndroid Build Coastguard Worker                    raise ValueError('plural form expression is too complex')
200*cda5da8dSAndroid Build Coastguard Worker            elif c == ')':
201*cda5da8dSAndroid Build Coastguard Worker                depth -= 1
202*cda5da8dSAndroid Build Coastguard Worker
203*cda5da8dSAndroid Build Coastguard Worker        ns = {'_as_int': _as_int}
204*cda5da8dSAndroid Build Coastguard Worker        exec('''if True:
205*cda5da8dSAndroid Build Coastguard Worker            def func(n):
206*cda5da8dSAndroid Build Coastguard Worker                if not isinstance(n, int):
207*cda5da8dSAndroid Build Coastguard Worker                    n = _as_int(n)
208*cda5da8dSAndroid Build Coastguard Worker                return int(%s)
209*cda5da8dSAndroid Build Coastguard Worker            ''' % result, ns)
210*cda5da8dSAndroid Build Coastguard Worker        return ns['func']
211*cda5da8dSAndroid Build Coastguard Worker    except RecursionError:
212*cda5da8dSAndroid Build Coastguard Worker        # Recursion error can be raised in _parse() or exec().
213*cda5da8dSAndroid Build Coastguard Worker        raise ValueError('plural form expression is too complex')
214*cda5da8dSAndroid Build Coastguard Worker
215*cda5da8dSAndroid Build Coastguard Worker
216*cda5da8dSAndroid Build Coastguard Workerdef _expand_lang(loc):
217*cda5da8dSAndroid Build Coastguard Worker    import locale
218*cda5da8dSAndroid Build Coastguard Worker    loc = locale.normalize(loc)
219*cda5da8dSAndroid Build Coastguard Worker    COMPONENT_CODESET   = 1 << 0
220*cda5da8dSAndroid Build Coastguard Worker    COMPONENT_TERRITORY = 1 << 1
221*cda5da8dSAndroid Build Coastguard Worker    COMPONENT_MODIFIER  = 1 << 2
222*cda5da8dSAndroid Build Coastguard Worker    # split up the locale into its base components
223*cda5da8dSAndroid Build Coastguard Worker    mask = 0
224*cda5da8dSAndroid Build Coastguard Worker    pos = loc.find('@')
225*cda5da8dSAndroid Build Coastguard Worker    if pos >= 0:
226*cda5da8dSAndroid Build Coastguard Worker        modifier = loc[pos:]
227*cda5da8dSAndroid Build Coastguard Worker        loc = loc[:pos]
228*cda5da8dSAndroid Build Coastguard Worker        mask |= COMPONENT_MODIFIER
229*cda5da8dSAndroid Build Coastguard Worker    else:
230*cda5da8dSAndroid Build Coastguard Worker        modifier = ''
231*cda5da8dSAndroid Build Coastguard Worker    pos = loc.find('.')
232*cda5da8dSAndroid Build Coastguard Worker    if pos >= 0:
233*cda5da8dSAndroid Build Coastguard Worker        codeset = loc[pos:]
234*cda5da8dSAndroid Build Coastguard Worker        loc = loc[:pos]
235*cda5da8dSAndroid Build Coastguard Worker        mask |= COMPONENT_CODESET
236*cda5da8dSAndroid Build Coastguard Worker    else:
237*cda5da8dSAndroid Build Coastguard Worker        codeset = ''
238*cda5da8dSAndroid Build Coastguard Worker    pos = loc.find('_')
239*cda5da8dSAndroid Build Coastguard Worker    if pos >= 0:
240*cda5da8dSAndroid Build Coastguard Worker        territory = loc[pos:]
241*cda5da8dSAndroid Build Coastguard Worker        loc = loc[:pos]
242*cda5da8dSAndroid Build Coastguard Worker        mask |= COMPONENT_TERRITORY
243*cda5da8dSAndroid Build Coastguard Worker    else:
244*cda5da8dSAndroid Build Coastguard Worker        territory = ''
245*cda5da8dSAndroid Build Coastguard Worker    language = loc
246*cda5da8dSAndroid Build Coastguard Worker    ret = []
247*cda5da8dSAndroid Build Coastguard Worker    for i in range(mask+1):
248*cda5da8dSAndroid Build Coastguard Worker        if not (i & ~mask):  # if all components for this combo exist ...
249*cda5da8dSAndroid Build Coastguard Worker            val = language
250*cda5da8dSAndroid Build Coastguard Worker            if i & COMPONENT_TERRITORY: val += territory
251*cda5da8dSAndroid Build Coastguard Worker            if i & COMPONENT_CODESET:   val += codeset
252*cda5da8dSAndroid Build Coastguard Worker            if i & COMPONENT_MODIFIER:  val += modifier
253*cda5da8dSAndroid Build Coastguard Worker            ret.append(val)
254*cda5da8dSAndroid Build Coastguard Worker    ret.reverse()
255*cda5da8dSAndroid Build Coastguard Worker    return ret
256*cda5da8dSAndroid Build Coastguard Worker
257*cda5da8dSAndroid Build Coastguard Worker
258*cda5da8dSAndroid Build Coastguard Workerclass NullTranslations:
259*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, fp=None):
260*cda5da8dSAndroid Build Coastguard Worker        self._info = {}
261*cda5da8dSAndroid Build Coastguard Worker        self._charset = None
262*cda5da8dSAndroid Build Coastguard Worker        self._fallback = None
263*cda5da8dSAndroid Build Coastguard Worker        if fp is not None:
264*cda5da8dSAndroid Build Coastguard Worker            self._parse(fp)
265*cda5da8dSAndroid Build Coastguard Worker
266*cda5da8dSAndroid Build Coastguard Worker    def _parse(self, fp):
267*cda5da8dSAndroid Build Coastguard Worker        pass
268*cda5da8dSAndroid Build Coastguard Worker
269*cda5da8dSAndroid Build Coastguard Worker    def add_fallback(self, fallback):
270*cda5da8dSAndroid Build Coastguard Worker        if self._fallback:
271*cda5da8dSAndroid Build Coastguard Worker            self._fallback.add_fallback(fallback)
272*cda5da8dSAndroid Build Coastguard Worker        else:
273*cda5da8dSAndroid Build Coastguard Worker            self._fallback = fallback
274*cda5da8dSAndroid Build Coastguard Worker
275*cda5da8dSAndroid Build Coastguard Worker    def gettext(self, message):
276*cda5da8dSAndroid Build Coastguard Worker        if self._fallback:
277*cda5da8dSAndroid Build Coastguard Worker            return self._fallback.gettext(message)
278*cda5da8dSAndroid Build Coastguard Worker        return message
279*cda5da8dSAndroid Build Coastguard Worker
280*cda5da8dSAndroid Build Coastguard Worker    def ngettext(self, msgid1, msgid2, n):
281*cda5da8dSAndroid Build Coastguard Worker        if self._fallback:
282*cda5da8dSAndroid Build Coastguard Worker            return self._fallback.ngettext(msgid1, msgid2, n)
283*cda5da8dSAndroid Build Coastguard Worker        if n == 1:
284*cda5da8dSAndroid Build Coastguard Worker            return msgid1
285*cda5da8dSAndroid Build Coastguard Worker        else:
286*cda5da8dSAndroid Build Coastguard Worker            return msgid2
287*cda5da8dSAndroid Build Coastguard Worker
288*cda5da8dSAndroid Build Coastguard Worker    def pgettext(self, context, message):
289*cda5da8dSAndroid Build Coastguard Worker        if self._fallback:
290*cda5da8dSAndroid Build Coastguard Worker            return self._fallback.pgettext(context, message)
291*cda5da8dSAndroid Build Coastguard Worker        return message
292*cda5da8dSAndroid Build Coastguard Worker
293*cda5da8dSAndroid Build Coastguard Worker    def npgettext(self, context, msgid1, msgid2, n):
294*cda5da8dSAndroid Build Coastguard Worker        if self._fallback:
295*cda5da8dSAndroid Build Coastguard Worker            return self._fallback.npgettext(context, msgid1, msgid2, n)
296*cda5da8dSAndroid Build Coastguard Worker        if n == 1:
297*cda5da8dSAndroid Build Coastguard Worker            return msgid1
298*cda5da8dSAndroid Build Coastguard Worker        else:
299*cda5da8dSAndroid Build Coastguard Worker            return msgid2
300*cda5da8dSAndroid Build Coastguard Worker
301*cda5da8dSAndroid Build Coastguard Worker    def info(self):
302*cda5da8dSAndroid Build Coastguard Worker        return self._info
303*cda5da8dSAndroid Build Coastguard Worker
304*cda5da8dSAndroid Build Coastguard Worker    def charset(self):
305*cda5da8dSAndroid Build Coastguard Worker        return self._charset
306*cda5da8dSAndroid Build Coastguard Worker
307*cda5da8dSAndroid Build Coastguard Worker    def install(self, names=None):
308*cda5da8dSAndroid Build Coastguard Worker        import builtins
309*cda5da8dSAndroid Build Coastguard Worker        builtins.__dict__['_'] = self.gettext
310*cda5da8dSAndroid Build Coastguard Worker        if names is not None:
311*cda5da8dSAndroid Build Coastguard Worker            allowed = {'gettext', 'ngettext', 'npgettext', 'pgettext'}
312*cda5da8dSAndroid Build Coastguard Worker            for name in allowed & set(names):
313*cda5da8dSAndroid Build Coastguard Worker                builtins.__dict__[name] = getattr(self, name)
314*cda5da8dSAndroid Build Coastguard Worker
315*cda5da8dSAndroid Build Coastguard Worker
316*cda5da8dSAndroid Build Coastguard Workerclass GNUTranslations(NullTranslations):
317*cda5da8dSAndroid Build Coastguard Worker    # Magic number of .mo files
318*cda5da8dSAndroid Build Coastguard Worker    LE_MAGIC = 0x950412de
319*cda5da8dSAndroid Build Coastguard Worker    BE_MAGIC = 0xde120495
320*cda5da8dSAndroid Build Coastguard Worker
321*cda5da8dSAndroid Build Coastguard Worker    # The encoding of a msgctxt and a msgid in a .mo file is
322*cda5da8dSAndroid Build Coastguard Worker    # msgctxt + "\x04" + msgid (gettext version >= 0.15)
323*cda5da8dSAndroid Build Coastguard Worker    CONTEXT = "%s\x04%s"
324*cda5da8dSAndroid Build Coastguard Worker
325*cda5da8dSAndroid Build Coastguard Worker    # Acceptable .mo versions
326*cda5da8dSAndroid Build Coastguard Worker    VERSIONS = (0, 1)
327*cda5da8dSAndroid Build Coastguard Worker
328*cda5da8dSAndroid Build Coastguard Worker    def _get_versions(self, version):
329*cda5da8dSAndroid Build Coastguard Worker        """Returns a tuple of major version, minor version"""
330*cda5da8dSAndroid Build Coastguard Worker        return (version >> 16, version & 0xffff)
331*cda5da8dSAndroid Build Coastguard Worker
332*cda5da8dSAndroid Build Coastguard Worker    def _parse(self, fp):
333*cda5da8dSAndroid Build Coastguard Worker        """Override this method to support alternative .mo formats."""
334*cda5da8dSAndroid Build Coastguard Worker        # Delay struct import for speeding up gettext import when .mo files
335*cda5da8dSAndroid Build Coastguard Worker        # are not used.
336*cda5da8dSAndroid Build Coastguard Worker        from struct import unpack
337*cda5da8dSAndroid Build Coastguard Worker        filename = getattr(fp, 'name', '')
338*cda5da8dSAndroid Build Coastguard Worker        # Parse the .mo file header, which consists of 5 little endian 32
339*cda5da8dSAndroid Build Coastguard Worker        # bit words.
340*cda5da8dSAndroid Build Coastguard Worker        self._catalog = catalog = {}
341*cda5da8dSAndroid Build Coastguard Worker        self.plural = lambda n: int(n != 1) # germanic plural by default
342*cda5da8dSAndroid Build Coastguard Worker        buf = fp.read()
343*cda5da8dSAndroid Build Coastguard Worker        buflen = len(buf)
344*cda5da8dSAndroid Build Coastguard Worker        # Are we big endian or little endian?
345*cda5da8dSAndroid Build Coastguard Worker        magic = unpack('<I', buf[:4])[0]
346*cda5da8dSAndroid Build Coastguard Worker        if magic == self.LE_MAGIC:
347*cda5da8dSAndroid Build Coastguard Worker            version, msgcount, masteridx, transidx = unpack('<4I', buf[4:20])
348*cda5da8dSAndroid Build Coastguard Worker            ii = '<II'
349*cda5da8dSAndroid Build Coastguard Worker        elif magic == self.BE_MAGIC:
350*cda5da8dSAndroid Build Coastguard Worker            version, msgcount, masteridx, transidx = unpack('>4I', buf[4:20])
351*cda5da8dSAndroid Build Coastguard Worker            ii = '>II'
352*cda5da8dSAndroid Build Coastguard Worker        else:
353*cda5da8dSAndroid Build Coastguard Worker            raise OSError(0, 'Bad magic number', filename)
354*cda5da8dSAndroid Build Coastguard Worker
355*cda5da8dSAndroid Build Coastguard Worker        major_version, minor_version = self._get_versions(version)
356*cda5da8dSAndroid Build Coastguard Worker
357*cda5da8dSAndroid Build Coastguard Worker        if major_version not in self.VERSIONS:
358*cda5da8dSAndroid Build Coastguard Worker            raise OSError(0, 'Bad version number ' + str(major_version), filename)
359*cda5da8dSAndroid Build Coastguard Worker
360*cda5da8dSAndroid Build Coastguard Worker        # Now put all messages from the .mo file buffer into the catalog
361*cda5da8dSAndroid Build Coastguard Worker        # dictionary.
362*cda5da8dSAndroid Build Coastguard Worker        for i in range(0, msgcount):
363*cda5da8dSAndroid Build Coastguard Worker            mlen, moff = unpack(ii, buf[masteridx:masteridx+8])
364*cda5da8dSAndroid Build Coastguard Worker            mend = moff + mlen
365*cda5da8dSAndroid Build Coastguard Worker            tlen, toff = unpack(ii, buf[transidx:transidx+8])
366*cda5da8dSAndroid Build Coastguard Worker            tend = toff + tlen
367*cda5da8dSAndroid Build Coastguard Worker            if mend < buflen and tend < buflen:
368*cda5da8dSAndroid Build Coastguard Worker                msg = buf[moff:mend]
369*cda5da8dSAndroid Build Coastguard Worker                tmsg = buf[toff:tend]
370*cda5da8dSAndroid Build Coastguard Worker            else:
371*cda5da8dSAndroid Build Coastguard Worker                raise OSError(0, 'File is corrupt', filename)
372*cda5da8dSAndroid Build Coastguard Worker            # See if we're looking at GNU .mo conventions for metadata
373*cda5da8dSAndroid Build Coastguard Worker            if mlen == 0:
374*cda5da8dSAndroid Build Coastguard Worker                # Catalog description
375*cda5da8dSAndroid Build Coastguard Worker                lastk = None
376*cda5da8dSAndroid Build Coastguard Worker                for b_item in tmsg.split(b'\n'):
377*cda5da8dSAndroid Build Coastguard Worker                    item = b_item.decode().strip()
378*cda5da8dSAndroid Build Coastguard Worker                    if not item:
379*cda5da8dSAndroid Build Coastguard Worker                        continue
380*cda5da8dSAndroid Build Coastguard Worker                    # Skip over comment lines:
381*cda5da8dSAndroid Build Coastguard Worker                    if item.startswith('#-#-#-#-#') and item.endswith('#-#-#-#-#'):
382*cda5da8dSAndroid Build Coastguard Worker                        continue
383*cda5da8dSAndroid Build Coastguard Worker                    k = v = None
384*cda5da8dSAndroid Build Coastguard Worker                    if ':' in item:
385*cda5da8dSAndroid Build Coastguard Worker                        k, v = item.split(':', 1)
386*cda5da8dSAndroid Build Coastguard Worker                        k = k.strip().lower()
387*cda5da8dSAndroid Build Coastguard Worker                        v = v.strip()
388*cda5da8dSAndroid Build Coastguard Worker                        self._info[k] = v
389*cda5da8dSAndroid Build Coastguard Worker                        lastk = k
390*cda5da8dSAndroid Build Coastguard Worker                    elif lastk:
391*cda5da8dSAndroid Build Coastguard Worker                        self._info[lastk] += '\n' + item
392*cda5da8dSAndroid Build Coastguard Worker                    if k == 'content-type':
393*cda5da8dSAndroid Build Coastguard Worker                        self._charset = v.split('charset=')[1]
394*cda5da8dSAndroid Build Coastguard Worker                    elif k == 'plural-forms':
395*cda5da8dSAndroid Build Coastguard Worker                        v = v.split(';')
396*cda5da8dSAndroid Build Coastguard Worker                        plural = v[1].split('plural=')[1]
397*cda5da8dSAndroid Build Coastguard Worker                        self.plural = c2py(plural)
398*cda5da8dSAndroid Build Coastguard Worker            # Note: we unconditionally convert both msgids and msgstrs to
399*cda5da8dSAndroid Build Coastguard Worker            # Unicode using the character encoding specified in the charset
400*cda5da8dSAndroid Build Coastguard Worker            # parameter of the Content-Type header.  The gettext documentation
401*cda5da8dSAndroid Build Coastguard Worker            # strongly encourages msgids to be us-ascii, but some applications
402*cda5da8dSAndroid Build Coastguard Worker            # require alternative encodings (e.g. Zope's ZCML and ZPT).  For
403*cda5da8dSAndroid Build Coastguard Worker            # traditional gettext applications, the msgid conversion will
404*cda5da8dSAndroid Build Coastguard Worker            # cause no problems since us-ascii should always be a subset of
405*cda5da8dSAndroid Build Coastguard Worker            # the charset encoding.  We may want to fall back to 8-bit msgids
406*cda5da8dSAndroid Build Coastguard Worker            # if the Unicode conversion fails.
407*cda5da8dSAndroid Build Coastguard Worker            charset = self._charset or 'ascii'
408*cda5da8dSAndroid Build Coastguard Worker            if b'\x00' in msg:
409*cda5da8dSAndroid Build Coastguard Worker                # Plural forms
410*cda5da8dSAndroid Build Coastguard Worker                msgid1, msgid2 = msg.split(b'\x00')
411*cda5da8dSAndroid Build Coastguard Worker                tmsg = tmsg.split(b'\x00')
412*cda5da8dSAndroid Build Coastguard Worker                msgid1 = str(msgid1, charset)
413*cda5da8dSAndroid Build Coastguard Worker                for i, x in enumerate(tmsg):
414*cda5da8dSAndroid Build Coastguard Worker                    catalog[(msgid1, i)] = str(x, charset)
415*cda5da8dSAndroid Build Coastguard Worker            else:
416*cda5da8dSAndroid Build Coastguard Worker                catalog[str(msg, charset)] = str(tmsg, charset)
417*cda5da8dSAndroid Build Coastguard Worker            # advance to next entry in the seek tables
418*cda5da8dSAndroid Build Coastguard Worker            masteridx += 8
419*cda5da8dSAndroid Build Coastguard Worker            transidx += 8
420*cda5da8dSAndroid Build Coastguard Worker
421*cda5da8dSAndroid Build Coastguard Worker    def gettext(self, message):
422*cda5da8dSAndroid Build Coastguard Worker        missing = object()
423*cda5da8dSAndroid Build Coastguard Worker        tmsg = self._catalog.get(message, missing)
424*cda5da8dSAndroid Build Coastguard Worker        if tmsg is missing:
425*cda5da8dSAndroid Build Coastguard Worker            if self._fallback:
426*cda5da8dSAndroid Build Coastguard Worker                return self._fallback.gettext(message)
427*cda5da8dSAndroid Build Coastguard Worker            return message
428*cda5da8dSAndroid Build Coastguard Worker        return tmsg
429*cda5da8dSAndroid Build Coastguard Worker
430*cda5da8dSAndroid Build Coastguard Worker    def ngettext(self, msgid1, msgid2, n):
431*cda5da8dSAndroid Build Coastguard Worker        try:
432*cda5da8dSAndroid Build Coastguard Worker            tmsg = self._catalog[(msgid1, self.plural(n))]
433*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
434*cda5da8dSAndroid Build Coastguard Worker            if self._fallback:
435*cda5da8dSAndroid Build Coastguard Worker                return self._fallback.ngettext(msgid1, msgid2, n)
436*cda5da8dSAndroid Build Coastguard Worker            if n == 1:
437*cda5da8dSAndroid Build Coastguard Worker                tmsg = msgid1
438*cda5da8dSAndroid Build Coastguard Worker            else:
439*cda5da8dSAndroid Build Coastguard Worker                tmsg = msgid2
440*cda5da8dSAndroid Build Coastguard Worker        return tmsg
441*cda5da8dSAndroid Build Coastguard Worker
442*cda5da8dSAndroid Build Coastguard Worker    def pgettext(self, context, message):
443*cda5da8dSAndroid Build Coastguard Worker        ctxt_msg_id = self.CONTEXT % (context, message)
444*cda5da8dSAndroid Build Coastguard Worker        missing = object()
445*cda5da8dSAndroid Build Coastguard Worker        tmsg = self._catalog.get(ctxt_msg_id, missing)
446*cda5da8dSAndroid Build Coastguard Worker        if tmsg is missing:
447*cda5da8dSAndroid Build Coastguard Worker            if self._fallback:
448*cda5da8dSAndroid Build Coastguard Worker                return self._fallback.pgettext(context, message)
449*cda5da8dSAndroid Build Coastguard Worker            return message
450*cda5da8dSAndroid Build Coastguard Worker        return tmsg
451*cda5da8dSAndroid Build Coastguard Worker
452*cda5da8dSAndroid Build Coastguard Worker    def npgettext(self, context, msgid1, msgid2, n):
453*cda5da8dSAndroid Build Coastguard Worker        ctxt_msg_id = self.CONTEXT % (context, msgid1)
454*cda5da8dSAndroid Build Coastguard Worker        try:
455*cda5da8dSAndroid Build Coastguard Worker            tmsg = self._catalog[ctxt_msg_id, self.plural(n)]
456*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
457*cda5da8dSAndroid Build Coastguard Worker            if self._fallback:
458*cda5da8dSAndroid Build Coastguard Worker                return self._fallback.npgettext(context, msgid1, msgid2, n)
459*cda5da8dSAndroid Build Coastguard Worker            if n == 1:
460*cda5da8dSAndroid Build Coastguard Worker                tmsg = msgid1
461*cda5da8dSAndroid Build Coastguard Worker            else:
462*cda5da8dSAndroid Build Coastguard Worker                tmsg = msgid2
463*cda5da8dSAndroid Build Coastguard Worker        return tmsg
464*cda5da8dSAndroid Build Coastguard Worker
465*cda5da8dSAndroid Build Coastguard Worker
466*cda5da8dSAndroid Build Coastguard Worker# Locate a .mo file using the gettext strategy
467*cda5da8dSAndroid Build Coastguard Workerdef find(domain, localedir=None, languages=None, all=False):
468*cda5da8dSAndroid Build Coastguard Worker    # Get some reasonable defaults for arguments that were not supplied
469*cda5da8dSAndroid Build Coastguard Worker    if localedir is None:
470*cda5da8dSAndroid Build Coastguard Worker        localedir = _default_localedir
471*cda5da8dSAndroid Build Coastguard Worker    if languages is None:
472*cda5da8dSAndroid Build Coastguard Worker        languages = []
473*cda5da8dSAndroid Build Coastguard Worker        for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
474*cda5da8dSAndroid Build Coastguard Worker            val = os.environ.get(envar)
475*cda5da8dSAndroid Build Coastguard Worker            if val:
476*cda5da8dSAndroid Build Coastguard Worker                languages = val.split(':')
477*cda5da8dSAndroid Build Coastguard Worker                break
478*cda5da8dSAndroid Build Coastguard Worker        if 'C' not in languages:
479*cda5da8dSAndroid Build Coastguard Worker            languages.append('C')
480*cda5da8dSAndroid Build Coastguard Worker    # now normalize and expand the languages
481*cda5da8dSAndroid Build Coastguard Worker    nelangs = []
482*cda5da8dSAndroid Build Coastguard Worker    for lang in languages:
483*cda5da8dSAndroid Build Coastguard Worker        for nelang in _expand_lang(lang):
484*cda5da8dSAndroid Build Coastguard Worker            if nelang not in nelangs:
485*cda5da8dSAndroid Build Coastguard Worker                nelangs.append(nelang)
486*cda5da8dSAndroid Build Coastguard Worker    # select a language
487*cda5da8dSAndroid Build Coastguard Worker    if all:
488*cda5da8dSAndroid Build Coastguard Worker        result = []
489*cda5da8dSAndroid Build Coastguard Worker    else:
490*cda5da8dSAndroid Build Coastguard Worker        result = None
491*cda5da8dSAndroid Build Coastguard Worker    for lang in nelangs:
492*cda5da8dSAndroid Build Coastguard Worker        if lang == 'C':
493*cda5da8dSAndroid Build Coastguard Worker            break
494*cda5da8dSAndroid Build Coastguard Worker        mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain)
495*cda5da8dSAndroid Build Coastguard Worker        if os.path.exists(mofile):
496*cda5da8dSAndroid Build Coastguard Worker            if all:
497*cda5da8dSAndroid Build Coastguard Worker                result.append(mofile)
498*cda5da8dSAndroid Build Coastguard Worker            else:
499*cda5da8dSAndroid Build Coastguard Worker                return mofile
500*cda5da8dSAndroid Build Coastguard Worker    return result
501*cda5da8dSAndroid Build Coastguard Worker
502*cda5da8dSAndroid Build Coastguard Worker
503*cda5da8dSAndroid Build Coastguard Worker# a mapping between absolute .mo file path and Translation object
504*cda5da8dSAndroid Build Coastguard Worker_translations = {}
505*cda5da8dSAndroid Build Coastguard Worker
506*cda5da8dSAndroid Build Coastguard Worker
507*cda5da8dSAndroid Build Coastguard Workerdef translation(domain, localedir=None, languages=None,
508*cda5da8dSAndroid Build Coastguard Worker                class_=None, fallback=False):
509*cda5da8dSAndroid Build Coastguard Worker    if class_ is None:
510*cda5da8dSAndroid Build Coastguard Worker        class_ = GNUTranslations
511*cda5da8dSAndroid Build Coastguard Worker    mofiles = find(domain, localedir, languages, all=True)
512*cda5da8dSAndroid Build Coastguard Worker    if not mofiles:
513*cda5da8dSAndroid Build Coastguard Worker        if fallback:
514*cda5da8dSAndroid Build Coastguard Worker            return NullTranslations()
515*cda5da8dSAndroid Build Coastguard Worker        from errno import ENOENT
516*cda5da8dSAndroid Build Coastguard Worker        raise FileNotFoundError(ENOENT,
517*cda5da8dSAndroid Build Coastguard Worker                                'No translation file found for domain', domain)
518*cda5da8dSAndroid Build Coastguard Worker    # Avoid opening, reading, and parsing the .mo file after it's been done
519*cda5da8dSAndroid Build Coastguard Worker    # once.
520*cda5da8dSAndroid Build Coastguard Worker    result = None
521*cda5da8dSAndroid Build Coastguard Worker    for mofile in mofiles:
522*cda5da8dSAndroid Build Coastguard Worker        key = (class_, os.path.abspath(mofile))
523*cda5da8dSAndroid Build Coastguard Worker        t = _translations.get(key)
524*cda5da8dSAndroid Build Coastguard Worker        if t is None:
525*cda5da8dSAndroid Build Coastguard Worker            with open(mofile, 'rb') as fp:
526*cda5da8dSAndroid Build Coastguard Worker                t = _translations.setdefault(key, class_(fp))
527*cda5da8dSAndroid Build Coastguard Worker        # Copy the translation object to allow setting fallbacks and
528*cda5da8dSAndroid Build Coastguard Worker        # output charset. All other instance data is shared with the
529*cda5da8dSAndroid Build Coastguard Worker        # cached object.
530*cda5da8dSAndroid Build Coastguard Worker        # Delay copy import for speeding up gettext import when .mo files
531*cda5da8dSAndroid Build Coastguard Worker        # are not used.
532*cda5da8dSAndroid Build Coastguard Worker        import copy
533*cda5da8dSAndroid Build Coastguard Worker        t = copy.copy(t)
534*cda5da8dSAndroid Build Coastguard Worker        if result is None:
535*cda5da8dSAndroid Build Coastguard Worker            result = t
536*cda5da8dSAndroid Build Coastguard Worker        else:
537*cda5da8dSAndroid Build Coastguard Worker            result.add_fallback(t)
538*cda5da8dSAndroid Build Coastguard Worker    return result
539*cda5da8dSAndroid Build Coastguard Worker
540*cda5da8dSAndroid Build Coastguard Worker
541*cda5da8dSAndroid Build Coastguard Workerdef install(domain, localedir=None, *, names=None):
542*cda5da8dSAndroid Build Coastguard Worker    t = translation(domain, localedir, fallback=True)
543*cda5da8dSAndroid Build Coastguard Worker    t.install(names)
544*cda5da8dSAndroid Build Coastguard Worker
545*cda5da8dSAndroid Build Coastguard Worker
546*cda5da8dSAndroid Build Coastguard Worker# a mapping b/w domains and locale directories
547*cda5da8dSAndroid Build Coastguard Worker_localedirs = {}
548*cda5da8dSAndroid Build Coastguard Worker# current global domain, `messages' used for compatibility w/ GNU gettext
549*cda5da8dSAndroid Build Coastguard Worker_current_domain = 'messages'
550*cda5da8dSAndroid Build Coastguard Worker
551*cda5da8dSAndroid Build Coastguard Worker
552*cda5da8dSAndroid Build Coastguard Workerdef textdomain(domain=None):
553*cda5da8dSAndroid Build Coastguard Worker    global _current_domain
554*cda5da8dSAndroid Build Coastguard Worker    if domain is not None:
555*cda5da8dSAndroid Build Coastguard Worker        _current_domain = domain
556*cda5da8dSAndroid Build Coastguard Worker    return _current_domain
557*cda5da8dSAndroid Build Coastguard Worker
558*cda5da8dSAndroid Build Coastguard Worker
559*cda5da8dSAndroid Build Coastguard Workerdef bindtextdomain(domain, localedir=None):
560*cda5da8dSAndroid Build Coastguard Worker    global _localedirs
561*cda5da8dSAndroid Build Coastguard Worker    if localedir is not None:
562*cda5da8dSAndroid Build Coastguard Worker        _localedirs[domain] = localedir
563*cda5da8dSAndroid Build Coastguard Worker    return _localedirs.get(domain, _default_localedir)
564*cda5da8dSAndroid Build Coastguard Worker
565*cda5da8dSAndroid Build Coastguard Worker
566*cda5da8dSAndroid Build Coastguard Workerdef dgettext(domain, message):
567*cda5da8dSAndroid Build Coastguard Worker    try:
568*cda5da8dSAndroid Build Coastguard Worker        t = translation(domain, _localedirs.get(domain, None))
569*cda5da8dSAndroid Build Coastguard Worker    except OSError:
570*cda5da8dSAndroid Build Coastguard Worker        return message
571*cda5da8dSAndroid Build Coastguard Worker    return t.gettext(message)
572*cda5da8dSAndroid Build Coastguard Worker
573*cda5da8dSAndroid Build Coastguard Worker
574*cda5da8dSAndroid Build Coastguard Workerdef dngettext(domain, msgid1, msgid2, n):
575*cda5da8dSAndroid Build Coastguard Worker    try:
576*cda5da8dSAndroid Build Coastguard Worker        t = translation(domain, _localedirs.get(domain, None))
577*cda5da8dSAndroid Build Coastguard Worker    except OSError:
578*cda5da8dSAndroid Build Coastguard Worker        if n == 1:
579*cda5da8dSAndroid Build Coastguard Worker            return msgid1
580*cda5da8dSAndroid Build Coastguard Worker        else:
581*cda5da8dSAndroid Build Coastguard Worker            return msgid2
582*cda5da8dSAndroid Build Coastguard Worker    return t.ngettext(msgid1, msgid2, n)
583*cda5da8dSAndroid Build Coastguard Worker
584*cda5da8dSAndroid Build Coastguard Worker
585*cda5da8dSAndroid Build Coastguard Workerdef dpgettext(domain, context, message):
586*cda5da8dSAndroid Build Coastguard Worker    try:
587*cda5da8dSAndroid Build Coastguard Worker        t = translation(domain, _localedirs.get(domain, None))
588*cda5da8dSAndroid Build Coastguard Worker    except OSError:
589*cda5da8dSAndroid Build Coastguard Worker        return message
590*cda5da8dSAndroid Build Coastguard Worker    return t.pgettext(context, message)
591*cda5da8dSAndroid Build Coastguard Worker
592*cda5da8dSAndroid Build Coastguard Worker
593*cda5da8dSAndroid Build Coastguard Workerdef dnpgettext(domain, context, msgid1, msgid2, n):
594*cda5da8dSAndroid Build Coastguard Worker    try:
595*cda5da8dSAndroid Build Coastguard Worker        t = translation(domain, _localedirs.get(domain, None))
596*cda5da8dSAndroid Build Coastguard Worker    except OSError:
597*cda5da8dSAndroid Build Coastguard Worker        if n == 1:
598*cda5da8dSAndroid Build Coastguard Worker            return msgid1
599*cda5da8dSAndroid Build Coastguard Worker        else:
600*cda5da8dSAndroid Build Coastguard Worker            return msgid2
601*cda5da8dSAndroid Build Coastguard Worker    return t.npgettext(context, msgid1, msgid2, n)
602*cda5da8dSAndroid Build Coastguard Worker
603*cda5da8dSAndroid Build Coastguard Worker
604*cda5da8dSAndroid Build Coastguard Workerdef gettext(message):
605*cda5da8dSAndroid Build Coastguard Worker    return dgettext(_current_domain, message)
606*cda5da8dSAndroid Build Coastguard Worker
607*cda5da8dSAndroid Build Coastguard Worker
608*cda5da8dSAndroid Build Coastguard Workerdef ngettext(msgid1, msgid2, n):
609*cda5da8dSAndroid Build Coastguard Worker    return dngettext(_current_domain, msgid1, msgid2, n)
610*cda5da8dSAndroid Build Coastguard Worker
611*cda5da8dSAndroid Build Coastguard Worker
612*cda5da8dSAndroid Build Coastguard Workerdef pgettext(context, message):
613*cda5da8dSAndroid Build Coastguard Worker    return dpgettext(_current_domain, context, message)
614*cda5da8dSAndroid Build Coastguard Worker
615*cda5da8dSAndroid Build Coastguard Worker
616*cda5da8dSAndroid Build Coastguard Workerdef npgettext(context, msgid1, msgid2, n):
617*cda5da8dSAndroid Build Coastguard Worker    return dnpgettext(_current_domain, context, msgid1, msgid2, n)
618*cda5da8dSAndroid Build Coastguard Worker
619*cda5da8dSAndroid Build Coastguard Worker
620*cda5da8dSAndroid Build Coastguard Worker# dcgettext() has been deemed unnecessary and is not implemented.
621*cda5da8dSAndroid Build Coastguard Worker
622*cda5da8dSAndroid Build Coastguard Worker# James Henstridge's Catalog constructor from GNOME gettext.  Documented usage
623*cda5da8dSAndroid Build Coastguard Worker# was:
624*cda5da8dSAndroid Build Coastguard Worker#
625*cda5da8dSAndroid Build Coastguard Worker#    import gettext
626*cda5da8dSAndroid Build Coastguard Worker#    cat = gettext.Catalog(PACKAGE, localedir=LOCALEDIR)
627*cda5da8dSAndroid Build Coastguard Worker#    _ = cat.gettext
628*cda5da8dSAndroid Build Coastguard Worker#    print _('Hello World')
629*cda5da8dSAndroid Build Coastguard Worker
630*cda5da8dSAndroid Build Coastguard Worker# The resulting catalog object currently don't support access through a
631*cda5da8dSAndroid Build Coastguard Worker# dictionary API, which was supported (but apparently unused) in GNOME
632*cda5da8dSAndroid Build Coastguard Worker# gettext.
633*cda5da8dSAndroid Build Coastguard Worker
634*cda5da8dSAndroid Build Coastguard WorkerCatalog = translation
635