home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 15 / AACD15.ISO / AACD / Programming / Python2 / Python20_source / Tools / i18n / msgfmt.py next >
Encoding:
Python Source  |  2000-10-25  |  5.0 KB  |  195 lines

  1. #! /usr/bin/env python
  2.  
  3. # Written by Martin v. Löwis <loewis@informatik.hu-berlin.de>
  4.  
  5. """Generate binary message catalog from textual translation description.
  6.  
  7. This program converts a textual Uniforum-style message catalog (.po file) into
  8. a binary GNU catalog (.mo file).  This is essentially the same function as the
  9. GNU msgfmt program, however, it is a simpler implementation.
  10.  
  11. Usage: msgfmt.py [OPTIONS] filename.po
  12.  
  13. Options:
  14.     -h
  15.     --help
  16.         Print this message and exit.
  17.  
  18.     -V
  19.     --version
  20.         Display version information and exit.
  21.  
  22. """
  23.  
  24. import sys
  25. import getopt
  26. import struct
  27. import array
  28.  
  29. __version__ = "1.0"
  30.  
  31. MESSAGES = {}
  32.  
  33.  
  34.  
  35. def usage(code, msg=''):
  36.     print >> sys.stderr, __doc__
  37.     if msg:
  38.         print >> sys.stderr, msg
  39.     sys.exit(code)
  40.  
  41.  
  42.  
  43. def add(id, str, fuzzy):
  44.     "Add a non-fuzzy translation to the dictionary."
  45.     global MESSAGES
  46.     if not fuzzy and str:
  47.         MESSAGES[id] = str
  48.  
  49.  
  50.  
  51. def generate():
  52.     "Return the generated output."
  53.     global MESSAGES
  54.     keys = MESSAGES.keys()
  55.     # the keys are sorted in the .mo file
  56.     keys.sort()
  57.     offsets = []
  58.     ids = strs = ''
  59.     for id in keys:
  60.         # For each string, we need size and file offset.  Each string is NUL
  61.         # terminated; the NUL does not count into the size.
  62.         offsets.append((len(ids), len(id), len(strs), len(MESSAGES[id])))
  63.         ids += id + '\0'
  64.         strs += MESSAGES[id] + '\0'
  65.     output = ''
  66.     # The header is 7 32-bit unsigned integers.  We don't use hash tables, so
  67.     # the keys start right after the index tables.
  68.     # translated string.
  69.     keystart = 7*4+16*len(keys)
  70.     # and the values start after the keys
  71.     valuestart = keystart + len(ids)
  72.     koffsets = []
  73.     voffsets = []
  74.     # The string table first has the list of keys, then the list of values.
  75.     # Each entry has first the size of the string, then the file offset.
  76.     for o1, l1, o2, l2 in offsets:
  77.         koffsets += [l1, o1+keystart]
  78.         voffsets += [l2, o2+valuestart]
  79.     offsets = koffsets + voffsets
  80.     output = struct.pack("iiiiiii",
  81.                          0x950412de,        # Magic
  82.                          0,                 # Version
  83.                          len(keys),         # # of entries
  84.                          7*4,               # start of key index
  85.                          7*4+len(keys)*8,   # start of value index
  86.                          0, 0)              # size and offset of hash table
  87.     output += array.array("i", offsets).tostring()
  88.     output += ids
  89.     output += strs
  90.     return output
  91.  
  92.  
  93.  
  94. def make(filename):
  95.     ID = 1
  96.     STR = 2
  97.  
  98.     # Compute .mo name from .po name
  99.     if filename.endswith('.po'):
  100.         infile = filename
  101.         outfile = filename[:-2] + 'mo'
  102.     else:
  103.         infile = filename + '.po'
  104.         outfile = filename + '.mo'
  105.     try:
  106.         lines = open(infile).readlines()
  107.     except IOError, msg:
  108.         print >> sys.stderr, msg
  109.         sys.exit(1)
  110.     
  111.     section = None
  112.     fuzzy = 0
  113.  
  114.     # Parse the catalog
  115.     lno = 0
  116.     for l in lines:
  117.         lno += 1
  118.         # If we get a comment line after a msgstr, this is a new entry
  119.         if l[0] == '#' and section == STR:
  120.             add(msgid, msgstr, fuzzy)
  121.             section = None
  122.             fuzzy = 0
  123.         # Record a fuzzy mark
  124.         if l[:2] == '#,' and l.find('fuzzy'):
  125.             fuzzy = 1
  126.         # Skip comments
  127.         if l[0] == '#':
  128.             continue
  129.         # Now we are in a msgid section, output previous section
  130.         if l.startswith('msgid'):
  131.             if section == STR:
  132.                 add(msgid, msgstr, fuzzy)
  133.             section = ID
  134.             l = l[5:]
  135.             msgid = msgstr = ''
  136.         # Now we are in a msgstr section
  137.         elif l.startswith('msgstr'):
  138.             section = STR
  139.             l = l[6:]
  140.         # Skip empty lines
  141.         l = l.strip()
  142.         if not l:
  143.             continue
  144.         # XXX: Does this always follow Python escape semantics?
  145.         l = eval(l)
  146.         if section == ID:
  147.             msgid += l
  148.         elif section == STR:
  149.             msgstr += l
  150.         else:
  151.             print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \
  152.                   'before:'
  153.             print >> sys.stderr, l
  154.             sys.exit(1)
  155.     # Add last entry
  156.     if section == STR:
  157.         add(msgid, msgstr, fuzzy)
  158.  
  159.     # Compute output
  160.     output = generate()
  161.  
  162.     # Save output
  163.     try:
  164.         open(outfile,"wb").write(output)
  165.     except IOError,msg:
  166.         print >> sys.stderr, msg
  167.                       
  168.  
  169.  
  170. def main():
  171.     try:
  172.         opts, args = getopt.getopt(sys.argv[1:], 'hV', ['help','version'])
  173.     except getopt.error, msg:
  174.         usage(1, msg)
  175.  
  176.     # parse options
  177.     for opt, arg in opts:
  178.         if opt in ('-h', '--help'):
  179.             usage(0)
  180.         elif opt in ('-V', '--version'):
  181.             print >> sys.stderr, "msgfmt.py", __version__
  182.             sys.exit(0)
  183.     # do it
  184.     if not args:
  185.         print >> sys.stderr, 'No input file given'
  186.         print >> sys.stderr, "Try `msgfmt --help' for more information."
  187.         return
  188.  
  189.     for filename in args:
  190.         make(filename)
  191.  
  192.  
  193. if __name__ == '__main__':
  194.     main()
  195.