1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
#!/usr/bin/env python
""" i18n handler module for OpenERP documentation
This module is simply a collection of utilities for managing i18n in
the OpenERP documentation: although the documentation has been
switched to Sphinx's i18n support
(http://sphinx-doc.org/latest/intl.html) some tasks still have to be
implemented "by hand", mostly the glue between various services:
* Sphinx's gettext builder exports all POT in the same
directory. Rosetta wants a directory per pot (and domain). The
``export`` tasks handles performing the gettext build and moving
everything to the right place under the i18n/ folder
* Rosetta only generates PO files, but Sphinx uses the standard
library's gettext support, which only uses MO files
(http://docs.python.org/2/library/gettext.html#gettext.translation). This
means either the MO files have to be committed (distateful) or they
have to be generated on the fly. The ``compile`` task does the
latter, and the ``make`` task will automatically ``compile`` for the
language it needs if invoked with a language specified
* Could this have been a makefile? Maybe, but then it coudn't be
imported from a Python script, and there's way too much string
munging for my tastes.
"""
import errno
import os
import shutil
import sys
try: import argparse
except ImportError:
print 'Requires argparse'
sys.exit(1)
try: from babel.messages import pofile, mofile
except ImportError:
print 'Requires babel'
sys.exit(1)
try: from sphinx.application import Sphinx
except ImportError:
print 'Requires sphinx'
sys.exit(1)
__all__ = ['export', 'make', 'compile', 'list_languages']
def move_template(name):
""" Moves a template ``name`` from build/gettext/$name.pot to
i18n/$name/$name.pot.
If there was already a template at the destination path, removes
it before applying the move.
"""
fname = "%s.pot" % name
dest = os.path.join('i18n', name, fname)
try: os.remove(dest)
except OSError, e:
# only ignore trying to remove a file which does not exist
if e.errno != errno.ENOENT:
raise
shutil.move(os.path.join('build', 'gettext', fname), dest)
def export(args=None):
""" Exports the documentation to PO template files, and moves all
generated templates to the right place under the i18n/ directory
"""
app = Sphinx('source', 'source', 'build/gettext', 'build/gettext/.doctrees', 'gettext')
app.build(force_all=True)
for f in os.listdir('build/gettext'):
name, ext = os.path.splitext(f)
if ext != '.pot': continue
move_template(name)
def make(args=None):
""" Compiles the documentation to HTML. If a lang is provided via
`args`, compiles PO files to MO and builds the localized documentation
"""
language = getattr(args, 'language', None)
if language:
compile(args)
dest = args.output
doctrees = os.path.join(dest, '.doctrees')
app = Sphinx('source', 'source', dest, doctrees, 'html', {
'language': language,
})
app.build()
def compile(args=None):
""" Compiles PO files below i18n to MO files
"""
pairs = ((domain, os.path.splitext(pofile)[0])
for domain in os.listdir('i18n')
for pofile in os.listdir(os.path.join('i18n', domain))
if pofile.endswith('.po'))
if getattr(args, 'language', None):
pairs = ((domain, lang) for domain, lang in pairs
if lang == args.language)
for domain, lang in pairs:
infile = os.path.join('i18n', domain, '%s.po' % lang)
outdir = os.path.join('build', 'mofiles', lang, 'LC_MESSAGES')
outfile = os.path.join(outdir, '%s.mo' % domain)
with open(infile, 'rb') as po:
try: os.makedirs(outdir)
except OSError, e:
if e.errno != errno.EEXIST: raise
with open(outfile, 'wb') as mo:
mofile.write_mo(mo, pofile.read_po(po))
def list_languages(args=None):
""" Yields every language available as a PO file anywhere under
i18n/. Some PO files may only be available for some domains. The
order is arbitrary, and merely the order of discovery of the
language.
"""
seen = set()
for _root, _dirs, files in os.walk('i18n'):
for f in files:
if not f.endswith('.po') or f in seen:
continue
seen.add(f)
yield f.replace('.po', '')
parser = argparse.ArgumentParser(
description="Utility for handling i18n in the OpenERP documentation.")
sp = parser.add_subparsers()
sp.add_parser('list', help="List available languages "
"(from in-repository PO files)"
).set_defaults(func=list_languages)
sp.add_parser('export',
help="Exports POT files from the documentation"
).set_defaults(func=export)
compiler = sp.add_parser('compile',
help="Compiles PO files to MO files"
).set_defaults(func=compile)
maker = sp.add_parser('make', help="Builds the documentation to HTML, "
"optionally localized")
maker.add_argument('-l', '--language', '--lang',
help="Language to build the documentation in, if no "
"language is provided builds the default "
"(untranslated) documentation")
maker.add_argument('-o', '--output', '--output-dir', default='build/html',
help="Folder where the build output should be generated")
maker.set_defaults(func=make)
if __name__ == '__main__':
# display help if the script is invoked directly, otherwise
# argparse just prints an error message and exits
if len(sys.argv) > 1:
args = parser.parse_args()
else:
args = parser.parse_args(['-h'])
result = args.func(args)
if result is None:
sys.exit(0)
elif isinstance(result, (int, long)):
sys.exit(result)
elif isinstance(result, basestring):
print result
else:
for r in result:
print r
|