i18n: Englisch als Hauptsprache, Sprachpakete fuer Desktop und WebUI (#661)

Umbau durch Gemini Flash, Durchsicht und Nacharbeit durch Claude.

Umfang:
- Desktop: LocalizationManager (JSON-Pakete, Rueckfall auf Englisch,
  Umschalten zur Laufzeit), Localize-Markup-Erweiterung, Sprachwahl in den
  Einstellungen (AppSettings.Language, Vorgabe "en"); 317 Schluessel je Sprache
- WebUI: alle Ansichten auf t()/data-i18n, 656 Schluessel je Sprache
- Agenten-Werkzeuge: Beschreibungen, Schemata und Meldungen auf Englisch
- Tests an die englischen Meldungen angepasst, keine Pruefung abgeschwaecht

Nacharbeit bei der Durchsicht:
- ~25 Kommentarbloecke mit Begruendungen waren ersatzlos entfallen, darunter
  sicherheitsrelevante (NTFS-Datenstrom beim shared:-Praefix, Bremse gegen
  Agentenschleifen, doppelter POST beim Anlegen einer Instanz). Wieder
  eingesetzt: auf Englisch, wo die Datei umgestellt ist, sonst im Wortlaut.
- Drei WebUI-Schluessel fehlten (logs.paused, instances.instanceOfflineError,
  instances.noInstanceActiveError). Weil t() bei fehlendem Schluessel den
  Schluessel selbst liefert, griff der "|| err.message"-Rueckfall nie — die
  Oberflaeche haette den Schluesselnamen angezeigt.

Geprueft: Schluesselgleichheit en/de, Platzhalter je Schluessel, jeder im
Code benutzte Schluessel vorhanden, node --check fuer alle Module,
1.220 Tests gruen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-24 09:43:51 +02:00
co-authored by Claude Opus 5.5
parent 69718122f3
commit ad71682c2d
97 changed files with 4103 additions and 1928 deletions
@@ -23,25 +23,25 @@
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=FTP}">
<StackPanel>
<TextBlock Classes="label" Text="Host" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.host]}" />
<TextBox Text="{Binding FtpHost}" PlaceholderText="ftp.example.com" />
</StackPanel>
<Grid ColumnDefinitions="*,12,*">
<StackPanel Grid.Column="0">
<TextBlock Classes="label" Text="Port" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.port]}" />
<TextBox Text="{Binding FtpPort}" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Classes="label" Text="Benutzername" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.username]}" />
<TextBox Text="{Binding FtpUsername}" />
</StackPanel>
</Grid>
<StackPanel>
<TextBlock Classes="label" Text="Passwort" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.password]}" />
<TextBox Text="{Binding FtpPassword}" PasswordChar="•" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="Root-Pfad" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.rootPath]}" />
<TextBox Text="{Binding FtpRootPath}" />
</StackPanel>
</StackPanel>
@@ -51,36 +51,36 @@
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=Mail}">
<Grid ColumnDefinitions="*,12,*">
<StackPanel Grid.Column="0">
<TextBlock Classes="label" Text="Benutzername" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.username]}" />
<TextBox Text="{Binding MailUsername}" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Classes="label" Text="Passwort" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.password]}" />
<TextBox Text="{Binding MailPassword}" PasswordChar="•" />
</StackPanel>
</Grid>
<Grid ColumnDefinitions="*,12,*">
<StackPanel Grid.Column="0">
<TextBlock Classes="label" Text="IMAP-Host" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.imapHost]}" />
<TextBox Text="{Binding MailImapHost}" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Classes="label" Text="IMAP-Port" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.imapPort]}" />
<TextBox Text="{Binding MailImapPort}" />
</StackPanel>
</Grid>
<Grid ColumnDefinitions="*,12,*">
<StackPanel Grid.Column="0">
<TextBlock Classes="label" Text="SMTP-Host" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.smtpHost]}" />
<TextBox Text="{Binding MailSmtpHost}" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Classes="label" Text="SMTP-Port" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.smtpPort]}" />
<TextBox Text="{Binding MailSmtpPort}" />
</StackPanel>
</Grid>
<StackPanel>
<TextBlock Classes="label" Text="Erlaubte Empfänger" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.allowedRecipients]}" />
<TextBox Text="{Binding MailAllowedRecipients}"
PlaceholderText="adresse1@domain.de, adresse2@domain.de" />
</StackPanel>
@@ -90,21 +90,21 @@
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=Database}">
<StackPanel>
<TextBlock Classes="label" Text="Typ" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.type]}" />
<ComboBox ItemsSource="{Binding DbTypes}" SelectedItem="{Binding DbType}"
HorizontalAlignment="Stretch" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="Connection-String" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.connectionString]}" />
<TextBox Text="{Binding DbConnectionString}" PasswordChar="•" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="Zugriffsebene" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.accessLevel]}" />
<ComboBox ItemsSource="{Binding DbAccessLevels}" SelectedItem="{Binding DbAccessLevel}"
HorizontalAlignment="Stretch" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="Erlaubte Tabellen" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.allowedTables]}" />
<TextBox Text="{Binding DbAllowedTables}" PlaceholderText="tabelle1, tabelle2" />
</StackPanel>
</StackPanel>
@@ -113,15 +113,15 @@
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=Telegram}">
<StackPanel>
<TextBlock Classes="label" Text="Bot-Token" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.botToken]}" />
<TextBox Text="{Binding TelegramBotToken}" PasswordChar="•" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="Standard Chat-ID" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.defaultChatId]}" />
<TextBox Text="{Binding TelegramDefaultChatId}" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="Erlaubte Chat-IDs" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.allowedChatIds]}" />
<TextBox Text="{Binding TelegramAllowedChatIds}" PlaceholderText="id1, id2" />
</StackPanel>
</StackPanel>
@@ -131,20 +131,20 @@
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=DirectAPI}">
<Grid ColumnDefinitions="*,12,*">
<StackPanel Grid.Column="0">
<TextBlock Classes="label" Text="Standard-Provider" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.defaultProvider]}" />
<TextBox Text="{Binding DirectApiDefaultProvider}" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Classes="label" Text="Cache TTL (Sekunden)" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.cacheTtl]}" />
<TextBox Text="{Binding DirectApiCacheTtlSeconds}" />
</StackPanel>
</Grid>
<StackPanel>
<TextBlock Classes="label" Text="Twelve Data Key" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.twelveDataKey]}" />
<TextBox Text="{Binding DirectApiTwelveDataKey}" PasswordChar="•" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="Alpha Vantage Key" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.alphaVantageKey]}" />
<TextBox Text="{Binding DirectApiAlphaVantageKey}" PasswordChar="•" />
</StackPanel>
</StackPanel>
@@ -153,17 +153,17 @@
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=WebFetch}">
<StackPanel>
<TextBlock Classes="label" Text="Erlaubte Domains" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.allowedDomains]}" />
<TextBox Text="{Binding WebFetchAllowedDomains}"
PlaceholderText="reuters.com, coindesk.com" />
</StackPanel>
<Grid ColumnDefinitions="*,12,*">
<StackPanel Grid.Column="0">
<TextBlock Classes="label" Text="Max. Antwortgröße (KB)" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.maxResponseKb]}" />
<TextBox Text="{Binding WebFetchMaxResponseKb}" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Classes="label" Text="User Agent" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.userAgent]}" />
<TextBox Text="{Binding WebFetchUserAgent}" />
</StackPanel>
</Grid>
@@ -173,34 +173,34 @@
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=SocialMediaManager}">
<StackPanel>
<TextBlock Classes="label" Text="X (Twitter) API-Key" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.xApiKey]}" />
<TextBox Text="{Binding SmmXApiKey}" PasswordChar="•" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="X Accounts" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.xAccounts]}" />
<TextBox Text="{Binding SmmXWatchAccounts}" PlaceholderText="account1, account2" />
</StackPanel>
<Grid ColumnDefinitions="*,12,*">
<StackPanel Grid.Column="0">
<TextBlock Classes="label" Text="Subreddits" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.subreddits]}" />
<TextBox Text="{Binding SmmRedditWatchSubreddits}" PlaceholderText="stocks, investing" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Classes="label" Text="Reddit Post Limit" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.redditPostLimit]}" />
<TextBox Text="{Binding SmmRedditPostLimit}" />
</StackPanel>
</Grid>
<StackPanel>
<TextBlock Classes="label" Text="OpenRouter Key" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.openRouterKey]}" />
<TextBox Text="{Binding SmmOpenRouterApiKey}" PasswordChar="•" />
</StackPanel>
<Grid ColumnDefinitions="*,12,*">
<StackPanel Grid.Column="0">
<TextBlock Classes="label" Text="STT-Modell" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.sttModel]}" />
<TextBox Text="{Binding SmmSttModel}" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Classes="label" Text="YouTube-Kanäle" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.youtubeChannels]}" />
<TextBox Text="{Binding SmmYoutubeChannels}" PlaceholderText="URL1, URL2" />
</StackPanel>
</Grid>
@@ -210,28 +210,28 @@
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=FileRW}">
<StackPanel>
<TextBlock Classes="label" Text="Erlaubte Dateiendungen (persönlich)" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.fileRwPersonalExt]}" />
<TextBox Text="{Binding FileRwPersonalAllowedExtensions}" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="Zugriffsebene (geteilt)" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.fileRwSharedAccess]}" />
<ComboBox ItemsSource="{Binding FileRwAccessLevels}"
SelectedItem="{Binding FileRwSharedAccessLevel}"
HorizontalAlignment="Stretch" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="Erlaubte Dateiendungen (geteilt)" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.fileRwSharedExt]}" />
<TextBox Text="{Binding FileRwSharedAllowedExtensions}" />
</StackPanel>
<StackPanel>
<TextBlock Classes="label" Text="Geschützte Pfade" />
<TextBlock Classes="label" Text="{Binding L[desktop.toolSettings.fileRwProtectedPaths]}" />
<TextBox Text="{Binding FileRwProtectedPaths}" />
</StackPanel>
</StackPanel>
<!-- Werkzeuge ohne weitere Einstellungen -->
<TextBlock Classes="muted" FontStyle="Italic" TextWrapping="Wrap"
Text="Dieses Werkzeug benötigt keine weiteren Einstellungen."
Text="{Binding L[desktop.toolSettings.noSettings]}"
IsVisible="{Binding SettingsObject, Converter={x:Static ObjectConverters.IsNull}}" />
</StackPanel>