feat(studio): app-wide i18n via flutter_localizations (v0.24.0)

Replaces the Store-only DE/EN toggle with an app-wide one
parked in the sidebar footer next to the theme button.
Pressing it flips every translated string at once: nav
labels, page titles, common buttons, the bilingual
store-index content.

Implementation:
- Adds `flutter_localizations` + `intl` to pubspec, plus
  `flutter.generate: true` so `flutter gen-l10n` runs in the
  build pipeline.
- ARB sources at `lib/l10n/app_en.arb` and `app_de.arb`. The
  EN file is the template; DE carries the German strings.
  Initial coverage: navigation, common buttons, page titles,
  channels / store / audit / modules / approvals headers,
  hub-unreachable copy, MCP + n8n panel headers + hints.
  Rest of the UI strings are still English-literal — those
  fall in incrementally as we touch each surface.
- Generated `AppLocalizations` lives at
  `lib/l10n/app_localizations*.dart` (regenerated via
  `flutter gen-l10n` on every ARB edit).
- `StudioAppState` gains `localeNotifier` alongside
  `modeNotifier`; persisted via SharedPreferences key
  `locale.code`.
- Sidebar `_LanguageToggle` reads/writes through the
  notifier. The Store's per-page locale state is gone:
  `_locale` now reads `Localizations.localeOf(context)
  .languageCode`, so the bilingual store-index content
  follows the global setting without a second toggle.
- `_NavPage.label` becomes `_NavPage.id` + `labelOf(context)`;
  Cmd+K palette and Sidebar both read the localized label.

Out of scope this iteration: localizing the remaining
~80% of UI strings (Settings dialog labels, Store search
hint, error messages). Those land incrementally — the i18n
infrastructure now means each 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:19:32 +02:00
parent d2a6ed32e2
commit ee83eb851a
16 changed files with 1440 additions and 67 deletions

93
lib/l10n/app_de.arb Normal file
View file

@ -0,0 +1,93 @@
{
"@@locale": "de",
"appTitle": "F∆I Studio",
"navDoctor": "Diagnose",
"navModules": "Module",
"navStore": "Store",
"navFlows": "Flows",
"navAudit": "Protokoll",
"navApprovals": "Freigaben",
"connectionConnected": "verbunden",
"connectionUnreachable": "nicht erreichbar",
"connectionConnecting": "verbinde…",
"connectionStartHub": "Hub starten",
"buttonCancel": "Abbrechen",
"buttonSave": "Speichern",
"buttonClose": "Schließen",
"buttonRetry": "Erneut versuchen",
"buttonRefresh": "Aktualisieren",
"buttonInstall": "Installieren",
"buttonUninstall": "Deinstallieren",
"buttonDetails": "Details",
"buttonAdd": "Hinzufügen",
"buttonRemove": "Entfernen",
"buttonOpen": "Öffnen",
"buttonExplain": "Erklären",
"buttonReadDocs": "Doku öffnen",
"settingsTitle": "Hub-Endpunkt",
"settingsHost": "Host",
"settingsPort": "Port",
"settingsTls": "TLS",
"settingsSaveAndConnect": "Speichern & verbinden",
"settingsLanguage": "Sprache",
"settingsLanguageHint": "Sprache der App. Wirkt auf alle Seiten; mehrsprachige Store-Einträge folgen ebenfalls.",
"channelsHeader": "KANÄLE",
"channelsActive": "aktiv",
"channelsRunning": "läuft",
"channelsStopped": "gestoppt",
"channelsConnect": "Studio mit diesem Kanal verbinden",
"channelsSwitch": "Diesen Kanal aktivieren",
"channelsEnableAutostart": "Autostart bei Anmeldung aktivieren",
"channelsDisableAutostart": "Autostart deaktivieren",
"storeSearchHint": "Module suchen — Name, Tagline, Tag, Capability…",
"storeFeatured": "EMPFOHLEN",
"storeFeaturedHint": "kuratierte Auswahl · klicken für Details",
"storeNoMatches": "Keine Treffer",
"storeNoMatchesHint": "Filter anpassen oder Suche leeren, um alle Einträge zu sehen.",
"storeClearFilters": "Filter zurücksetzen",
"storeStatusAll": "Alle",
"storeStatusPublished": "Veröffentlicht",
"storeStatusAlpha": "Alpha",
"storeStatusPlanned": "Geplant",
"storeInstalledOnly": "Installiert",
"storeNResults": "{n} Treffer",
"@storeNResults": {
"placeholders": { "n": { "type": "int" } }
},
"auditTitle": "Protokoll",
"auditNoEvents": "Noch keine Ereignisse",
"auditNoEventsHint": "Starte einen Flow, um den Audit-Stream zu füllen.\nEreignisse erscheinen innerhalb von Sekunden.",
"modulesTitle": "Module",
"modulesNoneTitle": "Noch keine Module",
"modulesRecentActivity": "LETZTE AKTIVITÄT",
"approvalsTitle": "Freigaben",
"approvalsTabPending": "Offen",
"approvalsTabHistory": "Verlauf",
"approvalsInboxZero": "Posteingang leer",
"approvalsInboxHint": "Alles erledigt. Neue `system.approval@^0`-Schritte landen automatisch hier.",
"doctorTitle": "Diagnose",
"mcpHeader": "MCP-CLIENTS",
"mcpHint": "Externe MCP-Server bringen ihre Tools als `mcp.<server>.<tool>` Capabilities ein. Einmal konfigurieren, im Store sichtbar.",
"mcpEmpty": "Keine MCP-Server konfiguriert. + klicken zum Hinzufügen.",
"n8nHeader": "N8N-ENDPUNKTE",
"n8nHint": "Aktive n8n-Workflows erscheinen als `n8n.<endpoint>.<slug>` Capabilities. Einmal konfigurieren, im Store sichtbar.",
"n8nEmpty": "Keine n8n-Endpunkte konfiguriert. + klicken zum Hinzufügen.",
"hubUnreachable": "Hub nicht erreichbar",
"hubUnreachableHint": "Hub starten mit `fai serve`.",
"languageEnglish": "English",
"languageGerman": "Deutsch"
}