wiki/c-work/4-done/2026-04-i18n-static-text-elimination.md
ValueOn AG 93edc94da3 upd
2026-04-10 22:44:27 +02:00

17 KiB
Raw Blame History

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 12: 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 17 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() in i18nRegistry.py erweitern: neben DATA_OBJECTS, RESOURCE_OBJECTS, TEMPLATE_ROLES auch scannen:
    • UI_OBJECTS[].label
    • Workflow-Gruppen-Labels (TRUSTEE_WORKFLOW_GROUPS, TRUSTEE_SERVICE_CATEGORIES)
    • Rollen-description Blö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: A1A11 (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() in i18nRegistry.py:
    • Scannt STATIC_NODE_TYPES aus nodeDefinitions/__init__.py
    • Registriert label, description, parameters[].description, outputLabels[] als Keys
    • Context: node.label, node.desc, node.param, node.output
  • Scannt portTypes.PORT_TYPE_CATALOG für Port-Feld-Descriptions
    • Context: port.desc
  • Scannt nodeRegistry Kategorie-Labels
    • Context: node.category
  • Scannt entryPoints Titel
    • Context: node.entry

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: B1B13 (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() in i18nRegistry.py:
    • Scannt alle Pydantic-Modelle mit json_schema_extrafrontend_options
    • Extrahiert label Dicts und registriert den deutschen (oder englischen) Text als Key
    • Context: option.{ModelName}.{fieldName}
  • BUILTIN_PLANS in datamodelSubscription.py: title/description zu 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: C1C12 (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_DESCRIPTIONS komplett entfernen (ungenutzt) + getCustomTypeDescription() und registerCustomType() vereinfachen oder entfernen
  • Accounting-Connectors: displayName() und Feld-Labels zu deutschen Strings

Dateien: D1D4 (4 Dateien)

Akzeptanzkriterien:

  • CUSTOM_TYPE_DESCRIPTIONS entfernt
  • 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 durch t("Y") ersetzen
  • Alle hardcodierten deutschen Strings in JSX-Attributen mit t() wrappen
  • mandate.ts Navigation-Labels: zu t()-Keys (Backend liefert bereits i18n-Keys)
  • _TAB_LABELS in UnifiedDataBar.tsx: zu t()-Aufrufen
  • FEATURE_DESCRIPTIONS in Store.tsx: zu t()-Keys

Dateien: E1E7 (~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