ruff format reflows long calls and normalises quotes, isort reorders the
imports of the documentation lint package, SIM117 folds nested `with`
statements into one parenthesised block, TC003 moves typing-only imports
under `if TYPE_CHECKING`, the JavaScript linter replaces a `var`, and the
YAML formatter rewraps five role files. None of it changes behaviour.
One entry does more than that: the formatter had reflowed two calls in
test_builder.py so their `# nocheck: cache-read` markers landed on the
closing parenthesis, where they suppress nothing and the cache-read lint
would have reported two real violations. Both calls are hoisted into a
variable so the marker sits on the reading line again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The two domains are built apart: docs passes through sphinx, where the
transform withholds a message that is nothing but a name, while core builds
its template straight from role descriptions, categories and menu labels. A
sphinx extension never reaches it, so the same message could be withheld from
one catalog and carried by the other.
build_template now applies the same predicate.
Its effect today is nil: counted directly, none of core's 421 messages is
withheld, and the extract reports 0 catalogs changed. This is an assurance for
what gets added rather than a repair of what is there - a role description
that is only a product name would have slipped through before.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
catalog.update() defaults to fuzzy matching: for every changed msgid it
searches all removed messages for a similar string and carries that
translation over as a fuzzy entry. The search is quadratic. Measured on
one catalog, it costs 11.01s for the 421-message core domain and did not
finish within 10 minutes for the 25182-message docs domain, per language,
across 20 pool workers.
The carried-over value is dead. Four places read the fuzzy flag and three
exclude it: translations(), the API reader and the docs builder. The
fourth, pending(), treats a fuzzy entry as untranslated, so the next
i18n-translate run overwrites it anyway.
With the search disabled, make i18n-extract domain=docs finishes in 2m4s
wall for 47 catalogs, of which user/real 1.56 shows almost all remaining
time is the single-threaded sphinx build. Checked msgid by msgid on two
catalogs: no surviving entry lost its translation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The header test writes a .po file into its tempdir and reads it back. The
shared cache is keyed by path, so a cached read answers with the content
from before the write. The marker sits on the call's own line because the
lint accepts it nowhere else, and the shorter comment survives the
formatter that had detached it once already.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
locale/<code>/LC_MESSAGES/core.po now exists for all 183 non-source
languages with the 407 core messages. German is machine-translated in full;
the 29 languages the logout panel shipped are carried over from
logout_i18n.yml.
The keycloak logout panel reads every language but English from core.po
through the new i18n_catalogue lookup, so logout_i18n.yml keeps only the
English source strings. Each language takes its text direction from
meta/languages.yml. The Playwright spec asserts the German catalogue on the
served logout page.
Catalogs no longer carry Babel's placeholders: render() writes the author,
kevinveenbirkenbach@infinito.nexus, https://infinito.nexus, the Infinito.Nexus
Community License and contact@infinito.nexus as the bug address into every
header it writes, from constants in utils/software.py.
tests/lint/repository/test_i18n_catalogs.py holds that every language has a
core catalog, none exists for an unknown language, the core catalogs hold
exactly the current sources, and every translation keeps its protected
spans.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Core and the documentation get gettext catalogs for all 184 ISO 639-1
languages listed in meta/languages.yml, 47 of which LibreTranslate can fill.
`make i18n-extract` merges the current sources into locale/<code>/
LC_MESSAGES/{core,docs}.po: role descriptions, category and menu titles and
the logout strings for the core domain, the Sphinx gettext output for the
docs domain. A changed source keeps its previous translation as a fuzzy
entry, and catalogs are written deterministically so an unchanged merge
leaves the files untouched.
`make i18n-translate domain=<core|docs> [languages=<codes>]` fills only the
empty and fuzzy entries with a short-lived LibreTranslate container (the
image web-svc-libretranslate pins) and removes it afterwards. It starts the
container, and loads models, only for languages that still have pending
entries. Code spans, placeholders, URLs, tags and dotted names such as
Infinito.Nexus are masked for the request and a translation that damages
one is discarded. An unreachable server stops the run instead of
discarding every remaining entry, so a rerun continues where it stopped;
machine translations carry the translator comment `libretranslate`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>