Skip to content

JA localization prep: <NT> marker, .mdx renames, and duplicate API sidebar labels - #188

Merged
zoltan-baba merged 6 commits into
mainfrom
ja-infra-prep
Aug 18, 2026
Merged

JA localization prep: <NT> marker, .mdx renames, and duplicate API sidebar labels#188
zoltan-baba merged 6 commits into
mainfrom
ja-infra-prep

Conversation

@matenadasdi

@matenadasdi matenadasdi commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Infrastructure prep split out of #140, so the risky parts of that PR can be reviewed on their own. No user-visible change except three corrected API reference sidebar labels.

#140 bundles three separable things: infrastructure, the translation pipeline (scripts + 3 workflows), and content (i18n/ja/ + one translated page). This is the first — the pieces that are inert on their own, are prerequisites for everything else, and carry the build risk worth isolating.

What's here

<NT> do-not-translate marker (src/components/NT) — renders a bare <span translate="no">. Nothing is tagged yet; this lands the component so the tagging pass (~17k wraps across ~500 files) can be reviewed on its own, against a repo that already builds with NT in place.

Two supporting changes it depends on:

  • NT added to ALLOWED in docusaurus.config.ts. The preprocessor escapes any JSX tag not on that list, so without this every <NT> would render as literal escaped text.
  • scripts/check-links-source.js now strips JSX/MDX tags before slugifying headings. Docusaurus renders a heading's element tree to plain text first, so ## The <NT>Dashboards</NT> page anchors at the-dashboards-page. Without the strip, the tag delimiters are dropped by the punctuation pass but the tag name survives as word characters (the-ntdashboardsnt-page), and the checker would flag every tagged heading as a broken anchor.

83 .md.mdx renames. add_notranslate_tags.py globs **/*.mdx only, so these pages would be silently skipped by the tagging pass. Content is byte-identical; routes are unaffected (every page carries an explicit slug:), so no redirects are needed. The real change is that docusaurus.config.ts's preprocessor is gated on .mdx, so these pages now go through expandListPartials and the JSX escaper and are parsed as MDX rather than CommonMark. That surfaced two unclosed <br> tags, fixed in a follow-up commit.

MCP sync kept in step (scripts/sync_mcp_docs.py). Two problems the rename creates, both fixed at the source since these files are generated and hand-edits don't survive a sync:

  • The script wrote each page under the source repo's own .md filename. With those eight pages now .mdx, the next sync would write a second .md copy of each — two files with the same explicit slug, a duplicate route.
  • <br><br/> is now applied as a sync-time transform, so the fix above isn't reverted by the next run.

Duplicate API sidebar labels (scripts/patch-api-info.js). Three generated operations share a summary with another operation. Docusaurus derives i18n translation keys from sidebar labels, so a duplicate breaks docusaurus write-translations outright — which is how these surfaced. The specs are themselves synced/generated and the plugin regenerates the pages on every run, so the fix goes in the existing patch script as an explicit keyed map. Both the page frontmatter and the separate label in the generated sidebar.ts need patching; sidebar.ts is what Docusaurus actually reads.

CLAUDE.md records the rule. The old guidance — use .md when a page has no JSX, rename only when adding a partial or <Tabs> — is now backwards: <NT> is JSX, so a .md page physically cannot carry one, and the tagging pass globs **/*.mdx, meaning a stray .md page is skipped with no error. Rewritten as an .mdx-only rule, with the two consequences that turned up as real build failures here (MDX parsing, and the preprocessor being gated on the extension) and the requirement that scripts writing pages pin the extension too. Also fixes the stale index.md topichead reference and the layout table's page row.

Verified

  • npm run build — green for both en and ja. Only warnings are pre-existing and unrelated (two unreadable _paligo SVGs, docusaurus-plugin-llms description length).
  • node scripts/check-links-source.js — clean, 631 files.
  • node scripts/patch-api-info.js re-run over the patched tree — no diff, idempotent.
  • python3 scripts/sync_mcp_docs.py against a real sync — reapplies both <br/>, produces no stray .md files.
  • <NT> end-to-end. Temporarily tagged one page in the four placements most likely to break, built, then reverted. All render as <span translate="no">, none leak as escaped literals, and — the one that mattered — a heading anchor is unchanged: ## Changing your <NT>project settings</NT> still emits id="changing-your-project-settings", confirming the check-links-source.js fix matches Docusaurus's real behavior.
    • plain body text ✅
    • inside a heading (anchor generation) ✅
    • inside an admonition title (:::note[First <NT>workspace</NT>]) ✅
    • wrapping an existing <GlossTerm>

Deliberately not here

  • The locale dropdown and its navbar CSS. The CSS exists to make the dropdown readable on the purple header, so it's dead on its own — and adding the dropdown makes /ja/ discoverable while ~440 of its pages are still English. Both belong with the locale-switch work.
  • The tagging pass itself. ~17k wraps; its own PR.
  • Scripts, workflows, localization/, i18n/ja/. The pipeline PR.

Follow-up found while testing (out of scope, separate PR)

sync_mcp_docs.py's rewrite_internal_links() still emits /en/-prefixed targets, so a sync run reintroduces eight links that 51f565b stripped repo-wide. Those break under any non-default locale (/ja/en/...). Pre-existing on main and unrelated to the rename, so left alone here.

🤖 Generated with Claude Code

matenadasdi and others added 5 commits August 18, 2026 16:09
scripts/add_notranslate_tags.py globs docs/**/*.mdx and src/partials/**/*.mdx
only, so the 83 pages still authored as plain .md would be silently skipped
by the do-not-translate tagging pass. Rename them now, on their own, so the
extension change is reviewable in isolation and any MDX parse fallout is
attributable to this commit rather than buried in a 17k-line tagging diff.

