~openerp-community/openobject-doc/5.0

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