Skip to content

Commit 07c35d8

Browse files
authored
Merge pull request #1404 from objectstack-ai/claude/metadata-translation-audit-WAA9X
2 parents fa64f4b + ad7cf16 commit 07c35d8

13 files changed

Lines changed: 1773 additions & 139 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
"@objectstack/spec": patch
3+
"@objectstack/rest": patch
4+
"@objectstack/platform-objects": patch
5+
"@objectstack/service-settings": patch
6+
---
7+
8+
Fix system-metadata translations: locale fallback, app/dashboard localization, and coverage gaps.
9+
10+
Switching the UI language left many surfaces in English. Three root causes
11+
are addressed:
12+
13+
- **Locale fallback (server).** The metadata translation resolver
14+
(`@objectstack/spec` `i18n-resolver`) now resolves a requested locale
15+
against the locales actually present in the bundle (exact →
16+
case-insensitive → base-language → variant), so a request for `zh`
17+
correctly hits the `zh-CN` bundle instead of falling back to English.
18+
This mirrors `resolveLocale` in `@objectstack/core` and benefits every
19+
resolver (objects, views, actions, settings, metadata forms).
20+
21+
- **App & dashboard localization (server).** Added `translateApp` and
22+
`translateDashboard` resolvers and wired `app`/`dashboard` into the REST
23+
`/meta` translation path. App labels, sidebar/navigation group labels,
24+
and dashboard titles/widgets were previously never localized at the API
25+
boundary even though the translation data existed.
26+
27+
- **Coverage & quality (data).** Added translations for the previously
28+
untranslated platform objects `sys_share_link`, `sys_view_definition`,
29+
and `sys_metadata_audit` (and registered them in the i18n-extract config
30+
so future extractions keep them). Replaced English placeholder strings
31+
left in the `zh-CN` / `ja-JP` / `es-ES` object and metadata-form bundles
32+
(notably action `confirmText` / `successMessage` prompts). Added the
33+
missing `es-ES` built-in Settings bundle in `@objectstack/service-settings`.

packages/platform-objects/scripts/i18n-extract.config.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
SysRolePermissionSet,
5959
SysRecordShare,
6060
SysSharingRule,
61+
SysShareLink,
6162
} from '../src/security/index.js';
6263