Content is byte-identical; only the extension moves. Routes are unaffected
(every page carries an explicit `slug:` and Docusaurus strips both
extensions), so no redirects are needed.

Note the real behavior change: docusaurus.config.ts's markdown.preprocessor
is gated on `.mdx`, so these pages now go through expandListPartials and the
JSX tag escaper, and are parsed as MDX rather than CommonMark.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Plain Markdown tolerates an unclosed <br>, but MDX parses it as JSX, which
requires a matching closing tag or a self-closing slash. Renaming these two
pages in the previous commit surfaces the mismatch ("Unexpected closing tag
</details>, expected corresponding closing tag for <br>") and fails the
build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`NT` wraps product names, UI labels, Step names, and other terms that must
stay in English in every locale. It renders a bare `<span translate="no">`
— no lookup, no styling — which also stops browser/OS auto-translate from
touching the content.

Nothing is tagged yet: this lands the component, its allow-list entry, and
the checker fix that the tagging pass depends on, so the tagging diff itself
(~17k wraps across ~500 files) can be reviewed on its own against a repo
that already builds with `NT` in place.

Two supporting changes:

- docusaurus.config.ts's preprocessor escapes any JSX tag not in ALLOWED, so
  without `NT` on that list every `<NT>` would render as literal escaped
  text instead of the component.

- scripts/check-links-source.js's heading slugifier now strips JSX/MDX tags
  before slugifying. Docusaurus's real slugger renders a heading's element
  tree to plain text first, so `## The <NT>Dashboards</NT> page` anchors at
  `the-dashboards-page`. Without the strip, the tag delimiters are dropped
  by the punctuation pass but the tag *name* survives as word characters
  ("the-ntdashboardsnt-page"), and the checker would flag every heading with
  a tag in it as a broken anchor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two different operations can share an identical OpenAPI `summary`. Docusaurus
derives i18n translation keys from sidebar labels, so a duplicate breaks
`docusaurus write-translations` outright — which is how these surfaced.

Three pairs exist today:

  organization-machine-type-update / user-machine-type-update
    both "Migrate machine types" -> qualified with (organization) / (user)
  codespaces-service-session-download
    duplicate of SessionDownloadFile, which supersedes it -> "Download files
    (deprecated)", matching the operation's own description

Fixing this upstream isn't an option here: api/bitrise-ci.json and
api/bitrise-rde.json are themselves synced/generated, and the plugin
regenerates the .api.mdx files from them on every run. So the overrides go in
scripts/patch-api-info.js alongside the existing displayed_sidebar and
license patches, as an explicit keyed map rather than an auto-disambiguation
heuristic — extend it if a future spec sync introduces another duplicate.

Both the page frontmatter and the separate `label` in the generated
sidebar.ts need patching; the sidebar.ts entry is what Docusaurus actually
reads, so patching only the page leaves the duplicate alive for i18n.

Verified idempotent: re-running `node scripts/patch-api-info.js` over the
patched tree produces no diff.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
scripts/sync_mcp_docs.py wrote each page under the source repo's own
filename, which is always .md. Now that these eight pages exist here as
.mdx, the next sync run would have written a second .md copy alongside each
one — two files carrying the same explicit `slug`, which Docusaurus rejects
as a duplicate route. Derive the destination from the stem and pin the
extension to .mdx instead.

Also self-close bare void tags (`<br>` -> `<br/>`) at sync time. The source
repo is plain Markdown, where an unclosed `<br>` is fine; as .mdx it is JSX
and fails the build. The two occurrences were fixed by hand two commits ago,
but these files are generated — CLAUDE.md's rule is not to hand-edit them —
so the fix only holds if the sync applies it. Verified against a real sync
run: the transform is idempotent, reapplies both `<br/>`, and no stray .md
files appear.

Unrelated pre-existing bug, left for its own PR: rewrite_internal_links()
still emits `/en/`-prefixed targets, so a sync run reintroduces eight links
that 51f565b stripped repo-wide. Those break under any non-default locale
(`/ja/en/...`).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

The previous guidance said to use .md when a page has no JSX, and to rename
only when adding a partial, <Tabs>, or <GlossTerm>. That's now backwards: a
<NT> do-not-translate wrapper is JSX, so a .md page physically cannot carry
one, and the tagging pass globs **/*.mdx — a stray .md page is skipped with
no error, leaving its product names and UI labels to be machine-translated.

Record the rule where the rename established it, plus the two consequences
that turned up as real build failures (MDX parsing, and the preprocessor
being gated on the extension), and the fact that scripts writing pages have
to pin .mdx as well.

Also fixes the stale `index.md` topichead reference, the `.md`/`.mdx` in the
description check, and the layout table's page row.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@zoltan-baba
zoltan-baba merged commit cb4b56b into main Aug 18, 2026
2 checks passed
@zoltan-baba
zoltan-baba deleted the ja-infra-prep branch August 18, 2026 14:45
matenadasdi added a commit that referenced this pull request Aug 18, 2026
Brings in the infrastructure that was split out of this PR and has since
merged — #188 (the <NT> component, the 83 .md -> .mdx renames, the
check-links-source.js anchor fix, the patch-api-info.js duplicate-label fix)
and #189 (the MCP sync /en/ fix) — plus #187's docusaurus-plugin-llms bump.

The overlapping work is identical on both sides, so it resolves without
conflict. What remains unique to this branch is the translation pipeline,
the localization glossary and guides, the i18n/ja content, and the locale
switcher.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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