fintl.py
上传用户:gyjinxi
上传日期:2007-01-04
资源大小:159k
文件大小:9k
源码类别:

WEB邮件程序

开发平台:

Python

  1. #!/usr/bin/python ## vim:ts=4:et:nowrap
  2. """i18n (multiple language) support.  Reads .mo files from GNU gettext msgfmt
  3. If you want to prepare your Python programs for i18n you could simply
  4. add the following lines to the top of a BASIC_MAIN module of your py-program:
  5.     try:
  6.         import fintl
  7.         gettext = fintl.gettext
  8.         fintl.bindtextdomain(YOUR_PROGRAM, YOUR_LOCALEDIR)
  9.         fintl.textdomain(YOUR_PROGRAM)
  10.     except ImportError:
  11.         def gettext(msg):
  12.             return msg
  13.     _ = gettext
  14. and/or also add the following to the top of any module containing messages:
  15.     import BASIC_MAIN
  16.     _ = BASIC_MAIN.gettext
  17.             
  18. Now you could use _("....") everywhere instead of "...." for message texts.
  19. Once you have written your internationalized program, you can use
  20. the suite of utility programs contained in the GNU gettext package to aid
  21. the translation into other languages.  
  22. You ARE NOT REQUIRED to release the sourcecode of your program, since 
  23. linking of your program against GPL code is avoided by this module.  
  24. Although it is possible to use the GNU gettext library by using the 
  25. *intl.so* module written by Martin von L鰓is if this is available.  But it is 
  26. not required to use it in the  first place.
  27. """
  28. # Copyright 1999 by <mailto: pf@artcom-gmbh.de> (Peter Funk)
  29. #  
  30. #                         All Rights Reserved
  31. #
  32. # Permission to use, copy, modify, and distribute this software and its
  33. # documentation for any purpose and without fee is hereby granted,
  34. # provided that the above copyright notice appear in all copies.
  35. # ArtCom GmbH AND Peter Funk DISCLAIMS ALL WARRANTIES WITH REGARD TO
  36. # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  37. # AND FITNESS, IN NO EVENT SHALL ArtCom GmBH or Peter Funk BE LIABLE
  38. # FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  39. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  40. # AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  41. # OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  42. _default_localedir = '/usr/share/locale'
  43. _default_domain = 'python'
  44. # check out, if Martin v. L鰓is 'intl' module interface to the GNU gettext
  45. # library is available and use it only, if it is available: 
  46. try:
  47.     from intl import *
  48. except ImportError:
  49.     # now do what the gettext library provides in pure Python:
  50.     error = 'fintl.error'
  51.     # some globals preserving state:
  52.     _languages = []
  53.     _default_mo = None # This is default message outfile used by 'gettext'
  54.     _loaded_mos = {}   # This is a dictionary of loaded message output files
  55.     # some small little helper routines:
  56.     def _check_env():
  57.         """examine language enviroment variables and return list of languages"""
  58.         # TODO: This should somehow try to find out locale information on
  59.         #       Non-unix platforms like WinXX and MacOS.  Suggestions welcome!
  60.         languages = []
  61.         import os, string
  62.         for envvar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
  63.             if os.environ.has_key(envvar):
  64.                 languages = string.split(os.environ[envvar], ':')
  65.                 break
  66.         # use locale 'C' as default fallback:
  67.         if 'C' not in _languages:
  68.             languages.append('C')
  69.         return languages
  70.     # Utility function used to decode binary .mo file header and seek tables:
  71.     def _decode_Word(bin):
  72.         # This assumes little endian (intel, vax) byte order.
  73.         return  ord(bin[0])        + (ord(bin[1]) <<  8) + 
  74.                (ord(bin[2]) << 16) + (ord(bin[3]) << 24)
  75.     # Now the methods designed to be used from outside:
  76.     def gettext(message):
  77.         """return localized version of a 'message' string"""
  78.         if _default_mo is None: 
  79.             textdomain()
  80.         return _default_mo.gettext(message)
  81.     _ = gettext
  82.     def dgettext(domain, message):
  83.         """like gettext but looks up 'message' in a special 'domain'"""
  84.         # This may useful for larger software systems
  85.         if not _loaded_mos.has_key(domain):
  86.             raise error, "No '" + domain + "' message domain"
  87.         return _loaded_mos[domain].gettext(message)
  88.     class _MoDict:
  89.         """read a .mo file into a python dictionary"""
  90.         __MO_MAGIC = 0x950412de # Magic number of .mo files
  91.         def __init__(self, domain=_default_domain, localedir=_default_localedir):
  92.             global _languages
  93.             self.catalog = {}
  94.             self.domain = domain
  95.             self.localedir = localedir
  96.             # delayed access to environment variables:
  97.             if not _languages:
  98.                 _languages = _check_env()
  99.             for self.lang in _languages:
  100.                 if self.lang == 'C':
  101.                     return
  102.                 mo_filename = "%s//%s/LC_MESSAGES/%s.mo" % (
  103.                                                   localedir, self.lang, domain)
  104.                 try:
  105.                      buffer = open(mo_filename, "rb").read()
  106.                      break
  107.                 except IOError:
  108.                      pass
  109.             else:
  110.                 return # assume C locale
  111.             # Decode the header of the .mo file (5 little endian 32 bit words):
  112.             if _decode_Word(buffer[:4]) != self.__MO_MAGIC :
  113.                 raise error, '%s seems not be a valid .mo file' % mo_filename
  114.             self.mo_version = _decode_Word(buffer[4:8])
  115.             num_messages    = _decode_Word(buffer[8:12])
  116.             master_index    = _decode_Word(buffer[12:16])
  117.             transl_index    = _decode_Word(buffer[16:20])
  118.             buf_len = len(buffer)
  119.             # now put all messages from the .mo file buffer in the catalog dict:
  120.             for i in xrange(0, num_messages):
  121.                 start_master= _decode_Word(buffer[master_index+4:master_index+8])
  122.                 end_master  = start_master + 
  123.                               _decode_Word(buffer[master_index:master_index+4])
  124.                 start_transl= _decode_Word(buffer[transl_index+4:transl_index+8])
  125.                 end_transl  = start_transl + 
  126.                               _decode_Word(buffer[transl_index:transl_index+4])
  127.                 if end_master <= buf_len and end_transl <= buf_len:
  128.                     self.catalog[buffer[start_master:end_master]]=
  129.                                  buffer[start_transl:end_transl]
  130.                 else: 
  131.                     raise error, ".mo file '%s' is corrupt" % mo_filename
  132.                 # advance to the next entry in seek tables:
  133.                 master_index= master_index + 8
  134.                 transl_index= transl_index + 8
  135.         def gettext(self, message):
  136.             """return the translation of a given message"""
  137.             try:
  138.                 return self.catalog[message]
  139.             except KeyError:
  140.                 return message
  141.         # _MoDict instances may be also accessed using mo[msg] or mo(msg):
  142.         __getitem = gettext
  143.         __call__ = gettext
  144.     def textdomain(domain=_default_domain):
  145.         """Sets the 'domain' to be used by this program. Defaults to 'python'"""
  146.         global _default_mo
  147.         if not _loaded_mos.has_key(domain):
  148.              _loaded_mos[domain] = _MoDict(domain)
  149.         _default_mo = _loaded_mos[domain]
  150.     def bindtextdomain(domain, localedir=_default_localedir):
  151.         global _default_mo
  152.         if not _loaded_mos.has_key(domain):
  153.             _loaded_mos[domain] = _MoDict(domain, localedir)
  154.         if _default_mo is not None: 
  155.             _default_mo = _loaded_mos[domain]
  156.     def translator(domain=_default_domain, localedir=_default_localedir):
  157.         """returns a gettext compatible function object
  158.         
  159.            which is bound to the domain given as parameter"""
  160.         pass  # TODO implement this 
  161. def _testdriver(argv):
  162.     message   = ""
  163.     domain    = _default_domain
  164.     localedir = _default_localedir
  165.     if len(argv) > 1:
  166.         message = argv[1]
  167.         if len(argv) > 2:
  168.             domain = argv[2]
  169.             if len(argv) > 3:
  170.                 localedir = argv[3]
  171.     # now perform some testing of this module:
  172.     bindtextdomain(domain, localedir)
  173.     textdomain(domain)
  174.     info = gettext('')  # this is where special info is often stored
  175.     if info:
  176.         print ".mo file for domain %s in %s contains:" % (domain, localedir)
  177.         print info
  178.     else:
  179.         print ".mo file contains no info"
  180.     if message:
  181.         print "Translation of '"+ message+ "' is '"+ _(message)+ "'"
  182.     else:
  183.         for msg in ("Cancel", "No", "OK", "Quit", "Yes"):
  184.             print "Translation of '"+ msg + "' is '"+ _(msg)+ "'"
  185. if __name__ == '__main__':
  186.     import sys
  187.     if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "-?"):
  188.         print "Usage :", sys.argv[0], "[ MESSAGE [ DOMAIN [ LOCALEDIR ]]]"
  189.     _testdriver(sys.argv)