6364
// ── Audit ─────────────────────────────────────────────────────────────────
@@ -84,7 +85,12 @@ import {
8485
import { SysWebhook } from '../src/integration/index.js';
8586

8687
// ── Metadata ──────────────────────────────────────────────────────────────
87-
import { SysMetadataObject, SysMetadataHistoryObject } from '../src/metadata/index.js';
88+
import {
89+
SysMetadataObject,
90+
SysMetadataHistoryObject,
91+
SysViewDefinitionObject,
92+
SysMetadataAuditObject,
93+
} from '../src/metadata/index.js';
8894

8995
// ── System ────────────────────────────────────────────────────────────────
9096
import { SysSetting, SysSecret, SysSettingAudit } from '../src/system/index.js';
@@ -147,6 +153,7 @@ export default defineStack({
147153
SysRolePermissionSet,
148154
SysRecordShare,
149155
SysSharingRule,
156+
SysShareLink,
150157

151158
// Audit
152159
SysAuditLog,
@@ -172,6 +179,8 @@ export default defineStack({
172179
// Metadata
173180
SysMetadataObject,
174181
SysMetadataHistoryObject,
182+
SysViewDefinitionObject,
183+
SysMetadataAuditObject,
175184

176185
// System
177186
SysSetting,

packages/platform-objects/src/apps/translations/en.objects.generated.ts

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,100 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
15561556
}
15571557
}
15581558
},
1559+
sys_share_link: {
1560+
label: "Share Link",
1561+
pluralLabel: "Share Links",
1562+
description: "Opaque capability token granting access to a single record. Notion/Figma-style public link sharing.",
1563+
fields: {
1564+
id: {
1565+
label: "Link ID"
1566+
},
1567+
token: {
1568+
label: "Token",
1569+
help: "Opaque URL-safe random token (≥ 22 chars). The only secret in this row."
1570+
},
1571+
object_name: {
1572+
label: "Object",
1573+
help: "Short object name of the shared record (e.g. ai_conversation, contracts_contract)"
1574+
},
1575+
record_id: {
1576+
label: "Record",
1577+
help: "Primary key of the shared record within object_name"
1578+
},
1579+
permission: {
1580+
label: "Permission",
1581+
help: "What the link holder can do with the record",
1582+
options: {
1583+
view: "View",
1584+
comment: "Comment",
1585+
edit: "Edit"
1586+
}
1587+
},
1588+
audience: {
1589+
label: "Audience",
1590+
help: "Gating layer applied on top of the token check",
1591+
options: {
1592+
public: "Public (indexable)",
1593+
link_only: "Anyone with the link",
1594+
signed_in: "Signed-in users",
1595+
email: "Specific emails"
1596+
}
1597+
},
1598+
expires_at: {
1599+
label: "Expires At",
1600+
help: "When set, resolveToken returns null after this timestamp"
1601+
},
1602+
email_allowlist: {
1603+
label: "Email Allowlist",
1604+
help: "Lowercased addresses checked when audience=email"
1605+
},
1606+
password_hash: {
1607+
label: "Password Hash",
1608+
help: "Argon2/bcrypt hash. When set, the UI prompts for a password before rendering."
1609+
},
1610+
redact_fields: {
1611+
label: "Per-Link Redactions",
1612+
help: "Extra fields stripped from the response, on top of the object-default set"
1613+
},
1614+
label: {
1615+
label: "Label",
1616+
help: "Free-text shown in the share dialog (e.g. \"ACME Q3 contract\")"
1617+
},
1618+
revoked_at: {
1619+
label: "Revoked At",
1620+
help: "When set, the link is permanently disabled"
1621+
},
1622+
created_by: {
1623+
label: "Created By",
1624+
help: "Issuer of the link"
1625+
},
1626+
created_at: {
1627+
label: "Created At"
1628+
},
1629+
last_used_at: {
1630+
label: "Last Used At",
1631+
help: "Stamped by resolveToken; used by the dashboard to highlight active links"
1632+
},
1633+
use_count: {
1634+
label: "Use Count",
1635+
help: "Incremented by resolveToken on every successful resolution"
1636+
}
1637+
},
1638+
_views: {
1639+
active_links: {
1640+
label: "Active"
1641+
},
1642+
by_me: {
1643+
label: "Created by Me"
1644+
},
1645+
revoked: {
1646+
label: "Revoked"
1647+
},
1648+
all_links: {
1649+
label: "All"
1650+
}
1651+
}
1652+
},
15591653
sys_audit_log: {
15601654
label: "Audit Log",
15611655
pluralLabel: "Audit Logs",
@@ -2861,6 +2955,147 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
28612955
}
28622956
}
28632957
},
2958+
sys_view_definition: {
2959+
label: "View Definition",
2960+
pluralLabel: "View Definitions",
2961+
description: "Runtime-authored view definitions (shared / personal layers). The package layer ships from source.",
2962+
fields: {
2963+
id: {
2964+
label: "ID"
2965+
},
2966+
name: {
2967+
label: "Name"
2968+
},
2969+
object: {
2970+
label: "Object"
2971+
},
2972+
view_kind: {
2973+
label: "View Kind",
2974+
options: {
2975+
list: "list",
2976+
form: "form"
2977+
}
2978+
},
2979+
label: {
2980+
label: "Label"
2981+
},
2982+
is_default: {
2983+
label: "Is Default"
2984+
},
2985+
view_order: {
2986+
label: "Order"
2987+
},
2988+
scope: {
2989+
label: "Scope",
2990+
options: {
2991+
shared: "shared",
2992+
personal: "personal"
2993+
}
2994+
},
2995+
owner: {
2996+
label: "Owner"
2997+
},
2998+
hidden: {
2999+
label: "Hidden"
3000+
},
3001+
config: {
3002+
label: "Config",
3003+
help: "ListView or FormView configuration (matches spec ViewItem.config)."
3004+
},
3005+
organization_id: {
3006+
label: "Organization",
3007+
help: "Organization for multi-tenant isolation."
3008+
},
3009+
state: {
3010+
label: "State",
3011+
options: {
3012+
draft: "draft",
3013+
active: "active",
3014+
archived: "archived"
3015+
}
3016+
},
3017+
created_by: {
3018+
label: "Created By"
3019+
},
3020+
created_at: {
3021+
label: "Created At"
3022+
},
3023+
updated_by: {
3024+
label: "Updated By"
3025+
},
3026+
updated_at: {
3027+
label: "Updated At"
3028+
}
3029+
}
3030+
},
3031+
sys_metadata_audit: {
3032+
label: "Metadata Audit",
3033+
pluralLabel: "Metadata Audit",
3034+
description: "Append-only audit trail of metadata write decisions (ADR-0010).",
3035+
fields: {
3036+
id: {
3037+
label: "ID"
3038+
},
3039+
occurred_at: {
3040+
label: "Occurred At"
3041+
},
3042+
actor: {
3043+
label: "Actor",
3044+
help: "Acting principal — user id, system id, or \"system\"."
3045+
},
3046+
source: {
3047+
label: "Source"
3048+
},
3049+
type: {
3050+
label: "Metadata Type"
3051+
},
3052+
name: {
3053+
label: "Name"
3054+
},
3055+
organization_id: {
3056+
label: "Organization"
3057+
},
3058+
operation: {
3059+
label: "Operation",
3060+
options: {
3061+
save: "save",
3062+
publish: "publish",
3063+
rollback: "rollback",
3064+
delete: "delete",
3065+
reset: "reset"
3066+
}
3067+
},
3068+
outcome: {
3069+
label: "Outcome",
3070+
options: {
3071+
allowed: "allowed",
3072+
denied: "denied",
3073+
forced: "forced"
3074+
}
3075+
},
3076+
code: {
3077+
label: "Code"
3078+
},
3079+
lock_state: {
3080+
label: "Lock State",
3081+
options: {
3082+
none: "none",
3083+
"no-overlay": "no-overlay",
3084+
"no-delete": "no-delete",
3085+
full: "full"
3086+
}
3087+
},
3088+
lock_overridden: {
3089+
label: "Lock Overridden"
3090+
},
3091+
request_id: {
3092+
label: "Request ID"
3093+
},
3094+
note: {
3095+
label: "Note"
3096+
}
3097+
}
3098+
},
28643099
sys_setting: {
28653100
label: "Setting",
28663101
pluralLabel: "Settings",

packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ export const esESMetadataForms: NonNullable<TranslationData['metadataForms']> =
857857
label: "Traducción"
858858
},
859859
router: {
860-
label: "Router"
860+
label: "Enrutador"
861861
},
862862
function: {
863863
label: "Función"

0 commit comments

Comments
 (0)