Skip to content

Commit d1cabaa

Browse files
authored
fix(i18n): translate SSO/SCIM/user-position/import-job admin objects (#3531)
Add i18n coverage for four live, UI-facing system objects that were registered but never extracted: sys_sso_provider, sys_scim_provider, sys_import_job (platform-objects) and sys_user_position (plugin-security). Real zh-CN/ja-JP/es-ES translations across all four locale bundles plus bundle-ownership guard entries. One patch changeset (shared fixed group).
1 parent 134df4f commit d1cabaa

13 files changed

Lines changed: 1418 additions & 8 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
"@objectstack/platform-objects": patch
3+
"@objectstack/plugin-security": patch
4+
---
5+
6+
fix(i18n): translate the SSO / SCIM / user-position / import-job admin objects
7+
8+
Four live, UI-facing system objects were registered but never added to their
9+
package's i18n extract config, so non-English admins saw raw English `label`
10+
metadata:
11+
12+
- `sys_sso_provider`, `sys_scim_provider` (platform-objects) — identity-provider
13+
admin grids plus the register / verify-domain actions.
14+
- `sys_user_position` (plugin-security) — delegated position assignment
15+
(`userActions` create/edit/delete); its sibling `sys_user_permission_set` was
16+
already translated, so this closes an inconsistency.
17+
- `sys_import_job` (platform-objects) — import history / progress, alongside the
18+
already-translated `sys_job` / `sys_job_run`.
19+
20+
Adds each object to its package's `scripts/i18n-extract.config.ts` and supplies
21+
real zh-CN / ja-JP / es-ES translations across all four locale bundles, and
22+
extends the bundle-ownership guards' `OWNED_OBJECTS` to cover them. The
23+
orphan-only guards from #3502 could not catch this "owned-and-live-but-never-
24+
extracted" gap.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ import {
5151
SysOauthClientResource,
5252
SysOauthClientAssertion,
5353
SysJwks,
54+
SysSsoProvider,
55+
SysScimProvider,
5456
} from '../src/identity/index.js';
5557

5658
// ── Security ──────────────────────────────────────────────────────────────
@@ -80,6 +82,7 @@ import {
8082
SysJob,
8183
SysJobRun,
8284
SysJobQueue,
85+
SysImportJob,
8386
} from '../src/audit/index.js';
8487

