Uh oh!
There was an error while loading. Please reload this page.
fix(fields): 让 TextAreaField 字数统计的可访问名跟随会话语言 (#3406) - #3409
Merged
Conversation
…me (#3406) The counter block TextAreaField renders when a field declares `maxLength` carried the English literal `Character count: ${n} of ${max}` as its `aria-label`, on an element with `aria-live="polite"`. The visible `{n}/{max}` is digits and needs no locale, so nothing looked wrong — but a zh/ja/ar session had that English sentence read out on every keystroke, and only screen reader users could perceive it. Unlike #3404 (keys existed, this path did not consume them), no character-count key existed in any of the ten packs. `fields.textarea.characterCount` is new in all ten, interpolating `{{count}}` and `{{max}}` as ONE sentence rather than parts assembled in code: ja/ko put the cap before the count, an order no concatenation can produce. The widget reads it through `useFieldTranslation()` (createSafeTranslation + English defaults), the same shape #3404 verified, so a provider-less embed keeps rendering a sentence instead of a raw key — measured: the bare hook returns `fields.textarea.characterCount` verbatim. The en pack value and the FIELD_DEFAULTS fallback are byte-identical to the literal they replace, so `en` and no-provider rendering are unchanged. `aria-live="polite"` and the per-keystroke recompute are deliberately untouched and tracked in #3408. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
…3406) The zero-length case asserted `value={undefined}`, which fails `tsc`: `FieldWidgetComponentProps` declares `value: T`, non-optional. The widget's `value || ''` guard predates this change and covers a JS host that ignores the type — pinning that out-of-contract shape here would be the lenient-consumer move AGENTS.md #0.1 rules out. `''` exercises the same thing the case was for: `0` is falsy, so an interpolation guarding on truthiness would speak "Character count: of 40". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
The latest updates on your projects. Learn more about Vercel for GitHub. |
…3406) Three prose spots explaining the ja/ko word order quoted the Japanese sentence itself, in a code comment, a locale-pack comment, a test header and the changeset. AGENTS.md #-1 makes the codebase English-only including comments and docs, so the order is now described rather than transcribed. The CJK that remains is assertion VALUES in the provider test — expected translations, which cannot be written any other way and match the existing `FullscreenFieldEditor.i18n.test.tsx`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
yinlianghui
marked this pull request as ready for review
August 5, 2026 20:54
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
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 freeto 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#3406
问题(已对
origin/main6fe485b 复核,前提成立)packages/fields/src/widgets/TextAreaField.tsx:84字面量原样存在:这块只在字段声明了
maxLength时渲染,视觉上是纯数字{n}/{max}(与语言无关),所以屏幕上看不出任何毛病。但该元素带aria-live="polite"—— 非英文会话下,读屏用户每次输入变化都会听到一句英文。只有读屏用户能感知,这也是它一直活着的原因。与 #3404 同族但不重叠:#3404 是「键早已存在、这条路径没消费」;这一条是十个语言包里根本没有任何 character/char count 键。
改动
新键
fields.textarea.characterCount,十包齐,插值{{count}}/{{max}}。写成一句带插值的整句而不是在代码里拼装,是因为 ja/ko 把上限放在计数前面(ja:「{{max}} 文字中 {{count}} 文字」;ko:「{{max}}자 중 {{count}}자」),任何「标签 + 数字 + 分隔符 + 上限」的代码侧拼接都产不出这个语序。测试里有一条专门钉住这个语序。
widget 走
useFieldTranslation()(createSafeTranslation+ 英文兜底),即 #3405 刚验证过的形制,而不是裸useObjectTranslation()。这不是风格选择,是实测:裸 hook 在无 provider 时返回的是键本身,见下方反向验证。en包的值与FIELD_DEFAULTS兜底逐字节等同于被替换的字面量,所以en会话与无 provider 的嵌入渲染完全没变。未改行为:
aria-live="polite"与每击键重算原样保留。#3406 正文提出的「这套是否本身该重设计」按 PM 裁定拆成 #3408,含实测(52 次击键 = 52 次整句播报,累计 979 字符);本 PR 里那条属性被测试钉住,免得后来者顺手改掉而无人察觉。文件面
packages/fields/src/widgets/TextAreaField.tsx—— 换键;hook 调用必须放在readonly提前返回之上(rules-of-hooks),有测试钉住该分支渲染未变。packages/fields/src/widgets/useFieldTranslation.ts—— 仅新增一条FIELD_DEFAULTS。packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts—— 新键。反向验证(方向是先预测再跑的,两个方向都符合预测)
A. 把英文字面量放回去 —— 预测:provider 用例转红,
en那条不动。实测6 failed | 8 passed:转红的正是 zh / ja / ru / ar / 逐字重算 / 旧式
max_length六条;en那条保持绿,no-provider 文件整个保持绿 —— 这恰恰就是「英文侧是 no-op」这一主张的证据,而不是遗漏。B. 把
useFieldTranslation()换成裸useObjectTranslation()—— 预测:no-provider 文件转红、provider 文件全绿(这正是这两个用例必须分文件的原因)。实测3 failed | 11 passed,红的三条全在 no-provider 文件:裸 hook 连插值都丢了,比被替换的字面量更差。no-provider 用例单独成文件的理由是 #3404 实测出来的:挂载
I18nProvider会把该实例装成 react-i18next 的全局默认,同文件内的后续用例读到的是泄漏的实例,会「因为错误的原因而绿」。消费半径清扫
repo 内
Character count的旧字面量已无任何残留(仅剩本 PR 的 en 包值、兜底与测试断言)。packages/components的内置分支、FullscreenFieldEditor的 footer({draft.length}/{maxLength})都没有 aria-label,不在此列(#3406 正文已注明)。验证
顺带发现
🤖 Generated with Claude Code
https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
Generated by Claude Code