Skip to content

docs: add explicit anchors for sections referenced by auto-generated ids - #4339

Merged
BsAtHome merged 1 commit into
LinuxCNC:masterfrom
grandixximo:docs-anchor-auto-ids
Aug 5, 2026
Merged

docs: add explicit anchors for sections referenced by auto-generated ids#4339
BsAtHome merged 1 commit into
LinuxCNC:masterfrom
grandixximo:docs-anchor-auto-ids

Conversation

@grandixximo

@grandixximo grandixximo commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

The CI htmldocs job (scripts/htmlcheck.sh -w, W3C checklink) currently warns about broken URI fragments in the translated HTML docs. One class of those failures comes from sections that are linked to via their auto-generated AsciiDoc id rather than an explicit anchor.

Auto-generated ids are derived from the section title (=== Normal Download becomes _normal_download). When a translation translates the title, the derived id changes (_normales_herunterladen) and every link using the English-derived id breaks in that language. This currently affects the de, es, ru, uk and nb builds:

  • <<_normal_download,...>> and <<_alternate_install_methods,...>> in getting-linuxcnc.adoc
  • <<_latency_tuning>> in latency-test.adoc
  • link:../gui/qtvcp.html#_ini_settings[...] in qtplasmac.adoc
  • link:../man/man1/halcmd.1.html#_commands[...] in qtvcp-widgets.adoc

This PR pins the five ids with explicit [[...]] anchors. The explicit id is identical to the previously auto-generated one, so the English output is unchanged and all existing references (including external deep links) keep working. po4a treats standalone anchor lines as structural and passes them through untranslated, so all translations inherit the stable id without any .po changes.

I verified locally that the rendered HTML for all five pages emits the same ids as before. The remaining CI link warnings are mangled anchors inside translated strings in the .po files and have to be fixed on Weblate

These five sections are linked to via their auto-generated ids
(_normal_download, _alternate_install_methods, _latency_tuning,
_ini_settings, _commands). Translations change the section titles,
which changes the generated id and breaks every such link in the
translated HTML (flagged by the CI checklink run for de, es, ru, uk
and nb). Pinning the id with an explicit anchor makes the targets
translation-proof. English output is unchanged since the explicit id
matches the auto-generated one.
grandixximo added a commit to grandixximo/linuxcnc that referenced this pull request Aug 5, 2026
scripts/docs-anchor-check.py, wired warn-only into the htmldocs CI
job, flags <<_derived>> xrefs and link:...html#_derived URLs whose
target has no explicit [[anchor]] in docs/src. Derived ids come from
section titles, so such references break when a title is translated or
retitled. Pin the target with an explicit anchor instead.

Also pins the remaining lint hits, all same-page links in untranslated
man pages (_subcommands, _mbccs_file_format, _modbus_functions,
_modbus_types, _hal_types). Explicit ids match the auto-generated
ones, so output is unchanged. Turns clean once PR LinuxCNC#4339 lands.
grandixximo added a commit to grandixximo/linuxcnc that referenced this pull request Aug 5, 2026
scripts/docs-anchor-check.py, wired warn-only into the htmldocs CI
job, flags <<_derived>> xrefs and link:...html#_derived URLs whose
target has no explicit [[anchor]] in docs/src. Derived ids come from
section titles, so such references break when a title is translated or
retitled. Pin the target with an explicit anchor instead.

Also pins the remaining lint hits, all same-page links in untranslated
man pages (_subcommands, _mbccs_file_format, _modbus_functions,
_modbus_types, _hal_types). Explicit ids match the auto-generated
ones, so output is unchanged. Turns clean once PR LinuxCNC#4339 lands.
@BsAtHome

BsAtHome commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

But isn't this a general problem, every time a translation alters a header then the link name changes?
It looks like fixing these instances will just be the top of the iceberg and every time a new link is written/created, you end up with the same problem. So we must either add the link target consistently, or consistently generate it automatically.

@grandixximo

Copy link
Copy Markdown
Contributor Author

Agreed, fixing instances alone is whack-a-mole. That is why this PR is paired with #4340, which adds a CI lint (scripts/docs-anchor-check.py, warn-only in the htmldocs job) that flags any <<_derived>> xref or link:...html#_derived[...] URL whose target has no explicit anchor in docs/src. New derived-id references get caught at PR time, before any translation exists.

On "add the target consistently vs generate automatically": automatic generation is the current state and is precisely what breaks, because the generated id derives from the title text and translations change titles. There is no translation-stable automatic scheme short of asciidoctor inventing language-independent ids, which it does not do. So the convention has to be explicit anchors, and the lint is there to make "consistently" enforceable. The lint starts warn-only for burn-in and can flip to --enforce once the tree is clean.

