fixed language logic items
This commit is contained in:
parent
93edc94da3
commit
cb18a584a2
5 changed files with 90 additions and 406 deletions
|
|
@ -1,4 +0,0 @@
|
|||
<!-- status: moved -->
|
||||
<!-- canonical: c-work/2-build/2026-04-i18n-static-text-elimination.md -->
|
||||
|
||||
Dieser Plan wurde nach **`c-work/2-build/2026-04-i18n-static-text-elimination.md`** verschoben. Bitte dort weiterarbeiten.
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
<!-- status: plan -->
|
||||
<!-- status: done -->
|
||||
<!-- started: 2026-04-08 -->
|
||||
<!-- implemented: 2026-04-11 -->
|
||||
<!-- component: gateway -->
|
||||
|
||||
# Gateway: INT-Stabilität — Analyse (Log) und Fix-Plan
|
||||
|
|
@ -8,7 +9,7 @@
|
|||
|
||||
Beim Testen der Applikation auf der **Integrations-Instanz (INT)** mit **mehreren parallelen Nutzern** wurden Symptome im Gateway-Log sichtbar (u. a. `gateway_log_2026-04-08T11-14-17-514Z.log`). Ziel dieses Dokuments ist eine **codebezogene Ursachenanalyse** und ein **Umsetzungs- und Testplan** — ohne die Themen mit Architektur-Storys zu vermischen.
|
||||
|
||||
**Business-Treiber:** Zuverlässiger Multi-User-Betrieb (Upload/Index, Workspace-Agent, Outlook-Tools, Knowledge/Neutralisierung) und weniger „sporadische“ Fehler, die Support-Zeit kosten.
|
||||
**Business-Treiber:** Zuverlässiger Multi-User-Betrieb (Upload/Index, Workspace-Agent, Outlook-Tools, Knowledge/Neutralisierung) und weniger „sporadische" Fehler, die Support-Zeit kosten.
|
||||
|
||||
**Risiko bei Nicht-Umsetzung:** Weiterhin nicht reproduzierbar wirkende Permission-Fehler nach erfolgreicher Verarbeitung, fehlgeschlagene OpenAI-Streaming-Calls wegen Tool-Schema, nutzloser Outlook-Tool-Flow bei UUID-Referenzen, Crashes/Fail-Safe-Sprünge bei Bild-only-Indexierung.
|
||||
|
||||
|
|
@ -21,35 +22,41 @@ Beim Testen der Applikation auf der **Integrations-Instanz (INT)** mit **mehrere
|
|||
- **Fragil:** `interfaceDbManagement.getInterface()` liefert eine **gemeinsame** `ComponentObjects`-Instanz (`"default"`); jeder Aufruf setzt `setUserContext` auf demselben Objekt.
|
||||
- **Edge Case:** Async-Pfade (z. B. `_autoIndexFile` mit `await knowledgeService.indexFile`) — zwischen Start und `updateFile` kann ein anderer Request den Kontext überschreiben → `PermissionError` auf `FileItem.update` trotz erfolgreicher Indexierung.
|
||||
- **Code:** `gateway/modules/interfaces/interfaceDbManagement.py`, `gateway/modules/routes/routeDataFiles.py` (`_autoIndexFile`).
|
||||
- **Fix:** `_autoIndexFile` holt `mgmtInterface` nach dem letzten `await` frisch. Singleton bleibt bestehen — vollständige Ablösung ist separates Architektur-Thema.
|
||||
|
||||
### Thema B — Agent-Tool-JSON-Schema: Arrays ohne `items`
|
||||
|
||||
- **Fragil:** `ActionToolAdapter` mappt `List[str]` auf JSON-Schema `type: array` **ohne** `items`.
|
||||
- **Edge Case:** OpenAI lehnt Tools mit HTTP 400 ab (*array schema missing items*); Failover zu anderen Providern maskiert das Problem.
|
||||
- **Code:** `gateway/modules/serviceCenter/services/serviceAgent/actionToolAdapter.py`; betroffene Aktion u. a. `ai.process` (`methodAi.py`, Parameter `documentList`).
|
||||
- **Fix:** Neues `_ARRAY_ITEMS_MAPPING` ordnet Python-Array-Typen dem korrekten `items`-Schema zu. `_convertParameterSchema` setzt bei `type: "array"` automatisch `items` (Fallback: `{"type": "string"}`).
|
||||
|
||||
### Thema C — Outlook: Referenzformat vs. Lookup
|
||||
|
||||
- **Fragil:** `listConnections` zeigt u. a. `id: <uuid>`; `getUserConnectionFromConnectionReference` parst nur `connection:{authority}:{username}`.
|
||||
- **Edge Case:** Modell übergibt UUID an Outlook-Tools → „Connection not found“.
|
||||
- **Edge Case:** Modell übergibt UUID an Outlook-Tools → „Connection not found".
|
||||
- **Code:** `coreTools/_connectionTools.py`, `mainServiceChat.getUserConnectionFromConnectionReference`, `methodOutlook/helpers/connection.py`.
|
||||
- **Fix:** `listConnections` gibt jetzt `connection:{authority}:{username}` aus statt UUIDs. `getUserConnectionFromConnectionReference` hat UUID-Fallback: wenn Format nicht `connection:…`, wird nach Connection-ID gesucht.
|
||||
|
||||
### Thema D — Knowledge: `_neutralSvc` bei nur Bildern
|
||||
|
||||
- **Bug:** `_neutralSvc` wird nur im Zweig mit Textobjekten gesetzt; Bild-Neutralisierung nutzt `_neutralSvc` danach — bei **keinen** Textobjekten → `UnboundLocalError` (im Log als fail-safe sichtbar).
|
||||
- **Code:** `gateway/modules/serviceCenter/services/serviceKnowledge/mainServiceKnowledge.py` (`indexFile`).
|
||||
- **Fix:** `_neutralSvc` wird vor Text- und Bild-Block initialisiert (`None`), sobald `_shouldNeutralize` wahr ist. Bild-Block prüft `_neutralSvc` in der Bedingung.
|
||||
|
||||
### Thema E — Workspace: Mandats-Kontext aus zwei Quellen
|
||||
|
||||
- **Fragil:** `_getChatInterface` nutzt `context.mandateId`; `ServiceCenterContext` für den Agent nutzt Mandat aus `_validateInstanceAccess` (Instanz).
|
||||
- **Edge Case:** Unterschiedliche `interfaceDbChat`-Cache-Keys für „denselben“ Workspace → seltene Inkonsistenzen (Workflow sichtbar / nicht sichtbar).
|
||||
- **Edge Case:** Unterschiedliche `interfaceDbChat`-Cache-Keys für „denselben" Workspace → seltene Inkonsistenzen (Workflow sichtbar / nicht sichtbar).
|
||||
- **Code:** `gateway/modules/features/workspace/routeFeatureWorkspace.py`.
|
||||
- **Fix:** `_getChatInterface` akzeptiert optionalen `mandateId`-Parameter. **Alle 34** `_validateInstanceAccess`-Aufrufe entpacken den Return-Wert. Alle `_getChatInterface`- und `ServiceCenterContext`-Aufrufe nutzen die validierte Instanz-`mandateId`.
|
||||
|
||||
### Thema F — Chat: `createMessage` / `getWorkflow` → None
|
||||
|
||||
- **Fragil:** `getWorkflow` liefert `None` bei leerem RBAC-Recordset **und** bei Exceptions beim Validieren des `ChatWorkflow`-Modells; `createMessage` meldet pauschal „No access to workflow“.
|
||||
- **Edge Case:** Agent-Lauf endet „complete“, Persistenz der Assistant-Message schlägt fehl — Ursache ohne besseres Logging schwer trennbar.
|
||||
- **Fragil:** `getWorkflow` liefert `None` bei leerem RBAC-Recordset **und** bei Exceptions beim Validieren des `ChatWorkflow`-Modells; `createMessage` meldet pauschal „No access to workflow".
|
||||
- **Edge Case:** Agent-Lauf endet „complete", Persistenz der Assistant-Message schlägt fehl — Ursache ohne besseres Logging schwer trennbar.
|
||||
- **Code:** `gateway/modules/interfaces/interfaceDbChat.py`.
|
||||
- **Fix:** `getWorkflow` unterscheidet RBAC-leer (`debug`: "RBAC filter or not found") vs. Validierungsfehler (`error`: "data validation failed"). `createMessage` loggt `warning` mit Verweis auf Ursache.
|
||||
|
||||
## Ziel und Nicht-Ziele
|
||||
|
||||
|
|
@ -68,36 +75,37 @@ Beim Testen der Applikation auf der **Integrations-Instanz (INT)** mit **mehrere
|
|||
| Datum | Entscheidung | Begründung |
|
||||
|-------|-------------|------------|
|
||||
| 2026-04-08 | Plan-Dokument angelegt | INT-Log + Code-Review; Umsetzung folgt in `2-build`. |
|
||||
| 2026-04-11 | Themen A–F umgesetzt (pragmatische Fixes) | Kein Grossrefactor Singleton; stattdessen Re-Acquire nach `await` + konsistente `mandateId`-Übergabe. |
|
||||
|
||||
## Umsetzungs-Checkliste (Fix-Reihenfolge empfohlen)
|
||||
|
||||
- [ ] **D** `_neutralSvc` / Bildpfad in `mainServiceKnowledge.indexFile` absichern (klein, klar).
|
||||
- [ ] **B** JSON-Schema für `List[str]` / `List[int]` (und ggf. weitere Array-Typen) in `actionToolAdapter` ergänzen.
|
||||
- [ ] **C** Connection-Lookup UUID **oder** einheitliche Ausgabe/Beschreibung in `listConnections` + Parser in `getUserConnectionFromConnectionReference`.
|
||||
- [ ] **A** Management-Interface: Request-scoped Instanz statt globalem Singleton **oder** dokumentierter alternativer Ansatz (kein geteilter mutable User-Kontext über `await`-Grenzen).
|
||||
- [ ] **E** Workspace: einheitliche `mandateId`-Quelle für Chat-Interface und `ServiceCenterContext` nach Instanz-Validierung.
|
||||
- [ ] **F** `getWorkflow` / `createMessage`: präziseres Logging bei Validierungsfehlern; ggf. Unterscheidung „nicht gefunden“ vs. „Daten ungültig“.
|
||||
- [x] **D** `_neutralSvc` / Bildpfad in `mainServiceKnowledge.indexFile` absichern — `_neutralSvc` wird vor Text- und Bild-Block initialisiert; Bild-Block prüft `_neutralSvc` in der Bedingung. Datei: `serviceKnowledge/mainServiceKnowledge.py`.
|
||||
- [x] **B** JSON-Schema für `List[str]` / `List[int]` in `actionToolAdapter` — neues `_ARRAY_ITEMS_MAPPING`; `_convertParameterSchema` setzt bei `type: "array"` automatisch `items`. Datei: `serviceAgent/actionToolAdapter.py`.
|
||||
- [x] **C** Connection-Lookup: `listConnections` gibt `connection:{authority}:{username}` aus statt UUIDs; `getUserConnectionFromConnectionReference` hat UUID-Fallback. Dateien: `coreTools/_connectionTools.py`, `mainServiceChat.py`.
|
||||
- [x] **A** Management-Interface: pragmatischer Fix — `_autoIndexFile` holt `mgmtInterface` nach dem letzten `await` frisch. Singleton bleibt; vollständige Ablösung ist separates Architektur-Thema. Datei: `routes/routeDataFiles.py`.
|
||||
- [x] **E** Workspace `mandateId`: `_getChatInterface` akzeptiert optionalen `mandateId`-Parameter; alle 34 `_validateInstanceAccess`-Aufrufe entpacken den Return-Wert; alle `_getChatInterface`- und `ServiceCenterContext`-Aufrufe nutzen die validierte Instanz-`mandateId`. Datei: `features/workspace/routeFeatureWorkspace.py`.
|
||||
- [x] **F** `getWorkflow` / `createMessage` Logging: `getWorkflow` unterscheidet RBAC-leer (`debug`) vs. Validierungsfehler (`error`); `createMessage` loggt `warning` mit Verweis auf Ursache. Datei: `interfaces/interfaceDbChat.py`.
|
||||
|
||||
Weitere Checkliste aus Template:
|
||||
|
||||
- [ ] API-Endpunkte (nur falls Signatur/Kontrakt ändert)
|
||||
- [ ] DB-Schema / Migration — nein
|
||||
- [ ] Frontend-Komponenten — nein (optional)
|
||||
- [ ] RBAC / Permissions — prüfen nach Fix A/E
|
||||
- [ ] Neutralisierung betroffen? — ja (Thema D)
|
||||
- [ ] Navigation / Routing — nein
|
||||
- [ ] Billing-Impact? — nein (keine bewusste Änderung der Abrechnungslogik)
|
||||
- [x] API-Endpunkte — keine Signaturänderung; `_getChatInterface` hat neuen optionalen Parameter (intern)
|
||||
- [x] DB-Schema / Migration — nein (nicht nötig)
|
||||
- [x] Frontend-Komponenten — nein
|
||||
- [x] RBAC / Permissions — keine Änderung; Fix E stellt sicher, dass Cache-Keys konsistent sind
|
||||
- [x] Neutralisierung betroffen? — ja (Thema D, behoben)
|
||||
- [x] Navigation / Routing — nein
|
||||
- [x] Billing-Impact? — nein
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
| # | Kriterium (Given-When-Then) | Prio |
|
||||
|---|---------------------------|------|
|
||||
| AC1 | Given eine Datei mit nur Bild-`contentObjects` und aktiver Neutralisierung, When `indexFile` läuft, Then kein `UnboundLocalError` und definiertes Verhalten (OK/skip mit Log). | must |
|
||||
| AC2 | Given generiertes Tool-Schema für `ai_process`, When es an die OpenAI-kompatible API geht, Then `documentList` ist ein `array` **mit** `items` (kein 400 wegen fehlender `items`). | must |
|
||||
| AC3 | Given `listConnections` liefert eine Verbindungs-UUID, When ein Outlook-Tool dieselbe Referenz nutzt, Then Verbindung wird aufgelöst **oder** die Tool-Konvention verbietet UUID explizit und der Agent folgt zuverlässig der Konvention. | must |
|
||||
| AC4 | Given zwei parallele Uploads/Auto-Index-Läufe unterschiedlicher User, When Indexierung endet, Then `updateFile(..., active)` schlägt nicht wegen **fremdem** User-Kontext fehl. | must |
|
||||
| AC5 | Given Workspace-Stream mit gültiger Instanz, When Chat-Interface und Agent-Chat-Service erzeugt werden, Then identische effektive `mandateId`/`featureInstanceId`-Kombination für den Chat-Cache. | should |
|
||||
| AC6 | Given `getWorkflow` scheitert an Datenvalidierung, When `createMessage` läuft, Then Log enthält **ursächliche** Exception, nicht nur „No access“. | should |
|
||||
| # | Kriterium (Given-When-Then) | Prio | Status |
|
||||
|---|---------------------------|------|--------|
|
||||
| AC1 | Given eine Datei mit nur Bild-`contentObjects` und aktiver Neutralisierung, When `indexFile` läuft, Then kein `UnboundLocalError` und definiertes Verhalten (OK/skip mit Log). | must | **done** |
|
||||
| AC2 | Given generiertes Tool-Schema für `ai_process`, When es an die OpenAI-kompatible API geht, Then `documentList` ist ein `array` **mit** `items` (kein 400 wegen fehlender `items`). | must | **done** |
|
||||
| AC3 | Given `listConnections` liefert eine Verbindungs-Referenz, When ein Outlook-Tool dieselbe Referenz nutzt, Then Verbindung wird aufgelöst (Format `connection:…` oder UUID-Fallback). | must | **done** |
|
||||
| AC4 | Given zwei parallele Uploads/Auto-Index-Läufe unterschiedlicher User, When Indexierung endet, Then `updateFile(..., active)` schlägt nicht wegen **fremdem** User-Kontext fehl. | must | **done** (Re-Acquire) |
|
||||
| AC5 | Given Workspace-Stream mit gültiger Instanz, When Chat-Interface und Agent-Chat-Service erzeugt werden, Then identische effektive `mandateId`/`featureInstanceId`-Kombination für den Chat-Cache. | should | **done** |
|
||||
| AC6 | Given `getWorkflow` scheitert an Datenvalidierung, When `createMessage` läuft, Then Log enthält **ursächliche** Exception, nicht nur „No access". | should | **done** |
|
||||
|
||||
## Testplan
|
||||
|
||||
|
|
@ -118,6 +126,6 @@ Weitere Checkliste aus Template:
|
|||
|
||||
## Abschluss
|
||||
|
||||
- [x] Alle Themen A–F umgesetzt (2026-04-11)
|
||||
- [ ] b-reference/ aktualisiert — z. B. `b-reference/gateway/architecture.md` nur wenn Architektur-Entscheid (Singleton) dauerhaft geändert wird
|
||||
- [ ] TOPICS.md aktualisiert (falls neues Thema)
|
||||
- [ ] Dieses Dokument bei Umsetzungsbeginn nach `c-work/2-build/` verschieben; nach Release nach `4-done/` bzw. `z-archive/`
|
||||
|
|
@ -1,375 +1,4 @@
|
|||
<!-- status: build -->
|
||||
<!-- lastReviewed: 2026-04-10 -->
|
||||
<!-- status: moved -->
|
||||
<!-- canonical: c-work/2-build/2026-04-i18n-static-text-elimination.md -->
|
||||
|
||||
# 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()` 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:** 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()` 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`:**
|
||||
```python
|
||||
# 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()` in `i18nRegistry.py`:
|
||||
- Scannt alle Pydantic-Modelle mit `json_schema_extra` → `frontend_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):**
|
||||
```python
|
||||
# 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_DESCRIPTIONS` komplett entfernen (ungenutzt) + `getCustomTypeDescription()` und `registerCustomType()` vereinfachen oder entfernen
|
||||
- Accounting-Connectors: `displayName()` und Feld-Labels zu deutschen Strings
|
||||
|
||||
**Dateien:** D1–D4 (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:** 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** |
|
||||
Dieser Plan wurde nach **`c-work/2-build/2026-04-i18n-static-text-elimination.md`** verschoben. Bitte dort weiterarbeiten.
|
||||
|
|
|
|||
|
|
@ -66,6 +66,57 @@ return {"message": t("Datei erfolgreich hochgeladen", "api.routeFiles",
|
|||
|
||||
**Nicht** mit `t()` taggen: Log-Eintraege, AI-Prompts, interne technische Fehlermeldungen.
|
||||
|
||||
### Statische Dicts mit i18n-Keys: t() auf Modul-Ebene registrieren
|
||||
|
||||
Wenn ein Modul statische Dicts/Listen mit UI-sichtbaren Texten definiert (z.B. `BADGE_DEFINITIONS`, Level-Labels), muessen die Keys **auf Modul-Ebene** via `t()` registriert werden. Nur so erscheinen sie im `xx`-Basisset und koennen uebersetzt werden.
|
||||
|
||||
```python
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
# Keys auf Modul-Ebene registrieren (Import-Zeit)
|
||||
_KEYS = [t("Erste Session"), t("3-Tage-Serie"), t("Wochenserie")]
|
||||
|
||||
# Dict mit deutschen Quelltexten als Strings (NICHT t() im Dict!)
|
||||
BADGE_DEFINITIONS = {
|
||||
"first_session": {"label": "Erste Session", "icon": "star"},
|
||||
"streak_3": {"label": "3-Tage-Serie", "icon": "fire"},
|
||||
}
|
||||
|
||||
# Bei Runtime nochmals t() aufrufen fuer aktuelle Sprache
|
||||
def getBadgeDefinitions():
|
||||
return {k: {**v, "label": t(v["label"])} for k, v in BADGE_DEFINITIONS.items()}
|
||||
```
|
||||
|
||||
**Warum?** `t()` registriert Keys nur beim ersten Aufruf. Wenn `t()` nur in einer Funktion steht, wird der Key erst beim ersten Request registriert -- **nach** dem Boot-Sync. Der Key fehlt dann im `xx`-Set und kann nicht uebersetzt werden.
|
||||
|
||||
### TextMultilingual-Felder: Backend loest auf
|
||||
|
||||
`TextMultilingual`-Felder (User-Content in mehreren Sprachen, z.B. `Role.description`) werden im Backend via `_resolveTextMultilingual()` aufgeloest bevor sie ans Frontend geliefert werden. Das Frontend erhaelt immer einen **String**, nie ein Dict.
|
||||
|
||||
```python
|
||||
from modules.shared.i18nRegistry import _getLanguage
|
||||
|
||||
def _resolveTextMultilingual(value) -> str:
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, dict):
|
||||
lang = _getLanguage()
|
||||
return value.get(lang) or value.get("de") or value.get("en") or ""
|
||||
return str(value) if value else ""
|
||||
```
|
||||
|
||||
### Frontend: Keine Dict-Fallbacks fuer Labels
|
||||
|
||||
Das Frontend darf **keine** `label?.de || label?.en || Object.values(...)` Ketten verwenden. Wenn das Backend korrekt arbeitet, sind alle Labels Strings. Defensive `typeof === 'object'` Checks verdecken Backend-Fehler und sind verboten.
|
||||
|
||||
```tsx
|
||||
// FALSCH - verdeckt Backend-Fehler
|
||||
const name = typeof m.name === 'object' ? m.name.de || m.name.en : m.name;
|
||||
|
||||
// RICHTIG - Backend liefert String
|
||||
const name = m.label || m.name || m.id;
|
||||
```
|
||||
|
||||
Fuer Route-Module gibt es den Shorthand `apiRouteContext`:
|
||||
|
||||
```python
|
||||
|
|
|
|||
Loading…
Reference in a new issue