]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | """Internationalization and localization support.\r |
2 | \r | |
3 | This module provides internationalization (I18N) and localization (L10N)\r | |
4 | support for your Python programs by providing an interface to the GNU gettext\r | |
5 | message catalog library.\r | |
6 | \r | |
7 | I18N refers to the operation by which a program is made aware of multiple\r | |
8 | languages. L10N refers to the adaptation of your program, once\r | |
9 | internationalized, to the local language and cultural habits.\r | |
10 | \r | |
11 | """\r | |
12 | \r | |
13 | # This module represents the integration of work, contributions, feedback, and\r | |
14 | # suggestions from the following people:\r | |
15 | #\r | |
16 | # Martin von Loewis, who wrote the initial implementation of the underlying\r | |
17 | # C-based libintlmodule (later renamed _gettext), along with a skeletal\r | |
18 | # gettext.py implementation.\r | |
19 | #\r | |
20 | # Peter Funk, who wrote fintl.py, a fairly complete wrapper around intlmodule,\r | |
21 | # which also included a pure-Python implementation to read .mo files if\r | |
22 | # intlmodule wasn't available.\r | |
23 | #\r | |
24 | # James Henstridge, who also wrote a gettext.py module, which has some\r | |
25 | # interesting, but currently unsupported experimental features: the notion of\r | |
26 | # a Catalog class and instances, and the ability to add to a catalog file via\r | |
27 | # a Python API.\r | |
28 | #\r | |
29 | # Barry Warsaw integrated these modules, wrote the .install() API and code,\r | |
30 | # and conformed all C and Python code to Python's coding standards.\r | |
31 | #\r | |
32 | # Francois Pinard and Marc-Andre Lemburg also contributed valuably to this\r | |
33 | # module.\r | |
34 | #\r | |
35 | # J. David Ibanez implemented plural forms. Bruno Haible fixed some bugs.\r | |
36 | #\r | |
37 | # TODO:\r | |
38 | # - Lazy loading of .mo files. Currently the entire catalog is loaded into\r | |
39 | # memory, but that's probably bad for large translated programs. Instead,\r | |
40 | # the lexical sort of original strings in GNU .mo files should be exploited\r | |
41 | # to do binary searches and lazy initializations. Or you might want to use\r | |
42 | # the undocumented double-hash algorithm for .mo files with hash tables, but\r | |
43 | # you'll need to study the GNU gettext code to do this.\r | |
44 | #\r | |
45 | # - Support Solaris .mo file formats. Unfortunately, we've been unable to\r | |
46 | # find this format documented anywhere.\r | |
47 | \r | |
48 | \r | |
49 | import locale, copy, os, re, struct, sys\r | |
50 | from errno import ENOENT\r | |
51 | \r | |
52 | \r | |
53 | __all__ = ['NullTranslations', 'GNUTranslations', 'Catalog',\r | |
54 | 'find', 'translation', 'install', 'textdomain', 'bindtextdomain',\r | |
55 | 'dgettext', 'dngettext', 'gettext', 'ngettext',\r | |
56 | ]\r | |
57 | \r | |
58 | _default_localedir = os.path.join(sys.prefix, 'share', 'locale')\r | |
59 | \r | |
60 | \r | |
61 | def test(condition, true, false):\r | |
62 | """\r | |
63 | Implements the C expression:\r | |
64 | \r | |
65 | condition ? true : false\r | |
66 | \r | |
67 | Required to correctly interpret plural forms.\r | |
68 | """\r | |
69 | if condition:\r | |
70 | return true\r | |
71 | else:\r | |
72 | return false\r | |
73 | \r | |
74 | \r | |
75 | def c2py(plural):\r | |
76 | """Gets a C expression as used in PO files for plural forms and returns a\r | |
77 | Python lambda function that implements an equivalent expression.\r | |
78 | """\r | |
79 | # Security check, allow only the "n" identifier\r | |
80 | try:\r | |
81 | from cStringIO import StringIO\r | |
82 | except ImportError:\r | |
83 | from StringIO import StringIO\r | |
84 | import token, tokenize\r | |
85 | tokens = tokenize.generate_tokens(StringIO(plural).readline)\r | |
86 | try:\r | |
87 | danger = [x for x in tokens if x[0] == token.NAME and x[1] != 'n']\r | |
88 | except tokenize.TokenError:\r | |
89 | raise ValueError, \\r | |
90 | 'plural forms expression error, maybe unbalanced parenthesis'\r | |
91 | else:\r | |
92 | if danger:\r | |
93 | raise ValueError, 'plural forms expression could be dangerous'\r | |
94 | \r | |
95 | # Replace some C operators by their Python equivalents\r | |
96 | plural = plural.replace('&&', ' and ')\r | |
97 | plural = plural.replace('||', ' or ')\r | |
98 | \r | |
99 | expr = re.compile(r'\!([^=])')\r | |
100 | plural = expr.sub(' not \\1', plural)\r | |
101 | \r | |
102 | # Regular expression and replacement function used to transform\r | |
103 | # "a?b:c" to "test(a,b,c)".\r | |
104 | expr = re.compile(r'(.*?)\?(.*?):(.*)')\r | |
105 | def repl(x):\r | |
106 | return "test(%s, %s, %s)" % (x.group(1), x.group(2),\r | |
107 | expr.sub(repl, x.group(3)))\r | |
108 | \r | |
109 | # Code to transform the plural expression, taking care of parentheses\r | |
110 | stack = ['']\r | |
111 | for c in plural:\r | |
112 | if c == '(':\r | |
113 | stack.append('')\r | |
114 | elif c == ')':\r | |
115 | if len(stack) == 1:\r | |
116 | # Actually, we never reach this code, because unbalanced\r | |
117 | # parentheses get caught in the security check at the\r | |
118 | # beginning.\r | |
119 | raise ValueError, 'unbalanced parenthesis in plural form'\r | |
120 | s = expr.sub(repl, stack.pop())\r | |
121 | stack[-1] += '(%s)' % s\r | |
122 | else:\r | |
123 | stack[-1] += c\r | |
124 | plural = expr.sub(repl, stack.pop())\r | |
125 | \r | |
126 | return eval('lambda n: int(%s)' % plural)\r | |
127 | \r | |
128 | \r | |
129 | \r | |
130 | def _expand_lang(locale):\r | |
131 | from locale import normalize\r | |
132 | locale = normalize(locale)\r | |
133 | COMPONENT_CODESET = 1 << 0\r | |
134 | COMPONENT_TERRITORY = 1 << 1\r | |
135 | COMPONENT_MODIFIER = 1 << 2\r | |
136 | # split up the locale into its base components\r | |
137 | mask = 0\r | |
138 | pos = locale.find('@')\r | |
139 | if pos >= 0:\r | |
140 | modifier = locale[pos:]\r | |
141 | locale = locale[:pos]\r | |
142 | mask |= COMPONENT_MODIFIER\r | |
143 | else:\r | |
144 | modifier = ''\r | |
145 | pos = locale.find('.')\r | |
146 | if pos >= 0:\r | |
147 | codeset = locale[pos:]\r | |
148 | locale = locale[:pos]\r | |
149 | mask |= COMPONENT_CODESET\r | |
150 | else:\r | |
151 | codeset = ''\r | |
152 | pos = locale.find('_')\r | |
153 | if pos >= 0:\r | |
154 | territory = locale[pos:]\r | |
155 | locale = locale[:pos]\r | |
156 | mask |= COMPONENT_TERRITORY\r | |
157 | else:\r | |
158 | territory = ''\r | |
159 | language = locale\r | |
160 | ret = []\r | |
161 | for i in range(mask+1):\r | |
162 | if not (i & ~mask): # if all components for this combo exist ...\r | |
163 | val = language\r | |
164 | if i & COMPONENT_TERRITORY: val += territory\r | |
165 | if i & COMPONENT_CODESET: val += codeset\r | |
166 | if i & COMPONENT_MODIFIER: val += modifier\r | |
167 | ret.append(val)\r | |
168 | ret.reverse()\r | |
169 | return ret\r | |
170 | \r | |
171 | \r | |
172 | \r | |
173 | class NullTranslations:\r | |
174 | def __init__(self, fp=None):\r | |
175 | self._info = {}\r | |
176 | self._charset = None\r | |
177 | self._output_charset = None\r | |
178 | self._fallback = None\r | |
179 | if fp is not None:\r | |
180 | self._parse(fp)\r | |
181 | \r | |
182 | def _parse(self, fp):\r | |
183 | pass\r | |
184 | \r | |
185 | def add_fallback(self, fallback):\r | |
186 | if self._fallback:\r | |
187 | self._fallback.add_fallback(fallback)\r | |
188 | else:\r | |
189 | self._fallback = fallback\r | |
190 | \r | |
191 | def gettext(self, message):\r | |
192 | if self._fallback:\r | |
193 | return self._fallback.gettext(message)\r | |
194 | return message\r | |
195 | \r | |
196 | def lgettext(self, message):\r | |
197 | if self._fallback:\r | |
198 | return self._fallback.lgettext(message)\r | |
199 | return message\r | |
200 | \r | |
201 | def ngettext(self, msgid1, msgid2, n):\r | |
202 | if self._fallback:\r | |
203 | return self._fallback.ngettext(msgid1, msgid2, n)\r | |
204 | if n == 1:\r | |
205 | return msgid1\r | |
206 | else:\r | |
207 | return msgid2\r | |
208 | \r | |
209 | def lngettext(self, msgid1, msgid2, n):\r | |
210 | if self._fallback:\r | |
211 | return self._fallback.lngettext(msgid1, msgid2, n)\r | |
212 | if n == 1:\r | |
213 | return msgid1\r | |
214 | else:\r | |
215 | return msgid2\r | |
216 | \r | |
217 | def ugettext(self, message):\r | |
218 | if self._fallback:\r | |
219 | return self._fallback.ugettext(message)\r | |
220 | return unicode(message)\r | |
221 | \r | |
222 | def ungettext(self, msgid1, msgid2, n):\r | |
223 | if self._fallback:\r | |
224 | return self._fallback.ungettext(msgid1, msgid2, n)\r | |
225 | if n == 1:\r | |
226 | return unicode(msgid1)\r | |
227 | else:\r | |
228 | return unicode(msgid2)\r | |
229 | \r | |
230 | def info(self):\r | |
231 | return self._info\r | |
232 | \r | |
233 | def charset(self):\r | |
234 | return self._charset\r | |
235 | \r | |
236 | def output_charset(self):\r | |
237 | return self._output_charset\r | |
238 | \r | |
239 | def set_output_charset(self, charset):\r | |
240 | self._output_charset = charset\r | |
241 | \r | |
242 | def install(self, unicode=False, names=None):\r | |
243 | import __builtin__\r | |
244 | __builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext\r | |
245 | if hasattr(names, "__contains__"):\r | |
246 | if "gettext" in names:\r | |
247 | __builtin__.__dict__['gettext'] = __builtin__.__dict__['_']\r | |
248 | if "ngettext" in names:\r | |
249 | __builtin__.__dict__['ngettext'] = (unicode and self.ungettext\r | |
250 | or self.ngettext)\r | |
251 | if "lgettext" in names:\r | |
252 | __builtin__.__dict__['lgettext'] = self.lgettext\r | |
253 | if "lngettext" in names:\r | |
254 | __builtin__.__dict__['lngettext'] = self.lngettext\r | |
255 | \r | |
256 | \r | |
257 | class GNUTranslations(NullTranslations):\r | |
258 | # Magic number of .mo files\r | |
259 | LE_MAGIC = 0x950412deL\r | |
260 | BE_MAGIC = 0xde120495L\r | |
261 | \r | |
262 | def _parse(self, fp):\r | |
263 | """Override this method to support alternative .mo formats."""\r | |
264 | unpack = struct.unpack\r | |
265 | filename = getattr(fp, 'name', '')\r | |
266 | # Parse the .mo file header, which consists of 5 little endian 32\r | |
267 | # bit words.\r | |
268 | self._catalog = catalog = {}\r | |
269 | self.plural = lambda n: int(n != 1) # germanic plural by default\r | |
270 | buf = fp.read()\r | |
271 | buflen = len(buf)\r | |
272 | # Are we big endian or little endian?\r | |
273 | magic = unpack('<I', buf[:4])[0]\r | |
274 | if magic == self.LE_MAGIC:\r | |
275 | version, msgcount, masteridx, transidx = unpack('<4I', buf[4:20])\r | |
276 | ii = '<II'\r | |
277 | elif magic == self.BE_MAGIC:\r | |
278 | version, msgcount, masteridx, transidx = unpack('>4I', buf[4:20])\r | |
279 | ii = '>II'\r | |
280 | else:\r | |
281 | raise IOError(0, 'Bad magic number', filename)\r | |
282 | # Now put all messages from the .mo file buffer into the catalog\r | |
283 | # dictionary.\r | |
284 | for i in xrange(0, msgcount):\r | |
285 | mlen, moff = unpack(ii, buf[masteridx:masteridx+8])\r | |
286 | mend = moff + mlen\r | |
287 | tlen, toff = unpack(ii, buf[transidx:transidx+8])\r | |
288 | tend = toff + tlen\r | |
289 | if mend < buflen and tend < buflen:\r | |
290 | msg = buf[moff:mend]\r | |
291 | tmsg = buf[toff:tend]\r | |
292 | else:\r | |
293 | raise IOError(0, 'File is corrupt', filename)\r | |
294 | # See if we're looking at GNU .mo conventions for metadata\r | |
295 | if mlen == 0:\r | |
296 | # Catalog description\r | |
297 | lastk = k = None\r | |
298 | for item in tmsg.splitlines():\r | |
299 | item = item.strip()\r | |
300 | if not item:\r | |
301 | continue\r | |
302 | if ':' in item:\r | |
303 | k, v = item.split(':', 1)\r | |
304 | k = k.strip().lower()\r | |
305 | v = v.strip()\r | |
306 | self._info[k] = v\r | |
307 | lastk = k\r | |
308 | elif lastk:\r | |
309 | self._info[lastk] += '\n' + item\r | |
310 | if k == 'content-type':\r | |
311 | self._charset = v.split('charset=')[1]\r | |
312 | elif k == 'plural-forms':\r | |
313 | v = v.split(';')\r | |
314 | plural = v[1].split('plural=')[1]\r | |
315 | self.plural = c2py(plural)\r | |
316 | # Note: we unconditionally convert both msgids and msgstrs to\r | |
317 | # Unicode using the character encoding specified in the charset\r | |
318 | # parameter of the Content-Type header. The gettext documentation\r | |
319 | # strongly encourages msgids to be us-ascii, but some applications\r | |
320 | # require alternative encodings (e.g. Zope's ZCML and ZPT). For\r | |
321 | # traditional gettext applications, the msgid conversion will\r | |
322 | # cause no problems since us-ascii should always be a subset of\r | |
323 | # the charset encoding. We may want to fall back to 8-bit msgids\r | |
324 | # if the Unicode conversion fails.\r | |
325 | if '\x00' in msg:\r | |
326 | # Plural forms\r | |
327 | msgid1, msgid2 = msg.split('\x00')\r | |
328 | tmsg = tmsg.split('\x00')\r | |
329 | if self._charset:\r | |
330 | msgid1 = unicode(msgid1, self._charset)\r | |
331 | tmsg = [unicode(x, self._charset) for x in tmsg]\r | |
332 | for i in range(len(tmsg)):\r | |
333 | catalog[(msgid1, i)] = tmsg[i]\r | |
334 | else:\r | |
335 | if self._charset:\r | |
336 | msg = unicode(msg, self._charset)\r | |
337 | tmsg = unicode(tmsg, self._charset)\r | |
338 | catalog[msg] = tmsg\r | |
339 | # advance to next entry in the seek tables\r | |
340 | masteridx += 8\r | |
341 | transidx += 8\r | |
342 | \r | |
343 | def gettext(self, message):\r | |
344 | missing = object()\r | |
345 | tmsg = self._catalog.get(message, missing)\r | |
346 | if tmsg is missing:\r | |
347 | if self._fallback:\r | |
348 | return self._fallback.gettext(message)\r | |
349 | return message\r | |
350 | # Encode the Unicode tmsg back to an 8-bit string, if possible\r | |
351 | if self._output_charset:\r | |
352 | return tmsg.encode(self._output_charset)\r | |
353 | elif self._charset:\r | |
354 | return tmsg.encode(self._charset)\r | |
355 | return tmsg\r | |
356 | \r | |
357 | def lgettext(self, message):\r | |
358 | missing = object()\r | |
359 | tmsg = self._catalog.get(message, missing)\r | |
360 | if tmsg is missing:\r | |
361 | if self._fallback:\r | |
362 | return self._fallback.lgettext(message)\r | |
363 | return message\r | |
364 | if self._output_charset:\r | |
365 | return tmsg.encode(self._output_charset)\r | |
366 | return tmsg.encode(locale.getpreferredencoding())\r | |
367 | \r | |
368 | def ngettext(self, msgid1, msgid2, n):\r | |
369 | try:\r | |
370 | tmsg = self._catalog[(msgid1, self.plural(n))]\r | |
371 | if self._output_charset:\r | |
372 | return tmsg.encode(self._output_charset)\r | |
373 | elif self._charset:\r | |
374 | return tmsg.encode(self._charset)\r | |
375 | return tmsg\r | |
376 | except KeyError:\r | |
377 | if self._fallback:\r | |
378 | return self._fallback.ngettext(msgid1, msgid2, n)\r | |
379 | if n == 1:\r | |
380 | return msgid1\r | |
381 | else:\r | |
382 | return msgid2\r | |
383 | \r | |
384 | def lngettext(self, msgid1, msgid2, n):\r | |
385 | try:\r | |
386 | tmsg = self._catalog[(msgid1, self.plural(n))]\r | |
387 | if self._output_charset:\r | |
388 | return tmsg.encode(self._output_charset)\r | |
389 | return tmsg.encode(locale.getpreferredencoding())\r | |
390 | except KeyError:\r | |
391 | if self._fallback:\r | |
392 | return self._fallback.lngettext(msgid1, msgid2, n)\r | |
393 | if n == 1:\r | |
394 | return msgid1\r | |
395 | else:\r | |
396 | return msgid2\r | |
397 | \r | |
398 | def ugettext(self, message):\r | |
399 | missing = object()\r | |
400 | tmsg = self._catalog.get(message, missing)\r | |
401 | if tmsg is missing:\r | |
402 | if self._fallback:\r | |
403 | return self._fallback.ugettext(message)\r | |
404 | return unicode(message)\r | |
405 | return tmsg\r | |
406 | \r | |
407 | def ungettext(self, msgid1, msgid2, n):\r | |
408 | try:\r | |
409 | tmsg = self._catalog[(msgid1, self.plural(n))]\r | |
410 | except KeyError:\r | |
411 | if self._fallback:\r | |
412 | return self._fallback.ungettext(msgid1, msgid2, n)\r | |
413 | if n == 1:\r | |
414 | tmsg = unicode(msgid1)\r | |
415 | else:\r | |
416 | tmsg = unicode(msgid2)\r | |
417 | return tmsg\r | |
418 | \r | |
419 | \r | |
420 | # Locate a .mo file using the gettext strategy\r | |
421 | def find(domain, localedir=None, languages=None, all=0):\r | |
422 | # Get some reasonable defaults for arguments that were not supplied\r | |
423 | if localedir is None:\r | |
424 | localedir = _default_localedir\r | |
425 | if languages is None:\r | |
426 | languages = []\r | |
427 | for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):\r | |
428 | val = os.environ.get(envar)\r | |
429 | if val:\r | |
430 | languages = val.split(':')\r | |
431 | break\r | |
432 | if 'C' not in languages:\r | |
433 | languages.append('C')\r | |
434 | # now normalize and expand the languages\r | |
435 | nelangs = []\r | |
436 | for lang in languages:\r | |
437 | for nelang in _expand_lang(lang):\r | |
438 | if nelang not in nelangs:\r | |
439 | nelangs.append(nelang)\r | |
440 | # select a language\r | |
441 | if all:\r | |
442 | result = []\r | |
443 | else:\r | |
444 | result = None\r | |
445 | for lang in nelangs:\r | |
446 | if lang == 'C':\r | |
447 | break\r | |
448 | mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain)\r | |
449 | if os.path.exists(mofile):\r | |
450 | if all:\r | |
451 | result.append(mofile)\r | |
452 | else:\r | |
453 | return mofile\r | |
454 | return result\r | |
455 | \r | |
456 | \r | |
457 | \r | |
458 | # a mapping between absolute .mo file path and Translation object\r | |
459 | _translations = {}\r | |
460 | \r | |
461 | def translation(domain, localedir=None, languages=None,\r | |
462 | class_=None, fallback=False, codeset=None):\r | |
463 | if class_ is None:\r | |
464 | class_ = GNUTranslations\r | |
465 | mofiles = find(domain, localedir, languages, all=1)\r | |
466 | if not mofiles:\r | |
467 | if fallback:\r | |
468 | return NullTranslations()\r | |
469 | raise IOError(ENOENT, 'No translation file found for domain', domain)\r | |
470 | # Avoid opening, reading, and parsing the .mo file after it's been done\r | |
471 | # once.\r | |
472 | result = None\r | |
473 | for mofile in mofiles:\r | |
474 | key = (class_, os.path.abspath(mofile))\r | |
475 | t = _translations.get(key)\r | |
476 | if t is None:\r | |
477 | with open(mofile, 'rb') as fp:\r | |
478 | t = _translations.setdefault(key, class_(fp))\r | |
479 | # Copy the translation object to allow setting fallbacks and\r | |
480 | # output charset. All other instance data is shared with the\r | |
481 | # cached object.\r | |
482 | t = copy.copy(t)\r | |
483 | if codeset:\r | |
484 | t.set_output_charset(codeset)\r | |
485 | if result is None:\r | |
486 | result = t\r | |
487 | else:\r | |
488 | result.add_fallback(t)\r | |
489 | return result\r | |
490 | \r | |
491 | \r | |
492 | def install(domain, localedir=None, unicode=False, codeset=None, names=None):\r | |
493 | t = translation(domain, localedir, fallback=True, codeset=codeset)\r | |
494 | t.install(unicode, names)\r | |
495 | \r | |
496 | \r | |
497 | \r | |
498 | # a mapping b/w domains and locale directories\r | |
499 | _localedirs = {}\r | |
500 | # a mapping b/w domains and codesets\r | |
501 | _localecodesets = {}\r | |
502 | # current global domain, `messages' used for compatibility w/ GNU gettext\r | |
503 | _current_domain = 'messages'\r | |
504 | \r | |
505 | \r | |
506 | def textdomain(domain=None):\r | |
507 | global _current_domain\r | |
508 | if domain is not None:\r | |
509 | _current_domain = domain\r | |
510 | return _current_domain\r | |
511 | \r | |
512 | \r | |
513 | def bindtextdomain(domain, localedir=None):\r | |
514 | global _localedirs\r | |
515 | if localedir is not None:\r | |
516 | _localedirs[domain] = localedir\r | |
517 | return _localedirs.get(domain, _default_localedir)\r | |
518 | \r | |
519 | \r | |
520 | def bind_textdomain_codeset(domain, codeset=None):\r | |
521 | global _localecodesets\r | |
522 | if codeset is not None:\r | |
523 | _localecodesets[domain] = codeset\r | |
524 | return _localecodesets.get(domain)\r | |
525 | \r | |
526 | \r | |
527 | def dgettext(domain, message):\r | |
528 | try:\r | |
529 | t = translation(domain, _localedirs.get(domain, None),\r | |
530 | codeset=_localecodesets.get(domain))\r | |
531 | except IOError:\r | |
532 | return message\r | |
533 | return t.gettext(message)\r | |
534 | \r | |
535 | def ldgettext(domain, message):\r | |
536 | try:\r | |
537 | t = translation(domain, _localedirs.get(domain, None),\r | |
538 | codeset=_localecodesets.get(domain))\r | |
539 | except IOError:\r | |
540 | return message\r | |
541 | return t.lgettext(message)\r | |
542 | \r | |
543 | def dngettext(domain, msgid1, msgid2, n):\r | |
544 | try:\r | |
545 | t = translation(domain, _localedirs.get(domain, None),\r | |
546 | codeset=_localecodesets.get(domain))\r | |
547 | except IOError:\r | |
548 | if n == 1:\r | |
549 | return msgid1\r | |
550 | else:\r | |
551 | return msgid2\r | |
552 | return t.ngettext(msgid1, msgid2, n)\r | |
553 | \r | |
554 | def ldngettext(domain, msgid1, msgid2, n):\r | |
555 | try:\r | |
556 | t = translation(domain, _localedirs.get(domain, None),\r | |
557 | codeset=_localecodesets.get(domain))\r | |
558 | except IOError:\r | |
559 | if n == 1:\r | |
560 | return msgid1\r | |
561 | else:\r | |
562 | return msgid2\r | |
563 | return t.lngettext(msgid1, msgid2, n)\r | |
564 | \r | |
565 | def gettext(message):\r | |
566 | return dgettext(_current_domain, message)\r | |
567 | \r | |
568 | def lgettext(message):\r | |
569 | return ldgettext(_current_domain, message)\r | |
570 | \r | |
571 | def ngettext(msgid1, msgid2, n):\r | |
572 | return dngettext(_current_domain, msgid1, msgid2, n)\r | |
573 | \r | |
574 | def lngettext(msgid1, msgid2, n):\r | |
575 | return ldngettext(_current_domain, msgid1, msgid2, n)\r | |
576 | \r | |
577 | # dcgettext() has been deemed unnecessary and is not implemented.\r | |
578 | \r | |
579 | # James Henstridge's Catalog constructor from GNOME gettext. Documented usage\r | |
580 | # was:\r | |
581 | #\r | |
582 | # import gettext\r | |
583 | # cat = gettext.Catalog(PACKAGE, localedir=LOCALEDIR)\r | |
584 | # _ = cat.gettext\r | |
585 | # print _('Hello World')\r | |
586 | \r | |
587 | # The resulting catalog object currently don't support access through a\r | |
588 | # dictionary API, which was supported (but apparently unused) in GNOME\r | |
589 | # gettext.\r | |
590 | \r | |
591 | Catalog = translation\r |