162
|
1 # -*- coding: utf-8 -*-
|
|
2 #
|
|
3 # Copyright (C) 2007 Edgewall Software
|
|
4 # All rights reserved.
|
|
5 #
|
|
6 # This software is licensed as described in the file COPYING, which
|
|
7 # you should have received as part of this distribution. The terms
|
|
8 # are also available at http://babel.edgewall.org/wiki/License.
|
|
9 #
|
|
10 # This software consists of voluntary contributions made by many
|
|
11 # individuals. For the exact contribution history, see the revision
|
|
12 # history and logs, available at http://babel.edgewall.org/log/.
|
|
13
|
|
14 """Writing of files in the ``gettext`` MO (machine object) format.
|
|
15
|
|
16 :see: `The Format of MO Files
|
|
17 <http://www.gnu.org/software/gettext/manual/gettext.html#MO-Files>`_
|
|
18 """
|
|
19
|
|
20 import array
|
|
21 import struct
|
|
22
|
|
23 def write_mo(fileobj, catalog, use_fuzzy=False):
|
|
24 """Write a catalog to the specified file-like object using the GNU MO file
|
|
25 format.
|
|
26
|
|
27 >>> from babel.messages import Catalog
|
|
28 >>> from gettext import GNUTranslations
|
|
29 >>> from StringIO import StringIO
|
|
30
|
|
31 >>> catalog = Catalog(locale='en_US')
|
|
32 >>> catalog.add('foo', 'Voh')
|
|
33 >>> catalog.add((u'bar', u'baz'), (u'Bahr', u'Batz'))
|
|
34 >>> catalog.add('fuz', 'Futz', flags=['fuzzy'])
|
|
35 >>> buf = StringIO()
|
|
36
|
|
37 >>> write_mo(buf, catalog)
|
|
38 >>> buf.seek(0)
|
|
39 >>> translations = GNUTranslations(fp=buf)
|
|
40 >>> translations.ugettext('foo')
|
|
41 u'Voh'
|
|
42 >>> translations.ungettext('bar', 'baz', 1)
|
|
43 u'Bahr'
|
|
44 >>> translations.ungettext('bar', 'baz', 2)
|
|
45 u'Batz'
|
|
46 >>> translations.ugettext('fuz')
|
|
47 u'fuz'
|
|
48
|
|
49 :param fileobj: the file-like object to write to
|
|
50 :param catalog: the `Catalog` instance
|
|
51 :param use_fuzzy: whether translations marked as "fuzzy" should be included
|
|
52 in the output
|
|
53 """
|
|
54 messages = list(catalog)
|
|
55 if not use_fuzzy:
|
|
56 messages[1:] = [m for m in messages[1:] if not m.fuzzy]
|
|
57 messages.sort(lambda x,y: cmp(x.id, y.id))
|
|
58
|
|
59 ids = strs = ''
|
|
60 offsets = []
|
|
61
|
|
62 for message in messages:
|
|
63 # For each string, we need size and file offset. Each string is NUL
|
|
64 # terminated; the NUL does not count into the size.
|
|
65 if message.pluralizable:
|
|
66 msgid = '\x00'.join([
|
|
67 msgid.encode(catalog.charset) for msgid in message.id
|
|
68 ])
|
|
69 msgstr = '\x00'.join([
|
|
70 msgstr.encode(catalog.charset) for msgstr in message.string
|
|
71 ])
|
|
72 else:
|
|
73 msgid = message.id.encode(catalog.charset)
|
|
74 msgstr = message.string.encode(catalog.charset)
|
|
75 offsets.append((len(ids), len(msgid), len(strs), len(msgstr)))
|
|
76 ids += msgid + '\x00'
|
|
77 strs += msgstr + '\x00'
|
|
78
|
|
79 # The header is 7 32-bit unsigned integers. We don't use hash tables, so
|
|
80 # the keys start right after the index tables.
|
|
81 keystart = 7 * 4 + 16 * len(messages)
|
|
82 valuestart = keystart + len(ids)
|
|
83
|
|
84 # The string table first has the list of keys, then the list of values.
|
|
85 # Each entry has first the size of the string, then the file offset.
|
|
86 koffsets = []
|
|
87 voffsets = []
|
|
88 for o1, l1, o2, l2 in offsets:
|
|
89 koffsets += [l1, o1 + keystart]
|
|
90 voffsets += [l2, o2 + valuestart]
|
|
91 offsets = koffsets + voffsets
|
|
92
|
|
93 fileobj.write(struct.pack('Iiiiiii',
|
|
94 0x950412deL, # magic
|
|
95 0, # version
|
|
96 len(messages), # number of entries
|
|
97 7 * 4, # start of key index
|
|
98 7 * 4 + len(messages) * 8, # start of value index
|
|
99 0, 0 # size and offset of hash table
|
|
100 ) + array.array("i", offsets).tostring() + ids + strs)
|