Skip to content

fix(platform-objects): translate the ja-JP and es-ES object labels, options and help that shipped as English copies - #20530

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-20493-ja-es-object-labels
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-20493-ja-es-object-labels

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

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.ts and es-ES.objects.generated.ts, but their values were byte copies of the en source that os i18n extract --fill=default seeds. This PR applies the zh-CN mechanism of PR #20490 (squash 9bf5e67af) to both locales, one PR, one ledger per locale.

  • ja-JP: 340 copied leaves translated, 43 kept as written by design.
  • es-ES: 338 copied leaves translated, 54 kept as written by design.
  • Leaf values only; the structure of both bundles is untouched. pnpm i18n:extract re-read both bundles byte-identical.
  • Two ledgers, one per locale, of the shape objects-zh-cn-echo-decisions.test.ts built (type Verdict = 'translate' | 'echo', a reason on every row, a departure on every echo, undeclaredEchoes proved on a synthetic row): objects-ja-jp-echo-decisions.test.ts and objects-es-es-echo-decisions.test.ts. No new mechanism.
  • Provenance companions ja-JP.source-hashes.generated.ts and es-ES.source-hashes.generated.ts regenerated by pnpm 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).
  • One sibling ledger followed: object-lifecycle-panel-echo-decisions.test.ts pinned sys_oauth_resource.fields.access_token_ttl.label as 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 the TTL token 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.
  • zh-CN, *.metadata-forms.generated.ts, the hand-authored ja-JP.ts / es-ES.ts and objectui are not touched.

The coverage-ratchet half is out of scope, per triage (5877552039, 「新增门禁默认否」). Teaching i18n-coverage-baseline to 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

leaf ja-JP es-ES pin
sys_member.fields.role.options.delegated_admin 委任管理者 Administrador delegado exact word, equal to the console's
sys_invitation.fields.role.options.delegated_admin 委任管理者 Administrador delegado equal to the sys_member options, one word per role
sys_team.fields.member_count.label メンバー数 Número de miembros not the en source, no provenance row

The console's words were read at objectui main 797a30f, read-only: packages/i18n/src/locales/ja.ts, key organization.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 its en source 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 en leaf at the same path, over every leaf of en.objects.generated.ts and the locale's bundle. All three bundles have 1529 leaves. "Before" is measured at base 397572ed5; the card's b810ddb6f counts are reproduced exactly (H1 holds).

key class ja-JP before ja-JP after es-ES before es-ES after
label (object, field, view, action param) 210 29 217 38
options.* 20 10 22 12
pluralLabel 12 0 12 0
help 121 0 121 0
description 12 0 12 0
placeholder 4 4 4 4
emptyState.title / .message 4 0 4 0
total 383 43 392 54
label + options.* 230 39 239 50

help / description / placeholder / emptyState were decided as the same class as label (H5), as zh-CN did: the extractor files each under the same source kind 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)

