Source code for univention.l10n.sourcefileprocessing

#!/usr/bin/env python3
"""
Generate gettext Portable Objects and message catalogs (gettext MO and a
Univention specific JSON-based format) from multiple source files by file type.
"""
#
# SPDX-FileCopyrightText: 2013-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only

import os
from collections.abc import Iterable

import polib
from lxml import etree

from . import message_catalogs, umc


[docs] class UnsupportedSourceType(Exception): pass
[docs] class SourceFileSet: def __init__(self, src_pkg_path: str, binary_pkg_name: str, files: Iterable[str]) -> None: self.files = files self.src_pkg_path = src_pkg_path self.binary_pkg_name = binary_pkg_name
[docs] def process_po(self, pot_path: str) -> None: self._create_po_template(pot_path)
[docs] def process_target(self, po_path: str, output_path: str) -> None: if os.path.isabs(output_path): output_path = os.path.relpath(output_path, '/') output_path = os.path.join(os.getcwd(), 'debian', self.binary_pkg_name, output_path) self._compile(po_path, output_path)
def _create_po_template(self, pot_path: str) -> None: raise NotImplementedError() def _compile(self, po_path: str, output_path: str) -> None: raise NotImplementedError()
[docs] class SourceFilesXgettext(SourceFileSet): def _create_po_file(self, gettext_lang: str, pot_path: str) -> None: umc.create_po_file(pot_path, self.binary_pkg_name, self.files, language=gettext_lang) def _compile(self, po_path: str, mo_output_path: str) -> None: umc.create_mo_file(po_path, mo_output_path)
[docs] class SourceFilesShell(SourceFilesXgettext): def _create_po_template(self, pot_path: str) -> None: super()._create_po_file('Shell', pot_path)
[docs] class SourceFilesPython(SourceFilesXgettext): def _create_po_template(self, pot_path: str) -> None: super()._create_po_file('Python', pot_path)
[docs] class SourceFilesJavaScript(SourceFilesXgettext): def _create_po_template(self, pot_path: str) -> None: super()._create_po_file('JavaScript', pot_path) def _compile(self, po_path: str, json_output_path: str) -> None: """ With UMC and univention-web based applications a custom, JSON-based message format is used. """ umc.po_to_json(po_path, json_output_path)
[docs] class SourceFilesHTML(SourceFileSet): def _create_po_template(self, pot_path: str) -> None: po_template = polib.POFile() html_parser = etree.HTMLParser() js_paths: list[str] = [] for html_path in self.files: with open(html_path, 'rb') as html_file: tree = etree.parse(html_file, html_parser) # noqa: S320 for element in tree.xpath('//*[@data-i18n]'): msgid = element.get('data-i18n') loc = (os.path.basename(html_path), element.sourceline) entry = po_template.find(msgid) if entry: if loc not in entry.occurrences: entry.occurrences.append(loc) else: new_entry = polib.POEntry(msgid=msgid, occurrences=[loc]) po_template.append(new_entry) if tree.xpath('//script'): js_paths.append(html_path) po_template.save(pot_path) # Inline JavaScript may use underscorce function, e.g. univention/management/index.html if js_paths: message_catalogs.join_existing('JavaScript', pot_path, js_paths) def _compile(self, po_path: str, json_output_path: str) -> None: umc.po_to_json(po_path, json_output_path)
[docs] class SourceFileSetCreator: process_by_type = { 'text/x-shellscript': SourceFilesShell, 'text/x-python': SourceFilesPython, 'text/html': SourceFilesHTML, 'application/javascript': SourceFilesJavaScript}
[docs] @classmethod def from_mimetype(cls, src_pkg_path: str, binary_pkg_name: str, mimetype: str, files: Iterable[str]) -> SourceFileSet: try: obj = cls.process_by_type[mimetype](src_pkg_path, binary_pkg_name, files) except KeyError: raise UnsupportedSourceType(files) else: return obj
[docs] def from_mimetype(src_pkg_path: str, binary_pkg_name: str, mimetype: str, files: Iterable[str]) -> SourceFileSet: return SourceFileSetCreator.from_mimetype(src_pkg_path, binary_pkg_name, mimetype, files)