Package turbogears :: Package i18n :: Package pygettext :: Module msgfmt

Source Code for Module turbogears.i18n.pygettext.msgfmt

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