source: drbl_ui/langs/msgfmt.py @ 192

Last change on this file since 192 was 20, checked in by chris, 17 years ago
File size: 5.4 KB
Line 
1#! /usr/bin/env python
2# -*- coding: iso-8859-1 -*-
3# Written by Martin v. L饖is <loewis@informatik.hu-berlin.de>
4
5"""Generate binary message catalog from textual translation description.
6
7This program converts a textual Uniforum-style message catalog (.po file) into
8a binary GNU catalog (.mo file).  This is essentially the same function as the
9GNU msgfmt program, however, it is a simpler implementation.
10
11Usage: msgfmt.py [OPTIONS] filename.po
12
13Options:
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
28import sys
29import os
30import getopt
31import struct
32import array
33
34__version__ = "1.1"
35
36MESSAGES = {}
37
38
39
40def usage(code, msg=''):
41    print >> sys.stderr, __doc__
42    if msg:
43        print >> sys.stderr, msg
44    sys.exit(code)
45
46
47
48def 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
56def 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
99def 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
175def 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
202if __name__ == '__main__':
203    main()
204
205
Note: See TracBrowser for help on using the repository browser.