188 def gettext(self, message): |
191 def gettext(self, message): |
189 if self._fallback: |
192 if self._fallback: |
190 return self._fallback.gettext(message) |
193 return self._fallback.gettext(message) |
191 return message |
194 return message |
192 |
195 |
|
196 def pgettext(self, context, message): |
|
197 if self._fallback: |
|
198 return self._fallback.pgettext(context, message) |
|
199 return message |
|
200 |
|
201 def lgettext(self, message): |
|
202 if self._fallback: |
|
203 return self._fallback.lgettext(message) |
|
204 return message |
|
205 |
|
206 def lpgettext(self, context, message): |
|
207 if self._fallback: |
|
208 return self._fallback.lpgettext(context, message) |
|
209 return message |
|
210 |
193 def ngettext(self, msgid1, msgid2, n): |
211 def ngettext(self, msgid1, msgid2, n): |
194 if self._fallback: |
212 if self._fallback: |
195 return self._fallback.ngettext(msgid1, msgid2, n) |
213 return self._fallback.ngettext(msgid1, msgid2, n) |
|
214 if n == 1: |
|
215 return msgid1 |
|
216 else: |
|
217 return msgid2 |
|
218 |
|
219 def npgettext(self, context, msgid1, msgid2, n): |
|
220 if self._fallback: |
|
221 return self._fallback.npgettext(context, msgid1, msgid2, n) |
|
222 if n == 1: |
|
223 return msgid1 |
|
224 else: |
|
225 return msgid2 |
|
226 |
|
227 def lngettext(self, msgid1, msgid2, n): |
|
228 if self._fallback: |
|
229 return self._fallback.lngettext(msgid1, msgid2, n) |
|
230 if n == 1: |
|
231 return msgid1 |
|
232 else: |
|
233 return msgid2 |
|
234 |
|
235 def lnpgettext(self, context, msgid1, msgid2, n): |
|
236 if self._fallback: |
|
237 return self._fallback.lnpgettext(context, msgid1, msgid2, n) |
196 if n == 1: |
238 if n == 1: |
197 return msgid1 |
239 return msgid1 |
198 else: |
240 else: |
199 return msgid2 |
241 return msgid2 |
200 |
242 |
201 def ugettext(self, message): |
243 def ugettext(self, message): |
202 if self._fallback: |
244 if self._fallback: |
203 return self._fallback.ugettext(message) |
245 return self._fallback.ugettext(message) |
204 return unicode(message) |
246 return unicode(message) |
205 |
247 |
|
248 def upgettext(self, context, message): |
|
249 if self._fallback: |
|
250 return self._fallback.upgettext(context, message) |
|
251 return unicode(message) |
|
252 |
206 def ungettext(self, msgid1, msgid2, n): |
253 def ungettext(self, msgid1, msgid2, n): |
207 if self._fallback: |
254 if self._fallback: |
208 return self._fallback.ungettext(msgid1, msgid2, n) |
255 return self._fallback.ungettext(msgid1, msgid2, n) |
|
256 if n == 1: |
|
257 return unicode(msgid1) |
|
258 else: |
|
259 return unicode(msgid2) |
|
260 |
|
261 def unpgettext(self, context, msgid1, msgid2, n): |
|
262 if self._fallback: |
|
263 return self._fallback.unpgettext(context, msgid1, msgid2, n) |
209 if n == 1: |
264 if n == 1: |
210 return unicode(msgid1) |
265 return unicode(msgid1) |
211 else: |
266 else: |
212 return unicode(msgid2) |
267 return unicode(msgid2) |
213 |
268 |
215 return self._info |
270 return self._info |
216 |
271 |
217 def charset(self): |
272 def charset(self): |
218 return self._charset |
273 return self._charset |
219 |
274 |
220 def install(self, unicode=False): |
275 def output_charset(self): |
|
276 return self._output_charset |
|
277 |
|
278 def set_output_charset(self, charset): |
|
279 self._output_charset = charset |
|
280 |
|
281 def install(self, unicode=False, names=None): |
221 import __builtin__ |
282 import __builtin__ |
222 __builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext |
283 __builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext |
|
284 if hasattr(names, "__contains__"): |
|
285 if "gettext" in names: |
|
286 __builtin__.__dict__['gettext'] = __builtin__.__dict__['_'] |
|
287 if "pgettext" in names: |
|
288 __builtin__.__dict__['pgettext'] = (unicode and self.upgettext |
|
289 or self.pgettext) |
|
290 if "ngettext" in names: |
|
291 __builtin__.__dict__['ngettext'] = (unicode and self.ungettext |
|
292 or self.ngettext) |
|
293 if "npgettext" in names: |
|
294 __builtin__.__dict__['npgettext'] = \ |
|
295 (unicode and self.unpgettext or self.npgettext) |
|
296 if "lgettext" in names: |
|
297 __builtin__.__dict__['lgettext'] = self.lgettext |
|
298 if "lpgettext" in names: |
|
299 __builtin__.__dict__['lpgettext'] = self.lpgettext |
|
300 if "lngettext" in names: |
|
301 __builtin__.__dict__['lngettext'] = self.lngettext |
|
302 if "lnpgettext" in names: |
|
303 __builtin__.__dict__['lnpgettext'] = self.lnpgettext |
223 |
304 |
224 |
305 |
225 class GNUTranslations(NullTranslations): |
306 class GNUTranslations(NullTranslations): |
226 # Magic number of .mo files |
307 # Magic number of .mo files |
227 LE_MAGIC = 0x950412deL |
308 LE_MAGIC = 0x950412deL |
228 BE_MAGIC = 0xde120495L |
309 BE_MAGIC = 0xde120495L |
|
310 |
|
311 # The encoding of a msgctxt and a msgid in a .mo file is |
|
312 # msgctxt + "\x04" + msgid (gettext version >= 0.15) |
|
313 CONTEXT_ENCODING = "%s\x04%s" |
229 |
314 |
230 def _parse(self, fp): |
315 def _parse(self, fp): |
231 """Override this method to support alternative .mo formats.""" |
316 """Override this method to support alternative .mo formats.""" |
232 unpack = struct.unpack |
317 unpack = struct.unpack |
233 filename = getattr(fp, 'name', '') |
318 filename = getattr(fp, 'name', '') |
260 else: |
345 else: |
261 raise IOError(0, 'File is corrupt', filename) |
346 raise IOError(0, 'File is corrupt', filename) |
262 # See if we're looking at GNU .mo conventions for metadata |
347 # See if we're looking at GNU .mo conventions for metadata |
263 if mlen == 0: |
348 if mlen == 0: |
264 # Catalog description |
349 # Catalog description |
265 # don't handle multi-lines fields here, and skip |
|
266 # lines which don't look like a header description |
|
267 # (e.g. "header: value") |
|
268 lastk = k = None |
350 lastk = k = None |
269 for item in tmsg.splitlines(): |
351 for item in tmsg.splitlines(): |
270 item = item.strip() |
352 item = item.strip() |
271 if not item or not ':' in item: |
353 if not item: |
272 continue |
354 continue |
273 k, v = item.split(':', 1) |
355 if ':' in item: |
274 k = k.strip().lower() |
356 k, v = item.split(':', 1) |
275 v = v.strip() |
357 k = k.strip().lower() |
276 self._info[k] = v |
358 v = v.strip() |
|
359 self._info[k] = v |
|
360 lastk = k |
|
361 elif lastk: |
|
362 self._info[lastk] += '\n' + item |
277 if k == 'content-type': |
363 if k == 'content-type': |
278 self._charset = v.split('charset=')[1] |
364 self._charset = v.split('charset=')[1] |
279 elif k == 'plural-forms': |
365 elif k == 'plural-forms': |
280 v = v.split(';') |
366 v = v.split(';') |
281 plural = v[1].split('plural=')[1] |
367 plural = v[1].split('plural=')[1] |
313 if tmsg is missing: |
399 if tmsg is missing: |
314 if self._fallback: |
400 if self._fallback: |
315 return self._fallback.gettext(message) |
401 return self._fallback.gettext(message) |
316 return message |
402 return message |
317 # Encode the Unicode tmsg back to an 8-bit string, if possible |
403 # Encode the Unicode tmsg back to an 8-bit string, if possible |
318 if self._charset: |
404 if self._output_charset: |
|
405 return tmsg.encode(self._output_charset) |
|
406 elif self._charset: |
319 return tmsg.encode(self._charset) |
407 return tmsg.encode(self._charset) |
320 return tmsg |
408 return tmsg |
|
409 |
|
410 def pgettext(self, context, message): |
|
411 ctxt_msg_id = self.CONTEXT_ENCODING % (context, message) |
|
412 missing = object() |
|
413 tmsg = self._catalog.get(ctxt_msg_id, missing) |
|
414 if tmsg is missing: |
|
415 if self._fallback: |
|
416 return self._fallback.pgettext(context, message) |
|
417 return message |
|
418 # Encode the Unicode tmsg back to an 8-bit string, if possible |
|
419 if self._output_charset: |
|
420 return tmsg.encode(self._output_charset) |
|
421 elif self._charset: |
|
422 return tmsg.encode(self._charset) |
|
423 return tmsg |
|
424 |
|
425 def lgettext(self, message): |
|
426 missing = object() |
|
427 tmsg = self._catalog.get(message, missing) |
|
428 if tmsg is missing: |
|
429 if self._fallback: |
|
430 return self._fallback.lgettext(message) |
|
431 return message |
|
432 if self._output_charset: |
|
433 return tmsg.encode(self._output_charset) |
|
434 return tmsg.encode(locale.getpreferredencoding()) |
|
435 |
|
436 def lpgettext(self, context, message): |
|
437 ctxt_msg_id = self.CONTEXT_ENCODING % (context, message) |
|
438 missing = object() |
|
439 tmsg = self._catalog.get(ctxt_msg_id, missing) |
|
440 if tmsg is missing: |
|
441 if self._fallback: |
|
442 return self._fallback.lpgettext(context, message) |
|
443 return message |
|
444 if self._output_charset: |
|
445 return tmsg.encode(self._output_charset) |
|
446 return tmsg.encode(locale.getpreferredencoding()) |
321 |
447 |
322 def ngettext(self, msgid1, msgid2, n): |
448 def ngettext(self, msgid1, msgid2, n): |
323 try: |
449 try: |
324 tmsg = self._catalog[(msgid1, self.plural(n))] |
450 tmsg = self._catalog[(msgid1, self.plural(n))] |
325 if self._charset: |
451 if self._output_charset: |
|
452 return tmsg.encode(self._output_charset) |
|
453 elif self._charset: |
326 return tmsg.encode(self._charset) |
454 return tmsg.encode(self._charset) |
327 return tmsg |
455 return tmsg |
328 except KeyError: |
456 except KeyError: |
329 if self._fallback: |
457 if self._fallback: |
330 return self._fallback.ngettext(msgid1, msgid2, n) |
458 return self._fallback.ngettext(msgid1, msgid2, n) |
|
459 if n == 1: |
|
460 return msgid1 |
|
461 else: |
|
462 return msgid2 |
|
463 |
|
464 def npgettext(self, context, msgid1, msgid2, n): |
|
465 ctxt_msg_id = self.CONTEXT_ENCODING % (context, msgid1) |
|
466 try: |
|
467 tmsg = self._catalog[(ctxt_msg_id, self.plural(n))] |
|
468 if self._output_charset: |
|
469 return tmsg.encode(self._output_charset) |
|
470 elif self._charset: |
|
471 return tmsg.encode(self._charset) |
|
472 return tmsg |
|
473 except KeyError: |
|
474 if self._fallback: |
|
475 return self._fallback.npgettext(context, msgid1, msgid2, n) |
|
476 if n == 1: |
|
477 return msgid1 |
|
478 else: |
|
479 return msgid2 |
|
480 |
|
481 def lngettext(self, msgid1, msgid2, n): |
|
482 try: |
|
483 tmsg = self._catalog[(msgid1, self.plural(n))] |
|
484 if self._output_charset: |
|
485 return tmsg.encode(self._output_charset) |
|
486 return tmsg.encode(locale.getpreferredencoding()) |
|
487 except KeyError: |
|
488 if self._fallback: |
|
489 return self._fallback.lngettext(msgid1, msgid2, n) |
|
490 if n == 1: |
|
491 return msgid1 |
|
492 else: |
|
493 return msgid2 |
|
494 |
|
495 def lnpgettext(self, context, msgid1, msgid2, n): |
|
496 ctxt_msg_id = self.CONTEXT_ENCODING % (context, msgid1) |
|
497 try: |
|
498 tmsg = self._catalog[(ctxt_msg_id, self.plural(n))] |
|
499 if self._output_charset: |
|
500 return tmsg.encode(self._output_charset) |
|
501 return tmsg.encode(locale.getpreferredencoding()) |
|
502 except KeyError: |
|
503 if self._fallback: |
|
504 return self._fallback.lnpgettext(context, msgid1, msgid2, n) |
331 if n == 1: |
505 if n == 1: |
332 return msgid1 |
506 return msgid1 |
333 else: |
507 else: |
334 return msgid2 |
508 return msgid2 |
335 |
509 |
340 if self._fallback: |
514 if self._fallback: |
341 return self._fallback.ugettext(message) |
515 return self._fallback.ugettext(message) |
342 return unicode(message) |
516 return unicode(message) |
343 return tmsg |
517 return tmsg |
344 |
518 |
|
519 def upgettext(self, context, message): |
|
520 ctxt_message_id = self.CONTEXT_ENCODING % (context, message) |
|
521 missing = object() |
|
522 tmsg = self._catalog.get(ctxt_message_id, missing) |
|
523 if tmsg is missing: |
|
524 if self._fallback: |
|
525 return self._fallback.upgettext(context, message) |
|
526 return unicode(message) |
|
527 return tmsg |
|
528 |
345 def ungettext(self, msgid1, msgid2, n): |
529 def ungettext(self, msgid1, msgid2, n): |
346 try: |
530 try: |
347 tmsg = self._catalog[(msgid1, self.plural(n))] |
531 tmsg = self._catalog[(msgid1, self.plural(n))] |
348 except KeyError: |
532 except KeyError: |
349 if self._fallback: |
533 if self._fallback: |
350 return self._fallback.ungettext(msgid1, msgid2, n) |
534 return self._fallback.ungettext(msgid1, msgid2, n) |
|
535 if n == 1: |
|
536 tmsg = unicode(msgid1) |
|
537 else: |
|
538 tmsg = unicode(msgid2) |
|
539 return tmsg |
|
540 |
|
541 def unpgettext(self, context, msgid1, msgid2, n): |
|
542 ctxt_message_id = self.CONTEXT_ENCODING % (context, msgid1) |
|
543 try: |
|
544 tmsg = self._catalog[(ctxt_message_id, self.plural(n))] |
|
545 except KeyError: |
|
546 if self._fallback: |
|
547 return self._fallback.unpgettext(context, msgid1, msgid2, n) |
351 if n == 1: |
548 if n == 1: |
352 tmsg = unicode(msgid1) |
549 tmsg = unicode(msgid1) |
353 else: |
550 else: |
354 tmsg = unicode(msgid2) |
551 tmsg = unicode(msgid2) |
355 return tmsg |
552 return tmsg |
412 for mofile in mofiles: |
609 for mofile in mofiles: |
413 key = os.path.abspath(mofile) |
610 key = os.path.abspath(mofile) |
414 t = _translations.get(key) |
611 t = _translations.get(key) |
415 if t is None: |
612 if t is None: |
416 t = _translations.setdefault(key, class_(open(mofile, 'rb'))) |
613 t = _translations.setdefault(key, class_(open(mofile, 'rb'))) |
417 # Copy the translation object to allow setting fallbacks. |
614 # Copy the translation object to allow setting fallbacks and |
418 # All other instance data is shared with the cached object. |
615 # output charset. All other instance data is shared with the |
|
616 # cached object. |
419 t = copy.copy(t) |
617 t = copy.copy(t) |
|
618 if codeset: |
|
619 t.set_output_charset(codeset) |
420 if result is None: |
620 if result is None: |
421 result = t |
621 result = t |
422 else: |
622 else: |
423 result.add_fallback(t) |
623 result.add_fallback(t) |
424 return result |
624 return result |
425 |
625 |
426 |
626 |
427 def install(domain, localedir=None, unicode=False): |
627 def install(domain, localedir=None, unicode=False, codeset=None, names=None): |
428 translation(domain, localedir, fallback=True).install(unicode) |
628 t = translation(domain, localedir, fallback=True, codeset=codeset) |
|
629 t.install(unicode, names) |
429 |
630 |
430 |
631 |
431 |
632 |
432 # a mapping b/w domains and locale directories |
633 # a mapping b/w domains and locale directories |
433 _localedirs = {} |
634 _localedirs = {} |
|
635 # a mapping b/w domains and codesets |
|
636 _localecodesets = {} |
434 # current global domain, `messages' used for compatibility w/ GNU gettext |
637 # current global domain, `messages' used for compatibility w/ GNU gettext |
435 _current_domain = 'messages' |
638 _current_domain = 'messages' |
436 |
639 |
437 |
640 |
438 def textdomain(domain=None): |
641 def textdomain(domain=None): |
441 _current_domain = domain |
644 _current_domain = domain |
442 return _current_domain |
645 return _current_domain |
443 |
646 |
444 |
647 |
445 def bindtextdomain(domain, localedir=None): |
648 def bindtextdomain(domain, localedir=None): |
|
649 global _localedirs |
446 if localedir is not None: |
650 if localedir is not None: |
447 _localedirs[domain] = localedir |
651 _localedirs[domain] = localedir |
448 return _localedirs.get(domain, _default_localedir) |
652 return _localedirs.get(domain, _default_localedir) |
449 |
653 |
450 |
654 |
|
655 def bind_textdomain_codeset(domain, codeset=None): |
|
656 global _localecodesets |
|
657 if codeset is not None: |
|
658 _localecodesets[domain] = codeset |
|
659 return _localecodesets.get(domain) |
|
660 |
|
661 |
451 def dgettext(domain, message): |
662 def dgettext(domain, message): |
452 try: |
663 try: |
453 t = translation(domain, _localedirs.get(domain, None)) |
664 t = translation(domain, _localedirs.get(domain, None), |
|
665 codeset=_localecodesets.get(domain)) |
454 except IOError: |
666 except IOError: |
455 return message |
667 return message |
456 return t.gettext(message) |
668 return t.gettext(message) |
457 |
669 |
|
670 def dpgettext(domain, context, message): |
|
671 try: |
|
672 t = translation(domain, _localedirs.get(domain, None), |
|
673 codeset=_localecodesets.get(domain)) |
|
674 except IOError: |
|
675 return message |
|
676 return t.pgettext(context, message) |
|
677 |
|
678 def ldgettext(domain, message): |
|
679 try: |
|
680 t = translation(domain, _localedirs.get(domain, None), |
|
681 codeset=_localecodesets.get(domain)) |
|
682 except IOError: |
|
683 return message |
|
684 return t.lgettext(message) |
|
685 |
|
686 def ldpgettext(domain, context, message): |
|
687 try: |
|
688 t = translation(domain, _localedirs.get(domain, None), |
|
689 codeset=_localecodesets.get(domain)) |
|
690 except IOError: |
|
691 return message |
|
692 return t.lpgettext(context, message) |
458 |
693 |
459 def dngettext(domain, msgid1, msgid2, n): |
694 def dngettext(domain, msgid1, msgid2, n): |
460 try: |
695 try: |
461 t = translation(domain, _localedirs.get(domain, None)) |
696 t = translation(domain, _localedirs.get(domain, None), |
|
697 codeset=_localecodesets.get(domain)) |
462 except IOError: |
698 except IOError: |
463 if n == 1: |
699 if n == 1: |
464 return msgid1 |
700 return msgid1 |
465 else: |
701 else: |
466 return msgid2 |
702 return msgid2 |
467 return t.ngettext(msgid1, msgid2, n) |
703 return t.ngettext(msgid1, msgid2, n) |
468 |
704 |
|
705 def dnpgettext(domain, context, msgid1, msgid2, n): |
|
706 try: |
|
707 t = translation(domain, _localedirs.get(domain, None), |
|
708 codeset=_localecodesets.get(domain)) |
|
709 except IOError: |
|
710 if n == 1: |
|
711 return msgid1 |
|
712 else: |
|
713 return msgid2 |
|
714 return t.npgettext(context, msgid1, msgid2, n) |
|
715 |
|
716 def ldngettext(domain, msgid1, msgid2, n): |
|
717 try: |
|
718 t = translation(domain, _localedirs.get(domain, None), |
|
719 codeset=_localecodesets.get(domain)) |
|
720 except IOError: |
|
721 if n == 1: |
|
722 return msgid1 |
|
723 else: |
|
724 return msgid2 |
|
725 return t.lngettext(msgid1, msgid2, n) |
|
726 |
|
727 def ldnpgettext(domain, context, msgid1, msgid2, n): |
|
728 try: |
|
729 t = translation(domain, _localedirs.get(domain, None), |
|
730 codeset=_localecodesets.get(domain)) |
|
731 except IOError: |
|
732 if n == 1: |
|
733 return msgid1 |
|
734 else: |
|
735 return msgid2 |
|
736 return t.lnpgettext(context, msgid1, msgid2, n) |
469 |
737 |
470 def gettext(message): |
738 def gettext(message): |
471 return dgettext(_current_domain, message) |
739 return dgettext(_current_domain, message) |
472 |
740 |
|
741 def pgettext(context, message): |
|
742 return dpgettext(_current_domain, context, message) |
|
743 |
|
744 def lgettext(message): |
|
745 return ldgettext(_current_domain, message) |
|
746 |
|
747 def lpgettext(context, message): |
|
748 return ldpgettext(_current_domain, context, message) |
473 |
749 |
474 def ngettext(msgid1, msgid2, n): |
750 def ngettext(msgid1, msgid2, n): |
475 return dngettext(_current_domain, msgid1, msgid2, n) |
751 return dngettext(_current_domain, msgid1, msgid2, n) |
476 |
752 |
|
753 def npgettext(context, msgid1, msgid2, n): |
|
754 return dnpgettext(_current_domain, context, msgid1, msgid2, n) |
|
755 |
|
756 def lngettext(msgid1, msgid2, n): |
|
757 return ldngettext(_current_domain, msgid1, msgid2, n) |
|
758 |
|
759 def lnpgettext(context, msgid1, msgid2, n): |
|
760 return ldnpgettext(_current_domain, context, msgid1, msgid2, n) |
477 |
761 |
478 # dcgettext() has been deemed unnecessary and is not implemented. |
762 # dcgettext() has been deemed unnecessary and is not implemented. |
479 |
763 |
480 # James Henstridge's Catalog constructor from GNOME gettext. Documented usage |
764 # James Henstridge's Catalog constructor from GNOME gettext. Documented usage |
481 # was: |
765 # was: |