@BsAtHome

BsAtHome commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Yes, and that is what I mean, autogenerate the anchors in the 'en' docs before you put them through translation.

Currently, the docs/src for 'en' is just copied (I think), while the translations are built from the docs/src. I suggest to process the 'en' source docs to add the anchors and make the translations use that version.

@grandixximo

Copy link
Copy Markdown
Contributor Author

I looked at autogenerating anchors into the en tree before po4a. It works mechanically, but it has two structural hazards that tipped me toward the lint instead.

First, duplicate titles. docs/src has 4,720 sections but only 2,598 distinct derived ids; 173 ids occur in multiple files (_name alone in 244 man pages, _description in 234). Injecting anchors everywhere makes the xref_resolver index fill with duplicate definitions: a warning flood per language build, and worse, first-wins index semantics mean a same-page <<_name>>-style reference would resolve to a different file's html. Fixing that means changing the resolver to prefer same-file anchors, which is a behavior change in the link machinery itself.

Second, fidelity and churn: the injector must reproduce asciidoctor's id derivation exactly (including duplicate suffixing) or the en build breaks, which practically means driving asciidoctor to compute them; and po4a masters becoming generated files rewrites every #: reference comment in all .po files, a large cosmetic Weblate diff.

The lint in #4340 covers every reference form (<<_x>> xrefs and link:#_x[...] URLs) at PR time, before translation exists. Total derived-id references in the whole tree today: 15. So the enforcement approach catches the same iceberg, one berg at a time, at the moment it forms, without touching the resolver or the po files.

If the preference is still generated anchors, the variant I would pick is a one-time bulk injection into docs/src itself (asciidoctor-computed ids, verified by diffing rendered en html before/after) rather than a build-time stage. That avoids the po4a master churn, but it still needs the resolver same-file preference fix and adds anchor lines to ~2,600 sections.

@BsAtHome

BsAtHome commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Ok, the po pollution problem settles it.

That just means we have to keep looking at the lint results. Maybe even start to fail eventually when we are stable (can/should we, with translations?).

@grandixximo

Copy link
Copy Markdown
Contributor Author

These are caught before translation

@grandixximo

grandixximo commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

The mechanism, in order:

  1. A contributor writes <<_some_title,Some Title>> in docs/src. At this point no translation of that string exists; it has not reached any .po file yet.
  2. CI on the PR runs the lint over docs/src. The target _some_title has no explicit [[...]] definition anywhere in the sources, so the lint flags it. The contributor pins the section with an explicit anchor as part of the same PR.
  3. Only then does the string flow to Weblate via the .pot. Translators translate the title, the explicit anchor line passes through po4a untouched, and every language's html keeps the same id.

So a translation can never introduce this class of breakage; it can only inherit already-pinned anchors. That is also what makes failing the build on the lint safe with respect to translations: the lint reads only docs/src, never .po content, so nothing Weblate does can trip it. The failure class that does live inside translations (anchors mangled in msgstr strings) stays with checklink in htmlcheck.sh, which remains warn-only.

@grandixximo

grandixximo commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

I don't think we should ever hard fail on translations, because as far as I understand, to actually fix anything and have it come back here from Weblate, we have to merge what came from Weblate first, before a new push arrives from there. Therefore if we enforce, we will have to merge and break the tree, and the merge the fix, which is not something I think we ever want to do.
Unless there is a different coordination that I am not aware of that needs to happen, but since I have not seen it yet, is probably not something that happens often...

@BsAtHome
BsAtHome merged commit 01de207 into LinuxCNC:master Aug 5, 2026
16 checks passed
@BsAtHome

BsAtHome commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

You are right. We cannot fail on translation breakage because the fix has to go through weblate. Not a path we want to have because it introduces a third party dependency.

grandixximo added a commit to grandixximo/linuxcnc that referenced this pull request Aug 5, 2026
scripts/docs-anchor-check.py, wired into the htmldocs CI job with
--enforce, flags <<_derived>> xrefs and link:...html#_derived URLs
whose target has no explicit [[anchor]] in docs/src. Derived ids come
from section titles, so such references break when a title is
translated or retitled. Pin the target with an explicit anchor
instead. The lint reads only the English sources, so translations
cannot trip it.

Also pins the remaining lint hits, all same-page links in untranslated
man pages (_subcommands, _mbccs_file_format, _modbus_functions,
_modbus_types, _hal_types). Explicit ids match the auto-generated
ones, so output is unchanged. With the five anchors from LinuxCNC#4339 merged,
the lint reports the tree clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants