Skip to content

Commit a84e1ce

Browse files
claude[bot]claude
andauthored
fix(i18n): metadata label lookup honours the declared fallbackLocale / defaultLocale instead of the literal en chain (#14882) (#15707)
* i18n: metadata label lookup honours the declared fallback locale The REST metadata translation seams pass the document translators the fallback chain the deployment declared — read from the new optional II18nService.getFallbackLocale(), implemented by FileI18nAdapter — instead of leaving the resolvers on their literal en default. A zh-CN workspace with a courtesy en bundle now resolves a zh-CN request to its authored Chinese labels. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf * test(rest): pin the declared fallback chain at the metadata seams Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf * docs(permissions): re-anchor the system-context census rows shifted by the rest-server edit Mechanical: node scripts/check-system-context-census.mjs --fix rewrote five line anchors on the manage_metadata row after translateOptionsFor was added above them; the census re-check is green. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf * docs(permissions): regenerate the system-context census page on the merged tree Regenerated with pnpm gen:system-context-census after merging origin/main 1c14214 (os-regen path, merged without a text merge). Anchor line numbers only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf * docs(permissions): regenerate the system-context census page on the merged tree Regenerated with pnpm gen:system-context-census after merging origin/main 8e500f2 (os-regen path, merged without a text merge). Anchor line numbers only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf --------- Co-authored-by: os-dev <noreply@anthropic.com>
1 parent c99449a commit a84e1ce

13 files changed

Lines changed: 650 additions & 10 deletions
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
"@objectstack/rest": patch
3+
---
4+
5+
fix(rest): metadata label lookup honours the stack's declared `i18n.fallbackLocale` / `defaultLocale` instead of falling through to the `en` bundle (#14882)
6+
7+
On a workspace whose labels are authored in `zh-CN` (`defaultLocale: 'zh-CN'`,
8+
`fallbackLocale: 'zh-CN'`) and which ships only a courtesy `en` translation bundle,
9+
`GET /api/v1/meta/object/:name`, the `/meta/:type` list, `GET /api/v1/meta` and the
10+
public-form schema served the ENGLISH bundle labels to a `zh-CN` request (`Entry Sheet`
11+
for an authored `填报单`, `KPI Assessment` for `KPI 考核管理`). The document translators walk
12+
`requested locale → fallback chain → authored label` and default the chain to a literal
13+
`['en']`; every REST seam passed none, so the declared fallback never reached the chain
14+
and `en` was consulted before the authored label.
15+
16+
Every metadata translation seam now passes `fallbackChain: [i18n.getFallbackLocale()]`
17+
the locale the i18n service's own `t()` falls back to, which `I18nServicePlugin` receives
18+
from the stack config as `fallbackLocale || defaultLocale || 'en'`. For the workspace
19+
above a `zh-CN` request now resolves `zh-CN → zh-CN → authored label` (the authored
20+
Chinese labels), an `en` request still gets the `en` bundle, and a `zh-CN` bundle, when one
21+
is shipped, still wins over the authored label.
22+
23+
Feature-detected: an i18n service that does not declare a fallback (the method is
24+
optional on `II18nService`; the core in-memory fallback has none) gets no chain and the
25+
resolver's own default applies exactly as before. A stack declaring `defaultLocale: 'zh-CN'`
26+
with `fallbackLocale: 'en'` is likewise unchanged — the declared `en` is honoured as it
27+
reads.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"@objectstack/service-i18n": minor
3+
---
4+
5+
feat(service-i18n): `FileI18nAdapter.getFallbackLocale()` reports the `fallbackLocale` the adapter was constructed with (#14882)
6+
7+
Implements the new optional `II18nService.getFallbackLocale()`. `I18nServicePlugin`
8+
already receives `fallbackLocale || defaultLocale || 'en'` from the stack's `i18n`
9+
config on both boot paths (`os serve`, the dev plugin); this makes that declaration
10+
readable, so the REST metadata reads pass the document translators the same fallback
11+
locale `t()` itself consults. Returns `undefined` when no `fallbackLocale` was given.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec): `II18nService.getFallbackLocale()` — the declared fallback locale is readable, so the metadata-document translators can be handed the chain the deployment declared (#14882)
6+
7+
`ResolveOptions.fallbackChain` on the `@objectstack/spec/system` label
8+
resolvers (`translateMetadataDocument`, `translateObject`, `translateApp`,
9+
`resolveViewLabel`, …) is the ordered list of locales consulted after the
10+
requested one and BEFORE the authored label. Nothing on `II18nService`
11+
exposed the deployment's declared fallback (`i18n.fallbackLocale`, else
12+
`defaultLocale`), so no serving layer could thread it, and every caller fell
13+
to the resolver's literal `['en']` default. A `zh-CN` workspace that shipped a
14+
courtesy `en` bundle therefore served English bundle text to a `zh-CN`
15+
request ahead of its own authored Chinese labels.
16+
17+
- New optional contract member `II18nService.getFallbackLocale?(): string | undefined`
18+
— the locale the service's own `t()` consults second. `undefined` (or the
19+
method absent) means nothing was declared, and a serving layer must then
20+
leave the resolver's default in place rather than invent a chain.
21+
- The `fallbackChain` documentation now states who supplies it (the serving
22+
layer, from `getFallbackLocale()`) and that the `['en']` default applies
23+
only when a caller declares no chain at all. The resolver's behaviour for
24+
a caller that passes nothing is unchanged.
25+
26+
Additive: no existing implementation or caller changes shape.

content/docs/permissions/system-context.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ The largest single consumer — **17 of the 106 sites**.
158158
|:--|:---|:---|:---|:---|
159159
| 48 | Object API-exposure gate bypassed (`apiEnabled` / `apiMethods`) | runtime | Get: internal self-writes ignore exposure declarations — these govern **external** exposure, not engine self-writes | `action-execution.ts:138` |
160160
| 49 | Action `requiredPermissions` bypassed | runtime | Get: engine self-invocation runs any action | `action-execution.ts:401` |
161-
| 50 | `manage_metadata` bypassed on metadata writes | runtime, rest | Get: schema writes without the capability | `domains/meta.ts:471`, `:874`, `rest-server.ts:5048`, `:6474`, `:6722`, `:7153`, `:7346` |
161+
| 50 | `manage_metadata` bypassed on metadata writes | runtime, rest | Get: schema writes without the capability | `domains/meta.ts:471`, `:874`, `rest-server.ts:5084`, `:6510`, `:6758`, `:7189`, `:7382` |
162162
| 51 | The shared metadata-write verdict itself returns `allowed` | metadata-core | Get: the one function all of row 50's doors consult answers yes before any capability is examined | `meta-write-capability.ts:134` |
163163
| 52 | Anonymous-deny seam satisfied on the domain dispatchers and the package/federation routes | runtime, rest | Get: passes with no `userId` | `domains/actions.ts:421`, `domains/ai.ts:60`, `domains/automation.ts:989`, `domains/meta.ts:232`, `domains/security.ts:78`, `domains/packages.ts:422`, `external-datasource-routes.ts:302`, `package-routes.ts:97` |
164164
| 53 | MCP principal check satisfied | runtime | Get: MCP surface reachable with no user | `domains/mcp.ts:61` |

0 commit comments

Comments
 (0)