home *** CD-ROM | disk | FTP | other *** search
/ H4CK3R 14 / hacker14.iso / programacao / pythonwin / python.exe / MSGFMT.PY < prev    next >
Encoding:
Python Source  |  2003-05-09  |  5.6 KB  |  204 lines

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