fix(l10n): consistent formal address across the German catalog
Some checks are pending
Security / Security check (push) Waiting to run
Some checks are pending
Security / Security check (push) Waiting to run
Store search, security panel, System-AI fix hint and the approvals payload hint addressed the operator informally while the rest of the app uses Sie — the panel flagged the break on every second screen. All DE strings now use the formal address, and a guard test rejects any future informal form in app_de.arb. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
3e1ce6e1fd
commit
ca01fd2ec0
3 changed files with 51 additions and 8 deletions
|
|
@ -285,7 +285,7 @@
|
||||||
"channelsEnableAutostart": "Autostart bei Anmeldung aktivieren",
|
"channelsEnableAutostart": "Autostart bei Anmeldung aktivieren",
|
||||||
"channelsDisableAutostart": "Autostart deaktivieren",
|
"channelsDisableAutostart": "Autostart deaktivieren",
|
||||||
"storeSearchHint": "Module suchen — Name, Tagline, Tag, Fähigkeit…",
|
"storeSearchHint": "Module suchen — Name, Tagline, Tag, Fähigkeit…",
|
||||||
"storeAskHint": "Was brauchst du? Versuch's mit „ein Modul, das tabellarische Daten liest\" oder tippe einfach einen Begriff.",
|
"storeAskHint": "Was brauchen Sie? Zum Beispiel „ein Modul, das tabellarische Daten liest\" — oder geben Sie einfach einen Begriff ein.",
|
||||||
"storeAskSubmit": "Suchen",
|
"storeAskSubmit": "Suchen",
|
||||||
"storeAskAi": "KI fragen",
|
"storeAskAi": "KI fragen",
|
||||||
"storeAskClear": "Frage zurücksetzen",
|
"storeAskClear": "Frage zurücksetzen",
|
||||||
|
|
@ -1074,7 +1074,7 @@
|
||||||
"approvalsPillRejected": "abgelehnt",
|
"approvalsPillRejected": "abgelehnt",
|
||||||
"approvalsPillExpired": "abgelaufen",
|
"approvalsPillExpired": "abgelaufen",
|
||||||
"approvalsPayloadPreview": "ZU PRÜFENDE DATEN",
|
"approvalsPayloadPreview": "ZU PRÜFENDE DATEN",
|
||||||
"approvalsNoPayload": "Keine Daten zum Prüfen angehängt. Der system.approval-Schritt des Flows bestimmt über sein \"show:\"-Feld, was angezeigt wird — setze es, um die Daten hinter dieser Entscheidung sichtbar zu machen.",
|
"approvalsNoPayload": "Keine Daten zum Prüfen angehängt. Der system.approval-Schritt des Flows bestimmt über sein \"show:\"-Feld, was angezeigt wird — setzen Sie es, um die Daten hinter dieser Entscheidung sichtbar zu machen.",
|
||||||
"approvalsNoDataConfirmTitle": "Ohne Prüfdaten freigeben?",
|
"approvalsNoDataConfirmTitle": "Ohne Prüfdaten freigeben?",
|
||||||
"approvalsNoDataConfirmBody": "Dieser Flow hat bewusst keine Prüfdaten hinterlegt (kein \"show:\" am Freigabe-Schritt). Sie können trotzdem freigeben — entscheiden dann aber, ohne die Daten hinter dieser Entscheidung gesehen zu haben.",
|
"approvalsNoDataConfirmBody": "Dieser Flow hat bewusst keine Prüfdaten hinterlegt (kein \"show:\" am Freigabe-Schritt). Sie können trotzdem freigeben — entscheiden dann aber, ohne die Daten hinter dieser Entscheidung gesehen zu haben.",
|
||||||
"approvalsNoDataConfirmAction": "Trotzdem freigeben",
|
"approvalsNoDataConfirmAction": "Trotzdem freigeben",
|
||||||
|
|
@ -1558,7 +1558,7 @@
|
||||||
"settingsIntegrationsPanelTitle": "Integrationen",
|
"settingsIntegrationsPanelTitle": "Integrationen",
|
||||||
"settingsIntegrationsPanelBody": "Externe Tool-Server, deren Capabilities der Hub föderiert. MCP-Server bieten Tool-Endpunkte; n8n-Endpunkte stellen gehostete Workflows bereit.",
|
"settingsIntegrationsPanelBody": "Externe Tool-Server, deren Capabilities der Hub föderiert. MCP-Server bieten Tool-Endpunkte; n8n-Endpunkte stellen gehostete Workflows bereit.",
|
||||||
"settingsSecurityPanelTitle": "Sicherheit & Zugangsdaten",
|
"settingsSecurityPanelTitle": "Sicherheit & Zugangsdaten",
|
||||||
"settingsSecurityPanelBody": "Tokens, die der Hub für dich verwahrt. Liegen in ~/.chain/ mit Modus 0600; werden nie als Telemetrie versendet.",
|
"settingsSecurityPanelBody": "Tokens, die der Hub für Sie verwahrt. Liegen in ~/.chain/ mit Modus 0600; werden nie als Telemetrie versendet.",
|
||||||
"settingsMaintenancePanelTitle": "Wartung",
|
"settingsMaintenancePanelTitle": "Wartung",
|
||||||
"settingsMaintenancePanelBody": "Destruktive Aktionen, abgesichert durch explizite Bestätigungs-Dialoge. Nutzen z.B. beim Re-Pilot-Testen oder Debuggen.",
|
"settingsMaintenancePanelBody": "Destruktive Aktionen, abgesichert durch explizite Bestätigungs-Dialoge. Nutzen z.B. beim Re-Pilot-Testen oder Debuggen.",
|
||||||
"tooltipMoveUp": "Nach oben",
|
"tooltipMoveUp": "Nach oben",
|
||||||
|
|
@ -1640,7 +1640,7 @@
|
||||||
},
|
},
|
||||||
"sysAiFixDisabled": "Richte die System-KI in den Einstellungen (Cmd+,) → Bereich System-KI ein.",
|
"sysAiFixDisabled": "Richte die System-KI in den Einstellungen (Cmd+,) → Bereich System-KI ein.",
|
||||||
"sysAiFixEnvMissing": "Die Umgebungsvariable für den API-Schlüssel ist leer. Setzen Sie sie in Ihrer Shell und starten Sie den Daemon neu.",
|
"sysAiFixEnvMissing": "Die Umgebungsvariable für den API-Schlüssel ist leer. Setzen Sie sie in Ihrer Shell und starten Sie den Daemon neu.",
|
||||||
"sysAiFixNetwork": "Der Hub erreicht den konfigurierten Endpunkt nicht. Läuft dein Anbieter?",
|
"sysAiFixNetwork": "Der Hub erreicht den konfigurierten Endpunkt nicht. Läuft Ihr Anbieter?",
|
||||||
"sysAiFixHttp": "Der Anbieter hat einen Status außerhalb 2xx geliefert. Prüfen Sie Endpunkt, Modellnamen und API-Schlüssel.",
|
"sysAiFixHttp": "Der Anbieter hat einen Status außerhalb 2xx geliefert. Prüfen Sie Endpunkt, Modellnamen und API-Schlüssel.",
|
||||||
"sysAiFixEmptyResponse": "Der Anbieter hat keinen Inhalt geliefert. Probiere ein anderes Modell oder formuliere den Prompt um.",
|
"sysAiFixEmptyResponse": "Der Anbieter hat keinen Inhalt geliefert. Probiere ein anderes Modell oder formuliere den Prompt um.",
|
||||||
"channelSwitchOk": "Aktiver Kanal auf \"{name}\" umgeschaltet. Daemon neu gestartet.",
|
"channelSwitchOk": "Aktiver Kanal auf \"{name}\" umgeschaltet. Daemon neu gestartet.",
|
||||||
|
|
|
||||||
|
|
@ -714,7 +714,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get storeAskHint =>
|
String get storeAskHint =>
|
||||||
'Was brauchst du? Versuch\'s mit „ein Modul, das tabellarische Daten liest\" oder tippe einfach einen Begriff.';
|
'Was brauchen Sie? Zum Beispiel „ein Modul, das tabellarische Daten liest\" — oder geben Sie einfach einen Begriff ein.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get storeAskSubmit => 'Suchen';
|
String get storeAskSubmit => 'Suchen';
|
||||||
|
|
@ -1963,7 +1963,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get approvalsNoPayload =>
|
String get approvalsNoPayload =>
|
||||||
'Keine Daten zum Prüfen angehängt. Der system.approval-Schritt des Flows bestimmt über sein \"show:\"-Feld, was angezeigt wird — setze es, um die Daten hinter dieser Entscheidung sichtbar zu machen.';
|
'Keine Daten zum Prüfen angehängt. Der system.approval-Schritt des Flows bestimmt über sein \"show:\"-Feld, was angezeigt wird — setzen Sie es, um die Daten hinter dieser Entscheidung sichtbar zu machen.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get approvalsNoDataConfirmTitle => 'Ohne Prüfdaten freigeben?';
|
String get approvalsNoDataConfirmTitle => 'Ohne Prüfdaten freigeben?';
|
||||||
|
|
@ -2697,7 +2697,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsSecurityPanelBody =>
|
String get settingsSecurityPanelBody =>
|
||||||
'Tokens, die der Hub für dich verwahrt. Liegen in ~/.chain/ mit Modus 0600; werden nie als Telemetrie versendet.';
|
'Tokens, die der Hub für Sie verwahrt. Liegen in ~/.chain/ mit Modus 0600; werden nie als Telemetrie versendet.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsMaintenancePanelTitle => 'Wartung';
|
String get settingsMaintenancePanelTitle => 'Wartung';
|
||||||
|
|
@ -2851,7 +2851,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sysAiFixNetwork =>
|
String get sysAiFixNetwork =>
|
||||||
'Der Hub erreicht den konfigurierten Endpunkt nicht. Läuft dein Anbieter?';
|
'Der Hub erreicht den konfigurierten Endpunkt nicht. Läuft Ihr Anbieter?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sysAiFixHttp =>
|
String get sysAiFixHttp =>
|
||||||
|
|
|
||||||
43
test/l10n_formality_test.dart
Normal file
43
test/l10n_formality_test.dart
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
// German strings address the operator as "Sie" — never "Du".
|
||||||
|
// The usertest panel flagged Du/Sie breaks (store search, flow
|
||||||
|
// empty states) as a recurring trust issue for the regulated
|
||||||
|
// audience, so this guard scans the DE catalog for unambiguous
|
||||||
|
// second-person-singular forms. Progress messages in the first
|
||||||
|
// person ("Starte …" = the app announcing its own action) stay
|
||||||
|
// legal; only forms that address the reader are rejected.
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('DE catalog contains no informal (Du) address', () {
|
||||||
|
final raw = File('lib/l10n/app_de.arb').readAsStringSync();
|
||||||
|
final map = jsonDecode(raw) as Map<String, dynamic>;
|
||||||
|
// Pronouns + possessives + common 2nd-person-singular verb
|
||||||
|
// forms. Word-bounded so "individuell" or "Bedingungen"
|
||||||
|
// never match.
|
||||||
|
final informal = RegExp(
|
||||||
|
r"\b(du|dich|dir|dein|deine|deiner|deinem|deinen|deins|"
|
||||||
|
r"brauchst|tippst|willst|kannst|hast|bist|machst|siehst|"
|
||||||
|
r"klickst|wählst|öffnest|startest|versuchst|versuch's)\b",
|
||||||
|
caseSensitive: false,
|
||||||
|
);
|
||||||
|
final offenders = <String>[];
|
||||||
|
map.forEach((key, value) {
|
||||||
|
if (key.startsWith('@') || value is! String) return;
|
||||||
|
final m = informal.firstMatch(value);
|
||||||
|
if (m != null) {
|
||||||
|
offenders.add('$key: "${m.group(0)}" in "$value"');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
offenders,
|
||||||
|
isEmpty,
|
||||||
|
reason:
|
||||||
|
'German UI strings must use the formal Sie address:\n'
|
||||||
|
'${offenders.join('\n')}',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue