Skip to content

fix(cli): translate section, page, and variantId values inside variant layouts#15864

Open
devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
devin/1778654943-fix-variant-overlay-layout
Open

fix(cli): translate section, page, and variantId values inside variant layouts#15864
devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
devin/1778654943-fix-variant-overlay-layout

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented May 13, 2026

Description

When a docs site uses tabs: + variants: and adds non-default locales via translations:, the resulting localized sidebar leaves every section and page title in the source language. The middle-of-page content translates (MDX frontmatter handles that), but the left-hand navigation stays in English.

Root cause is in the translation overlay system inside the CLI:

  • VariantOverlay had no layout field, so the overlay parser dropped every nested section/page declared inside a variant — only title / subtitle / slug survived parsing (see parseVariantOverlays in packages/cli/configuration-loader/src/docs-yml/parseDocsConfiguration.ts).
  • The applier had no varianted parent-type branch in applyChildOverlays, so the per-variant overlay layout never reached the variant's children.
  • Slug matching was strict last-segment comparison, so overlay slugs that mirror docs.yml syntax (slug: / for a root page) did not line up with the tree's resolved slug (home).
  • When a variant overlay set title:, only the variant's display title field was updated. The variant's variantId — which is what downstream renderers like custom navbar dropdowns key off of — still carried the source-language label. Even after the layout fix, custom dropdowns (e.g. coreweave's tab-variants.js) still rendered "Home" / "Support Home" in the navbar dropdown's first item on FR / JA / KO pages.

This PR fixes all four issues so overlays of the shape

navigation:
  - tab: products
    variants:
      - title: Accueil
        layout:
          - page: Documentation Weights & Biases
            slug: /
          - section: Détails de la plateforme
            slug: platform-details
            contents:
              - page: Configurer SMTP
                slug: smtp

now translate the variant's section and page titles, and update the variant's variantId so custom renderers stay in sync.

Changes Made

  • VariantOverlay gains an optional layout: NavigationItemOverlay[] field, mirroring the source navigation shape.
  • parseVariantOverlays() parses the new layout field via parseNavigationItemOverlays(...).
  • applyChildOverlays() now handles the varianted parent-type by walking each variant against overlay.variants and scoping the matched variant's layout as the navigation overlay for its children.
  • applyTabOverlayToNode() no longer special-cases a non-existent variants tree node; instead it emits a scoped overlay carrying either the tab's flat layout or a synthetic Tab entry with the variants. The downstream varianted branch unwraps it. It also shallow-copies the input node before mutating so the input tree is never modified in place.
  • applyVariantOverlays() is rewritten to:
    • match by variant slug first, then positionally over no-slug overlays (with consumed-index tracking so noSlug entries are not double-consumed);
    • apply title: / subtitle: overrides;
    • also override variantId to mirror the translated title so custom renderers built off the typed variant identifier (e.g. navbar dropdowns) display the translated label;
    • scope the matched variant's layout: as the navigation overlay for its children (variants directly own pages/sections, so applySidebarChildOverlays runs over the variant's children).
  • matchSectionOverlay() / matchPageOverlay() use normalizeOverlaySlug() so overlay slugs that mirror docs.yml syntax (/, /path/to/leaf) line up symmetrically with the tree's resolved last-segment slug. Positional fallback is kept for overlays that explicitly omit slug: (the intentional positional case). Overlay entries with a non-matching slug: are NOT silently re-attached positionally — instead they are reported via a CLI warning from applyTranslatedNavigationOverlays so drifted overlays surface during fern check / fern generate instead of producing mistranslated navigation.
  • New unit tests cover (1) the full tab → sidebarRoot → varianted → variant → children tree shape, including tab title, variant title, variantId mirroring, and per-variant nested section/page title overrides; (2) the strict matcher behavior (slug-bearing overlay with no matching sibling emits a warning and is ignored, while overlays without slug: continue to match positionally without warning).
  • Updated README.md generator (if applicable)

Testing

  • Unit tests added/updated — applyTranslatedNavigationOverlays.test.ts covers tab title, variant titles, variantId mirroring, per-variant nested section/page title overrides, and the new strict matcher / warning behavior; full file passes locally.

  • Manual testing completed — built the production CLI with pnpm fern:build and ran FERN_NO_VERSION_REDIRECTION=true fern docs dev against fern-demo/coreweave@fern-support/tab-variants, which has FR/JA/KO overlays mirroring the new tabs+variants structure. Before this PR the FR/JA/KO sidebars rendered in English; after this PR they render fully translated, and the navbar dropdown first item now matches the variant's translated title:

    Locale Products dropdown first item Support dropdown first item
    /fr Accueil Accueil support
    /ja ホーム サポートホーム
    /ko 지원 홈

Link to Devin session: https://app.devin.ai/sessions/208b696a97f94e13a952f6f99ecc4c75

@devin-ai-integration devin-ai-integration Bot requested a review from amckinney as a code owner May 13, 2026 07:30
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 13, 2026

Docs Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-05-13T05:16:22Z).

Fixture main PR Delta
docs 275.8s (n=5) 228.4s (35 versions) -47.4s (-17.2%)

