Commit 576d5df
fix(platform-objects): decide the object field-editor panel en-echoes per leaf, and derive the panel pin (#19418)
Part of #19403
Clause-②: no
## What this is
One panel family, decided **leaf by leaf**. The card's deliverable is a
per-leaf judgement with its reason recorded, not 84 translations; every
leaf below was decided on its own evidence, and the verdicts with their
reasons live in a ledger the suite reads
(`object-field-editor-panel-echo-decisions.test.ts`), not only in this
body.
**The family: the object field editor** — `object.fields['fields.*']`,
the panel the card samples (`placeholder`, `valueDomain`, `rows`,
`deleteBehavior`, `expression` …). It is the most trafficked authoring
surface in Studio: every field an author creates is typed into it.
⛔ Not `Fixes` — this is one family of the card's population, and the
card outlives this round. **Eight families remain** (65 label keys),
listed at the foot of this body.
## The reading, re-taken on this base — the card's numbers are NOT
inherited
Census instrument: flatten every `*.metadata-forms.generated.ts` bundle
and compare `.label` leaves against the `en` bundle. Population fixed
here: **the `.label` leaves of the metadata-form catalogs** (the seat's
population, 538 leaves), **plus the sibling `helpText` leaf of every key
this round decides** — a panel whose field name is Chinese and whose
tooltip is English is the same defect half fixed.
Measured on worktree base `0862063ba`:
| reading | result |
|:--|:--|
| `en` string leaves in the metadata-form catalogs | 893 |
| of which `.label` leaves | 538 |
| ⭐ POSITIVE CONTROL — labels genuinely translated | **459** (`zh-CN`) ·
**443** (`ja-JP`) · **443** (`es-ES`) |
| labels echoing the `en` source, per locale | 79 · 95 · 95 |
| label keys echoing in ALL THREE locales | **79** ⇒ 237 leaves |
⭐ The control is what makes the echo count mean something: 459
translated against 79 echoing is a discrimination, not a parser that
matches everything.
**This is 79 / 237, not the card's 83 / 249, and the difference is fully
accounted for**: the card's table was taken before PR #19401 landed,
which translated exactly four label leaves (`report.fields.dataset` /
`values` / `rows`, `dataset.fields.measures`). 83 − 4 = 79, and the
control moves the same way (455 → 459, 439 → 443, 439 → 443), matching
the reading the dispatching seat took after #19401 to the unit. The
other count on the card (82 / 230, over 417 declared form-field paths,
older base) answers a different question and is not comparable — stated
there, confirmed here.
**After this change**: 65 keys / 195 leaves echo in all three locales;
control 473 · 457 · 457.
## The verdicts — 14 keys × 2 string leaves × 3 locales = 84 decisions
Every one is `translate`, and **not one of them is "translate because it
is English"**. The echo direction was tested per leaf by asking what
this catalog's own authored corpus answers for that term:
- **Same-string precedent, authored in all three locales** —
`placeholder` (`action.fields.params.placeholder.label` reads 占位文本 /
プレースホルダー / Marcador de posición), `deleteBehavior` and `expression` and
`summaryOperations` (all three authored on the sibling `field` panel,
label and helpText), `summaryOperations.object` / `.field` (nine and
seven other paths), `visibleWhen` (two other paths).
- **Decided AGAINST a same-string precedent** — `fields.rows.label`.
`report.fields.rows.label` is authored as 行 / 行 / Filas, but that leaf
names a pivot axis and this one is a COUNT of text rows (its own
helpText says so), so `zh-CN` and `ja-JP` depart to 行数 while `es-ES`
keeps Filas, which carries both senses. This is the leaf that shows the
judgement is per leaf and not per string.
- **Machine tokens kept English inside translated prose** — `lookup` in
`lookupFilters` (the authored `fields.reference.helpText` keeps `lookup`
and `tree` in all three locales), `count` in
`summaryOperations.field.helpText`, `CEL`, and the autonumber literals
`"INV-{0000}"` / `{YYYY}/{MM}/{DD}` / `{field_name}`.
- **A distinction the source draws, preserved** —
`summaryOperations.function.label` is rendered as the plain word (函数 /
関数 / Función), deliberately not as "aggregation function", because the
source says `Function` at the label and `Aggregation function` at the
helpText one line below.
- **Panel-internal control** for the prose leaves: each carried an entry
in this package's per-locale `source-hashes.generated.ts` provenance
table in all three locales, while every authored neighbour on the same
panel carried none — which is what an unauthored extractor fill looks
like here.
Per-leaf reasons for all 28 leaves are in the ledger; this list is the
summary, not the record.
## The instrument, reused not reinvented
`object-field-editor-panel-echo-decisions.test.ts` is the #19355 shape:
one row per leaf, verdict per locale, reason, and the `en` source pinned
EQUAL to the live bundle so rewording the English reds the row instead
of leaving a decision standing over text nobody judged. An `echo`
verdict must carry its own per-locale departure reason.
Two additions, both because this round would otherwise ship a check that
cannot fail:
1. **The echo-reason rule is a predicate with a dark control.** Every
verdict here is `translate`, so asserting the rule over the ledger alone
evaluates nothing — the suite feeds it a synthetic `echo` row and
asserts it is refused, then asserts it is accepted once a departure
reason is added.
2. **The panel population is DERIVED, which is the structural half the
card asks for.** `repeater-row-properties.test.ts` already refuses an
en-echo inside its population, and it filters on `spec.type ===
'repeater'`; the object form declares its field editor as `{ field:
'fields', type: 'record' }` with 35 children, so that derivation yields
exactly four row properties for `objectForm` (`fields.options.*`) and
**none** of the 14 keys here — the card's "0 of the keys sit inside it",
measured rather than assumed. The new file walks the
`object.fields['fields.*']` leaves out of the `en` catalog and refuses
any that reads its source without a ledger row saying so, so a re-fill
or a field added to the editor tomorrow is red on the day it lands.
## Regeneration — the silent-loss hazard on this path
Translated-locale values are hand-written; the provenance tables are
not. Regenerated with **`pnpm i18n:extract`** (the command this
package's extract config documents), never by hand, and **no key was
added or removed** — so the `merge=os-regen` gap on
`*.metadata-forms.generated.ts` (`git check-attr merge` reads
`unspecified` on these paths) has nothing to lose even in principle. The
bundle diff is 84 insertions / 84 deletions, leaf for leaf; the
extract's own diff is 84 deletions and zero insertions — the 28
provenance rows per locale that now record these leaves as authored.
`pnpm check:i18n` green is the proof that the hand-written values are
byte-identical to what a fresh extract writes, across all nine bundle
sets.
## Reverse verification — the new pin can fail
Committed first, then ablated through `scripts/ablation-replace.mjs`
(anchor must hit; on-disk counts and blob hashes are the tool's own
verdict), one decided leaf reverted to its English source in `zh-CN`:
- before: `label: "占位文本",` x1 · after: x0, `label: "Placeholder",` x1 →
x2, blob `8f480a6bbe30` → `6bd6a8c0b277`
- suite under the mutation: **2 failed | 9 passed** — the enumerated
verdict (`zh-CN object.fields.fields.placeholder.label reads its en
source again`) AND the derived panel pin, independently
- restored: blob back to `8f480a6bbe30` == HEAD, `git diff HEAD` empty,
working tree clean
- suite restored: **11 passed**
## Verification
- `pnpm --filter @objectstack/platform-objects test` — 44 files, **610
passed**
- `pnpm --filter @objectstack/platform-objects typecheck` — green (its
`tsconfig.test.json` program reaches the new file; `--listFiles`
confirms it, and it contributes no errors)
- `pnpm check:i18n` — **green**, 9 packages in sync (prerequisite
closure built first; its first run exited **3**, PREREQUISITE NOT MET,
which is not a reading)
- `pnpm lint` — repo-wide, **exit 0**, at `da144a4d`
- Gate families derived with `scripts/pm/dispatch-gates.mjs` and
reconciled with `--ran`: **59 derived · 58 run green · 0 unrun · 1 NOT
MEASURED** — `pnpm check:dual-build-cjs-loads` exits 3 (PREREQUISITE NOT
MET: it reads built output for every package in the repo). That
prerequisite is a whole-repo build, which is CI's `Build Core`, and this
diff adds no package and changes no export.
## Changeset, measured
`@objectstack/platform-objects` is `private: false` with `files:
["dist", …]`, and `src/metadata-translations/index.ts` imports all three
translated bundles. Probed on the built output: a new leaf (`Dominio de
valores`, and 占位文本 in its esbuild-escaped form) is present in
`dist/metadata-translations/index.js`, `dist/index.js` and
`dist/plugin.js`, with a pre-existing translated leaf as the positive
control and an invented string as the negative control. ⇒ the diff
publishes, `skip-changeset` would be wrong, and a **`patch`** changeset
is included.
1 parent a0e62e6 commit 576d5df
8 files changed
Lines changed: 642 additions & 168 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 | + | |
Lines changed: 28 additions & 28 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
112 | | - | |
113 | | - | |
| 112 | + | |
| 113 | + | |
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
| |||
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
124 | | - | |
125 | | - | |
| 124 | + | |
| 125 | + | |
126 | 126 | | |
127 | 127 | | |
128 | | - | |
129 | | - | |
| 128 | + | |
| 129 | + | |
130 | 130 | | |
131 | 131 | | |
132 | 132 | | |
| |||
165 | 165 | | |
166 | 166 | | |
167 | 167 | | |
168 | | - | |
169 | | - | |
| 168 | + | |
| 169 | + | |
170 | 170 | | |
171 | 171 | | |
172 | | - | |
173 | | - | |
| 172 | + | |
| 173 | + | |
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
177 | 177 | | |
178 | 178 | | |
179 | 179 | | |
180 | | - | |
181 | | - | |
| 180 | + | |
| 181 | + | |
182 | 182 | | |
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
187 | 187 | | |
188 | | - | |
189 | | - | |
| 188 | + | |
| 189 | + | |
190 | 190 | | |
191 | 191 | | |
192 | | - | |
193 | | - | |
| 192 | + | |
| 193 | + | |
194 | 194 | | |
195 | 195 | | |
196 | | - | |
197 | | - | |
| 196 | + | |
| 197 | + | |
198 | 198 | | |
199 | 199 | | |
200 | | - | |
201 | | - | |
| 200 | + | |
| 201 | + | |
202 | 202 | | |
203 | 203 | | |
204 | | - | |
205 | | - | |
| 204 | + | |
| 205 | + | |
206 | 206 | | |
207 | 207 | | |
208 | 208 | | |
| |||
213 | 213 | | |
214 | 214 | | |
215 | 215 | | |
216 | | - | |
217 | | - | |
| 216 | + | |
| 217 | + | |
218 | 218 | | |
219 | 219 | | |
220 | | - | |
221 | | - | |
| 220 | + | |
| 221 | + | |
222 | 222 | | |
223 | 223 | | |
224 | | - | |
225 | | - | |
| 224 | + | |
| 225 | + | |
226 | 226 | | |
227 | 227 | | |
228 | 228 | | |
| |||
Lines changed: 0 additions & 28 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
102 | 102 | | |
103 | 103 | | |
104 | 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 | 105 | | |
134 | 106 | | |
135 | 107 | | |
| |||
Lines changed: 28 additions & 28 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
112 | | - | |
113 | | - | |
| 112 | + | |
| 113 | + | |
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
| |||
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
124 | | - | |
125 | | - | |
| 124 | + | |
| 125 | + | |
126 | 126 | | |
127 | 127 | | |
128 | | - | |
129 | | - | |
| 128 | + | |
| 129 | + | |
130 | 130 | | |
131 | 131 | | |
132 | 132 | | |
| |||
165 | 165 | | |
166 | 166 | | |
167 | 167 | | |
168 | | - | |
169 | | - | |
| 168 | + | |
| 169 | + | |
170 | 170 | | |
171 | 171 | | |
172 | | - | |
173 | | - | |
| 172 | + | |
| 173 | + | |
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
177 | 177 | | |
178 | 178 | | |
179 | 179 | | |
180 | | - | |
181 | | - | |
| 180 | + | |
| 181 | + | |
182 | 182 | | |
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
187 | 187 | | |
188 | | - | |
189 | | - | |
| 188 | + | |
| 189 | + | |
190 | 190 | | |
191 | 191 | | |
192 | | - | |
193 | | - | |
| 192 | + | |
| 193 | + | |
194 | 194 | | |
195 | 195 | | |
196 | | - | |
197 | | - | |
| 196 | + | |
| 197 | + | |
198 | 198 | | |
199 | 199 | | |
200 | | - | |
201 | | - | |
| 200 | + | |
| 201 | + | |
202 | 202 | | |
203 | 203 | | |
204 | | - | |
205 | | - | |
| 204 | + | |
| 205 | + | |
206 | 206 | | |
207 | 207 | | |
208 | 208 | | |
| |||
213 | 213 | | |
214 | 214 | | |
215 | 215 | | |
216 | | - | |
217 | | - | |
| 216 | + | |
| 217 | + | |
218 | 218 | | |
219 | 219 | | |
220 | | - | |
221 | | - | |
| 220 | + | |
| 221 | + | |
222 | 222 | | |
223 | 223 | | |
224 | | - | |
225 | | - | |
| 224 | + | |
| 225 | + | |
226 | 226 | | |
227 | 227 | | |
228 | 228 | | |
| |||
Lines changed: 0 additions & 28 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
102 | 102 | | |
103 | 103 | | |
104 | 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 | 105 | | |
134 | 106 | | |
135 | 107 | | |
| |||
0 commit comments