Skip to content

feat(compare): Chinese (zh) locale for /compare and /compare-per-dollar#427

Open
functionstackx wants to merge 1 commit into
masterfrom
feat/compare-zh-locale
Open

feat(compare): Chinese (zh) locale for /compare and /compare-per-dollar#427
functionstackx wants to merge 1 commit into
masterfrom
feat/compare-zh-locale

Conversation

@functionstackx

@functionstackx functionstackx commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

What

Adds a Chinese-language version of the compare master pages and their detail subpages, served under a /zh URL prefix:

English Chinese
/compare /zh/compare
/compare/<slug> /zh/compare/<slug>
/compare-per-dollar /zh/compare-per-dollar
/compare-per-dollar/<slug> /zh/compare-per-dollar/<slug>

There is no i18n framework in the repo, so this uses parallel routes that reuse the same React components with a lang prop + a Chinese dictionary. The English routes are untouched and render byte-identically.

Scope (per agreed decisions)

  • URL strategy: /zh route prefix (no framework / middleware).
  • Depth: compare copy only. The embedded interactive chart (InferenceChartDisplay, shared app-wide) stays English.

What's translated

  • Index: headings, lede, vendor-group descriptions, per-dollar CTA, model subtext
  • Detail: eyebrow, description paragraph, H1, empty states, cross-links, pricing line, figcaption, narrative caveat
  • SSR narrative prose — full Chinese template pools, 1:1 with the English pools (per-dollar both/tied/zero/single + full both/single)
  • Interpolated table: header + metric display labels (internal metric keys stay English so filtering / React keys are language-independent)
  • SEO: metadata title/description + hreflang alternates (en / zh-CN); JSON-LD ItemList / Dataset / BreadcrumbList human-readable fields; locale-aware breadcrumb index URLs
  • Sitemap: zh index + detail URLs added

Implementation notes

  • lib/compare/i18n.tsLang type, locale path helpers, en/zh dictionary (English entries copied verbatim from the old hard-coded pages).
  • lib/compare-ssr.ts — threaded lang through formatModelList, compareTableNarrative, buildJsonLd, buildBreadcrumbJsonLd; English output unchanged when lang='en'.
  • Extracted shared server views (index-view, full-detail-view, per-dollar-detail-view) so en + zh share slug parsing, the 308 canonicalization redirect (now locale-aware), the benchmark fetch, and the SSR interpolation. The original en route files are now thin wrappers. The two page-client.tsx components moved into lib/compare/ and gained a lang prop (dict for simple strings; inline lang === 'zh' JSX branch for the few markup-rich sentences where word order differs).
  • The per-dollar hero / OG PNG is a language-neutral data graphic, so zh reuses the canonical English performance-per-dollar.png endpoint rather than duplicating the ~500-line Satori route.

Tests

cypress/e2e/compare-zh.cy.ts — zh index + detail + per-dollar: localized copy renders, interpolated table renders with localized labels, the interactive chart mounts, and cross-links stay within the zh locale.

Verification

  • pnpm build registers all four /zh routes (dynamic); English routes intact.
  • Runtime smoke (prod server): /zh/compare and /zh/compare-per-dollar return 200 with Chinese copy; the zh alias slug 308-redirects to the canonical zh URL; /compare (en) unchanged.
  • pnpm test:unit — all 2006 app tests pass. Typecheck + lint clean.

Known limitation

  • Detail-page data fetch needs the Vercel Blob token, which is absent locally — that path 500s identically for en and zh, so it's an environment limitation (exercised by Cypress in CI, where the existing compare-table.cy.ts already hits detail pages).
  • Pages render under the root layout's <html lang="en">; a per-page html lang isn't overridable without changing the root layout. The hreflang alternates carry the locale signal for crawlers instead.

🤖 Generated with Claude Code


Note

Medium Risk
Broad refactor of high-traffic compare SSR/metadata and redirects; regressions could affect English SEO or canonical URLs, though English strings are intended to stay byte-identical.

