summaryrefslogtreecommitdiffstats
path: root/utils/babel_runner.py
blob: 32e6fe64238e390e5c95d9b4536121b38149aaf5 (plain)
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
"""Run babel for translations.

Usage:

babel_runner.py extract
    Extract messages from the source code and update the ".pot" template file.

babel_runner.py update
    Update all language catalogues in "sphinx/locale/<language>/LC_MESSAGES"
    with the current messages in the template file.

babel_runner.py compile
    Compile the ".po" catalogue files to ".mo" and ".js" files.
"""

import json
import logging
import os
import sys

from babel.messages.frontend import compile_catalog, extract_messages, update_catalog
from babel.messages.pofile import read_po

import sphinx

ROOT = os.path.realpath(os.path.join(os.path.abspath(__file__), "..", ".."))


class compile_catalog_plusjs(compile_catalog):
    """
    An extended command that writes all message strings that occur in
    JavaScript files to a JavaScript file along with the .mo file.

    Unfortunately, babel's setup command isn't built very extensible, so
    most of the run() code is duplicated here.
    """

    def run(self):
        if super().run():
            print("Compiling failed.", file=sys.stderr)
            raise SystemExit(2)

        for domain in self.domain:
            self._run_domain_js(domain)

    def _run_domain_js(self, domain):
        po_files = []
        js_files = []

        if not self.input_file:
            if self.locale:
                po_files.append((self.locale,
                                 os.path.join(self.directory, self.locale,
                                              'LC_MESSAGES',
                                              domain + '.po')))
                js_files.append(os.path.join(self.directory, self.locale,
                                             'LC_MESSAGES',
                                             domain + '.js'))
            else:
                for locale in os.listdir(self.directory):
                    po_file = os.path.join(self.directory, locale,
                                           'LC_MESSAGES',
                                           domain + '.po')
                    if os.path.exists(po_file):
                        po_files.append((locale, po_file))
                        js_files.append(os.path.join(self.directory, locale,
                                                     'LC_MESSAGES',
                                                     domain + '.js'))
        else:
            po_files.append((self.locale, self.input_file))
            if self.output_file:
                js_files.append(self.output_file)
            else:
                js_files.append(os.path.join(self.directory, self.locale,
                                             'LC_MESSAGES',
                                             domain + '.js'))

        for js_file, (locale, po_file) in zip(js_files, po_files):
            with open(po_file, encoding='utf8') as infile:
                catalog = read_po(infile, locale)

            if catalog.fuzzy and not self.use_fuzzy:
                continue

            self.log.info('writing JavaScript strings in catalog %s to %s',
                          po_file, js_file)

            jscatalog = {}
            for message in catalog:
                if any(x[0].endswith(('.js', '.js_t', '.html'))
                       for x in message.locations):
                    msgid = message.id
                    if isinstance(msgid, (list, tuple)):
                        msgid = msgid[0]
                    jscatalog[msgid] = message.string

            obj = json.dumps({
                'messages': jscatalog,
                'plural_expr': catalog.plural_expr,
                'locale': f'{catalog.locale!s}'
            }, sort_keys=True, indent=4)
            with open(js_file, 'w', encoding='utf8') as outfile:
                outfile.write(f'Documentation.addTranslations({obj});')


def _get_logger():
    log = logging.getLogger('babel')
    log.setLevel(logging.INFO)
    handler = logging.StreamHandler()
    handler.setFormatter(logging.Formatter('%(message)s'))
    log.addHandler(handler)
    return log


def run_extract():
    os.chdir(ROOT)
    command = extract_messages()
    command.log = _get_logger()
    command.initialize_options()

    command.keywords = "_ __ l_ lazy_gettext"
    command.mapping_file = "babel.cfg"
    command.output_file = os.path.join("sphinx", "locale", "sphinx.pot")
    command.project = "Sphinx"
    command.version = sphinx.__version__
    command.input_paths = "sphinx"

    command.finalize_options()
    return command.run()


def run_update():
    os.chdir(ROOT)
    command = update_catalog()
    command.log = _get_logger()
    command.initialize_options()

    command.domain = "sphinx"
    command.input_file = os.path.join("sphinx", "locale", "sphinx.pot")
    command.output_dir = os.path.join("sphinx", "locale")

    command.finalize_options()
    return command.run()


def run_compile():
    os.chdir(ROOT)
    command = compile_catalog_plusjs()
    command.log = _get_logger()
    command.initialize_options()

    command.domain = "sphinx"
    command.directory = os.path.join("sphinx", "locale")

    command.finalize_options()
    return command.run()


if __name__ == '__main__':
    try:
        action = sys.argv[1].lower()
    except IndexError:
        print(__doc__, file=sys.stderr)
        raise SystemExit(2)

    if action == "extract":
        raise SystemExit(run_extract())
    if action == "update":
        raise SystemExit(run_update())
    if action == "compile":
        raise SystemExit(run_compile())