fix(components): ActionParamDialog's select placeholder reads the common.select pack key - #4397
Merged
yinlianghui merged 3 commits intoAug 12, 2026
Merged
Conversation
) The `select` branch rendered `param.placeholder || 'Select...'` in a file that imported no translation hook, so the fallback — which fires whenever an action param of kind `select` declares no placeholder of its own — was hardcoded English in every locale and spelled its ellipsis in ASCII, which #3878 converged the ten locale packs away from. Route the fallback through the existing `common.select` pack key, read via this package's `createSafeTranslation` pattern. The key is REUSED, not added: it already ships in all ten packs and `packages/fields`' LookupField already consumes it for the structurally identical job (a Radix select trigger's placeholder behind the same authored-metadata-wins shape). No locale pack file changed. Because `common.select` is already in ellipsis-glyph-3878's CONVERGED_KEYS, the U+2026 this site now renders is pinned for free. Authored `param.placeholder` keeps priority — only the fallback changed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
…4386) The no-provider file's docblock claimed it was green on both sides of the fix — the intuitive expectation for a FALLBACK pin, since the English bytes are supposed to be unchanged. Measured, it is RED before and GREEN after: the literal being replaced was `Select...`, so the English bytes did NOT survive unchanged — the ellipsis moved from ASCII to U+2026, and `getByText('Select…')` is what fails on the revert. Also separates the two assertions by the mutation each catches: the glyph one goes red on reverting the fix, the `common.select` raw-key one goes red only if the defaults-map entry is dropped, which is the regression this file exists to catch long after #4386. Docblock only — no assertion changed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
…ion-param-select-placeholder
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Collaborator
Author
|
ACCEPT — PM 复核 (session
Flipping ready + arming auto-merge. Generated by Claude Code |
yinlianghui
marked this pull request as ready for review
August 12, 2026 02:07
yinlianghui
deleted the
claude/issue-4386-action-param-select-placeholder
branch
August 12, 2026 02:08
This was referenced Aug 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4386
ActionParamDialog'sselectbranch renderedparam.placeholder || 'Select...'in a file that imported no translation hook at all. The fallback — which fires whenever an action param of kindselectdeclares noplaceholderof its own, the ordinary case for hand-written action metadata — was therefore hardcoded English in every locale, and spelled its ellipsis in ASCII, which #3878 converged the ten packs away from.The fallback now reads the existing
common.selectpack key through this package'screateSafeTranslationpattern. Authoredparam.placeholderkeeps priority — only the fallback changed.Key choice:
common.select, REUSED — no pack file changedThe PM ruling on the card was reuse-first (the #4375/#4376 pair was in flight across the packs). Measured, not assumed:
common.selectSelect…选择…common.selectOptionSelect an option请选择common.selectalready ships in all ten packs (arاختر…,deAuswählen…,enSelect…,esSeleccionar…,frSélectionner…,ja選択…,ko선택…,ptSelecionar…,ruВыбрать…,zh选择…).packages/fields'LookupFieldalready consumes it for the structurally identical job — the placeholder of a Radix select trigger, behind the same authored-metadata-wins shape (LookupField.tsx:987). One key, one wording, on both select-trigger paths.common.selectOption(used by the form renderer's built-in select) was the near miss. Rejected on two counts: it is a different sentence, so adopting it would silently rewrite this dialog's visible copy; and neither itsennor itszhvalue ends in an ellipsis, so it would have dropped the very glyph half of the defect.t(key) + '...'— the ellipsis is concatenated in code, so it stays ASCII in all ten locales and cannot be translated away #4375/list.loadingserves the raw English sentence "Loading records…" in eight of the nine packs (zh alone translated it) — plus 3 more prose values byte-identical to en #4376 pair. Becausecommon.selectis already inellipsis-glyph-3878.test.ts'sCONVERGED_KEYS, the U+2026 this site now renders is pinned by that gate for free, in every locale.Note this is a different semantic from #4391's measurement, which crowned
table.searchfor SEARCH placeholders. This is a SELECT-trigger placeholder;common.selectis that family's established key.Why the safe hook
createSafeTranslation, not a bareuseObjectTranslation: this dialog is rendered with noI18nProviderby embedded hosts and by this package's own bare-render suites (action-param-dialog-aria-required,action-param-dialog-label-associationboth mount it provider-less). With no provider the bare hook returns the raw KEY, so the placeholder would readcommon.select. The defaults-map entry is the pack's stand-in on that path and is byte-identical toen— verified programmatically, both sides['0x53','0x65','0x6c','0x65','0x63','0x74','0x2026'].Pre-fix red, and reverse verification
Three reverse verifications, each with the direction predicted before running it.
1. Revert the fix (restore the
'Select...'literal). Predicted 3 RED / 2 GREEN — measured exactly that:All three fail on the right assertion —
Unable to find an element with the text: 选择…/Select…. The 2 that stay green are the authored-placeholder cases, which never reach the fallback: that is the surviving pin that authored metadata keeps priority.2. Empty the defaults map (fix otherwise intact). Predicted 1 RED / 4 GREEN, because the defaults map serves only the provider-less path — measured exactly that, and the DOM dump shows the literal text
common.selectrendering. The provider-mounted cases stayed green, which is what proves the two paths are independent.3. Point the call site at a bogus key. This proves the new
t()call site is genuinely visible to the i18n gate rather than passing vacuously:A correction is folded in as its own commit: the no-provider file's docblock originally claimed it was green on both sides of the fix (the intuitive expectation for a fallback pin). Measured, it is RED before and GREEN after — the literal being replaced was
Select..., so the English bytes did not survive unchanged; the ellipsis moved from ASCII to U+2026. The docblock now records the measured direction and separates which assertion catches which mutation.Verification
All from the repo root, on the merge with
origin/mainatbb58d1d61:vitest run packages/components/vitest run packages/i18n/ packages/fields/tsc --noEmit(components src)tsc -p tsconfig.test.json(components tests)check:i18n-keyscheck:i18n-driftcheck:control-byteschangeset:check+check-changeset-presencepatch, nomajoreslinton the 3 changed filesanywarnings, all present onorigin/mainat lines 41/63/64/77)Changeset:
patch(nevermajor, per the version-alignment rule).Recovered after a host restart
The original dev agent for this card was killed near the finish line. Inherited from it, then re-audited line by line: the implementation commit
d0204ecdfand one uncommitted docblock edit. Everything below the implementation was redone from scratch in this session — the previous agent's verification results died with the host and none were trusted.d0204ecdf, after auditing it against the card — call site fixed, red-first tests present, defaults-map entry confirmed byte-identical toen, key choice re-audited against reuse-first and found defensible. Not churned.pnpm install, theorigin/mainmerge (clean, no conflicts), all three reverse verifications, both tsc runs, every gate, and the full package suites.origin/mainmoved tobb58d1d61(#4391) under this branch and is merged in. No overlap: #4391 moved the two SEARCH placeholders totable.searchand translated four English-serving values; it did not touchcommon.selectnoraction-param-dialog.tsx.Per the card's own grading,
examples/schema-catalog/.../with-placeholder.jsonis left as-is — sample authored metadata demonstrating theplaceholderprop, not product copy.Generated by Claude Code