Files
computer-playbook/plugins/lookup/i18n_sample.py
T
kevinveenbirkenbachandClaude Opus 5 6a23926648 feat(docs): build and serve a translated site per language
Every version whose tree holds a locale/<code>/LC_MESSAGES/docs.po with at
least one translation now gets one site per such language next to the
English one, built with `-D language=<code> -D html_copy_source=0` after
the English site is published. Sphinx reads one docs.po per language
(locale_dirs, gettext_compact = "docs"); YAML pages stay untranslated
through an extension that runs before the Locale transform.

The deployed working tree is built as the unlisted version `deployed` from a
snapshot baked into the image, because the git mirror only holds what is
pushed to main. The server answers /<version>/<code>/ and
/api/languages/<version>, a language without a site is a 404 rather than the
English page, and a switcher on every page lists the built languages and
keeps the page path. min_storage grows to 13GB for the translated sites.

The 47 docs.po catalogs hold the 24869 documentation messages; German is
partially machine-translated so far. The raw-docker and primary-domain lints
skip .po files, which carry the Markdown's own commands and hosts as
translation sources.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 18:56:06 +02:00

51 lines
1.8 KiB
Python

from __future__ import annotations
import re
from pathlib import Path
from typing import Any
from ansible.errors import AnsibleError
from ansible.plugins.lookup import LookupBase
from utils.cache.files import read_text
from utils.i18n.catalog import catalog_path, read_catalog, translations
REWRITTEN = re.compile(r"[*`\[\]!<>|#\"'‘’“”]|--|\.\.\.")
MINIMUM_LENGTH = 30
class LookupModule(LookupBase):
"""
Usage:
{{ lookup('i18n_sample', 'docs', 'de', 'README.md', 5) }}
Returns up to ``count`` translated messages of ``domain`` in language
``code`` whose source occurs verbatim in the repository file
``source_file``, as ``{source: translation}``. Messages carrying markup,
quotes, dashes or ellipses are skipped, because the renderer rewrites
them and a page could never contain them verbatim.
"""
def run(self, terms, variables: dict[str, Any] | None = None, **kwargs):
if len(terms) != 4:
raise AnsibleError(
"lookup('i18n_sample', domain, code, source_file, count) expects exactly 4 terms"
)
domain, code, source_file, count = terms
root = Path(str((variables or {})["playbook_dir"]))
text = read_text(str(root / str(source_file)))
path = catalog_path(root, str(code), str(domain))
sample: dict[str, str] = {}
if not path.is_file():
return [sample]
for (_, source), target in sorted(translations(read_catalog(path)).items()):
if (
len(source) >= MINIMUM_LENGTH
and source in text
and not REWRITTEN.search(source + target)
):
sample[source] = target
if len(sample) >= int(count):
break
return [sample]