Updated for 2.1a3
[python/dscho.git] / Tools / i18n / msgfmt.py
blobaa72a075ecf49cb8cab010a02e08a44d282cd146
1 #! /usr/bin/env python
3 # Written by Martin v. Löwis <loewis@informatik.hu-berlin.de>
5 """Generate binary message catalog from textual translation description.
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.
11 Usage: msgfmt.py [OPTIONS] filename.po
13 Options:
15 --help
16 Print this message and exit.
19 --version
20 Display version information and exit.
22 """
24 import sys
25 import getopt
26 import struct
27 import array
29 __version__ = "1.0"
31 MESSAGES = {}
35 def usage(code, msg=''):
36 print >> sys.stderr, __doc__
37 if msg:
38 print >> sys.stderr, msg
39 sys.exit(code)
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
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
94 def make(filename):
95 ID = 1
96 STR = 2
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)
111 section = None
112 fuzzy = 0
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)
159 # Compute output
160 output = generate()
162 # Save output
163 try:
164 open(outfile,"wb").write(output)
165 except IOError,msg:
166 print >> sys.stderr, msg
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)
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
189 for filename in args:
190 make(filename)
193 if __name__ == '__main__':
194 main()