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