fix(platform-objects): translate the ja-JP and es-ES object labels, options and help that shipped as English copies - #20530
Conversation
…at shipped as English copies 340 ja-JP and 338 es-ES leaves that were byte copies of the en source now carry their locale's words; leaf values only, structure untouched. Claude-Session: https://claude.ai/code/session_01N8TPEsoJxPsdSdNKGnNGEN Co-authored-by: Claude <noreply@anthropic.com>
… object leaves in per-locale echo-decision ledgers Claude-Session: https://claude.ai/code/session_01N8TPEsoJxPsdSdNKGnNGEN Co-authored-by: Claude <noreply@anthropic.com>
…mpanions with i18n:extract pnpm i18n:extract dropped the rows of every translated leaf (-340 ja-JP, -338 es-ES, 0 added); the rows that remain are the declared echoes. Claude-Session: https://claude.ai/code/session_01N8TPEsoJxPsdSdNKGnNGEN Co-authored-by: Claude <noreply@anthropic.com>
…ct translations Claude-Session: https://claude.ai/code/session_01N8TPEsoJxPsdSdNKGnNGEN Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8TPEsoJxPsdSdNKGnNGEN Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 31 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e85907f49743da94871f00ea9e161684d9301ec7 && git checkout e85907f49743da94871f00ea9e161684d9301ec7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0368a336dbd6935385b442503b043d937357f4df d616c8cc3f875fbd993787920c60160ccc0c2d68 && git checkout -B drift-repro 0368a336dbd6935385b442503b043d937357f4df && git merge --no-ff d616c8cc3f875fbd993787920c60160ccc0c2d68
node scripts/docs-audit/affected-docs.mjs --json 0368a336dbd6935385b442503b043d937357f4df
|
Contract reviewServed-tier: Inputs, and nothing else: card #20493 body and all three comments (triage 5877552039, claim 5880394970, os-dev-report 5881052776); PR #20530 body, its 8-file list and the net diff against merge-base ① Derived judgments
Gate verdicts, final read 2026-09-29T00:28:43Z: 34 check-runs on the head, all completed: 31 success, 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke: path / opt-in skips), 0 in progress, 0 failed. Success: Lint and Repo Gates (the ② Semver level
Changeset sentences: the title sentence TRUE in the sense its third bullet qualifies (the 340 / 338 copies are gone; the 43 / 54 that remain are declared by design). PR-body sentences: "Fixes #20493" TRUE, the only closing keyword. The what-changed paragraph and its bullets (340 / 43, 338 / 54, leaf values only, structure untouched, two ledgers of the zh-CN shape with no new mechanism, companions −340 / −338 and 0 added with the remainder set-equal to the echoes, one sibling ledger followed and the other eight needing no edit, zh-CN / metadata-forms / hand-authored files / objectui untouched) each TRUE, measured above; " ③ Boundary flags
Implemented-by: VERDICT: PASS |
Fixes #20493
Clause-②: no
What changed
A ja-JP or es-ES console read English on Setup surfaces: the Invite user dialog's role list carried Delegated Admin beside the translated roles, and every Setup record page read the same copied leaves. The keys were present in
packages/platform-objects/src/apps/translations/ja-JP.objects.generated.tsandes-ES.objects.generated.ts, but their values were byte copies of theensource thatos i18n extract --fill=defaultseeds. This PR applies the zh-CN mechanism of PR #20490 (squash9bf5e67af) to both locales, one PR, one ledger per locale.pnpm i18n:extractre-read both bundles byte-identical.objects-zh-cn-echo-decisions.test.tsbuilt (type Verdict = 'translate' | 'echo', a reason on every row, a departure on every echo,undeclaredEchoesproved on a synthetic row):objects-ja-jp-echo-decisions.test.tsandobjects-es-es-echo-decisions.test.ts. No new mechanism.ja-JP.source-hashes.generated.tsandes-ES.source-hashes.generated.tsregenerated bypnpm i18n:extract, never by hand: −340 and −338 rows, 0 added. The rows that remain are set-equal to each ledger's declared echoes (43 and 54).object-lifecycle-panel-echo-decisions.test.tspinnedsys_oauth_resource.fields.access_token_ttl.labelas an unauthored fill in ja-JP and es-ES. Both are now authored (アクセストークン TTL, TTL del token de acceso), so the test now asserts all three locales authored with theTTLtoken kept and no provenance row. The other eight sibling ledgers already asked for a non-empty table (> 0) since PR fix(platform-objects): translate the zh-CN object labels, options and help that shipped as English copies #20490 and needed no edit.*.metadata-forms.generated.ts, the hand-authoredja-JP.ts/es-ES.tsand objectui are not touched.The coverage-ratchet half is out of scope, per triage (5877552039, 「新增门禁默认否」). Teaching
i18n-coverage-baselineto count a copied source string as untranslated widens an existing gate; if the maintainer wants that ratchet, it is a separate gate card. For the same reason neither ledger asserts that no other leaf of its locale is a copy.Pinned strings
sys_member.fields.role.options.delegated_adminsys_invitation.fields.role.options.delegated_adminsys_memberoptions, one word per rolesys_team.fields.member_count.labelensource, no provenance rowThe console's words were read at objectui
main797a30f, read-only:packages/i18n/src/locales/ja.ts, keyorganization.roles.delegatedAdmin= 委任管理者;packages/i18n/src/locales/es.ts, same key = Administrador delegado. The role test also asserts the four role words are distinct, identical on both objects, Japanese-script in ja-JP and none equal to itsensource in es-ES.Copy count, before and after, by key class
A string leaf counts as a copy when it is byte-equal to the
enleaf at the same path, over every leaf ofen.objects.generated.tsand the locale's bundle. All three bundles have 1529 leaves. "Before" is measured at base397572ed5; the card'sb810ddb6fcounts are reproduced exactly (H1 holds).label(object, field, view, action param)options.*pluralLabelhelpdescriptionplaceholderemptyState.title/.messagelabel+options.*help/description/placeholder/emptyStatewere decided as the same class aslabel(H5), as zh-CN did: the extractor files each under the samesourcekind as its element's label (packages/cli/src/utils/i18n-extract.ts). All four placeholders in both locales are typed literals and stay as written.Kept as written by design — ja-JP (43)
sys_account._actions.link_social.params.provider.options.{google, github, microsoft, apple, facebook, gitlab, discord}(7)fields.id.labelon the 23 objects zh-CN lists (sys_oauth_*×7,sys_sso_provider, the eightsys_scim_*,sys_email_template,sys_metadata,sys_metadata_history,sys_view_definition,sys_metadata_audit,sys_secret,sys_setting_audit) (23)sys_oauth_application.fields.jwks.label,.jwks_uri.label(2)sys_jwksis authored 署名キー (JWKS), and URIs stay URI (リダイレクト URI). Theirhelpleaves are translated.sys_oauth_application._actions.create_oauth_application.params.type.options.web(1)sys_oauth_application.fields.type.options.webas Web; the dialog and the record field read one word.sys_sso_provider._actions.register_saml_provider.params.entryPoint.label(1)sys_sso_provider._actions.register_sso_provider.params.{scopes, mapEmail, mapName}.placeholder(3)sys_sso_provider._actions.register_saml_provider.params.identifierFormat.placeholder(1)sys_email.fields.message_id.label(1)sys_email.fields.cc_addresses.label,.bcc_addresses.label(2)sys_setting_audit.fields.source.options.{ui, api}(2)sys_metadata.fields.source.options.apiis authored API).Where ja-JP departs from zh-CN: the result dialog's
Client ID/Client Secretwere never copies here (authored クライアント ID / クライアントシークレット);WebandCc/Bccare ja-JP decisions of their own.Kept as written by design — es-ES (54)
fields.id.labelon the same 23 objects (23)sys_oauth_application.fields.jwks.label,.jwks_uri.label(2)sys_jwksis authored Clave de firma (JWKS); URIs stay URI (URI de redirección)....create_oauth_application.params.type.options.web(1)fields.type.options.webis authored Web.sys_email.fields.message_id.label(1)sys_email.fields.cc_addresses.label(1)sys_setting_audit.fields.source.options.{ui, api}(2)sys_metadatasource optionapiis authored API).sys_email.fields.error.label,sys_job_run.fields.error.label(2)sys_job_queue.sys_notification.fields.actor_id.label,sys_metadata_audit.fields.actor.label,sys_setting_audit.fields.actor_id.label(3)sys_setting.fields.scope.options.global,sys_setting_audit.fields.scope.options.global(2)sys_email_template.fields.variables_json.label(1)sys_oauth_access_token.fields.token.label,sys_oauth_refresh_token.fields.token.label(2)sys_metadata.fields.checksum.label,sys_metadata_history.fields.checksum.label(2)sys_organization.fields.slug.label(1)Where es-ES departs from zh-CN:
Client ID/Client Secretin the result dialog andIdP SSO URLwere never copies here (authored); the 14 Spanish-as-written rows above are es-ES's own class.Terminology (H4: one term, one rendering)
sys_scim_projection_grant.display_titleauthors them), リース and 冪等キー (sys_job_queue), トピック / 重大度 / 情報・警告・重大 / 操作者 (the messaging bundle), ポジション (the security bundle), 管理者 forManaged By(assys_metadata), 所属組織 for My Memberships (the account nav's label,ja-JP.tsnav_account_memberships), 配属 for Placement (objectui's word). Decommission is 廃止; tombstone is トゥームストーン, the service-storage ja-JP objects bundle's existing rendering.sys_job_queue's lease), Tema / Severidad / Información · Advertencia · Crítica (the messaging bundle), Puesto (the security bundle), principal for Parent (the bundle's word throughout: Unidad de negocio principal, Objeto principal), Mis organizaciones (es-ES.tsnav_account_memberships), Asignación for Placement (objectui's word). Decommission is retirada; tombstone is lápida.Verification
Measured at head
d616c8cc3, a true merge oforigin/mainfb386074f(parents8f3e7cae3,fb386074f). The CLI closure was rebuilt after the merge.pnpm check:i18n:397572ed5bytes;platform-objects DRIFTED (2): the two provenance companions (zh-CN's run readDRIFTED (1), one locale);pnpm i18n:extract, which changed only the two companions (−340 / −338 rows, 0 added). Remaining rows vs remaining copies: 43 = 43 and 54 = 54, set difference 0. The mechanism carried over unchanged; the only per-locale difference is which leaves each locale keeps.d616c8cc3(derived gate run below).pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2: 58 files, 942 tests passed (lock verdict command-exit 0), atd616c8cc3and before the merge.pnpm --filter @objectstack/platform-objects typecheck: exit 0.tsc -p tsconfig.test.json --listFilesOnlylists both new ledgers (2 hits).scripts/ablation-replace.mjs(anchor hit counts declared, blob change proved, restore proved blob == HEAD andgit diff HEADempty each time), each red as predicted:delegated_admin: "委任管理者"→"Delegated Admin": 2 failed / 9 passed (verdict row; role test "expected 'Delegated Admin' to match" the Japanese-script pattern).delegated_admin: "Administrador delegado"→"Delegated Admin": 2 failed / 8 passed (verdict row; "es-ES role delegated_admin reads its en source").google: "Google"→"グーグル": 1 failed / 10 passed ("is a declared echo and must stay the en source").global: "Global"(×2) →"Mundial": 1 failed / 9 passed.委任管理者(×2) →委託管理者, still Japanese: 1 failed / 10 passed, only the console-word pin ("expected '委託管理者' to be '委任管理者'").object-lifecycle-panel-echo-decisions.test.tsrestored to its397572ed5bytes against the new bundles: 1 failed / 34 passed ("ja-JP sys_oauth_resource.fields.access_token_ttl.label: expected 'アクセストークン TTL' to be 'Access Token TTL'"); restored to HEAD, blob equal.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackatd616c8cc3printed 61 commands. All 61 were run, each exit code written to a file before any pipe.pnpm check:dual-build-cjs-loadsfirst answered PREREQUISITE NOT MET (exit 3:dist/missing for packages outside the CLI closure). Not a measurement. Rerun after a workspace build (71 of 71 tasks cached): exit 0.--ranreconciliation: 61 derived, 61 run, 0 NOT-MEASURED, 0 UNRUN (exit 0).pnpm check:i18n-coverage: exit 0, "621 baselined untranslated string(s), none new". The ratchet is unchanged, as expected.eslint --no-inline-config --format jsonover the 7 changed.tsfiles: 7 files linted (each confirmed not ignored by the repo config's ownisPathIgnored), 0 errors, 0 warnings.eslint.config.mjsnever enables type-aware linting (noparserOptions.project, stated in its own comment), so this diff cannot move a verdict on any untouched file. The repo-widepnpm lintis CI's.Acceptance notes
Parentas principal throughout the bundle, so the pre-existingsys_business_unit.fields.parent_business_unit_id.labelandsys_user.fields.primary_business_unit_id.labelboth read Unidad de negocio principal. This PR follows the bundle's word forParent Organization(Organización principal) and leaves the pre-existing pair alone. Carrier: none.Locked Untiltwo ways already (ロック解除日時 onsys_user, ロック期限 onsys_job_queue). This PR uses ロック解除日時 forsys_two_factor, the same lockout concept assys_user.Managed Byis 管理者,sys_metadata's word, sosys_email_templatereads 管理者 as the label and as theadminoption. 管理元 would disambiguate; one line changes it if the maintainer prefers it.ensource, for examplesys_two_factor.fields.backup_codes.help(still "JSON-serialized backup codes" whereennow says an opaque ciphertext) andsys_notification.description. Authored leaves are legacy-trusted by the provenance design and outside this card's copy class. Carrier: none.> 0floors they guard hold for all three locales now. Comment-only drift, not edited here.Generated by Claude Code