17 KiB
Statische Texte eliminieren — vollständige i18n-Migration
Fortschritt
| Phase | Inhalt | Status |
|---|---|---|
| 1 | Feature-Module (mainXxx.py), registry.py, interfaceBootstrap.py |
done |
| 2 | nodeDefinitions/*, portTypes.py, nodeRegistry.py, entryPoints.py |
done |
| 3 | Datamodel-Options (12 Dateien) + SubscriptionPlan title/description |
done |
| 4 | Accounting-Connectors, frontendTypes (keine CUSTOM_TYPE_DESCRIPTIONS mehr) |
done |
| 5 | Store, mandate.ts / Admin-Labels, Trustee-Views, UnifiedDataBar, featuresApi Mock |
done |
Umsetzung 1–2: Skript local/scripts/_flatten_i18n_dicts.py (mehrsprachige Dicts → deutscher String / json.dumps). Anschliessend manuell: mainSystem.py AICore-providerLabels-Fallback; portTypes.PortField.description auf str; _deriveFormPayloadSchema / _deriveTransformSchema; entryPoints._normalize_title liefert str. Role.description bleibt TextMultilingual: coerce_text_multilingual() in datamodelUtils.py wandelt Template-Strings/Dicts; Bootstrap- und Feature-Role(...)-Aufrufe nutzen coerce_text_multilingual(...).
Umsetzung 3: Flatten-Skript (Phase 3) für alle 12 Datamodel-Dateien. Neues Regex-Pattern en, fr, de für Trustee-Reihenfolge. Manuell: SubscriptionPlan.title und .description von Dict[str, str] auf str geändert; Downstream-Fixes in routeSubscription.py, mainServiceSubscription.py, stripeBootstrap.py (.get("de") → direkter String-Zugriff). _registerDatamodelOptionLabels() in i18nRegistry.py war bereits kompatibel (nutzt _extractDe das str und dict handelt).
Umsetzung 4: PHASE4 im Flatten-Skript; frontendTypes.py: CUSTOM_TYPE_DESCRIPTIONS, getCustomTypeDescription, registerCustomType entfernt (ungenutzt). Umsetzung 5: Store.tsx / storeApi / routeStore: Label & Description als deutsche i18n-Keys + t(); FEATURE_REGISTRY und MandateFeature.label als string; Hilfsfunktion labelAsI18nKey(); Admin-Seiten mit t(labelAsI18nKey(...)); routeAdminFeatures.get_my_feature_instances: _featureLabelPlain() liefert String-Labels statt Dict-Fallback.
Problemstellung
Trotz Phase 1–7 der i18n-Unification existieren noch ~640+ statische mehrsprachige Dicts ({"en":…, "de":…, "fr":…}) im Gateway und ~60+ Stellen im Frontend. Diese Dicts:
- Müssen bei jeder neuen Sprache manuell erweitert werden
- Umgehen das zentrale i18n-System (
t(), Admin-UI, AI-Übersetzung) - Sind inkonsistent (manche nur en/de, manche nur en/fr, manche en/de/fr)
Ziel: Jeder UI-sichtbare Text wird als deutscher Klartext-Key gespeichert und zur Laufzeit über t() übersetzt. Mehrsprachige Dicts verschwinden komplett.
Architektur-Entscheidung
Prinzip: Deutscher Klartext = i18n-Key
# VORHER (statisch, 3 Sprachen hardcoded):
"label": {"en": "Documents", "de": "Dokumente", "fr": "Documents"}
# NACHHER (dynamisch, beliebig viele Sprachen):
"label": "Dokumente"
Beim Boot registriert _registerXxxLabels() den deutschen Text als Key im xx-Basisset. t("Dokumente") liefert zur Laufzeit die Übersetzung in der aktuellen Sprache.
Sonderfälle
| Fall | Lösung |
|---|---|
outputLabels (Listen von Strings pro Sprache) |
Jedes Element einzeln als Key: "Ja", "Nein" |
_ISO_LABELS (Sprachnamen) |
Bleiben statisch — sind ISO-Referenzdaten, keine UI-Texte |
Locale-Code-Maps ("de": "de-DE") |
Bleiben statisch — technische Mappings |
BUILTIN_PLANS (Subscription) |
Werden zu Keys, AI übersetzt |
Datamodel frontend_options |
Labels werden zu Keys, Boot-Registrierung |
Datei-Index (vollständig)
Gateway — Feature-Module (Kategorie A: RBAC/Katalog-Labels)
| # | Datei | Stellen | Muster | Phase |
|---|---|---|---|---|
| A1 | features/trustee/mainTrustee.py |
~62 | DATA_OBJECTS, RESOURCE_OBJECTS, UI_OBJECTS, TEMPLATE_ROLES, Workflow-Gruppen, Rollen-Beschreibungen |
1 |
| A2 | system/mainSystem.py |
~27 | DATA_OBJECTS, LLM-Provider-Labels |
1 |
| A3 | features/commcoach/mainCommcoach.py |
~17 | DATA_OBJECTS, RESOURCE_OBJECTS, TEMPLATE_ROLES |
1 |
| A4 | features/teamsbot/mainTeamsbot.py |
~11 | DATA_OBJECTS, RESOURCE_OBJECTS, TEMPLATE_ROLES |
1 |
| A5 | features/workspace/mainWorkspace.py |
~10 | DATA_OBJECTS, RESOURCE_OBJECTS, TEMPLATE_ROLES |
1 |
| A6 | features/chatbot/mainChatbot.py |
~7 | DATA_OBJECTS, TEMPLATE_ROLES |
1 |
| A7 | features/neutralization/mainNeutralization.py |
~7 | DATA_OBJECTS, RESOURCE_OBJECTS |
1 |
| A8 | features/realEstate/mainRealEstate.py |
~6 | DATA_OBJECTS, RESOURCE_OBJECTS |
1 |
| A9 | features/graphicalEditor/mainGraphicalEditor.py |
~6 | Permissions, Rollen-Beschreibungen | 1 |
| A10 | serviceCenter/registry.py |
~14 | Service-Kategorie-Labels | 1 |
| A11 | interfaces/interfaceBootstrap.py |
~4 | Template-Rollen-Beschreibungen (admin/user/viewer/sysadmin) | 1 |
Summe Kategorie A: ~171 Stellen, 11 Dateien
Gateway — Graph-Editor Node-Definitionen (Kategorie B)
| # | Datei | Stellen | Muster | Phase |
|---|---|---|---|---|
| B1 | graphicalEditor/nodeDefinitions/clickup.py |
~50 | Node-Label, Parameter-Descriptions | 2 |
| B2 | graphicalEditor/nodeDefinitions/input.py |
~31 | Node-Label, Parameter-Descriptions | 2 |
| B3 | graphicalEditor/portTypes.py |
~33 | Port-Feld-Descriptions | 2 |
| B4 | graphicalEditor/nodeDefinitions/sharepoint.py |
~27 | Node-Label, Parameter-Descriptions | 2 |
| B5 | graphicalEditor/nodeDefinitions/email.py |
~27 | Node-Label, Parameter-Descriptions | 2 |
| B6 | graphicalEditor/nodeDefinitions/ai.py |
~23 | Node-Label, Parameter-Descriptions | 2 |
| B7 | graphicalEditor/nodeDefinitions/trustee.py |
~20 | Node-Label, Parameter-Descriptions | 2 |
| B8 | graphicalEditor/nodeDefinitions/flow.py |
~14 | Node-Label, outputLabels (Sonderfall: Liste) |
2 |
| B9 | graphicalEditor/nodeDefinitions/data.py |
~9 | Node-Label, Parameter-Descriptions | 2 |
| B10 | graphicalEditor/nodeDefinitions/triggers.py |
~8 | Node-Label, Parameter-Descriptions | 2 |
| B11 | graphicalEditor/nodeDefinitions/file.py |
~7 | Node-Label, Parameter-Descriptions | 2 |
| B12 | graphicalEditor/nodeRegistry.py |
~10 | Kategorie-Labels | 2 |
| B13 | graphicalEditor/entryPoints.py |
~3 | Entry-Point-Titel | 2 |
Summe Kategorie B: ~262 Stellen, 13 Dateien
Gateway — Datamodel-Options (Kategorie C)
| # | Datei | Stellen | Muster | Phase |
|---|---|---|---|---|
| C1 | datamodels/datamodelRbac.py |
~19 | Scope/Access-Level Option-Labels (en/de/fr) | 3 |
| C2 | datamodels/datamodelChat.py |
~7 | Status/Mode Option-Labels (en/fr, kein de!) | 3 |
| C3 | datamodels/datamodelMessaging.py |
~11 | Channel/Status Option-Labels (en/fr) | 3 |
| C4 | datamodels/datamodelNotification.py |
~8 | Type/Status Option-Labels (en/de) | 3 |
| C5 | datamodels/datamodelUam.py |
~7 | Subscription-Status + Sprach-Options (en/de/fr) | 3 |
| C6 | datamodels/datamodelSubscription.py |
~8 | Plan-Titel + Descriptions (en/de/fr, teils ohne fr) | 3 |
| C7 | datamodels/datamodelFiles.py |
~4 | Scope Option-Labels (en/de) | 3 |
| C8 | datamodels/datamodelDataSource.py |
~4 | Scope Option-Labels (en/de) | 3 |
| C9 | datamodels/datamodelFeatureDataSource.py |
~4 | Scope Option-Labels (en/de) | 3 |
| C10 | datamodels/datamodelUiLanguage.py |
~3 | Sync-Status Option-Labels (de/en) | 3 |
| C11 | features/trustee/datamodelFeatureTrustee.py |
~13 | Währung + Dokumenttyp Option-Labels | 3 |
| C12 | features/neutralization/datamodelFeatureNeutralizer.py |
~4 | Scope Option-Labels | 3 |
Summe Kategorie C: ~92 Stellen, 12 Dateien
Gateway — Sonstige (Kategorie D)
| # | Datei | Stellen | Muster | Phase |
|---|---|---|---|---|
| D1 | shared/frontendTypes.py |
~14 | CUSTOM_TYPE_DESCRIPTIONS (aktuell ungenutzt) |
4 |
| D2 | features/trustee/accounting/connectors/accountingConnectorBexio.py |
~4 | Connector-Label + Feld-Labels | 4 |
| D3 | features/trustee/accounting/connectors/accountingConnectorRma.py |
~4 | Connector-Label + Feld-Labels | 4 |
| D4 | features/trustee/accounting/connectors/accountingConnectorAbacus.py |
~5 | Connector-Label + Feld-Labels | 4 |
Summe Kategorie D: ~27 Stellen, 4 Dateien
Frontend (Kategorie E)
| # | Datei | Stellen | Muster | Phase |
|---|---|---|---|---|
| E1 | pages/Store.tsx |
~5 | FEATURE_DESCRIPTIONS Dict (de/en/fr) |
5 |
| E2 | types/mandate.ts |
~45 | Statische Navigation-Labels (de/en) | 5 |
| E3 | pages/views/trustee/TrusteeAbschlussView.tsx |
~6 | Tile-Titel/Descriptions (de/en/fr) | 5 |
| E4 | pages/views/trustee/TrusteeAnalyseView.tsx |
~4 | Tile-Descriptions (de/en) | 5 |
| E5 | components/UnifiedDataBar/UnifiedDataBar.tsx |
~3 | _TAB_LABELS (de/en/fr) |
5 |
| E6 | api/featuresApi.ts |
~4 | Mock-Labels (de/en) | 5 |
| E7 | Diverse (12 Dateien) | ~12 | Hardcoded deutsche Strings ohne t() |
5 |
Summe Kategorie E: ~79 Stellen, ~18 Dateien
Ausnahmen (bleiben statisch)
| Datei | Grund |
|---|---|
routes/routeI18n.py (_ISO_LABELS) |
ISO-Referenzdaten (Sprachnamen), kein UI-Text |
serviceAgent/coreTools/_mediaTools.py |
Locale-Code-Map ("de"→"de-DE"), technisch |
serviceAgent/conversationManager.py |
Sprach-Name-Map für Agent-Kontext, technisch |
Gesamtübersicht
| Kategorie | Dateien | Stellen | Komplexität |
|---|---|---|---|
| A: Feature-Module | 11 | ~171 | Mittel — uniformes Muster, braucht Boot-Registrierung |
| B: Node-Definitionen | 13 | ~262 | Mittel — uniformes Muster, braucht eigene Registrierung |
| C: Datamodel-Options | 12 | ~92 | Hoch — verschiedene Patterns, braucht generische Lösung |
| D: Sonstige Gateway | 4 | ~27 | Niedrig — einfache Ersetzung |
| E: Frontend | ~18 | ~79 | Niedrig — t() wrappen |
| Total | ~58 | ~631 |
Umsetzungsplan (5 Phasen)
Phase 1: Feature-Module (Kategorie A) — Composer-geeignet
Aufwand: Mittel | Modell: Composer (schnelles Modell) | Risiko: Niedrig
Vorbereitung (einmalig, Opus):
_registerRbacLabels()ini18nRegistry.pyerweitern: nebenDATA_OBJECTS,RESOURCE_OBJECTS,TEMPLATE_ROLESauch scannen:UI_OBJECTS[].label- Workflow-Gruppen-Labels (
TRUSTEE_WORKFLOW_GROUPS,TRUSTEE_SERVICE_CATEGORIES) - Rollen-
descriptionBlöcke - Service-Kategorie-Labels (
serviceCenter/registry.py) - Bootstrap-Rollen (
interfaceBootstrap.py)
Transformation (pro Datei, Composer):
Jedes "label": {"en": "X", "de": "Y", "fr": "Z"} wird zu "label": "Y" (deutscher Text).
Jedes "description": {"en": "X", "de": "Y", "fr": "Z"} wird zu "description": "Y".
Regel für Composer:
In der Datei [DATEI]:
- Ersetze jedes Dict {"en": "...", "de": "DEUTSCH", "fr": "..."} durch den deutschen Wert "DEUTSCH"
- Ersetze jedes Dict {"de": "DEUTSCH", "en": "...", "fr": "..."} durch "DEUTSCH"
- Wenn kein "de" vorhanden: nimm "en" Wert
- Lasse alle anderen Felder unverändert
Dateien: A1–A11 (11 Dateien)
Akzeptanzkriterien:
- Kein
"fr":mehr in den 11 Dateien (ausser Ausnahmen) _registerRbacLabels()registriert alle neuen Key-Kategorien- Gateway startet fehlerfrei
- Admin-UI Sprachen-Seite zeigt neue Keys im xx-Set
Phase 2: Node-Definitionen (Kategorie B) — Composer-geeignet
Aufwand: Mittel | Modell: Composer (schnelles Modell) | Risiko: Niedrig
Vorbereitung (einmalig, Opus):
- Neue Funktion
_registerNodeLabels()ini18nRegistry.py:- Scannt
STATIC_NODE_TYPESausnodeDefinitions/__init__.py - Registriert
label,description,parameters[].description,outputLabels[]als Keys - Context:
node.label,node.desc,node.param,node.output
- Scannt
- Scannt
portTypes.PORT_TYPE_CATALOGfür Port-Feld-Descriptions- Context:
port.desc
- Context:
- Scannt
nodeRegistryKategorie-Labels- Context:
node.category
- Context:
- Scannt
entryPointsTitel- Context:
node.entry
- Context:
Transformation (pro Datei, Composer): Identisches Muster wie Phase 1: Dict → deutscher String.
Sonderfall flow.py outputLabels:
# VORHER:
"outputLabels": {"en": ["Yes", "No"], "de": ["Ja", "Nein"], "fr": ["Oui", "Non"]}
# NACHHER:
"outputLabels": ["Ja", "Nein"]
Dateien: B1–B13 (13 Dateien)
Akzeptanzkriterien:
- Kein
"fr":mehr in den 13 Dateien _registerNodeLabels()registriert alle Node-Keys- Graph-Editor zeigt Nodes korrekt an (Labels übersetzt)
- Node-Parameter-Descriptions im Config-Panel korrekt
Phase 3: Datamodel-Options (Kategorie C) — Opus nötig
Aufwand: Hoch | Modell: Opus | Risiko: Mittel
Warum Opus: Die Patterns sind uneinheitlich (en/fr, en/de, en/de/fr), die frontend_options Struktur variiert, und die Boot-Registrierung muss generisch über alle Datamodels funktionieren.
Vorbereitung:
- Neue generische Funktion
_registerDatamodelOptionLabels()ini18nRegistry.py:- Scannt alle Pydantic-Modelle mit
json_schema_extra→frontend_options - Extrahiert
labelDicts und registriert den deutschen (oder englischen) Text als Key - Context:
option.{ModelName}.{fieldName}
- Scannt alle Pydantic-Modelle mit
BUILTIN_PLANSindatamodelSubscription.py:title/descriptionzu Keys
Transformation (pro Datei):
# VORHER:
{"value": "active", "label": {"en": "Active", "de": "Aktiv", "fr": "Actif"}}
# NACHHER:
{"value": "active", "label": "Aktiv"}
Problem: Fehlende Sprachen. Einige Dicts haben kein "de" (z.B. datamodelChat.py nur en/fr). Hier wird "en" als Fallback genommen und der englische Text als Key registriert — die AI-Übersetzung erzeugt dann de und fr.
Dateien: C1–C12 (12 Dateien)
Akzeptanzkriterien:
- Kein
"fr":/"en":Dict-Pattern mehr in den 12 Dateien _registerDatamodelOptionLabels()registriert alle Option-Keys- Frontend Select-Felder zeigen korrekte Labels
- Subscription-Plan-Texte korrekt übersetzt
Phase 4: Sonstige Gateway (Kategorie D) — Composer-geeignet
Aufwand: Niedrig | Modell: Composer (schnelles Modell) | Risiko: Niedrig
Transformation:
frontendTypes.py:CUSTOM_TYPE_DESCRIPTIONSkomplett entfernen (ungenutzt) +getCustomTypeDescription()undregisterCustomType()vereinfachen oder entfernen- Accounting-Connectors:
displayName()und Feld-Labels zu deutschen Strings
Dateien: D1–D4 (4 Dateien)
Akzeptanzkriterien:
CUSTOM_TYPE_DESCRIPTIONSentfernt- Accounting-Connector-Labels als deutsche Strings
- Kein
"fr":mehr in den 4 Dateien
Phase 5: Frontend (Kategorie E) — Composer-geeignet
Aufwand: Niedrig | Modell: Composer (schnelles Modell) | Risiko: Niedrig
Transformation:
- Alle
{"en": "X", "de": "Y", "fr": "Z"}Dicts durcht("Y")ersetzen - Alle hardcodierten deutschen Strings in JSX-Attributen mit
t()wrappen mandate.tsNavigation-Labels: zut()-Keys (Backend liefert bereits i18n-Keys)_TAB_LABELSinUnifiedDataBar.tsx: zut()-AufrufenFEATURE_DESCRIPTIONSinStore.tsx: zut()-Keys
Dateien: E1–E7 (~18 Dateien)
Akzeptanzkriterien:
- Kein statisches
de:/en:/fr:Dict-Pattern im Frontend - Alle UI-Texte via
t()getaggt - Sprache wechseln → alle Texte ändern sich
Composer-Anweisungen (Copy-Paste-fertig)
Für Phase 1 + 2 + 4 (uniforme Dict→String Ersetzung):
Aufgabe: Ersetze in [DATEI] alle statischen mehrsprachigen Dicts durch den deutschen Klartext-String.
Regeln:
1. {"en": "...", "de": "DEUTSCH", "fr": "..."} → "DEUTSCH"
2. {"de": "DEUTSCH", "en": "...", "fr": "..."} → "DEUTSCH"
3. {"en": "ENGLISH", "fr": "..."} (kein "de") → "ENGLISH"
4. {"en": "ENGLISH", "de": "DEUTSCH"} (kein "fr") → "DEUTSCH"
5. Listen-Sonderfall: {"en": [...], "de": [LISTE], "fr": [...]} → [LISTE]
6. Nur "label", "description", "title", "displayName" Felder ändern
7. Keine Imports, Funktionssignaturen oder Logik ändern
8. Keine Kommentare hinzufügen
Für Phase 5 (Frontend t()-Wrapping):
Aufgabe: Ersetze in [DATEI] alle statischen Texte durch t()-Aufrufe.
Regeln:
1. {"en": "...", "de": "DEUTSCH", "fr": "..."} → t("DEUTSCH")
2. Hardcoded string "DEUTSCH" in label/title/placeholder/aria-label → t("DEUTSCH")
3. Import { useLanguage } from '...LanguageContext' hinzufügen falls fehlend
4. const { t } = useLanguage(); in der Komponente falls fehlend
5. Keine Logik ändern, nur Texte wrappen
Reihenfolge und Abhängigkeiten
Phase 1 ──→ Phase 2 ──→ Phase 3 ──→ Phase 4
(A) (B) (C) (D)
│
▼
Phase 5
(E)
- Phase 1 muss zuerst: erweitert
_registerRbacLabels()als Grundlage - Phase 2 nach 1: braucht das Pattern von Phase 1
- Phase 3 nach 2: komplexeste Phase, braucht neue generische Registrierung
- Phase 4 + 5: unabhängig voneinander, nach Phase 1
Aufwandsschätzung
| Phase | Modell | Dateien | Geschätzte Zeit |
|---|---|---|---|
| 1 (Vorbereitung) | Opus | 1 | 15 min |
| 1 (Transformation) | Composer | 11 | 30 min |
| 2 (Vorbereitung) | Opus | 1 | 15 min |
| 2 (Transformation) | Composer | 13 | 30 min |
| 3 | Opus | 13 | 45 min |
| 4 | Composer | 4 | 10 min |
| 5 | Composer | 18 | 30 min |
| Total | ~58 | ~3 Stunden |