docs(spec,i18n): GET /i18n/locales stops declaring label a display name - #7757
Conversation
…y name
`GetLocalesResponseSchema` described each locale descriptor's `label` as
"Display name of the locale" while the sole producer — `toLocaleDescriptors`
in `system/i18n-resolver.ts`, shared deliberately by the runtime dispatcher's
`/i18n` domain and service-i18n's autonomous route — sets it to the code.
`GET /api/v1/i18n/locales` answers `{ code: 'th', label: 'th' }`, never
`{ code: 'th', label: 'ไทย' }`. Declared not enforced (ADR-0049), one field
wide, and the describe carries the claim into the JSON Schema, the OpenAPI
surface and the SDK type.
Measured before choosing the fix: no consumer anywhere reads `label`. In this
repo every read of the body takes `code` or `isDefault`; the one wire fixture
spelling `label` sets it to the code and asserts only the array length. In
objectui the one real consumer, `apps/console/src/loadLocales.ts`, reads
`entry?.code` and documents that the descriptor's label is not a display name
(objectui#4039). With nothing consuming the field, the honest declaration is
the whole fix — runtime behaviour is unchanged.
The declaration now states the convention it ships, and `i18n-resolver.test.ts`
pins both sides of it: a producer that starts inventing display names and a
describe that starts promising them each go red separately. Serving real
display names (CLDR data on the server for something every client computes)
and retiring the field are both left open on #7634 — neither has a ruling.
Fixes #7634
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also reference the affected code. These are read-only:
|
Fixes #7634
GetLocalesResponseSchemadescribed each locale descriptor'slabelas "Display name of the locale", and no producer has ever written one. The sole producer istoLocaleDescriptors(packages/spec/src/system/i18n-resolver.ts:1118) — shared deliberately by the runtime dispatcher's/i18ndomain andservice-i18n's autonomous route, so there is no second implementation to diverge — and it setslabelto the code.GET /api/v1/i18n/localesanswerscode: 'th', label: 'th', neverlabel: 'ไทย'. Declared-not-enforced (ADR-0049), one field wide, and the describe is what carries the claim into the generated JSON Schema, the OpenAPI surface and the SDK type.The premise, measured first
The card's fork was conditional: describe-only fix if no consumer treats
labelas a human display name; stop and report if one does. Both halves were measured, and no consumer reads the field.This repo — quoted-exact-name greps over
toLocaleDescriptors/LocaleDescriptor/GetLocalesResponse/i18n/locales, plus a co-occurrence sweep forlabelin any file mentioninglocales. Every read of the body takescodeorisDefault:packages/runtime/src/http-dispatcher.test.ts(4 sites)l.codepackages/runtime/src/domain-handler-registry.test.ts:130l.codepackages/runtime/src/i18n-success-envelope.conformance.test.ts:162l.isDefaultpackages/spec/src/system/i18n-resolver.test.ts:1624l.code/isDefaultpackages/client/src/i18n-wire-dialect.test.ts:46label: 'zh-CN'(the code); asserts only array lengthPositive control: the same grep method finds real
isDefaultandcodereads across the repo, so the absence oflabelreads is a measurement rather than a missed pattern.objectui — verified directly against a read-only clone at
3b7d1cc, not taken on the filer's word. The one real consumer isapps/console/src/loadLocales.ts, which readsentry?.codeand says so in its header: "that helper setslabelto the code itself, so the descriptor's label is not a display name and the switcher names locales for itself."packages/app-shell/src/layout/LocaleSwitcher.tsxnames locales from a built-in native-name table plusIntl.DisplayNames, and its header gives the reason — "a server label would putthin the menu whereไทยbelongs".So the premise holds, and the narrowest fix applies. No consumer changes; runtime behaviour is byte-identical.
What changed
packages/spec/src/api/protocol.zod.ts—label's describe now states the convention it ships: "Locale label. Equalscodeon every serving surface today — the client names locales for its UI (i18n:GET /i18n/localesdeclares a localelabelas "Display name" but every producer sets it to the code #7634)", with a schema-level comment recording why display naming stays a client concern (Intl.DisplayNamesis in every runtime that matters, and which language to name a locale in — its own, or the requester'sAccept-Language— is the caller's choice).packages/spec/src/system/i18n-resolver.ts—LocaleDescriptor.label's JSDoc said "Display name. Falls back to the code"; it now says the field is the code. ThetoLocaleDescriptorsheader records the no-pull measurement.packages/spec/src/system/i18n-resolver.test.ts— one pin asserting the substance: producer output equals the declared shape, on both sides.Deliberately not done
Both are contract actions nobody has ruled, and they stay open on #7634:
Tests
The pin is two-sided, and reverse verification was run in both directions with the expected direction predicted first — the halves move independently, which is the point:
label: NAME(${code})) → RED on the value assertion:expected 'NAME(en)' to be 'en'. Describe assertions stayed green.Expected: /display name/i · Received: "Display name of the locale". The value assertion stayed green.Restored via
git checkout HEAD -- ...against a checkpoint commit; tree verified clean at HEAD afterwards. Nogit stash.Gates run locally, scoped to this card's surface:
@objectstack/specfull suite@objectstack/spectypecheck (+ scripts, + test-typecheck)check:generated(13 artifacts)check:authorable-surface,check:api-surface,check:docscheck:generated)pnpm check:i18nEXIT=0read explicitly, not via a pipecheck:nul-bytesruntimei18n conformance + domain-handler-registryservice-i18nfull suiteclienti18n wire-dialectcheck:i18nneeded the built CLI on its first run (pnpm exec turbo run build --filter=@objectstack/cli) — a fresh-worktree prerequisite, not drift; green after building.Generated-artifact closure: the new describe reaches
packages/spec/json-schema/api/GetLocalesResponse.jsonand the bundledobjectstack.json, both of which are gitignored build outputs.content/docs/references/api/protocol.mdxrenders only the array-level describe for this schema, so no committed generated file changes —check:generatedreports all 13 artifacts current.Changeset:
patchon@objectstack/spec. User-visible because the describe ships in the JSON Schema, OpenAPI and SDK type surfaces; patch because no runtime behaviour, no type shape and no field moves.Generated by Claude Code