Overview
Adds Chinese (/zh) compare routes that mirror /compare and /compare-per-dollar (index + slug detail), using the same components with a lang prop and a new lib/compare/i18n.ts dictionary plus locale path helpers.

English compare pages are refactored into thin route wrappers that delegate to shared CompareIndexView, FullDetailView, and PerDollarDetailView, so slug parsing, 308 canonical redirects (now locale-scoped), benchmark SSR, and JSON-LD stay unified. Copy, metadata hreflang alternates (en / zh-CN), SSR narrative template pools, breadcrumbs/JSON-LD labels, interpolated table UI strings, sitemap entries, and Cypress smoke tests (compare-zh.cy.ts) are wired for zh; the embedded inference chart remains English.

Reviewed by Cursor Bugbot for commit 7b73b58. Bugbot is set up for automated code reviews on this repo. Configure here.

Adds a Chinese-language variant of the compare master + detail pages, served
under a /zh URL prefix. No i18n framework existed, so this uses parallel routes
that reuse the same React components with a `lang` prop and a Chinese
dictionary — English routes are untouched and byte-identical.

New URLs:
  /zh/compare                  /zh/compare/<slug>
  /zh/compare-per-dollar       /zh/compare-per-dollar/<slug>

What's translated (per scope decision — "compare copy only"):
- Index headings, lede, vendor-group descriptions, per-dollar CTA
- Detail eyebrow, description, H1, empty states, cross-links, pricing line,
  figcaption, caveat
- SSR narrative prose (full Chinese template pools, 1:1 with the English pools)
- Interpolated-table header + metric display labels (keys stay English so
  filtering/React keys are language-independent)
- SEO metadata (title/description) + hreflang alternates, JSON-LD ItemList /
  Dataset / BreadcrumbList human-readable fields
The embedded interactive chart (shared app-wide InferenceChartDisplay) stays
English, as agreed.

Implementation:
- lib/compare/i18n.ts: Lang type, locale path helpers, en/zh dictionary
- lib/compare-ssr.ts: threaded `lang` through formatModelList,
  compareTableNarrative (+ Chinese template pools), buildJsonLd,
  buildBreadcrumbJsonLd — English output unchanged when lang='en'
- Extracted shared server views (index-view, full-detail-view,
  per-dollar-detail-view) so en + zh routes share slug parsing, the 308
  canonicalization redirect (now locale-aware), the benchmark fetch, and the
  SSR interpolation. English route files are now thin wrappers.
- Moved the two page-client components into lib/compare/ and made them +
  compare-interpolated-table lang-aware (dict for simple strings, inline JSX
  branch for the few markup-rich sentences).
- Reused the canonical English performance-per-dollar.png hero/OG graphic for
  zh (language-neutral data graphic) rather than duplicating the ~500-line
  Satori route.
- sitemap.ts: emit the zh index + detail URLs.

Tests: added cypress/e2e/compare-zh.cy.ts (zh index + detail + per-dollar:
localized copy, table, chart mount, locale-correct cross-links).

Verified: prod build registers all four /zh routes (dynamic); zh + en index
pages render at runtime; zh alias slug 308-redirects to the canonical zh URL;
all 2006 app unit tests pass; typecheck + lint clean. (Detail-page data fetch
needs the Vercel Blob token, absent locally — that path 500s identically for en
and zh, so it's an env limitation, exercised by Cypress in CI.)

Note: pages render under the root layout's <html lang="en">; per-page html lang
isn't overridable without a root-layout change. hreflang alternates carry the
locale signal for crawlers instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@functionstackx functionstackx requested a review from adibarra as a code owner June 7, 2026 21:19
@vercel

vercel Bot commented Jun 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
inferencemax-app Ready Ready Preview, Comment Jun 7, 2026 9:20pm

Request Review

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.

1 participant