Skip to content

feat(i18n): convert product name catalog batch 1 - #4656

Open
ShotaroKataoka wants to merge 1 commit into
kirodotdev:mainfrom
ShotaroKataoka:feat/i18n-product-name-catalog-1
Open

feat(i18n): convert product name catalog batch 1#4656
ShotaroKataoka wants to merge 1 commit into
kirodotdev:mainfrom
ShotaroKataoka:feat/i18n-product-name-catalog-1

Conversation

@ShotaroKataoka

Copy link
Copy Markdown
Contributor

Problem / Motivation

#1964 added the supported {{productName}} interpolation seam, but the existing i18n catalogs still hardcode Kiro Crew. Converting all 181 eligible keys in one PR produces a diff over the 1 MB review-pipeline limit, while splitting by locale would violate catalogParity placeholder parity.

Why it matters

Until the catalog conversion finishes, a downstream edition can call setProductName() but most visible copy still renders the stock name. Converting reviewable batches closes that gap without asking maintainers or AI review lanes to audit a 1.4 MB mechanical diff, and without leaving any intermediate commit where one locale carries a placeholder that another drops.

What changed (motivation → approach → change)

This is batch 1 of 3 catalog PRs. It converts exactly 60 product-name keys across every catalog at once:

  • Replaces the literal Kiro Crew with {{productName}} in the same 60 logical keys across en.json, en.manual.json, and all 11 shipped translations.
  • Regenerates en-XA.json from the merged English catalog; it is not edited by hand.
  • Keeps every apps.<id>.manifest.* value unchanged because [manifest-sync] requires byte-identical Python app.json prose.
  • Normalizes Korean particles whose form depends on the rendered product name to both forms (이(가), 을(를), 은(는), 와(과), (으)로). An edition may supply a name with or without a final consonant, so a single fixed form is no longer grammatically valid.
  • Deliberately skips 12 eligible keys that carry inherited untranslated values. Merely replacing the product name in those values makes the diff-scoped [changed-passthrough] gate correctly fail; the final batch will translate and convert them together rather than mixing translation cleanup into this mechanical chunk.

For every non-Korean shipped value in this diff, resolving productName to the stock default reproduces the previous value exactly. The Korean differences are only the required variable-dependent particle forms.

Tests

  • I18N_BASE_REF=upstream/main npm run i18n:check — all 18 checks pass, including placeholder parity, changed-value QA, changed-passthrough, pseudolocale freshness, DNT integrity, and manifest sync.
  • npx vitest run src/i18n/ — 604 passed, 1 expected failure.
  • npm run build — passes.
  • BRAND_BASE_REF=upstream/main python3 scripts/check_brand_name.py and ./scripts/scrub-lint.sh --no-history — pass.
  • Full frontend coverage run: 22,205 tests passed; two timing assertions in the unrelated CliPanelCoverage.test.tsx theme-sync suite failed only in the 38-minute whole-suite run. That file passes 41/41 in isolation, and this PR changes catalog JSON only. Electron tests pass.

Manual verification

N/A — the structural behavior is covered more strongly by the catalog-wide parity and changed-value gates: all locales carry the same placeholder set, non-Korean stock rendering is exactly preserved, and Korean variable-dependent grammar is enforced by its style gate.

Related Issues

Checklist

Convert the first 60 debt-free product-name keys across every locale to
{{productName}}. Splitting by key across all locales preserves placeholder
parity while keeping the review diff below the 1 MB pipeline limit.

Regenerate en-XA from the merged English catalog. Korean particles whose
form depends on the rendered product name use both forms because an edition
can override the default with a name whose final consonant is unknown at
authoring time.

Twelve keys carrying inherited untranslated values are deliberately left
for the final batch: touching them without translating them fails the
changed-passthrough gate and would mix translation cleanup into this
mechanical chunk.

Refs kirodotdev#1957
@ShotaroKataoka
ShotaroKataoka requested a review from a team as a code owner August 20, 2026 07:57
@github-actions github-actions Bot added fork Pull request from a fork (external contributor) readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Aug 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

GPT 5.6 Review (fork) — ✅ no blocking findings

Reviewed 088c1bb19f985ca6099ef12d4cb66072e504f6c8 via the fork AI-review pipeline; updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 088c1bb

@github-actions

Copy link
Copy Markdown
Contributor

Design Review (Fable 5, fork) — ✅ PASS

Design-level review of 088c1bb19f985ca6099ef12d4cb66072e504f6c8 via the fork AI-review pipeline — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

The design checks out: the interpolation seam exists in the base (website/src/i18n/index.ts setProductName/defaultVariables), the batch axis (same keys across all locales) is the only one compatible with the placeholder-parity gate, the manifest-key exclusion matches the documented exception, and the Korean both-forms particle treatment is the standard correct solution for a variable noun. Description ↔ diff line up (60 logical keys; en split 34+26 across generated and manual catalogs).

Design-Verdict: PASS

Mechanical catalog conversion riding a seam built for exactly this, batched on the only axis the parity gates permit; no design-level concerns.

[DESIGN-REVIEWED] 088c1bb

@github-actions

Copy link
Copy Markdown
Contributor