8588
// ── Integration ───────────────────────────────────────────────────────────
@@ -151,6 +154,8 @@ export default defineStack({
151154
SysOauthClientResource,
152155
SysOauthClientAssertion,
153156
SysJwks,
157+
SysSsoProvider,
158+
SysScimProvider,
154159

155160
// Security: RBAC moved to @objectstack/plugin-security, sharing to
156161
// @objectstack/plugin-sharing (ADR-0029 K2 / D8).
@@ -168,6 +173,7 @@ export default defineStack({
168173
SysJob,
169174
SysJobRun,
170175
SysJobQueue,
176+
SysImportJob,
171177

172178
// Integration: sys_webhook moved to @objectstack/plugin-webhooks (ADR-0029 D8).
173179

packages/platform-objects/src/apps/translations/bundle-ownership.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ const OWNED_OBJECTS = new Set([
2424
'sys_user_preference', 'sys_oauth_application', 'sys_oauth_access_token',
2525
'sys_oauth_refresh_token', 'sys_oauth_consent', 'sys_oauth_resource',
2626
'sys_oauth_client_resource', 'sys_oauth_client_assertion', 'sys_jwks',
27+
// identity — external SSO / SCIM providers (admin-facing, better-auth-managed)
28+
'sys_sso_provider', 'sys_scim_provider',
2729
// audit / messaging-adjacent (still owned here)
2830
'sys_notification', 'sys_attachment', 'sys_email', 'sys_email_template',
2931
'sys_saved_report', 'sys_report_schedule', 'sys_job', 'sys_job_run', 'sys_job_queue',
32+
'sys_import_job',
3033
// metadata
3134
'sys_metadata', 'sys_metadata_history', 'sys_view_definition', 'sys_metadata_audit',
3235
// system

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

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,203 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
16671667
}
16681668
}
16691669
},
1670+
sys_sso_provider: {
1671+
label: "SSO Provider",
1672+
pluralLabel: "SSO Providers",
1673+
description: "External SSO identity providers (OIDC / SAML) this environment federates login to",
1674+
fields: {
1675+
id: {
1676+
label: "ID"
1677+
},
1678+
provider_id: {
1679+
label: "Provider ID",
1680+
help: "Stable provider identifier (unique within the environment)"
1681+
},
1682+
issuer: {
1683+
label: "Issuer",
1684+
help: "IdP issuer URL"
1685+
},
1686+
domain: {
1687+
label: "Email Domain",
1688+
help: "Email domain routed to this IdP (e.g. acme.com)"
1689+
},
1690+
domain_verified: {
1691+
label: "Domain Verified",
1692+
help: "Whether DNS ownership of the email domain has been proven (ADR-0024 ②). Set by “Verify Domain” after the DNS TXT record resolves. Managed by better-auth — not directly editable. Only enforced when domain verification is enabled for the environment."
1693+
},
1694+
oidc_config: {
1695+
label: "OIDC Config",
1696+
help: "JSON: clientId, clientSecret, endpoints, scopes, mapping, pkce (managed by better-auth)"
1697+
},
1698+
saml_config: {
1699+
label: "SAML Config",
1700+
help: "JSON: entryPoint, cert, identifierFormat, mapping (managed by better-auth)"
1701+
},
1702+
user_id: {
1703+
label: "Registered By",
1704+
help: "User who registered this provider"
1705+
},
1706+
organization_id: {
1707+
label: "Organization",
1708+
help: "Organization scope (when org-scoped SSO is used)"
1709+
},
1710+
created_at: {
1711+
label: "Created At"
1712+
},
1713+
updated_at: {
1714+
label: "Updated At"
1715+
}
1716+
},
1717+
_views: {
1718+
all: {
1719+
label: "All",
1720+
emptyState: {
1721+
title: "No SSO providers yet",
1722+
message: "Register your organization’s external IdP — OIDC (Okta, Entra, Auth0, …) with “Register SSO Provider”, or SAML 2.0 with “Register SAML Provider”. Members whose email domain matches can then sign in through it."
1723+
}
1724+
}
1725+
},
1726+
_actions: {
1727+
register_sso_provider: {
1728+
label: "Register SSO Provider",
1729+
params: {
1730+
providerId: {
1731+
label: "Provider ID",
1732+
helpText: "Stable identifier, e.g. \"okta\" or \"acme-entra\"."
1733+
},
1734+
issuer: {
1735+
label: "Issuer URL",
1736+
helpText: "IdP issuer, e.g. https://acme.okta.com. Discovery is fetched from here unless an explicit URL is given below."
1737+
},
1738+
domain: {
1739+
label: "Email Domain",
1740+
helpText: "Users with this email domain are routed to this IdP, e.g. acme.com."
1741+
},
1742+
clientId: {
1743+
label: "Client ID",
1744+
helpText: "OAuth client ID issued by the IdP for this environment."
1745+
},
1746+
clientSecret: {
1747+
label: "Client Secret",
1748+
helpText: "OAuth client secret (stored encrypted by better-auth)."
1749+
},
1750+
discoveryEndpoint: {
1751+
label: "Discovery URL",
1752+
helpText: "Optional. OIDC discovery document URL. Leave blank to derive `<issuer>/.well-known/openid-configuration`."
1753+
},
1754+
scopes: {
1755+
label: "Scopes",
1756+
helpText: "Optional. Space- or comma-separated OAuth scopes. Defaults to \"openid email profile\".",
1757+
placeholder: "openid email profile"
1758+
},
1759+
mapId: {
1760+
label: "Map: User ID claim",
1761+
helpText: "Optional. ID-token claim mapped to the user ID. Defaults to \"sub\".",
1762+
placeholder: "sub"
1763+
},
1764+
mapEmail: {
1765+
label: "Map: Email claim",
1766+
helpText: "Optional. Claim mapped to email. Defaults to \"email\".",
1767+
placeholder: "email"
1768+
},
1769+
mapName: {
1770+
label: "Map: Name claim",
1771+
helpText: "Optional. Claim mapped to display name. Defaults to \"name\".",
1772+
placeholder: "name"
1773+
}
1774+
}
1775+
},
1776+
register_saml_provider: {
1777+
label: "Register SAML Provider",
1778+
params: {
1779+
providerId: {
1780+
label: "Provider ID",
1781+
helpText: "Stable identifier, e.g. \"acme-saml\"."
1782+
},
1783+
issuer: {
1784+
label: "IdP Entity ID",
1785+
helpText: "The IdP’s SAML EntityID (issuer), e.g. https://saml.acme.com/entityid."
1786+
},
1787+
domain: {
1788+
label: "Email Domain",
1789+
helpText: "Users with this email domain are routed to this IdP, e.g. acme.com."
1790+
},
1791+
entryPoint: {
1792+
label: "IdP SSO URL",
1793+
helpText: "The IdP’s SAML single sign-on (redirect) endpoint that receives the SAMLRequest."
1794+
},
1795+
cert: {
1796+
label: "IdP Signing Certificate",
1797+
helpText: "The IdP’s X.509 signing certificate (PEM body). Used to verify assertion signatures."
1798+
},
1799+
identifierFormat: {
1800+
label: "NameID Format",
1801+
helpText: "Optional. Requested SAML NameID format. Defaults to the IdP’s configured format.",
1802+
placeholder: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
1803+
}
1804+
}
1805+
},
1806+
request_domain_verification: {
1807+
label: "Request Domain Verification",
1808+
resultDialog: {
1809+
title: "Verify your domain",
1810+
description: "Add the DNS TXT record below at your domain’s DNS provider, then run “Verify Domain”. The token is shown once.",
1811+
acknowledge: "Done",
1812+
fields: {
1813+
dnsRecordType: "Record type",
1814+
dnsRecordName: "Name / Host",
1815+
dnsRecordValue: "Value"
1816+
}
1817+
}
1818+
},
1819+
verify_domain: {
1820+
label: "Verify Domain",
1821+
successMessage: "Domain ownership verified"
1822+
},
1823+
delete_sso_provider: {
1824+
label: "Delete SSO Provider",
1825+
confirmText: "Delete this SSO provider? Users from its domain will no longer be able to sign in through it.",
1826+
successMessage: "SSO provider deleted"
1827+
}
1828+
}
1829+
},
1830+
sys_scim_provider: {
1831+
label: "SCIM Provider",
1832+
pluralLabel: "SCIM Providers",
1833+
description: "SCIM 2.0 connections (bearer tokens) external IdPs use to provision/deprovision this environment's users",
1834+
fields: {
1835+
id: {
1836+
label: "ID"
1837+
},
1838+
provider_id: {
1839+
label: "Provider ID",
1840+
help: "Stable SCIM provider identifier (e.g. \"okta-scim\")"
1841+
},
1842+
scim_token: {
1843+
label: "SCIM Token (hash)",
1844+
help: "Hashed bearer credential for this SCIM connection — the plaintext is shown once at generate-token. Sensitive; do not expose."
1845+
},
1846+
organization_id: {
1847+
label: "Organization",
1848+
help: "Organization scope of this token (org-scoped tokens restrict provisioning to that org)"
1849+
},
1850+
user_id: {
1851+
label: "Owned By",
1852+
help: "User who generated this token (when provider-ownership is enabled)"
1853+
},
1854+
created_at: {
1855+
label: "Created At"
1856+
},
1857+
updated_at: {
1858+
label: "Updated At"
1859+
}
1860+
},
1861+
_views: {
1862+
all: {
1863+
label: "All"
1864+
}
1865+
}
1866+
},
16701867
sys_notification: {
16711868
label: "Notification",
16721869
pluralLabel: "Notifications",
@@ -2236,6 +2433,92 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
22362433
}
22372434
}
22382435
},
2436+
sys_import_job: {
2437+
label: "Import Job",
2438+
pluralLabel: "Import Jobs",
2439+
description: "Asynchronous bulk-import job state, progress, and history",
2440+
fields: {
2441+
id: {
2442+
label: "Job ID"
2443+
},
2444+
object_name: {
2445+
label: "Object",
2446+
help: "API name of the object being imported into"
2447+
},
2448+
status: {
2449+
label: "Status",
2450+
options: {
2451+
pending: "pending",
2452+
running: "running",
2453+
succeeded: "succeeded",
2454+
failed: "failed",
2455+
cancelled: "cancelled"
2456+
}
2457+
},
2458+
total_rows: {
2459+
label: "Total Rows"
2460+
},
2461+
processed_rows: {
2462+
label: "Processed Rows"
2463+
},
2464+
created_count: {
2465+
label: "Created"
2466+
},
2467+
updated_count: {
2468+
label: "Updated"
2469+
},
2470+
skipped_count: {
2471+
label: "Skipped"
2472+
},
2473+
error_count: {
2474+
label: "Errors"
2475+
},
2476+
write_mode: {
2477+
label: "Write Mode",
2478+
options: {
2479+
insert: "insert",
2480+
update: "update",
2481+
upsert: "upsert"
2482+
}
2483+
},
2484+
dry_run: {
2485+
label: "Dry Run"
2486+
},
2487+
run_automations: {
2488+
label: "Run Automations"
2489+
},
2490+
treat_as_historical: {
2491+
label: "Treat As Historical"
2492+
},
2493+
error: {
2494+
label: "Fatal Error"
2495+
},
2496+
results: {
2497+
label: "Row Results (sample)",
2498+
help: "Capped sample of per-row results (failures first) for the UI"
2499+
},
2500+
undo_log: {
2501+
label: "Undo Log",
2502+
help: "Reversal instructions ({created:[ids], updated:[{id,before}]}) captured for small non-dry-run jobs so the import can be undone"
2503+
},
2504+
reverted_at: {
2505+
label: "Reverted At",
2506+
help: "Set when the import was undone (created records deleted, updated records restored)"
2507+
},
2508+
started_at: {
2509+
label: "Started At"
2510+
},
2511+
completed_at: {
2512+
label: "Completed At"
2513+
},
2514+
created_by: {
2515+
label: "Created By"
2516+
},
2517+
created_at: {
2518+
label: "Created At"
2519+
}
2520+
}
2521+
},
22392522
sys_metadata: {
22402523
label: "System Metadata",
22412524
pluralLabel: "System Metadata",

0 commit comments

Comments
 (0)