Anchor links
When docsOutput.style = "flat", output rewrites relative paths between pages for each locale (guide.md → guide.de.md). Anchor links — the usual markdown inline form with a # after the path — jump to a section inside the target file:
Read the [installation checklist](setup.md#first-run) before you deploy.Here the link target is setup.md, and #first-run is the anchor: it should scroll to the right heading inside that file.
Why anchor links need attention
rewriteRelativeLinksfixes the filename for each locale (setup.md→setup.de.md).- Many renderers derive the
#slug from the visible heading text. After translation, headings differ per locale, so an auto-generated slug can change while the rewritten link might still say#first-run— or your English#…anchor no longer matches the slug the renderer builds from the translated heading. - Result: readers land on the right file but the wrong line, or the browser finds no matching heading.
What to do
Docusaurus sites (preferred)
On Docusaurus documentation (docsOutput.style = "docusaurus"), prefer Docusaurus's native heading IDs instead of ai-i18n-tools write-heading-ids:
- Add an explicit id on the heading line with Docusaurus's
{#…}suffix, e.g.## TLS configuration {#tls-configuration}. Duringtranslate-docs, only the visible heading text is translated — the{#tls-configuration}suffix is preserved in every locale. - Run
docusaurus write-heading-idsfrom your Docusaurus project root (oftenpnpm run write-heading-idswhen wired inpackage.json) to add or refresh{#…}suffixes on headings that lack them. Re-run after renaming headings so stale ids match the current titles.
Point your markdown anchor links at those stable ids, e.g. [label](other.md#tls-configuration), where the fragment matches the {#…} suffix — not a slug guessed from English words alone. See examples/docusaurus-docs for committed docs that use this pattern.
Other layouts (flat, Starlight, VitePress, etc.)
When you are not on Docusaurus, or you need HTML anchors instead of {#…} suffixes:
- Run
ai-i18n-tools write-heading-idson your source.md/.mdxbeforetranslate-docs(samedocs[]/contentPathsas usual). It inserts explicit HTML anchors on the line before each heading soidvalues are shared by every translated copy. Re-run it after renaming headings so stale anchor ids are refreshed to match the current title. - Point your markdown anchor links at those stable ids, e.g.
[label](other.md#section-id), wheresection-idmatches the anchor the tool wrote — not a guess from English words alone.
Example
Docusaurus {#…} suffix
docs/overview.md:
See [TLS setup](security.md#tls-configuration) for certificate steps.docs/security.md (English source):
## TLS configuration {#tls-configuration}
Your CA and cert steps…After translate-docs, the link fragment stays #tls-configuration in every locale; only the heading text and link label change:
Siehe [TLS-Einrichtung](security.md#tls-configuration) für die Zertifikatsschritte.HTML anchors (write-heading-ids)
docs/overview.md:
See [TLS setup](security.md#tls-configuration) for certificate steps.docs/security.md after write-heading-ids (simplified):
<a id="tls-configuration"></a>
---
# TLS configuration
Your CA and cert steps…After translate-docs, file paths and #… anchors stay aligned in every locale file, for example:
Siehe [TLS-Einrichtung](security.de.md#tls-configuration) für die Zertifikatsschritte.The #tls-configuration anchor is the same in all locales because the id is fixed in the source; only the heading text and the link label are translated.
If links still fail after translation, see Troubleshooting.