feat(studio): Doctor + Modules localized + sparse-store federation nudge (v0.25.0)

Two threads.

Translation expansion: every visible string on the Doctor
page (section headers, status text, button labels, daemon-
control card, daemon-files panel, update banner) and the
Modules page (recent-activity strip, capabilities label,
uninstall dialog + toast) flips between DE and EN with the
sidebar toggle. Adds ~50 ARB keys split between
`app_en.arb` / `app_de.arb`. Pluralised + parametrised
strings (`{n} events verified`, `Running on {name}: {endpoint}`)
use the standard ICU placeholder syntax so future locales
slot in without code changes.

Sparse-store federation nudge: the Store page renders an
inline banner above the grid when the operator has zero
federated entries — single biggest store-fullness lever is
configuring an MCP server / n8n endpoint, so the banner
says exactly that and the button opens Settings straight
to the editor. Banner dismisses automatically the next
render after a federated entry appears.

Coverage stand for translation: Doctor + Modules + sidebar
+ page titles + nav + Cmd+K labels are bilingual. Settings
dialog, Approvals cards, Audit drilldown, MCP/n8n editors,
Store search hint + filter chips remain English-literal.
The infrastructure (ARB plumbing, generator hookup,
locale-notifier) means each follow-up surface is a
one-line ARB edit + one call-site swap.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-05-08 01:39:42 +02:00
parent ee83eb851a
commit 349619d68e
11 changed files with 1091 additions and 80 deletions

View file

@ -62,6 +62,9 @@
"@storeNResults": {
"placeholders": { "n": { "type": "int" } }
},
"storeFederationNudgeTitle": "Want more capabilities?",
"storeFederationNudgeBody": "Configure an MCP server or n8n endpoint in Settings → MCP Clients / N8N Endpoints. Discovered tools and workflows surface here as `mcp.<server>.<tool>` and `n8n.<endpoint>.<workflow>` capabilities — every Anthropic / community MCP server adds a handful at once.",
"storeFederationNudgeButton": "Open Settings",
"auditTitle": "Audit",
"auditNoEvents": "No events yet",
@ -69,7 +72,32 @@
"modulesTitle": "Modules",
"modulesNoneTitle": "No modules yet",
"modulesNoneHint": "Run `fai install <capability-name>` or check ~/.fai/modules/.",
"modulesReloadTooltip": "Reload",
"modulesRecentActivity": "RECENT ACTIVITY",
"modulesRecentActivityHint": "last {n} install/uninstall events from the audit log",
"@modulesRecentActivityHint": { "placeholders": { "n": { "type": "int" } } },
"modulesActivityInstalled": "installed",
"modulesActivityUninstalled": "uninstalled",
"modulesCapabilitiesLabel": "Capabilities",
"modulesUninstallTitle": "Uninstall module?",
"modulesUninstallBody": "Removes {name} v{version} from this hub. Flows that reference it will fail at the next run. A `module.uninstalled` audit event is recorded.",
"@modulesUninstallBody": {
"placeholders": {
"name": { "type": "String" },
"version": { "type": "String" }
}
},
"modulesUninstalledToast": "Uninstalled {name} v{version}.",
"@modulesUninstalledToast": {
"placeholders": {
"name": { "type": "String" },
"version": { "type": "String" }
}
},
"modulesUninstallFailed": "Uninstall failed: {error}",
"@modulesUninstallFailed": { "placeholders": { "error": { "type": "String" } } },
"modulesUninstalling": "Uninstalling…",
"approvalsTitle": "Approvals",
"approvalsTabPending": "Pending",
@ -78,6 +106,73 @@
"approvalsInboxHint": "All caught up. New `system.approval@^0` steps land here automatically.",
"doctorTitle": "Doctor",
"doctorRecheckTooltip": "Re-check",
"doctorEventLogSection": "Event log",
"doctorModulesApprovalsSection": "Modules & approvals",
"doctorHostServicesSection": "Host services",
"doctorDaemonFilesSection": "Daemon files",
"doctorDaemonControlSection": "Daemon control",
"doctorChainIntact": "Hash chain intact",
"doctorChainIntactDetail": "{n} events · prev_event_sha256 verified end-to-end",
"@doctorChainIntactDetail": { "placeholders": { "n": { "type": "int" } } },
"doctorChainTampered": "Tampering detected",
"doctorChainTamperedDetail": "{verified} of {total} verified before mismatch at {id}",
"@doctorChainTamperedDetail": {
"placeholders": {
"verified": { "type": "int" },
"total": { "type": "int" },
"id": { "type": "String" }
}
},
"doctorChainPillOk": "WORM-1",
"doctorChainPillTamper": "TAMPER",
"doctorVerifyNow": "Verify now",
"doctorRestart": "Restart",
"doctorStart": "Start",
"doctorStop": "Stop",
"doctorStatusAction": "Status",
"doctorRunningOn": "Running on {name}: {endpoint}",
"@doctorRunningOn": {
"placeholders": {
"name": { "type": "String" },
"endpoint": { "type": "String" }
}
},
"doctorStoppedOn": "{name} daemon stopped",
"@doctorStoppedOn": { "placeholders": { "name": { "type": "String" } } },
"doctorStatusUnknown": "Daemon status: …",
"doctorPillRunning": "running",
"doctorPillStopped": "stopped",
"doctorDaemonControlHint": "Studio shells out to the platform binary — mirrors `fai daemon …` exactly so the CLI and UI stay in lockstep.",
"doctorDaemonControlWorking": "Working…",
"doctorPathLog": "Log",
"doctorPathConfig": "Config",
"doctorPathDb": "Audit DB",
"doctorPathModules": "Modules dir",
"doctorPathFlows": "Flows dir",
"doctorPathPid": "PID file",
"doctorPathsEmpty": "Daemon did not report file paths. Update the running hub via `fai daemon restart`.",
"doctorOpenError": "Could not open: {detail}",
"@doctorOpenError": { "placeholders": { "detail": { "type": "String" } } },
"doctorUpdateAvailable": "Update available — {version}",
"@doctorUpdateAvailable": { "placeholders": { "version": { "type": "String" } } },
"doctorUpdateBody": "Channel {channel} now offers {version}. Apply via the button — Studio shells out to `fai update apply --channel {channel}`.",
"@doctorUpdateBody": {
"placeholders": {
"channel": { "type": "String" },
"version": { "type": "String" }
}
},
"doctorUpdateUnreachable": "Release host unreachable",
"doctorUpdateUnreachableBody": "Could not contact the release feed for {channel}.",
"@doctorUpdateUnreachableBody": { "placeholders": { "channel": { "type": "String" } } },
"doctorReleaseNotes": "notes: {url}",
"@doctorReleaseNotes": { "placeholders": { "url": { "type": "String" } } },
"doctorPillNew": "new",
"doctorPillOffline": "offline",
"doctorApplyUpdate": "Apply update",
"doctorApplying": "Applying…",
"doctorApplyDone": "Update applied. Restart Studio to see the new daemon version.",
"mcpHeader": "MCP CLIENTS",
"mcpHint": "External MCP servers federate their tools as `mcp.<server>.<tool>` capabilities. Configure once, see them in the Store.",