UX Review (Fable 5, fork) — 🟡 CONCERNS

UX-level review of 088c1bb19f985ca6099ef12d4cb66072e504f6c8 via the fork AI-review pipeline — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Reviewed the full patch (14 locale files, 60 keys), the i18n interpolation seam in website/src/i18n/index.ts, and the PR description.

UX-Verdict: CONCERNS

Rendering is byte-identical for stock users everywhere except Korean, where correct particles were downgraded to hedged dual forms across ~30 visible strings.

Watch

  • Korean grammar regression in the stock build. Every converted ko string trades a previously correct particle for the parenthesized dual form — e.g. "Kiro Crew가 열려 있고""{{productName}}이(가) 열려 있고", which renders as "Kiro Crew이(가)…" for all Korean users. These include high-frequency chrome (gateway disconnected banner, all four approval-mode tooltips/descriptions), so the awkward form is seen constantly and permanently — friction, not failure, but a visible quality drop where quality existed. Smallest fix: an i18next interpolation formatter that picks the josa from the resolved name's final jongseong ({{productName, josa(이/가)}}), keeping "Kiro Crew가" in stock and staying correct for editions.
  • Mixed product names on one surface for editions mid-migration. In the approval-mode popover, yolo_tooltip now interpolates while the adjacent skipped key yolo_expiration_until_shutdown ("Stays on until Kiro Crew restarts…") stays literal, so an edition renders two product names in one popover until batch 3 lands. Transitional and edition-only; worth confirming batch 3 lands before any edition ships from an intermediate commit.

[UX-REVIEWED] 088c1bb

@github-actions

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5, fork) — ✅ PASS

Premise-level review of 088c1bb19f985ca6099ef12d4cb66072e504f6c8 via the fork AI-review pipeline — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All evidence checked. The patch is a pure data conversion: every one of its 780 added lines carries {{productName}}, every one of its 780 removed lines carries the literal (720 Kiro Crew, 60 accented pseudolocale equivalents), 780 = 60 keys × 13 catalogs exactly as declared. The seam it feeds is already live at base (website/src/i18n/index.ts:187 defaultVariables: { productName }), and website/docs/i18n-catalog.md:97-100 explicitly plans this batched conversion. The Korean dual-particle forms follow a convention already used 171 times in the base ko.json.

First-Principles-Verdict: PASS

Completes a documented, already-seamed migration in declared mechanical batches; every difference is either the conversion itself or a deferral the description names.

What this change ships

Intent: make existing catalog copy actually render an edition's overridden product name — a FIX of the gap #1964 left (seam live, catalogs still literal).

  1. 60 strings in all 13 locales now render the configurable product name — justified (mandated by website/docs/i18n-catalog.md §product-name, transitional note plans exactly this).
  2. Korean copy shows dual particles (이(가), 을(를), 은(는), (으)로) around the name — declared; matches 171 existing dual-particle uses in base ko.json (grepped), required once the name is variable.
  3. Pseudolocale en-XA regenerated with matching placeholders — justified, generated file.
  4. 12 eligible keys left literal until the final batch — declared deferral, keeps the [changed-passthrough] gate honest.
  5. apps.<id>.manifest.* strings keep the literal — justified ([manifest-sync] byte-identity invariant).

No new key, flag, code path, or public surface is introduced; the only consumer mechanism (interpolation.defaultVariables, index.ts:187) predates this PR. Verified no rider: zero added lines lack {{productName}}, zero removed lines lack the literal.

[FIRST-PRINCIPLES-REVIEWED] 088c1bb

@github-actions

Copy link
Copy Markdown
Contributor

Opus 4.8 Review (fork) — ✅ no blocking findings

Reviewed 088c1bb19f985ca6099ef12d4cb66072e504f6c8 via the fork AI-review pipeline; updated in place on each push.

Review details

I've verified the key risks directly from the code:

  • JSON structure & interpolation wiring: The {{productName}} variable pre-exists in index.ts (wired as interpolation.defaultVariables, defaulting to "Kiro Crew"), so rendered English is byte-identical to the old literals and component tests asserting "Kiro Crew" still pass.
  • Manifest exception: In en.json the apps.*.manifest.* values (e.g. line 1150) appear only as unchanged context lines — they retain the literal, so the byte-for-byte check-app-manifest-sync.mjs gate is not broken.
  • Placeholder parity (catalogParity.test.ts): the substitution is a brand-aware codemod, not a naive replace — it substituted the en-XA pseudolocale's transliterated form (Ķìŕø Çŕèẁ{{productName}}) too, and every key I sampled (star_kirocrew_on_github, feature_off, updating_kirocrew, the agentImportFlow block) is substituted consistently across en, en-XA, and the translated catalogs, while a deliberately-skipped key (kiro_crew_uses_kiro_cli_as_its_agent_engine_comp) is left literal everywhere.

The discovery pass produced no candidates, and I could not ground any defect myself to the (a)/(b)/(c) bar at 80+ confidence.

No findings.

[OPUS-REVIEWED] 088c1bb

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fork Pull request from a fork (external contributor) readiness: passed Eligible automated validation passed for the current revision

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant