"""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//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())