From cb18a584a2f22b1a26e8777260b7751ca395b0e1 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Sat, 11 Apr 2026 19:45:08 +0200 Subject: [PATCH] fixed language logic items --- ...customer-trustee-tooling-and-demo-prep.md} | 0 .../2026-04-i18n-static-text-elimination.md | 4 - ...6-04-gateway-int-stability-and-bugfixes.md | 64 +-- .../2026-04-i18n-static-text-elimination.md | 377 +----------------- d-guides/coding-conventions.md | 51 +++ 5 files changed, 90 insertions(+), 406 deletions(-) rename c-work/1-plan/{2026-04-trustee-tooling-and-demo-prep.md => 2026-04-customer-trustee-tooling-and-demo-prep.md} (100%) delete mode 100644 c-work/1-plan/2026-04-i18n-static-text-elimination.md rename c-work/{1-plan => 4-done}/2026-04-gateway-int-stability-and-bugfixes.md (62%) diff --git a/c-work/1-plan/2026-04-trustee-tooling-and-demo-prep.md b/c-work/1-plan/2026-04-customer-trustee-tooling-and-demo-prep.md similarity index 100% rename from c-work/1-plan/2026-04-trustee-tooling-and-demo-prep.md rename to c-work/1-plan/2026-04-customer-trustee-tooling-and-demo-prep.md diff --git a/c-work/1-plan/2026-04-i18n-static-text-elimination.md b/c-work/1-plan/2026-04-i18n-static-text-elimination.md deleted file mode 100644 index 9b69965..0000000 --- a/c-work/1-plan/2026-04-i18n-static-text-elimination.md +++ /dev/null @@ -1,4 +0,0 @@ - - - -Dieser Plan wurde nach **`c-work/2-build/2026-04-i18n-static-text-elimination.md`** verschoben. Bitte dort weiterarbeiten. diff --git a/c-work/1-plan/2026-04-gateway-int-stability-and-bugfixes.md b/c-work/4-done/2026-04-gateway-int-stability-and-bugfixes.md similarity index 62% rename from c-work/1-plan/2026-04-gateway-int-stability-and-bugfixes.md rename to c-work/4-done/2026-04-gateway-int-stability-and-bugfixes.md index 417b4a0..58aba00 100644 --- a/c-work/1-plan/2026-04-gateway-int-stability-and-bugfixes.md +++ b/c-work/4-done/2026-04-gateway-int-stability-and-bugfixes.md @@ -1,5 +1,6 @@ - + + # 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: `; `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/` diff --git a/c-work/4-done/2026-04-i18n-static-text-elimination.md b/c-work/4-done/2026-04-i18n-static-text-elimination.md index a620eb9..9b69965 100644 --- a/c-work/4-done/2026-04-i18n-static-text-elimination.md +++ b/c-work/4-done/2026-04-i18n-static-text-elimination.md @@ -1,375 +1,4 @@ - - + + -# 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. diff --git a/d-guides/coding-conventions.md b/d-guides/coding-conventions.md index 8d9f026..38e18bf 100644 --- a/d-guides/coding-conventions.md +++ b/d-guides/coding-conventions.md @@ -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