Repository navigation
Commit eec56c3
fix(platform-objects): keep the userFilters element tokens English in translated tooltips (#19520)
Fixes #19430
Clause-②: no
The accept set does not move: no key joins a published payload, no
member joins an enum, no symbol is exported, nothing is registered. The
diff is three translated string leaves, one unit test file that exports
nothing, and a changeset. `packages/spec` is untouched — the schema is
not changing in this round.
## What was wrong
`metadataForms.view.fields.userFilters.helpText` names the legal values
of a **strict** enum, and it is the only place the `view` panel names
them at all. All three translated catalogs rendered those tokens as
ordinary words, so an author — or an AI — working in a translated locale
was shown a value the schema refuses.
This is the inverse of #19403's defect: there English leaked into
translated UI; here translation leaked into a machine token that has to
stay English to be writable. The prose around the values is translated
correctly and stays translated.
## Per-locale before/after, beside the enum it must agree with
The enum, unchanged by this PR, read on this branch at
`packages/spec/src/ui/view.zod.ts:1506` (inside `UserFiltersSchema`,
declared at `:1497`):
```ts
element: z.enum(['dropdown', 'tabs', 'toggle']).default('dropdown')
```
| locale | before | after |
|:--|:--|:--|
| `en` (control, ⛔ not edited) | `Quick-filter bar: element style
(dropdown / tabs / toggle) + exposed fields or tab presets` | unchanged
|
| `zh-CN` | `快速筛选栏:控件样式(下拉 / 标签页 / 开关)+ 暴露的字段或标签页预设` |
`快速筛选栏:控件样式(dropdown / tabs / toggle)+ 暴露的字段或标签页预设` |
| `ja-JP` | `クイックフィルターバー:要素スタイル(ドロップダウン / タブ / トグル)+ 公開フィールドまたはタブプリセット`
| `クイックフィルターバー:要素スタイル(dropdown / tabs / toggle)+ 公開フィールドまたはタブプリセット` |
| `es-ES` | `Barra de filtros rápidos: estilo de elemento (desplegable /
pestañas / interruptor) + campos expuestos o preajustes de pestañas` |
`Barra de filtros rápidos: estilo de elemento (dropdown / tabs / toggle)
+ campos expuestos o preajustes de pestañas` |
(The table renders the CJK brackets as ASCII for legibility; the files
keep each locale's own full-width punctuation, unchanged by this PR —
read the diff for the bytes.)
⇒ after: every token the tooltip names is a value `UserFiltersSchema`
accepts, in all four locales. The prose keeps its own language: 快速筛选栏 ·
クイックフィルターバー · Barra de filtros rápidos, and the trailing "tab presets"
prose (标签页预设 · タブプリセット · preajustes de pestañas) stays translated — it
is prose, not a token.
Only leaf string values changed. No key was added or dropped, so the
bundle structure is untouched and no `*.source-hashes*.ts` file moves:
`metadataForms.view.fields.userFilters.helpText` carries no provenance
entry in any of the three locales (it is LEGACY-TRUSTED — already
translator-written before this round), and `pnpm check:i18n` reports all
9 packages in sync.
## The two `element` enums — which tooltip describes which, and the
verdict on the second
Both readings taken on this branch at `1b2abe57`, where
`packages/spec/src/ui/view.zod.ts` is byte-identical to `origin/main` at
`48c39e00`:
| line | enum | owning schema |
|:--|:--|:--|
| `view.zod.ts:1506` | `z.enum(['dropdown', 'tabs', 'toggle'])` |
`UserFiltersSchema` (`:1497`) |
| `view.zod.ts:4205` | `z.enum(['dropdown', 'toggle'])` — ⛔ no `tabs` |
`ObjectUserFiltersSchema` (`:4195`) |
The card measured the second one at `:4193`; on this tree it is `:4205`.
Same declaration, re-taken here.
**The repaired tooltip describes the FIRST enum.** The chain, each link
read on this tree:
1. The tooltip's source is `packages/spec/src/ui/view.form.ts:119`,
inside `defineForm({ schemaId: 'view' })` (`view.form.ts:18-19`).
2. `schemaId: 'view'` resolves through `METADATA_FORM_REGISTRY` to
`ViewMetadataSchema` (`view.zod.ts:5586`), a union over four wire
members (`VIEW_METADATA_MEMBERS`, `:5181`).
3. The form's `userFilters` is a **top-level** field. The member that
carries a top-level `userFilters` is `ListViewOverlayWireSchema` —
`ListViewShapeSchema.extend(...)` — and
`ListViewShapeSchema.userFilters` is `UserFiltersSchema.optional()` at
`view.zod.ts:2410`. The `viewItem` member carries the same schema one
level down, under `config`.
4. So the three values the tooltip names are exactly the three
`UserFiltersSchema.element` accepts. The `en` copy was already correct;
that is why it is the control.
**Verdict on the second surface (`:4205`): no defect, because no tooltip
reaches it.** `ObjectUserFiltersSchema` is referenced from exactly one
place — `ObjectListViewSchema` (`view.zod.ts:4220-4222`) — which is what
`ViewSchema`'s container keys `views.list` and `views.listViews` use
(`:4306`, `:4308`). No metadata form declares a field for them:
- `grep -n "field: '"` over `packages/spec/src/data/object.form.ts`
returns no `views`, `listViews` or `userFilters` entry;
- `grep -n "listViews" en.metadata-forms.generated.ts` returns nothing;
- `grep -n "dropdown"` over the whole `en` catalog returns exactly
**one** line — `view.fields.userFilters.helpText`, the leaf repaired
here.
⇒ the second enum has **no** metadata-form tooltip, so it carries
neither this defect nor the worse one (a tooltip naming a value its own
enum refuses). Its own English `.describe()` at `view.zod.ts:4206` is
correct and says so explicitly: `"tabs" is page-only — use listViews for
named presets`.
One neighbour worth stating because it looks adjacent and is not:
`page.fields['interfaceConfig.userFilters']` describes the SAME
`UserFiltersSchema` (`page.zod.ts:591`), and its tooltip already keeps
`None / Tabs / Dropdown` verbatim English in all three locales — the
shape PR #19428 chose. ⛔ It is **not** the shape this PR applies, and
the corrected changeset now says so: those three are the `filter-mode`
widget's UI mode names, capitalised, and `z.enum` is case-sensitive, so
the enum refuses all three; `None` stands for the absence of the config
rather than a value; `toggle` is deliberately not offered there. What
this PR keeps verbatim is the enum's own tokens, which is the stricter
requirement, because they are values an author types. <sub>— clause
corrected in place by the `domain:spec` seat 3 at 2026-09-21T06:20Z,
after the at-tier review found it false; the original read 「and the
shape this PR applies to the `view` panel」. Disclosed here and in the
review record rather than silently rewritten.</sub>
## The pin, and the four controls that keep it honest
`packages/platform-objects/src/apps/translations/user-filters-element-tokens.test.ts`
— 11 cases, one leaf, three locales. Not a census: it reads one key.
1. **The set is derived, not listed.** `SCHEMA_ELEMENT_VALUES` is read
off `UserFiltersSchema.shape.element` (unwrapping `.default()`) and
asserted SET-EQUAL to the pinned literal; every tooltip assertion then
runs over the derived set. So enum **growth** reddens, not only
shrinkage — a hand-listed set catches a dropped member and lets a newly
legal value go unnamed in all four catalogs with the pin green, which is
this card's own defect class reintroduced.
2. **Schema leg.** Every derived token is `safeParse`d through
`UserFiltersSchema` and must be ACCEPTED; the words the translations
used to carry (下拉/开关 · ドロップダウン/トグル · desplegable/interruptor) and the
neighbouring panel's capitalised `None / Tabs / Dropdown` are
`safeParse`d and must be REFUSED.
3. **Exact-set leg.** The tooltip's parenthetical must name the derived
values and nothing else, in order. Checking only "no fewer" lets
`(dropdown / tabs / toggle / chips)` through.
4. **Still-translated leg.** Each translated helpText must differ from
the `en` source and must still carry its own prose at BOTH ends of the
sentence. Anchoring the leading fragment alone lets the trailing half
revert to English — #19403's defect surviving on the larger half of the
sentence.
`tabs` is deliberately absent from the "former words" row of every
locale: its former word is also the locale's ordinary word for the
tab-presets **prose** at the end of the same sentence, which is
translated on purpose and must stay. Asserting its absence would demand
the prose be de-translated; it is covered by controls 1, 3 and the
presence leg instead. The reason is written in the test.
**Reverse verification** — five legs, each driven from the committed
state with `scripts/ablation-replace.mjs` (anchor hit asserted, blob
hash before/after recorded, restore proved by `blob == HEAD` and an
empty `git diff HEAD`):
| mutation | expected | observed |
|:--|:--|:--|
| `zh-CN` helpText reverted to the pre-fix translated words | red | **1
failed / 10 passed** — `zh-CN helpText does not name 'dropdown'` |
| `zh-CN` helpText replaced by the `en` source sentence | red, on the
still-translated leg | **1 failed / 10 passed** — `zh-CN helpText is a
byte copy of the en source` |
| `zh-CN` trailing clause replaced by its English source | red — the N1
escape, now closed | **1 failed / 10 passed** — `zh-CN helpText lost its
trailing prose` |
| `zh-CN` parenthetical grown to `(dropdown / tabs / toggle / chips)` |
red — the N2 escape, now closed | **1 failed / 10 passed** — `names a
different set from the enum` |
| `UserFiltersSchema.element` grown by a fourth member in
`packages/spec/src/ui/view.zod.ts`, spec rebuilt | red — the B2 class |
**5 failed / 6 passed** — the derived-set control plus all four
catalogs' presence legs |
Every leg restored: `blob == HEAD`, `git diff HEAD` empty. The
fourth-member leg additionally left a mutated `packages/spec/dist`, so
spec was rebuilt and the restore proved by grep with a lit control: the
mutated spelling is absent from `packages/spec/dist` (grep exit 1, 0
hits) while the canonical one is present in 22 files (grep exit 0). ⛔
`view.zod.ts` is untouched by the delivered diff — that mutation was an
ablation, restored, never committed.
<sub>1 parent a60c913 commit eec56c3
5 files changed
Lines changed: 230 additions & 3 deletions
File tree
- .changeset
- packages/platform-objects/src/apps/translations
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
737 | 737 | | |
738 | 738 | | |
739 | 739 | | |
740 | | - | |
| 740 | + | |
741 | 741 | | |
742 | 742 | | |
743 | 743 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
737 | 737 | | |
738 | 738 | | |
739 | 739 | | |
740 | | - | |
| 740 | + | |
741 | 741 | | |
742 | 742 | | |
743 | 743 | | |
| |||
Lines changed: 216 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
737 | 737 | | |
738 | 738 | | |
739 | 739 | | |
740 | | - | |
| 740 | + | |
741 | 741 | | |
742 | 742 | | |
743 | 743 | | |
| |||
0 commit comments