leaves value(s) reason
sys_account._actions.link_social.params.provider.options.{google, github, microsoft, apple, facebook, gitlab, discord} (7) Google, GitHub, Microsoft, Apple, Facebook, GitLab, Discord Sign-in provider brands. The bundle renders no brand name in kana: the authored SSO empty state keeps Okta, Entra and Auth0 verbatim.
fields.id.label on the 23 objects zh-CN lists (sys_oauth_* ×7, sys_sso_provider, the eight sys_scim_*, sys_email_template, sys_metadata, sys_metadata_history, sys_view_definition, sys_metadata_audit, sys_secret, sys_setting_audit) (23) ID The bundle keeps ID verbatim in every label that carries it (ユーザー ID, 組織 ID, クライアント ID); a bare ID has no noun to render.
sys_oauth_application.fields.jwks.label, .jwks_uri.label (2) JWKS, JWKS URI Protocol names: sys_jwks is authored 署名キー (JWKS), and URIs stay URI (リダイレクト URI). Their help leaves are translated.
sys_oauth_application._actions.create_oauth_application.params.type.options.web (1) Web The bundle already renders the sibling sys_oauth_application.fields.type.options.web as Web; the dialog and the record field read one word.
sys_sso_provider._actions.register_saml_provider.params.entryPoint.label (1) IdP SSO URL Protocol initialisms only; the sibling SAML params are authored IdP エンティティ ID and IdP 署名証明書.
sys_sso_provider._actions.register_sso_provider.params.{scopes, mapEmail, mapName}.placeholder (3) openid email profile, email, name Typed literals: OIDC scope and claim names; the authored help on the same params keeps them (既定値は「email」).
sys_sso_provider._actions.register_saml_provider.params.identifierFormat.placeholder (1) the SAML NameID emailAddress URN Typed literal: an identifier, not prose.
sys_email.fields.message_id.label (1) Message-ID RFC 5322 header name; the authored help keeps it (トランスポートが割り当てる RFC-5322 Message-ID).
sys_email.fields.cc_addresses.label, .bcc_addresses.label (2) Cc, Bcc Header abbreviations Japanese mail clients print in Latin letters, with no kana or kanji form in use. The prose labels on the same record are authored (送信元, 宛先, and 返信先 for Reply-To in this PR).
sys_setting_audit.fields.source.options.{ui, api} (2) UI, API Initialisms the bundle keeps (API キー, UI には表示されません; sys_metadata.fields.source.options.api is authored API).

Where ja-JP departs from zh-CN: the result dialog's Client ID / Client Secret were never copies here (authored クライアント ID / クライアントシークレット); Web and Cc / Bcc are ja-JP decisions of their own.

Kept as written by design — es-ES (54)

leaves value(s) reason
the 7 sign-in provider options Google … Discord Brands; the authored SSO empty state keeps Okta, Entra and Auth0 verbatim.
fields.id.label on the same 23 objects (23) ID The bundle keeps ID verbatim in every label that carries it (ID de usuario, ID de organización, ID de cliente).
sys_oauth_application.fields.jwks.label, .jwks_uri.label (2) JWKS, JWKS URI sys_jwks is authored Clave de firma (JWKS); URIs stay URI (URI de redirección).
...create_oauth_application.params.type.options.web (1) Web The sibling fields.type.options.web is authored Web.
the 3 OIDC placeholders + the SAML NameID URN placeholder (4) openid email profile, email, name, URN Typed literals; the authored help keeps them (Valor predeterminado: «email»).
sys_email.fields.message_id.label (1) Message-ID Authored help: Message-ID RFC-5322 asignado por el transporte.
sys_email.fields.cc_addresses.label (1) Cc The Spanish copy abbreviation (con copia) is Cc too; the blind-copy sibling is authored Cco.
sys_setting_audit.fields.source.options.{ui, api} (2) UI, API Initialisms the bundle keeps (nunca se muestra en la UI; sys_metadata source option api is authored API).
sys_email.fields.error.label, sys_job_run.fields.error.label (2) Error Spanish spells it the same (el error); the bundle writes Último error on sys_job_queue.
sys_notification.fields.actor_id.label, sys_metadata_audit.fields.actor.label, sys_setting_audit.fields.actor_id.label (3) Actor Spanish spells it the same (el actor); authored help reads Último actor que escribió esta fila, and the messaging bundle authors its actor label Actor.
sys_setting.fields.scope.options.global, sys_setting_audit.fields.scope.options.global (2) Global Spanish spells it the same, beside the authored sibling options Inquilino and Usuario.
sys_email_template.fields.variables_json.label (1) Variables (JSON) Spanish spells it the same; the authored description of the same object says (asunto + contenido + variables).
sys_oauth_access_token.fields.token.label, sys_oauth_refresh_token.fields.token.label (2) Token The bundle's own word for the concept (Token de sesión, Token de acceso).
sys_metadata.fields.checksum.label, sys_metadata_history.fields.checksum.label (2) Checksum The bundle's own word (Checksum anterior).
sys_organization.fields.slug.label (1) Slug The bundle's own word (Cambiar slug); the console says the same (Ese slug ya está en uso).

Where es-ES departs from zh-CN: Client ID / Client Secret in the result dialog and IdP SSO URL were never copies here (authored); the 14 Spanish-as-written rows above are es-ES's own class.

