]> git.proxmox.com Git - mirror_edk2.git/blame - AppPkg/Applications/Python/Python-2.7.2/Lib/gettext.py
EmbeddedPkg: Extend NvVarStoreFormattedLib LIBRARY_CLASS
[mirror_edk2.git] / AppPkg / Applications / Python / Python-2.7.2 / Lib / gettext.py
CommitLineData
4710c53d 1"""Internationalization and localization support.\r
2\r
3This module provides internationalization (I18N) and localization (L10N)\r
4support for your Python programs by providing an interface to the GNU gettext\r
5message catalog library.\r
6\r
7I18N refers to the operation by which a program is made aware of multiple\r
8languages. L10N refers to the adaptation of your program, once\r
9internationalized, 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
49import locale, copy, os, re, struct, sys\r
50from 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
61def 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
75def 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
130def _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
173class 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
257class 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
421def 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
461def 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
492def 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
506def 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
513def 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
520def 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
527def 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
535def 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
543def 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
554def 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
565def gettext(message):\r
566 return dgettext(_current_domain, message)\r
567\r
568def lgettext(message):\r
569 return ldgettext(_current_domain, message)\r
570\r
571def ngettext(msgid1, msgid2, n):\r
572 return dngettext(_current_domain, msgid1, msgid2, n)\r
573\r
574def 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
591Catalog = translation\r