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