Mercurial > pyntnclick
comparison scripts/msgfmt.py @ 854:3577c51029f1 default tip
Remove Suspended Sentence. pyntnclick is the library we extracted from it
author | Stefano Rivera <stefano@rivera.za.net> |
---|---|
date | Sat, 21 Jun 2014 22:15:54 +0200 |
parents | f95830b58336 |
children |
comparison
equal
deleted
inserted
replaced
853:f95830b58336 | 854:3577c51029f1 |
---|---|
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 -s | |
28 --statistics | |
29 Display fuzzy and untranslated counts | |
30 """ | |
31 | |
32 import os | |
33 import sys | |
34 import ast | |
35 import getopt | |
36 import struct | |
37 import array | |
38 | |
39 __version__ = "1.1" | |
40 | |
41 MESSAGES = {} | |
42 | |
43 total_fuzzy = 0 | |
44 total_untrans = 0 | |
45 | |
46 | |
47 def usage(code, msg=''): | |
48 print >> sys.stderr, __doc__ | |
49 if msg: | |
50 print >> sys.stderr, msg | |
51 sys.exit(code) | |
52 | |
53 | |
54 | |
55 def add(id, str, fuzzy): | |
56 "Add a non-fuzzy translation to the dictionary." | |
57 global MESSAGES | |
58 global total_fuzzy | |
59 global total_untrans | |
60 if not fuzzy and str: | |
61 MESSAGES[id] = str | |
62 if fuzzy and str: | |
63 total_fuzzy += 1 | |
64 elif not str: | |
65 total_untrans += 1 | |
66 | |
67 | |
68 | |
69 def generate(): | |
70 "Return the generated output." | |
71 global MESSAGES | |
72 keys = MESSAGES.keys() | |
73 # the keys are sorted in the .mo file | |
74 keys.sort() | |
75 offsets = [] | |
76 ids = strs = '' | |
77 for id in keys: | |
78 # For each string, we need size and file offset. Each string is NUL | |
79 # terminated; the NUL does not count into the size. | |
80 offsets.append((len(ids), len(id), len(strs), len(MESSAGES[id]))) | |
81 ids += id + '\0' | |
82 strs += MESSAGES[id] + '\0' | |
83 output = '' | |
84 # The header is 7 32-bit unsigned integers. We don't use hash tables, so | |
85 # the keys start right after the index tables. | |
86 # translated string. | |
87 keystart = 7*4+16*len(keys) | |
88 # and the values start after the keys | |
89 valuestart = keystart + len(ids) | |
90 koffsets = [] | |
91 voffsets = [] | |
92 # The string table first has the list of keys, then the list of values. | |
93 # Each entry has first the size of the string, then the file offset. | |
94 for o1, l1, o2, l2 in offsets: | |
95 koffsets += [l1, o1+keystart] | |
96 voffsets += [l2, o2+valuestart] | |
97 offsets = koffsets + voffsets | |
98 output = struct.pack("Iiiiiii", | |
99 0x950412deL, # Magic | |
100 0, # Version | |
101 len(keys), # # of entries | |
102 7*4, # start of key index | |
103 7*4+len(keys)*8, # start of value index | |
104 0, 0) # size and offset of hash table | |
105 output += array.array("i", offsets).tostring() | |
106 output += ids | |
107 output += strs | |
108 return output | |
109 | |
110 | |
111 | |
112 def make(filename, outfile): | |
113 ID = 1 | |
114 STR = 2 | |
115 | |
116 # Compute .mo name from .po name and arguments | |
117 if filename.endswith('.po'): | |
118 infile = filename | |
119 else: | |
120 infile = filename + '.po' | |
121 if outfile is None: | |
122 outfile = os.path.splitext(infile)[0] + '.mo' | |
123 | |
124 try: | |
125 lines = open(infile).readlines() | |
126 except IOError, msg: | |
127 print >> sys.stderr, msg | |
128 sys.exit(1) | |
129 | |
130 section = None | |
131 fuzzy = 0 | |
132 | |
133 # Parse the catalog | |
134 lno = 0 | |
135 for l in lines: | |
136 lno += 1 | |
137 # If we get a comment line after a msgstr, this is a new entry | |
138 if l[0] == '#' and section == STR: | |
139 add(msgid, msgstr, fuzzy) | |
140 section = None | |
141 fuzzy = 0 | |
142 # Record a fuzzy mark | |
143 if l[:2] == '#,' and 'fuzzy' in l: | |
144 fuzzy = 1 | |
145 # Skip comments | |
146 if l[0] == '#': | |
147 continue | |
148 # Now we are in a msgid section, output previous section | |
149 if l.startswith('msgid') and not l.startswith('msgid_plural'): | |
150 if section == STR: | |
151 add(msgid, msgstr, fuzzy) | |
152 section = ID | |
153 l = l[5:] | |
154 msgid = msgstr = '' | |
155 is_plural = False | |
156 # This is a message with plural forms | |
157 elif l.startswith('msgid_plural'): | |
158 if section != ID: | |
159 print >> sys.stderr, 'msgid_plural not preceeded by msgid on %s:%d' %\ | |
160 (infile, lno) | |
161 sys.exit(1) | |
162 l = l[12:] | |
163 msgid += '\0' # separator of singular and plural | |
164 is_plural = True | |
165 # Now we are in a msgstr section | |
166 elif l.startswith('msgstr'): | |
167 section = STR | |
168 if l.startswith('msgstr['): | |
169 if not is_plural: | |
170 print >> sys.stderr, 'plural without msgid_plural on %s:%d' %\ | |
171 (infile, lno) | |
172 sys.exit(1) | |
173 l = l.split(']', 1)[1] | |
174 if msgstr: | |
175 msgstr += '\0' # Separator of the various plural forms | |
176 else: | |
177 if is_plural: | |
178 print >> sys.stderr, 'indexed msgstr required for plural on %s:%d' %\ | |
179 (infile, lno) | |
180 sys.exit(1) | |
181 l = l[6:] | |
182 # Skip empty lines | |
183 l = l.strip() | |
184 if not l: | |
185 continue | |
186 l = ast.literal_eval(l) | |
187 if section == ID: | |
188 msgid += l | |
189 elif section == STR: | |
190 msgstr += l | |
191 else: | |
192 print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \ | |
193 'before:' | |
194 print >> sys.stderr, l | |
195 sys.exit(1) | |
196 # Add last entry | |
197 if section == STR: | |
198 add(msgid, msgstr, fuzzy) | |
199 | |
200 # Compute output | |
201 output = generate() | |
202 | |
203 try: | |
204 open(outfile,"wb").write(output) | |
205 except IOError,msg: | |
206 print >> sys.stderr, msg | |
207 | |
208 | |
209 | |
210 def main(): | |
211 try: | |
212 opts, args = getopt.getopt(sys.argv[1:], 'hVso:', | |
213 ['help', 'version', 'output-file=', | |
214 'statistics']) | |
215 except getopt.error, msg: | |
216 usage(1, msg) | |
217 | |
218 outfile = None | |
219 # parse options | |
220 stats = False | |
221 global total_fuzzy | |
222 global total_untrans | |
223 | |
224 for opt, arg in opts: | |
225 if opt in ('-h', '--help'): | |
226 usage(0) | |
227 elif opt in ('-V', '--version'): | |
228 print >> sys.stderr, "msgfmt.py", __version__ | |
229 sys.exit(0) | |
230 elif opt in ('-o', '--output-file'): | |
231 outfile = arg | |
232 elif opt in ('-s', '--statistics'): | |
233 stats = True | |
234 # do it | |
235 if not args: | |
236 print >> sys.stderr, 'No input file given' | |
237 print >> sys.stderr, "Try `msgfmt --help' for more information." | |
238 return | |
239 | |
240 for filename in args: | |
241 make(filename, outfile) | |
242 | |
243 if stats: | |
244 print '%s: %d fuzzy strings, %d untranslated strings' % (filename, | |
245 total_fuzzy, total_untrans) | |
246 | |
247 | |
248 if __name__ == '__main__': | |
249 main() |