Docs generation runs fern generate --docs --preview end-to-end against the benchmark fixture with 35 API versions (each version: markdown processing + OpenAPI-to-IR + FDR upload).
Delta is computed against the nightly baseline on main.
Baseline from nightly run(s) on main (latest: 2026-05-13T05:16:22Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-05-13 18:16 UTC

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 13, 2026

SDK Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-05-13T05:16:22Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 68s (n=5) 101s (n=5) 60s -8s (-11.8%)
go-sdk square 129s (n=5) 275s (n=5) 128s -1s (-0.8%)
java-sdk square 205s (n=5) 264s (n=5) 200s -5s (-2.4%)
php-sdk square 56s (n=5) 77s (n=5) 51s -5s (-8.9%)
python-sdk square 132s (n=5) 231s (n=5) 137s +5s (+3.8%)
ruby-sdk-v2 square 81s (n=5) 118s (n=5) 87s +6s (+7.4%)
rust-sdk square 158s (n=5) 157s (n=5) 159s +1s (+0.6%)
swift-sdk square 53s (n=5) 746s (n=5) 51s -2s (-3.8%)
ts-sdk square 89s (n=5) 107s (n=5) 209s +120s (+134.8%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-05-13T05:16:22Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-05-13 18:18 UTC

@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

Test results — variant overlay translation on coreweave

End-to-end tested PR c1cb588 against fern-demo/coreweave @ fern-support/tab-variants (727bf9c).

Case URL Result
EN baseline /home passed
FR /fr/home passed
JA /ja/home passed
KO /ko/home passed

All variant-tab sidebar section labels render in the target language; no English leaked. Page children (Configurer SMTP, デプロイオプション, 엔터프라이즈 라이선스, …) also rendered translated.

EN baseline — /home

Sidebar: Weights & Biases Documentation, Products, Platform Details, Resources.

EN home

FR — /fr/home (the bug scenario, now fixed)

Sidebar: Documentation Weights & Biases, Produits, Détails de la plateforme, Ressources.

FR home

JA — /ja/home

Sidebar: Weights & Biases ドキュメント, プロダクト, プラットフォーム詳細, リソース.

JA home

KO — /ko/home

Sidebar: Weights & Biases 문서, 제품, 플랫폼 세부 정보, 리소스.

KO home

Setup notes
  • Built the CLI from c1cb588 with pnpm fern:build.
  • Started docs dev from inside coreweave at 727bf9c with FERN_NO_VERSION_REDIRECTION=true because fern.config.json pins version: 5.20.1 (the published CLI without this fix), which would otherwise be auto-fetched and used in place of the local build.
  • No code or YAML changes on the coreweave side beyond the regenerated overlays already merged in fern-demo/coreweave#22.

Recording · Devin session

@devin-ai-integration devin-ai-integration Bot force-pushed the devin/1778654943-fix-variant-overlay-layout branch from c1cb588 to 3cbf628 Compare May 13, 2026 15:00
Translation overlays for tabs that use variants now support a per-variant
`layout:` field whose section and page titles override the resolved
navigation tree. This is needed for docs sites that use `tabs:` +
`variants:` and want non-default locale sidebars to translate fully.

- VariantOverlay gains an optional layout array of NavigationItemOverlay
- parseVariantOverlays threads through the layout field
- applyTranslatedNavigationOverlays handles the 'varianted' parent type
  and scopes each matched variant's layout as the navigation context for
  its children
- matchSectionOverlay / matchPageOverlay match overlay slugs leniently:
  '/' and empty mean 'root', and absolute slugs are normalised to their
  last segment so they line up with the tree's resolved slug

Co-Authored-By: will.kendall@buildwithfern.com <wpk235@gmail.com>
@devin-ai-integration devin-ai-integration Bot force-pushed the devin/1778654943-fix-variant-overlay-layout branch from 3cbf628 to 4683d35 Compare May 13, 2026 15:31
When a variant overlay sets `title:`, also override the variant node's
`variantId` so downstream renderers that key off the typed variant
identifier (e.g. coreweave's custom navbar dropdown) display the
translated label. The original `variantId` mirrors the source variant
`title` (see DocsDefinitionResolver.toVariantNode), so keeping these in
sync per locale is consistent with the source behaviour.

Also stop mutating the input root in applyTabOverlayToNode (caught by
the existing 'does not mutate the original root' test).

Co-Authored-By: will.kendall@buildwithfern.com <wpk235@gmail.com>
@devin-ai-integration devin-ai-integration Bot changed the title fix(cli): translate section and page titles inside variant layouts fix(cli): translate section, page, and variantId values inside variant layouts May 13, 2026
Translation overlays for sections/pages are no longer silently re-attached
positionally when their slug doesn't match any sibling navigation entry.
Instead the matcher warns via the CLI logger so drifted overlays surface
during fern generate / fern check instead of producing mistranslated nav.

Overlays without a slug remain positional (the intentional case), and the
existing leaf-segment slug normalization continues to handle absolute and
relative slug forms symmetrically.

Co-Authored-By: will.kendall@buildwithfern.com <wpk235@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

0 participants