Skip to content
28 changes: 28 additions & 0 deletions .changeset/20462-zh-cn-object-labels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
'@objectstack/platform-objects': patch
---

fix(platform-objects): the zh-CN platform-object bundle no longer ships English labels, options and help as copies of the source (#20462)

Clause-②: no

A zh-CN console showed English on Setup surfaces: the Invite user dialog listed
the membership roles as 所有者 / 管理员 / Delegated Admin / 成员, and a team record
read MEMBER COUNT. The keys were present in `zh-CN.objects.generated.ts`, but
their values were byte copies of the English source that `os i18n extract
--fill=default` seeds.

- 320 of the 362 zh-CN string leaves that equalled their `en` source are now
translated: labels, plural labels, select options, help, descriptions and
empty states. `delegated_admin` reads 受托管理员 on both
`sys_member` and `sys_invitation`, the word the console already uses for that
role.
- The other 42 stay English by design, and each is recorded with its reason in
`objects-zh-cn-echo-decisions.test.ts`: sign-in provider brands (Google, GitHub,
…), the bare `ID` initialism, protocol names (JWKS, IdP SSO URL, Message-ID,
UI / API), the OAuth credential names in the create-application result dialog,
and placeholders that show a value the admin types.

`zh-CN.source-hashes.generated.ts` was regenerated by `pnpm i18n:extract`: it
records a leaf only while the leaf is a copy of its source, so it now lists
exactly those 42. `ja-JP` and `es-ES` are unchanged.
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,9 @@ describe('#19403 round 5 — the provenance table agrees these leaves are now au
it(`${locale}: no decided leaf is still recorded as an extractor fill`, () => {
// Lit — the table really loaded, so "no entry" cannot pass by the import
// having come back empty.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(100);
// Non-empty, not a debt size: since #20462 zh-CN's objects slice holds only
// its declared English-by-design leaves, so the table is small by design.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(0);
const stillFilled = DECISIONS.filter((d) => table[provenanceKey(d)] !== undefined).map(idOf);
expect(stillFilled, 'these leaves are still byte copies of their source revision').toEqual([]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,9 @@ describe('#19403 round 7 — the provenance table agrees these leaves are now au
it(`${locale}: no decided leaf is still recorded as an extractor fill`, () => {
// Lit — the table really loaded, so "no entry" cannot pass by the import
// having come back empty.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(100);
// Non-empty, not a debt size: since #20462 zh-CN's objects slice holds only
// its declared English-by-design leaves, so the table is small by design.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(0);
const stillFilled = DECISIONS.filter((d) => table[provenanceKey(d)] !== undefined).map(idOf);
expect(stillFilled, 'these leaves are still byte copies of their source revision').toEqual([]);
});
Expand Down Expand Up @@ -754,7 +756,9 @@ describe('#19403 round 7 — the provenance table agrees these leaves are now au
// of my twelve is still a fill", but "no metadata type's own display pair
// is still a fill, anywhere in this table". Lit by the table being large,
// so a zero cannot come from an empty import.
expect(Object.keys(table).length).toBeGreaterThan(100);
// Non-empty, not a debt size: since #20462 zh-CN's objects slice holds only
// its declared English-by-design leaves, so the table is small by design.
expect(Object.keys(table).length).toBeGreaterThan(0);
const typeLevel = Object.keys(table).filter((k) => /^metadataForms\.[^.]+\.(label|description)$/.test(k));
expect(typeLevel, 'a metadata type display pair is still an extractor fill').toEqual([]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,9 @@ describe('#19403 round 3 — the provenance table agrees these leaves are now au
it(`${locale}: no decided leaf is still recorded as an extractor fill`, () => {
// Lit — the table really loaded, so "no entry" cannot pass by the import
// having come back empty.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(100);
// Non-empty, not a debt size: since #20462 zh-CN's objects slice holds only
// its declared English-by-design leaves, so the table is small by design.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(0);
const stillFilled = DECISIONS.filter((d) => table[provenanceKey(d)] !== undefined).map(idOf);
expect(stillFilled, 'these leaves are still byte copies of their source revision').toEqual([]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,9 @@ describe('#19403 round 4 — the provenance table agrees these leaves are now au
it(`${locale}: no decided leaf is still recorded as an extractor fill`, () => {
// Lit — the table really loaded, so "no entry" cannot pass by the import
// having come back empty.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(100);
// Non-empty, not a debt size: since #20462 zh-CN's objects slice holds only
// its declared English-by-design leaves, so the table is small by design.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(0);
const stillFilled = DECISIONS.filter((d) => table[provenanceKey(d)] !== undefined).map(idOf);
expect(stillFilled, 'these leaves are still byte copies of their source revision').toEqual([]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,9 @@ describe('#19403 round 6 — the provenance table agrees these leaves are now au
it(`${locale}: no decided leaf is still recorded as an extractor fill`, () => {
// Lit — the table really loaded, so "no entry" cannot pass by the import
// having come back empty.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(100);
// Non-empty, not a debt size: since #20462 zh-CN's objects slice holds only
// its declared English-by-design leaves, so the table is small by design.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(0);
const stillFilled = DECISIONS.filter((d) => table[provenanceKey(d)] !== undefined).map(idOf);
expect(stillFilled, 'these leaves are still byte copies of their source revision').toEqual([]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,9 @@ describe('#19403 round 8 — the provenance table agrees these leaves are now au
it(`${locale}: no decided leaf is still recorded as an extractor fill`, () => {
// Lit — the table really loaded, so "no entry" cannot pass by the import
// having come back empty.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(100);
// Non-empty, not a debt size: since #20462 zh-CN's objects slice holds only
// its declared English-by-design leaves, so the table is small by design.
expect(Object.keys(table).length, `${locale} provenance table is empty`).toBeGreaterThan(0);
const stillFilled = DECISIONS.filter((d) => table[provenanceKey(d)] !== undefined).map(idOf);
expect(stillFilled, 'these leaves are still byte copies of their source revision').toEqual([]);
});
Expand Down Expand Up @@ -807,7 +809,9 @@ describe('#19403 round 8 — the provenance table agrees these leaves are now au
const authored = [...enObjectPaths]
.filter(([path, en]) => localeObjects.has(path) && localeObjects.get(path) !== en)
.map(([path]) => path);
expect(echoing.length, `${locale} has no echoing objects leaf to sample`).toBeGreaterThan(50);
// At least one of each is what makes a positive AND a negative; since
// #20462 zh-CN's echoing objects leaves are its 42 declared English ones.
expect(echoing.length, `${locale} has no echoing objects leaf to sample`).toBeGreaterThan(0);
expect(authored.length, `${locale} has no authored objects leaf to sample`).toBeGreaterThan(50);
expect(
echoing.filter((path) => table[`objects.${path}`] === undefined),
Expand Down Expand Up @@ -848,7 +852,9 @@ describe('#19403 round 8 — the provenance table agrees these leaves are now au
// The strongest form the verdict above can take for this class: not "none
// of my eleven", but "nothing this population reaches, outside the
// declared deferral". Lit by the table being large.
expect(Object.keys(table).length).toBeGreaterThan(100);
// Non-empty, not a debt size: since #20462 zh-CN's objects slice holds only
// its declared English-by-design leaves, so the table is small by design.
expect(Object.keys(table).length).toBeGreaterThan(0);
const stillFilled = PANEL_LEAVES.filter(
(l) => !inDeferral(l) && table[`metadataForms.object.fields.${l.path}.${l.prop}`] !== undefined,
).map((l) => `${l.path}.${l.prop}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1283,15 +1283,24 @@ describe('#19403 round 10 — ⚠️⚠️ the phantom-translation trap, asserte
});

it('⭐ the near-twin that was LOOKED UP AND REFUSED is asserted to be a fill, not a decision', () => {
// `Access Token TTL` is byte-identical in all three locales and reads like a
// standing decision to keep TTL verbatim. It is an unauthored fill in every
// locale — including zh-CN — and the provenance tables say so. Asserted so
// that no later round leans on it.
// `Access Token TTL` was byte-identical in all three locales and read like a
// standing decision to keep TTL verbatim. It was an unauthored fill in every
// locale, and the provenance tables said so. Asserted so that no later round
// leans on it. #20462 then AUTHORED the zh-CN leaf (访问令牌 TTL): the
// concept is rendered and the initialism survives, the same treatment the
// lifecycle rows give TTL. ja-JP and es-ES are still fills.
const key = 'sys_oauth_resource.fields.access_token_ttl.label';
const en = FLAT_OBJECTS.get('en')!.get(key);
expect(en).toBe('Access Token TTL');
for (const [locale, table] of PROVENANCE) {
expect(FLAT_OBJECTS.get(locale)!.get(key), `${locale} ${key}`).toBe(en);
const value = FLAT_OBJECTS.get(locale)!.get(key);
if (locale === 'zh-CN') {
expect(value, `${locale} ${key} reads its en source again`).not.toBe(en);
expect(carriesToken(value!, 'TTL'), `${locale} dropped the machine token`).toBe(true);
expect(table[`objects.${key}`], `${locale} still records the authored ${key} as a fill`).toBeUndefined();
continue;
}
expect(value, `${locale} ${key}`).toBe(en);
expect(
table[`objects.${key}`],
`${locale} no longer records ${key} as a fill — if a translator authored it, this ledger note is stale`,
Expand All @@ -1308,7 +1317,9 @@ describe('#19403 round 10 — the SECOND WITNESS, over the `metadataForms.`-pref
// exists exactly while the leaf IS a byte copy of the source revision, so
// a leaf this round authored must have lost its row.
const keys = Object.keys(table);
expect(keys.length, 'the provenance table is empty — the witness cannot testify').toBeGreaterThan(300);
// Non-empty, not a debt size: since #20462 zh-CN's objects slice holds only
// its declared English-by-design leaves, so the table is small by design.
expect(keys.length, 'the provenance table is empty — the witness cannot testify').toBeGreaterThan(0);
const metadataFormRows = keys.filter((k) => k.startsWith('metadataForms.'));
// The reading: NOTHING under the metadataForms. prefix is a fill any more.
expect(metadataFormRows, `${locale} still records a metadata-form leaf as an unauthored fill`).toEqual([]);
Expand All @@ -1333,7 +1344,9 @@ describe('#19403 round 10 — the SECOND WITNESS, over the `metadataForms.`-pref
const loc = FLAT_OBJECTS.get(locale)!;
const echoing = [...en].filter(([k, v]) => loc.get(k) === v).map(([k]) => k);
const authored = [...en].filter(([k, v]) => loc.get(k) !== undefined && loc.get(k) !== v).map(([k]) => k);
expect(echoing.length, `${locale} has no echoing objects leaf to sample`).toBeGreaterThan(50);
// At least one of each is what makes a positive AND a negative; since
// #20462 zh-CN's echoing objects leaves are its 42 declared English ones.
expect(echoing.length, `${locale} has no echoing objects leaf to sample`).toBeGreaterThan(0);
expect(authored.length, `${locale} has no authored objects leaf to sample`).toBeGreaterThan(50);
expect(
echoing.filter((k) => table[`objects.${k}`] === undefined),
Expand Down
Loading
Loading