Terminology (H4: one term, one rendering)

  • ja-JP takes the bundle's already-authored words: 作成日時 / 更新日時 / 有効期限 / 失効日時 / ローテーション日時, クライアント ID, スコープ, クレーム (claim, as in マッピング: メールクレーム), プロビジョニング (as in IdP プロビジョニング), 投影 and 付与 (as sys_scim_projection_grant.display_title authors them), リース and 冪等キー (sys_job_queue), トピック / 重大度 / 情報・警告・重大 / 操作者 (the messaging bundle), ポジション (the security bundle), 管理者 for Managed By (as sys_metadata), 所属組織 for My Memberships (the account nav's label, ja-JP.ts nav_account_memberships), 配属 for Placement (objectui's word). Decommission is 廃止; tombstone is トゥームストーン, the service-storage ja-JP objects bundle's existing rendering.
  • es-ES likewise: Creado el / Actualizado el / Caduca el / Revocado el / Rotado el, ID de cliente, Ámbitos, reclamo (claim, as in Mapeo: reclamo de correo), aprovisionamiento (as in Aprovisionada por IdP), concesión (as in Tipos de concesión), arrendamiento (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.ts nav_account_memberships), Asignación for Placement (objectui's word). Decommission is retirada; tombstone is lápida.

Verification

Measured at head d616c8cc3, a true merge of origin/main fb386074f (parents 8f3e7cae3, fb386074f). The CLI closure was rebuilt after the merge.

  • H2, pnpm check:i18n:
    • exit 0 with both bundles restored to their 397572ed5 bytes;
    • exit 1 after the leaf edits alone, platform-objects DRIFTED (2): the two provenance companions (zh-CN's run read DRIFTED (1), one locale);
    • exit 0 after 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.
    • exit 0 again at 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), at d616c8cc3 and before the merge.
  • pnpm --filter @objectstack/platform-objects typecheck: exit 0. tsc -p tsconfig.test.json --listFilesOnly lists both new ledgers (2 hits).
  • Reverse verification. Five mutation legs through scripts/ablation-replace.mjs (anchor hit counts declared, blob change proved, restore proved blob == HEAD and git diff HEAD empty each time), each red as predicted:
    1. ja-JP both delegated_admin: "委任管理者" → "Delegated Admin": 2 failed / 9 passed (verdict row; role test "expected 'Delegated Admin' to match" the Japanese-script pattern).
    2. es-ES both delegated_admin: "Administrador delegado" → "Delegated Admin": 2 failed / 8 passed (verdict row; "es-ES role delegated_admin reads its en source").
    3. ja-JP declared echo google: "Google" → "グーグル": 1 failed / 10 passed ("is a declared echo and must stay the en source").
    4. es-ES declared echo global: "Global" (×2) → "Mundial": 1 failed / 9 passed.
    5. ja-JP 委任管理者 (×2) → 委託管理者, still Japanese: 1 failed / 10 passed, only the console-word pin ("expected '委託管理者' to be '委任管理者'").
    6. Sibling leg: object-lifecycle-panel-echo-decisions.test.ts restored to its 397572ed5 bytes 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.
  • Derived gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at d616c8cc3 printed 61 commands. All 61 were run, each exit code written to a file before any pipe.
    • 60 exited 0 on their first run.
    • pnpm check:dual-build-cjs-loads first 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.
    • --ran reconciliation: 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, narrowed and declared: eslint --no-inline-config --format json over the 7 changed .ts files: 7 files linted (each confirmed not ignored by the repo config's own isPathIgnored), 0 errors, 0 warnings. eslint.config.mjs never enables type-aware linting (no parserOptions.project, stated in its own comment), so this diff cannot move a verdict on any untouched file. The repo-wide pnpm lint is CI's.
  • H6, size: the derivation reads 2787 changed lines (+1416 / −1371) over 8 files, under the 5000 human-merge threshold.
  • NOT MEASURED locally, declared to CI: the 6 workflow-valued families, the 11 wide-population families and the path-scheduled CI jobs the derivation names. The Invite dialog was not re-driven in a browser (NOT MEASURED: browser rendering; reason: bundle leaves are read directly, as the card measured them).

Acceptance notes

  • es-ES renders Parent as principal throughout the bundle, so the pre-existing sys_business_unit.fields.parent_business_unit_id.label and sys_user.fields.primary_business_unit_id.label both read Unidad de negocio principal. This PR follows the bundle's word for Parent Organization (Organización principal) and leaves the pre-existing pair alone. Carrier: none.
  • The ja-JP bundle renders Locked Until two ways already (ロック解除日時 on sys_user, ロック期限 on sys_job_queue). This PR uses ロック解除日時 for sys_two_factor, the same lockout concept as sys_user.
  • ja-JP Managed By is 管理者, sys_metadata's word, so sys_email_template reads 管理者 as the label and as the admin option. 管理元 would disambiguate; one line changes it if the maintainer prefers it.
  • A few authored (not copied) ja-JP / es-ES leaves no longer match their moved en source, for example sys_two_factor.fields.backup_codes.help (still "JSON-serialized backup codes" where en now says an opaque ciphertext) and sys_notification.description. Authored leaves are legacy-trusted by the provenance design and outside this card's copy class. Carrier: none.
  • The eight sibling ledgers' comments name only zh-CN ("since platform-objects: the zh-CN bundle ships 192 labels and options as the English source string, and the i18n coverage baseline counts them as translated #20462 zh-CN's objects slice holds only its declared English-by-design leaves"); the > 0 floors they guard hold for all three locales now. Comment-only drift, not edited here.

Generated by Claude Code

…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>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Sep 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/platform-objects, touching 34 documentable anchor(s).

31 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 0368a336dbd6935385b442503b043d937357f4df.

⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: sys_user (symbol, 38 pages)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0368a336dbd6935385b442503b043d937357f4df → packageMentionDocs.

Which tree this was computed on

This run read content/docs from e85907f49743da94871f00ea9e161684d9301ec7 — the merge of head d616c8cc3f875fbd993787920c60160ccc0c2d68 into base 0368a336dbd6935385b442503b043d937357f4df, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 0368a336dbd6935385b442503b043d937357f4df → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d616c8cc3f875fbd993787920c60160ccc0c2d68
Local-runs: none

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 fb386074f (read-only git fetch, git show, git diff, git log, git grep); the check-runs on the head, read twice (first read 00:20Z, final read 2026-09-29T00:28:43Z, below); the precedent PR #20490 body, its review 5876239898 and delta 5876450868, and the #20462 thread; objectui main 12a6688cb packages/i18n/src/locales/ja.ts and es.ts for the console words. The three locale bundles, the en bundle and the two provenance companions were parsed from blobs in scratch (node and python over git show output) at base and head. No worktree, checkout, build, test, gate or ablation.

① Derived judgments

  1. Structure untouched in both bundles. Key sets are identical base to head and identical to en in both locales, 1529 string leaves each. ja-JP.objects.generated.ts: 340 leaf values change, 0 keys added, removed or renamed; es-ES.objects.generated.ts: 338 / 0. Every changed leaf was byte-equal to its en leaf at base (0 authored translations overwritten, both locales), none equals en at head, and every new ja-JP value carries kana or kanji. The bundle diffs are exactly 340 and 338 one-line pairs (no header or comment moved), and en.objects.generated.ts and zh-CN.objects.generated.ts are blob-identical base to head. RIGHT: leaf values only, as the claim requires; check:i18n is green on the head (Lint and Repo Gates).
  2. The companions were regenerated, not hand-edited. ja-JP.source-hashes.generated.ts: 383 rows to 43, 340 removed, 0 added, 0 digests changed; es-ES: 392 to 54, 338 removed, 0 added, 0 changed; rows sorted at both revisions. The removed rows are exactly the changed leaves in both locales. At base the rows equal the byte-copies (383 / 392); at head the remaining rows equal the remaining byte-copies AND each ledger's declared echo set (43 / 54), set difference empty in both directions. RIGHT: a faithful pnpm i18n:extract output. The ledgers assert one direction per verdict (every echo has its row; no pinned translation has one; the table is non-empty); the equality is measured here and deliberately not asserted, as each header says.
  3. Each locale's English-by-design set, every declared echo read with its reason. ja-JP 43: 7 sign-in brands (the bundle keeps Okta, Entra and Auth0 verbatim, each present); 23 bare ID labels (the bundle carries ユーザー ID ×4, 組織 ID, クライアント ID ×9); JWKS / JWKS URI (both help leaves ARE translated in this diff; 署名キー (JWKS) ×2 and リダイレクト URI ×4 present); Web (the sibling fields.type.options.web reads Web at base, authored); IdP SSO URL (siblings IdP エンティティ ID / IdP 署名証明書 authored); the 4 typed placeholders (OIDC scope and claim names, the SAML NameID URN; the authored help 既定値は「email」 present); Message-ID (the authored help keeps it); Cc / Bcc (Japanese mail clients print these in Latin letters; 送信元 / 宛先 are authored at base and Reply-To is authored 返信先 ×2 in this diff); UI / API (API キー ×13, UI には表示されません, sys_metadata.fields.source.options.api reads API). All real by-design keeps; no ordinary label hides among them. es-ES 54: the same 40 minus IdP SSO URL (authored in es at base, per the set logic in ②) plus fourteen Spanish-as-written rows, each precedent found: Error ×2 (Último error ×2), Actor ×3 (Último actor que escribió esta fila; the service-messaging es-ES bundle authors its actor label Actor, line 38), Global ×2 (the plugin-security es-ES bundle's authored help "nulo = global (entre inquilinos)", line 425; siblings Inquilino / Usuario authored), Variables (JSON) ((asunto + contenido + variables) present), Token ×2 (Token de sesión ×2, Token de acceso), Checksum ×2 (Checksum anterior), Slug (Cambiar slug; objectui es.ts "Ese slug ya está en uso", "Slug de URL"), Cc (the blind-copy sibling is authored Cco at base). Each is a word Spanish spells the same or a loanword the bundle already uses for the concept. No skipped user-facing translation is declared as an echo in either locale, and no undeclared copy remains (item 2). RIGHT.
  4. Translation quality: all 340 and 338 changed leaves were read, not a sample (ja-JP label 181, options 10, pluralLabel 12, help 121, description 12, emptyState 4; es-ES 179 / 10 / 12 / 121 / 12 / 4), including every option value (delegated_admin on both objects, severity info / warning / critical, managed_by platform / package / admin, lock_state no-overlay / no-delete) and every help against its en. No mistranslation found; no help text's meaning changed. Terminology follows the bundles' authored words, every precedent the PR names found: ja-JP 作成日時 (34 to 44) / 更新日時 / 有効期限 / 失効日時 / ローテーション日時, クライアント ID, スコープ, クレーム (マッピング: メールクレーム at base), プロビジョニング, 投影 and 付与 (sys_scim_projection_grant's display_title), リース and 冪等キー, トピック / 重大度 / 情報・警告・重大 / 操作者 (all in the service-messaging ja-JP bundle), ポジション (plugin-security ja-JP bundle, 29 hits), 所属組織 (ja-JP.ts:22 nav_account_memberships), 配属 (objectui ja.ts placementLabel); es-ES Creado el / Actualizado el / Caduca el / Revocado el / Rotado el, ID de cliente, Ámbitos, reclamo (Mapeo: reclamo de correo at base), aprovisionamiento (Aprovisionada por IdP), concesión (Tipos de concesión), arrendamiento, Tema / Severidad / Información · Advertencia · Crítica (service-messaging es-ES bundle), Puesto (plugin-security, 11 hits), principal, Mis organizaciones (es-ES.ts:22), Asignación (objectui es.ts placementLabel). One term one rendering, measured mechanically over all 1529 leaves: en values rendered two ways in a group touching a changed leaf are ja-JP 2 (Label: ラベル, siding with the pre-existing ラベル on sys_view_definition against the pre-existing authored 表示名 on sys_email_template; Locked Until, disclosed) and es-ES 3 (Active Activo / Activa and All Todos / Todas, grammatical gender agreement with each object's noun, which the bundle already did at base and Spanish requires; Label Etiqueta against the pre-existing Nombre visible). Bundle-wide, en values rendered two ways fell from 32 to 10 (ja-JP) and 34 to 13 (es-ES); this PR adds no new pair. Nits, none blocking: ja sys_scim_identity_tombstone.label renders Identity as ID (SCIM ID トゥームストーン; ID doubles for identity in Japanese IT usage and the description reads SCIM ID likewise); ja sys_email_template.fields.managed_by.label 管理者 equals its own admin option 管理者 (③.4); two es labels are full clauses (El arrendamiento de la retirada caduca el; La reproducción de la rotación caduca el), faithful but long; es lápida (tombstone) and compuerta (gate) are literal and consistent, and lápida is already the service-storage es-ES bundle's word.
  5. The objectui words. objectui main 12a6688cb (2026-09-28T23:52Z): ja.ts:2689 organization.roles.delegatedAdmin: '委任管理者', es.ts:2691 'Administrador delegado'. The head bundles read 委任管理者 / Administrador delegado on both sys_member and sys_invitation (measured), and each ledger asserts sys_invitation options toEqual sys_member's, four distinct words, Japanese script (ja) or not-the-en-source (es), and the exact console word. RIGHT. The other three roles agree with objectui in es (Propietario / Administrador / Miembro); in ja the pre-existing authored owner leaf reads 所有者 where objectui reads オーナー, not this card's leaf, noted only.
  6. The sibling ledger edit. sys_oauth_resource.fields.access_token_ttl.label was a byte copy in ja-JP and es-ES at base and is translated here (アクセストークン TTL / TTL del token de acceso), so the old ja / es branch (value equals en, provenance row present) would red on this head: the edit is required by a leaf this card translated. The new loop asserts, for every locale, value not equal to en, carriesToken(value, 'TTL') and no provenance row: the zh-CN assertions are unchanged and ja / es now carry the same authored-state assertions zh-CN got in fix(platform-objects): translate the zh-CN object labels, options and help that shipped as English copies #20490. Nothing weakened. Of the 39 objects-bundle leaf paths the eleven sibling ledgers reference, only this one is in the changed set, and none remains a byte copy at head; the other eight ledgers' provenance floors have been greater than 0 since fix(platform-objects): translate the zh-CN object labels, options and help that shipped as English copies #20490 and their authored.length floors only grow. RIGHT; Test Core 1/6 to 6/6 green.
  7. The before / after counts. Recomputed from the blobs with the PR's stated method (byte-equal string leaves at the same path, 1529 per bundle): ja-JP 383 to 43 (label 210 to 29, options 20 to 10, pluralLabel 12 to 0, help 121 to 0, description 12 to 0, placeholder 4 to 4, emptyState 4 to 0; label plus options 230 to 39); es-ES 392 to 54 (217 to 38, 22 to 12, 12 to 0, 121 to 0, 12 to 0, 4 to 4, 4 to 0; 239 to 50). Every cell of the PR table matches. The en, ja-JP, es-ES bundles and both companions are byte-identical at the PR's base 397572ed5, the card's b810ddb6f and the merge-base fb386074f, so H1 holds.
  8. The two ledgers are the zh-CN shape applied per locale: Verdict = 'translate' | 'echo', Decision with en-pinned rows, reason greater than 40 characters on every row, departure on every echo, undeclaredEchoes proved on a synthetic row, the dark predicate tests, the provenance both-directions test (11 tests ja, 10 es, consistent with the dev's ablation counts). ja adds a JAPANESE script predicate with its own dark test; es, having no script to test, uses the en catalog as the negative in the role test; both add the exact console-word pin inside the existing role test. No new mechanism. Type Check lanes green.
  9. H5 class decision. packages/cli/src/utils/i18n-extract.ts:1192-1193 file a field's help and placeholder under source 'field', line 1177 files description under 'object': the same class as the element's label. RIGHT to translate help / description / emptyState; the four placeholders are typed literals and stay (4 to 4, all four declared echoes).
  10. Size and surface. 8 files, +1416 / −1371 = 2787 changed lines, under the 5000 human-merge threshold; every file inside the claim 5880394970 (leaf-only bundles, two ledgers, two regenerated companions, one sibling ledger that reads a translated leaf, one changeset); no zh-CN, no *.metadata-forms.generated.ts, no hand-authored ja-JP.ts / es-ES.ts, no scripts/i18n-coverage-baseline.json. Governed Surface Queue Guard and the single-writer-path guard green.

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 check:i18n family) at 00:18:48Z; Test Core rollup and 1/6 to 6/6; Type Check workspace / source gates / consumer gates / debt ledger and TypeScript Type Check; Build Core; Check Changeset; Dogfood Regression Gate rollup and 1/3 to 3/3; Dogfood Verify CLI; Temporal Conformance (live PG + MySQL); Governed Surface Queue Guard; The card this PR closes must claim this branch; No other open PR may claim the same issue / the same single-writer path; Part-of PR must not also close its card; Check PR Size; Check Documentation Links; Flag docs affected by code changes; Auto Label; filter. mergeable: true, mergeable_state: clean; the PR is still a draft.

② Semver level

.changeset/20493-ja-es-object-labels.md declares @objectstack/platform-objects: patch. RIGHT: the diff changes 678 string values inside two published generated bundles, drops 678 provenance rows, adds two test files and edits one; no export, type, key, schema or API moves, and nothing a consumer's code binds to changes. A copy fix in user-visible text is a patch. Clause-②: no in the claim, the changeset and the PR body; right, no forward-compat lever, flag or removal is involved. Check Changeset is green.

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). Clause-②: no TRUE. The symptom sentence TRUE per the card (bundle read, not re-driven in a browser). "byte copies of the English source that os i18n extract --fill=default seeds" TRUE (--fill=default is in the root i18n:extract script; byte-equal at base). "ja-JP: 340 of the 383 … labels, plural labels, select options, help, descriptions and empty states" TRUE, numbers and class list (no placeholder translated). "delegated_admin reads 委任管理者 on both … the word the console already uses" TRUE. "es-ES: 338 of the 392 … Administrador delegado on both objects, again the console's word" TRUE. "The rest stay as written by design, each recorded with its reason in a per-locale ledger" TRUE. "ja-JP keeps 43: brands, the bare ID, protocol names (JWKS, IdP SSO URL, Message-ID, UI / API), the Web client type, the Cc / Bcc header abbreviations, and placeholders" TRUE (7 + 23 + 2 + 1 + 1 + 2 + 1 + 2 + 4 = 43). "es-ES keeps 54: the same brands, ID, JWKS, Message-ID, UI / API, Web and placeholders, plus … (Error, Actor, Global, Variables), … (Token, Checksum, Slug) and Cc" TRUE (40 + 8 + 5 + 1 = 54; IdP SSO URL correctly absent). The companions bullet ("regenerated by pnpm i18n:extract … now list exactly those 43 and 54. zh-CN is unchanged") TRUE, measured.

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; "pnpm i18n:extract re-read both bundles byte-identical" is the dev's local reading, consistent with Lint and Repo Gates green. The coverage-ratchet paragraph TRUE (no baseline in the diff; neither ledger asserts the table is a subset of the echoes). The pinned-strings table TRUE, every cell. "The console's words were read at objectui main 797a30f": the words TRUE at current main 12a6688cb; that sha itself is not re-measurable now that main has moved. "The role test also asserts the four role words are distinct, identical on both objects, Japanese-script in ja-JP and none equal to its en source in es-ES" TRUE. The count section: every sentence and cell TRUE, including "All three bundles have 1529 leaves", the base 397572ed5 and the H1 reproduction. The H5 sentence TRUE (①.9). The ja-JP echo table: every row's leaves, values and reasons TRUE against the bundle, and the departures paragraph TRUE (クライアント ID / クライアントシークレット authored at base, 9 hits each; Web and Cc / Bcc absent from the zh-CN ledger). The es-ES echo table: every row TRUE (①.3), and the departures sentence TRUE. Terminology bullets: every precedent TRUE (①.4) except one parenthesis: "tombstone is トゥームストーン (no prior rendering in any ja-JP bundle)" is FALSE in its parenthesis, since the service-storage ja-JP objects bundle already renders トゥームストーン (line 97); the word chosen is therefore the existing rendering, which is better than the sentence claims. Non-blocking. The verification section's local runs are the dev's and are not re-measured here; every CI-declared family is green on the head; "a true merge of origin/main fb386074f (parents 8f3e7cae3, fb386074f)" TRUE; "H6 … 2787 changed lines (+1416 / −1371) over 8 files" TRUE. Acceptance notes: each TRUE, judged in ③.

③ Boundary flags

  1. Deviation 1, a third pinned translate row (sys_team.fields.member_count.label) beyond the claim's delegated_admin pair: the leaf was a byte copy in both locales and is translated (メンバー数 / Número de miembros); the row is an extra assertion of the zh-CN shape, harmless and on-surface. Accepted, no escalation.
  2. Deviation 2, the console-word pin as an exact-value assertion inside the role test rather than a new Decision field: satisfies the claim's pin and keeps the ledger shape identical to zh-CN's. Accepted.
  3. Deviation 3, es-ES has no script to test, so its translate verdict asserts not-the-en-source and the role test uses the en catalog as the negative: the only honest predicate for a Latin-script locale; the exact-word pin and the distinctness assertion cover the card's leaves. Accepted.
  4. Deviation 4, the model-free trailer pair per AGENTS.md instead of the harness reminder's model-named trailer: every PR commit carries Claude-Session: and Co-authored-by: Claude; "The card this PR closes must claim this branch" and Lint and Repo Gates are green. A process note, no action.
  5. Deviation 5, cleanup after the report: process only, no effect on the diff.
  6. open_questions is empty; nothing to answer.
  7. Out-of-scope note 1 (es Parent as principal; the pre-existing parent_business_unit_id / primary_business_unit_id pair both read Unidad de negocio principal): measured TRUE at base and untouched; this PR's Organización principal follows the bundle's word. No defect introduced; carrier none agreed. A bundle-wide Parent / Primary disambiguation would be its own card if the maintainer wants one.
  8. Out-of-scope note 2 (authored ja / es leaves stale against a moved en source, e.g. sys_two_factor.fields.backup_codes.help still "JSON-serialized" where en now says an opaque ciphertext, and sys_notification.description): measured TRUE at base and head in both locales; pre-existing, outside the copy class, legacy-trusted by the provenance design's own ruling. Not this PR's defect. The backup_codes help now states the opposite of the source, so the seat may file one bare card for the present-but-stale class with the dev's dedupe words; not a blocker here.
  9. Out-of-scope note 3 (Locked Until rendered two ways in ja-JP already; this PR uses sys_user's ロック解除日時 for sys_two_factor): pre-existing pair, the same side fix(platform-objects): translate the zh-CN object labels, options and help that shipped as English copies #20490 took for zh-CN. No defect introduced.
  10. Out-of-scope note 4 (ja Managed By is 管理者, so sys_email_template reads 管理者 as the label and as its admin option): this is the one note whose surface IS new in this PR, since sys_metadata (whose authored label 管理者 the PR follows) has no admin option and so never collided. The meaning is recoverable from the option set (プラットフォーム / パッケージ / 管理者), zh-CN and es-ES do not collide (管理方 / 管理员; Gestionado por / Administrador), and the alternative 管理元 would render Managed By two ways unless the authored sys_metadata label (outside the copy class) moved with it. Non-blocking; the maintainer's one-line choice, recommended as a text-only follow-up on both labels rather than a re-review trigger.
  11. Out-of-scope note 5 (the eight sibling ledgers' floor comments name only zh-CN): comment-only drift outside the file surface; the floors hold for all three locales at head. No action.
  12. The PR-body slip in ② (the tombstone parenthesis): one FALSE parenthesis in a terminology bullet, no value or assertion behind it; drop the parenthesis if the branch is re-pushed for any other reason.

Implemented-by: claude/issue-20493-ja-es-object-labels
Reviewed-by: session_01N8TPEsoJxPsdSdNKGnNGEN

VERDICT: PASS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

2 participants