29 KiB
Gateway i18n: Einheitliches Sprachsystem
Beschreibung und Kontext
Das Frontend nutzt bereits ein DB-basiertes Sprachsystem (t() -> UiLanguageSet -> AI-Uebersetzung). Das Gateway hat aktuell kein einheitliches System -- UI-sichtbare Texte sind hardcoded (deutsch/englisch), Model-Labels werden pro Datei mit registerModelLabels() in allen Sprachen manuell gepflegt, und TextMultilingual-Felder haben einen starren 4-Sprachen-Renderer.
Business-Treiber: Mehrsprachigkeit ist Pflicht fuer SaaS. Aktuell muss jede neue Sprache manuell in ~115 registerModelLabels()-Aufrufen und ~150 HTTPException-Texten nachgepflegt werden. Das skaliert nicht. Mit dem @i18nModel-Decorator entfallen die manuellen Aufrufe komplett.
Risiko bei Nicht-Umsetzung: Neue Sprachen erfordern Code-Aenderungen in 29+ Dateien. Inkonsistente Uebersetzungen. Kein AI-Uebersetzungsworkflow fuer Backend-Texte.
Fokus und kritische Details
- Caching:
@i18nModelDecorator wird bei Import-Time ausgefuehrt (~29 Klassen). Diet()-Funktion darf NICHT pro Aufruf die DB abfragen. Loesung: In-Memory-Cache beim Boot laden, danach O(1)-Lookup. - Boot-Reihenfolge: Der Gateway-Boot-Scan muss NACH DB-Init laufen, aber VOR dem ersten Request.
- Abwaertskompatibilitaet:
getModelLabels()undgetModelLabel()muessen weiterhin funktionieren (werden vonrouteAttributes.py,routeSystem.pyetc. aufgerufen). Die neue Implementierung ersetzt die interne Logik, nicht die API. - Context-Namensraum: Gateway-Texte bekommen
context = "api.*", Model-Labelscontext = "table.<ClassName>.*". Kein Konflikt mit Frontendcontext = "ui". - AI-Kontext (value): Wie im Frontend wird der
valueim xx-Set als Kontext-Beschreibung fuer die AI genutzt. Fuer Model-Labels wird der Kontext automatisch aus Pydantic-Metadaten (Class-Docstring, Field-Description) extrahiert. Fuer HTTPExceptions wird er manuell mitgegeben oder aus Route-Name + HTTP-Status abgeleitet.
Ziel und Nicht-Ziele
- Ziel: Alle UI-sichtbaren Gateway-Texte laufen ueber das bestehende
UiLanguageSet-System und werden per AI uebersetzt wie Frontend-Texte. - Ziel:
registerModelLabels()wird komplett durch@i18nModelDecorator ersetzt -- Labels und AI-Kontext werden automatisch aus Pydantic-Metadaten extrahiert. - Ziel:
TextMultilingual-Felder im UI dynamisch fuer alle verfuegbaren Sprachen rendern (nicht nur 4 hardcoded). - Explizit NICHT: Log-Eintraege und AI-Prompts werden NICHT mehrsprachig. Interne Fehlermeldungen die nie im UI erscheinen bleiben englisch.
Betroffene Module
- Gateway:
modules/shared/i18nRegistry.py-- NEUES Modul:t(),@i18nModel, Boot-Scan, Cache, DB-Syncmodules/shared/attributeUtils.py--getModelLabels()/getModelLabel()auf neues System umstellen,registerModelLabels()entfernenmodules/routes/routeI18n.py-- Erweiterung: Gateway-Keys in sync-xx integrierenmodules/datamodels/*.py(~29 Dateien) --@i18nModelDecorator +json_schema_extra["label"],registerModelLabels()entfernenmodules/routes/*.py(~21 Dateien) --HTTPException(detail=...)auft()umstellenapp.py-- Boot-Hook fuer Gateway-Key-Scan
- Frontend:
FormGeneratorForm.tsx--TextMultilingual-Renderer dynamisch machenFormGeneratorTable.tsx--formatTextMultilingualdynamisch machenAdminLanguagesPage.tsx-- Gateway-Keys in sync-xx anzeigen (optional)
- DB-Migration: Nein (nutzt bestehendes
UiLanguageSet-Schema) - Andere Komponenten: Keine
Entscheidungen
| Datum | Entscheidung | Begruendung |
|---|---|---|
| 2026-04-09 | Gateway-t() mit In-Memory-Cache, kein DB-Call pro Aufruf |
Performance: ~115 registerModelLabels + ~150 HTTPException = ~265+ Aufrufe pro Request-Zyklus |
| 2026-04-09 | Boot-Scan beim App-Start, automatisch in DB schreiben | Konsistenz: Jeder Gateway-Start hat ein vollstaendiges Key-Set in der DB |
| 2026-04-09 | Context-Prefix api fuer Gateway-UI-Texte, table.<Class> fuer Model-Labels |
Klare Trennung von Frontend (ui) und Backend-Texten |
| 2026-04-09 | TextMultilingual dynamisch aus availableLanguages rendern |
Skaliert mit neuen Sprachen ohne Code-Aenderung |
| 2026-04-09 | @i18nModel Decorator ersetzt registerModelLabels() komplett |
Eliminiert Duplikation: Labels + AI-Kontext kommen aus Pydantic-Metadaten. Entwickler schreibt weniger Code, neue Felder werden automatisch registriert |
Architektur
Datenfluss
Gateway Boot:
1. DB init (PostgreSQL)
2. Module laden -> @i18nModel Decorator + t() Aufrufe -> Keys + Kontext in _REGISTRY sammeln
3. Boot-Hook: _REGISTRY -> UiLanguageSet(xx) sync
- Nur context="api"/"table.*" Keys mergen (UI-Keys context="ui" nicht anfassen)
- Neue Keys hinzufuegen, verwaiste entfernen, Kontext (value) aktualisieren
4. Sprach-Cache laden: alle UiLanguageSets -> In-Memory Dict
Request:
User (language=fr) -> Middleware setzt _CURRENT_LANGUAGE="fr"
-> Gateway -> t("Zugriff verweigert") -> Cache["fr"]["Zugriff verweigert"] -> "Accès refusé"
AI-Uebersetzung (Admin sync):
xx-Entry: {context: "api.routeSecurityLocal", key: "Zugriff verweigert",
value: "Fehlermeldung bei fehlendem Zugriff auf eine Ressource"}
-> AI bekommt: {"key": "Zugriff verweigert",
"context": "Fehlermeldung bei fehlendem Zugriff auf eine Ressource"}
-> AI uebersetzt kontextbewusst: "Access denied" (en), "Accès refusé" (fr)
Kontext-Strategie fuer die AI-Uebersetzung
Das Frontend speichert im xx-Set den value als Kontext-Beschreibung fuer die AI:
- Entry:
{context: "ui", key: "Abbrechen", value: "pages/admin/AdminLanguagesPage.tsx > AdminLanguagesPage"} - Die AI bekommt:
{"key": "Abbrechen", "context": "Button in Admin-Sprachverwaltung"}und weiss damit, wie der Text zu uebersetzen ist.
Fuer das Backend bauen wir denselben Mechanismus. Der Kontext wird automatisch aus der Code-Struktur abgeleitet:
| Kategorie | context | value (AI-Kontext) | Beispiel |
|---|---|---|---|
| Model-Label (Tabelle) | table.Feature |
Pydantic-Class-Docstring oder "Datenmodell: Feature" |
key="Feature", value="Datenmodell fuer installierbare Funktionsmodule" |
| Model-Attribut | table.Feature.label |
Field-Description aus Pydantic Field(description=...) |
key="Bezeichnung", value="Anzeigename des Features (mehrsprachig)" |
| API-Fehlermeldung | api.route.<routeName> |
Automatisch aus Route-Funktion + HTTP-Status | key="Zugriff verweigert", value="HTTP 403 in routeSecurityLocal.login" |
| API-UI-Text | api.<modul> |
Manuell uebergeben oder aus Funktionsname | key="Datei hochgeladen", value="Erfolgsmeldung nach Datei-Upload" |
Neues Modul: i18nRegistry.py
# gateway/modules/shared/i18nRegistry.py
_REGISTRY: Dict[str, I18nRegistryEntry] = {} # key -> {context, value}
_CACHE: Dict[str, Dict[str, str]] = {} # lang -> {key -> translation}
_CURRENT_LANGUAGE: ContextVar[str] = ContextVar("i18n_lang", default="de")
@dataclass
class I18nRegistryEntry:
context: str # z.B. "table.Feature.label", "api.routeSecurityLocal"
value: str # AI-Kontext: Beschreibung wo/wie der Text verwendet wird
def t(key: str, context: str = "api", value: str = "") -> str:
"""Tag und uebersetze einen UI-sichtbaren Text.
Beim Import: registriert den Key mit Kontext und AI-Beschreibung.
Zur Laufzeit: liefert die Uebersetzung aus dem Cache.
Args:
key: Deutscher Basis-Text (identisch mit Frontend-t())
context: Herkunft des Texts (table.*, api.*)
value: AI-Kontext-Beschreibung fuer die Uebersetzung
"""
if key not in _REGISTRY:
_REGISTRY[key] = I18nRegistryEntry(context=context, value=value)
lang = _CURRENT_LANGUAGE.get()
return _CACHE.get(lang, {}).get(key, key)
def _setLanguage(lang: str):
"""Setzt die Sprache fuer den aktuellen Request (Middleware)."""
_CURRENT_LANGUAGE.set(lang)
async def _syncRegistryToDb():
"""Boot-Hook: Schreibt alle gesammelten Keys in UiLanguageSet(xx).
Merged mit bestehenden UI-Keys (context=ui), ueberschreibt nur api/table-Keys.
"""
async def _loadCache():
"""Boot-Hook: Laedt alle UiLanguageSets in den In-Memory-Cache."""
# Fuer jede Sprache: {key: value} aus entries[]
# _CACHE["fr"] = {"Zugriff verweigert": "Accès refusé", ...}
registerModelLabels() entfaellt -- ersetzt durch @i18nModel Decorator
Analyse: registerModelLabels() dupliziert Information die bereits in der Pydantic-Klasse steckt:
- Model-Label = deutscher Name -> kann aus Class-Docstring oder explizitem
_labelkommen - Attribut-Labels = deutsche Feldnamen -> koennen als
json_schema_extra["label"]am Field definiert werden - AI-Kontext = Beschreibung -> kommt aus
Field(description=...)und Class-Docstring
Ein Decorator kann all das automatisch extrahieren. registerModelLabels() wird komplett entfernt.
Vorher (~115 manuelle Aufrufe in 29 Dateien, pro Sprache gepflegt):
class Feature(PowerOnModel):
"""Feature-Definition (global, z.B. 'trustee', 'chatbot')."""
code: str = Field(description="Unique feature code")
label: TextMultilingual = Field(description="Feature label in multiple languages")
icon: str = Field(default="", description="Icon identifier for the feature")
registerModelLabels(
"Feature",
{"en": "Feature", "de": "Feature", "fr": "Fonctionnalité"},
{"code": {"en": "Code", "de": "Code", "fr": "Code"},
"label": {"en": "Label", "de": "Bezeichnung", "fr": "Libellé"},
"icon": {"en": "Icon", "de": "Symbol", "fr": "Icône"}},
)
Nachher (Decorator, kein separater Aufruf, keine manuellen Uebersetzungen):
@i18nModel("Feature", "Feature-Definition (global, z.B. 'trustee', 'chatbot')")
class Feature(PowerOnModel):
"""Feature-Definition (global, z.B. 'trustee', 'chatbot')."""
code: str = Field(
description="Unique feature code",
json_schema_extra={"label": "Code", ...}
)
label: TextMultilingual = Field(
description="Feature label in multiple languages",
json_schema_extra={"label": "Bezeichnung", ...}
)
icon: str = Field(
default="",
description="Icon identifier for the feature",
json_schema_extra={"label": "Symbol", ...}
)
Was der Decorator macht:
def i18nModel(modelLabel: str, aiContext: str = ""):
"""Class-Decorator: registriert Model- und Feld-Labels fuer i18n.
1. Registriert t(modelLabel, "table.<ClassName>", aiContext or docstring)
2. Fuer jedes Field mit json_schema_extra["label"]:
Registriert t(label, "table.<ClassName>.<fieldName>", field.description)
3. Speichert alles in MODEL_LABELS fuer getModelLabels()/getModelLabel()
"""
def _decorator(cls):
className = cls.__name__
# Model-Label registrieren
ctx = aiContext or (cls.__doc__ or "").strip().split("\n")[0]
t(modelLabel, f"table.{className}", ctx)
# Feld-Labels aus json_schema_extra["label"] extrahieren
for fieldName, fieldInfo in cls.model_fields.items():
extra = (fieldInfo.json_schema_extra or {})
label = extra.get("label")
if label:
desc = fieldInfo.description or ""
t(label, f"table.{className}.{fieldName}", desc)
# Abwaertskompatibel: MODEL_LABELS befuellen
MODEL_LABELS[className] = {
"model": modelLabel, # str statt Dict
"attributes": {
name: (info.json_schema_extra or {}).get("label", name)
for name, info in cls.model_fields.items()
}
}
return cls
return _decorator
Vorteile:
registerModelLabels()entfaellt komplett (115 Aufrufe in 29 Dateien geloescht)- Kein Import von
registerModelLabelsmehr noetig - AI-Kontext kommt automatisch aus
Field(description=...)-- der Entwickler muss nichts extra schreiben - Neue Felder werden automatisch registriert (kein Vergessen moeglich)
json_schema_extra["label"]ist die Single Source of Truth fuer den deutschen Feldnamen
Migration: Fuer jede Klasse:
registerModelLabels(...)Aufruf loeschen@i18nModel("Deutscher Modelname")Decorator hinzufuegenjson_schema_extra={"label": "Deutscher Feldname", ...}zu jedem Field hinzufuegen (wo noch nicht vorhanden)
getModelLabels() und getModelLabel() bleiben als API erhalten, lesen aber intern aus dem neuen System (MODEL_LABELS + t()-Cache).
HTTPException-Texte -- Vorher/Nachher
Vorher (hardcoded, kein Kontext):
raise HTTPException(status_code=403, detail="Zugriff verweigert")
Nachher (mit AI-Kontext):
raise HTTPException(
status_code=403,
detail=t("Zugriff verweigert", "api.routeSecurityLocal", "Fehlermeldung bei fehlendem Zugriff auf eine Ressource")
)
Der Kontext api.<routeModulName> wird automatisch aus dem Dateinamen abgeleitet. Der value beschreibt die Situation fuer die AI.
TextMultilingual UI-Renderer -- Vorher/Nachher
Vorher (4 hardcoded Sprachen):
// FormGeneratorForm.tsx
const LANGS = ['en', 'ge', 'fr', 'it'];
// 4 fixe Input-Felder
Nachher (dynamisch aus availableLanguages):
const { availableLanguages } = useLanguage();
const langCodes = availableLanguages
.filter(l => l.code !== 'xx')
.map(l => ({ code: l.code, label: l.label }));
// N Input-Felder basierend auf verfuegbaren Sprachen
Request-Middleware
# In app.py oder middleware
@app.middleware("http")
async def _i18nMiddleware(request, call_next):
lang = request.headers.get("Accept-Language", "de")[:2]
_setLanguage(lang)
return await call_next(request)
Umsetzungs-Checkliste
Phase 1: Infrastruktur + Decorator -- Cursor: Opus 4.6
Architektur-Arbeit: neues Modul, Boot-Integration, Decorator-Logik, attributeUtils Umbau. Braucht tiefes Verstaendnis der bestehenden Codebasis (DB-Schema, Boot-Reihenfolge, ContextVar, async).
i18nRegistry.pyerstellen:t(),@i18nModel,_REGISTRY,_CACHE,_setLanguage,_syncRegistryToDb,_loadCacheapp.py: Boot-Hook nach DB-Init:_syncRegistryToDb()+_loadCache()app.py: Request-Middleware fuer_setLanguage()(aus Accept-Language oder User-Session)routeI18n.py:_loadCache()nach sync/update aufrufen (Cache invalidieren)attributeUtils.py:getModelLabels()/getModelLabel()auf neues MODEL_LABELS-Format (str statt Dict) + t()-Cache umstellen- 2-3 Beispiel-Models migrieren (
datamodelBase.py,datamodelFeatures.py) als Referenz fuer Phase 2 - Gateway starten, pruefen ob Decorator + Boot-Sync funktioniert
Phase 2: Pydantic-Models migrieren (~27 verbleibende Dateien) -- Cursor: Auto / Fast
Repetitive Arbeit nach klarem Pattern aus Phase 1: Decorator drauf, json_schema_extra["label"] ergaenzen, registerModelLabels() loeschen. In Batches von 5-8 Dateien.
- Alle verbleibenden Pydantic-Models:
@i18nModel("Label")Decorator +json_schema_extra={"label": "..."}zu Fields - Alle
registerModelLabels()-Aufrufe loeschen from modules.shared.attributeUtils import registerModelLabelsImports entfernen- Nach jedem Batch: Gateway starten und pruefen
Phase 3: HTTPException-Texte umstellen (~21 Dateien, ~150 Stellen) -- Cursor: Auto / Fast
Mechanische Arbeit: detail="Text" wird zu detail=routeApiMsg("Text") mit routeApiMsg = apiRouteContext("route<Dateiname>") (Shorthand fuer t(key, "api.route<Dateiname>", "")). Skript: gateway/scripts/wrapRouteHttpDetails.py (nur einzeilige detail="..."; mehrzeilige Imports und f-Strings manuell pruefen).
- Alle UI-sichtbaren
HTTPException(detail="...")inmodules/**/route*.pyaufrouteApiMsg(...)umgestellt - Interne/technische Fehlermeldungen (
detail=str(e),detail=f"...{e}...") unveraendert gelassen
Phase 4: TextMultilingual UI (Frontend, 2-3 Dateien) -- Cursor: Opus 4.6
React-Hooks, dynamische Rendering-Logik, ge->de Mapping. Braucht Verstaendnis des Frontend-i18n-Systems und FormGenerator-Architektur.
FormGeneratorForm.tsx:renderMultilingualFielddynamisch ausavailableLanguages(Fallback auf[en, ge]wenn keine Sprachen geladen)FormGeneratorTable.tsx:formatTextMultilingual+convertToDisplayStringdynamisch (hardcodedlangMapdurch zentrales_toBackendLangersetzt)TextMultilingualBackend-Typ:get_text()undfrom_dict()akzeptieren jetztdeals Alias fuerge. DB-Schema bleibtge(bestehende Daten).
Phase 5: Integration + Test -- Cursor: Opus 4.6
End-to-End Pruefung, Debugging, Log-Analyse, ggf. Fixes. Braucht das staerkste Modell fuer Fehleranalyse.
- Gateway-Import aller Route-Module: 36/41 ok (5 Fehler = fehlende optionale Deps: aiohttp, langchain_core, tavily)
@i18nModelregistriert 90 Models mit 490 table-Keys in_REGISTRY+MODEL_LABELSgetModelLabel/getModelLabels/getModelAttributeDefinitionslesen korrekt ausi18nRegistry.MODEL_LABELSrouteApiMsg(api-Keys) registrieren sich lazy beim erstent()-Aufruf zur Laufzeitapp.py: Middleware (_i18nMiddleware) + Boot-Hooks (_syncRegistryToDb,_loadCache) vorhanden- Admin-UI: Sprache synchronisieren, Gateway-Keys werden uebersetzt (manuell getestet)
- UI mit anderer Sprache testen: Fehlermeldungen, Tabellen-Labels, TextMultilingual-Felder (manuell getestet)
Phase 6: Wiki / Coding-Conventions aktualisieren -- Cursor: Auto / Fast
Reine Doku-Arbeit. Inhalte stehen bereits im Plan (siehe "Wiki-Anpassungen fuer Cursor AI Coding" unten). Nur Einpflegen und Formatieren.
d-guides/coding-conventions.md: Backend-i18n-Regeln hinzugefuegt (t(), @i18nModel, json_schema_extra["label"], apiRouteContext)d-guides/coding-conventions.md: Regel fuer HTTPException-Texte mit routeApiMsg()d-guides/coding-conventions.md: Regel fuer neue Pydantic-Models (@i18nModel Pflicht)b-reference/gateway/architecture.md: i18n-Architektur dokumentiert (i18nRegistry, Boot-Sync, Cache, Context-Namensraeume, Entry-Identitaet)b-reference/frontend-nyla/architecture.md: TextMultilingual dynamisch + AdminLanguagesKeepAlive dokumentiertTOPICS.md: Thema "i18n / Mehrsprachigkeit" hinzugefuegt (Cross-Cutting + Aktive Arbeiten)
Abschluss
- RBAC / Permissions: nicht betroffen
- Neutralisierung: nicht betroffen
- Navigation / Routing: nicht betroffen
- Billing-Impact: nicht betroffen
Wiki-Anpassungen fuer Cursor AI Coding
Die Coding-Conventions (d-guides/coding-conventions.md) werden um folgende Regeln erweitert, damit die Cursor AI beim Coden die Sprach- und Class-Regeln kennt:
Backend-Ergaenzung: i18n-Pflicht fuer UI-sichtbare Texte
### i18n-Pflicht: `t()` fuer alle UI-sichtbaren Texte im Gateway
Jeder Text der im Frontend angezeigt wird (HTTPException-Details, API-Response-Messages,
Erfolgs-/Fehlermeldungen) **muss** mit `t()` getaggt werden.
from modules.shared.i18nRegistry import t
# Fehlermeldung (context automatisch = "api")
raise HTTPException(status_code=403, detail=t("Zugriff verweigert", "api.routeSecurity",
"Fehlermeldung bei fehlendem Zugriff"))
# Erfolgsmeldung
return {"message": t("Datei erfolgreich hochgeladen", "api.routeFiles",
"Bestaetigung nach Datei-Upload")}
**Nicht** mit t() taggen: Log-Eintraege, AI-Prompts, interne technische Fehlermeldungen.
Backend-Ergaenzung: @i18nModel fuer Pydantic-Models
### Pydantic-Models: @i18nModel Decorator Pflicht
Jedes Pydantic-Model das im UI angezeigt wird (Tabellen, Formulare) **muss** den
`@i18nModel` Decorator haben. Feld-Labels werden in `json_schema_extra["label"]` definiert.
from modules.shared.i18nRegistry import i18nModel
@i18nModel("Benutzer")
class User(PowerOnModel):
name: str = Field(
description="Full name of the user",
json_schema_extra={"label": "Name", "frontend_type": "text"}
)
email: str = Field(
description="Email address for login and notifications",
json_schema_extra={"label": "E-Mail-Adresse", "frontend_type": "text"}
)
- `@i18nModel("Deutscher Modelname")` -- der AI-Kontext kommt automatisch aus dem Class-Docstring
- `json_schema_extra={"label": "Deutscher Feldname"}` -- Pflicht fuer jedes UI-sichtbare Feld
- `Field(description=...)` -- wird als AI-Kontext fuer die Uebersetzung verwendet
- **Kein** `registerModelLabels()` mehr verwenden (deprecated, wird beim Boot ignoriert)
Frontend-Ergaenzung: TextMultilingual dynamisch
### TextMultilingual: Dynamische Sprachen
TextMultilingual-Felder rendern automatisch Eingabefelder fuer alle verfuegbaren Sprachen
(aus `availableLanguages`). Keine hardcodierten Sprach-Codes (en/ge/fr/it) mehr verwenden.
**Geplanter UX-Mehrwert:** Pro mehrsprachigem Feld ein Button (z. B. „In alle Sprachen
uebersetzen“), der den Inhalt der **Quellsprache** (Default: z. B. `en` als Pflichtfeld im
Modell, oder die vom Nutzer befuellte Zeile / aktuelle UI-Sprache — festzulegen) per **KI**
in alle **anderen** Sprachfelder uebernimmt — dieselbe Logik wie die Admin-Sprachen-AI
(siehe *Phase 7b* im Hauptdokument).
Akzeptanzkriterien
| # | Kriterium (Given-When-Then) | Prio |
|---|---|---|
| 1 | Given Gateway gestartet, When Admin oeffnet Sprachen-Seite, Then sind alle Gateway-Keys (context=api, table.*) im xx-Basisset sichtbar | must |
| 2 | Given Sprache "fr" synchronisiert, When User mit language=fr eine HTTPException ausloest, Then ist die Fehlermeldung auf Franzoesisch | must |
| 3 | Given Sprache "fr" synchronisiert, When User eine Tabelle oeffnet, Then sind Spalten-Labels auf Franzoesisch | must |
| 4 | Given 3 Sprachen verfuegbar (de, en, fr), When User ein TextMultilingual-Feld editiert, Then sieht er 3 Eingabefelder (nicht 4 hardcoded) | must |
| 5 | Given Gateway-Boot, When ~29 @i18nModel Decorators + ~150 t()-Aufrufe, Then Boot-Zeit erhoet sich um max 500ms | should |
| 6 | Given neuer Gateway-Text mit t() hinzugefuegt, When Gateway neustartet, Then erscheint der Key automatisch im xx-Set | must |
Testplan
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|---|---|---|---|---|---|
| T1 | 1 | api | nein | manuell: Admin-UI Sprachen-Seite | pending |
| T2 | 2 | api | nein | manuell: HTTPException in anderer Sprache | pending |
| T3 | 3 | ui | nein | manuell: Tabelle mit fr-Labels | pending |
| T4 | 4 | ui | nein | manuell: TextMultilingual-Editor | pending |
| T5 | 5 | perf | nein | manuell: Boot-Zeit messen | pending |
| T6 | 6 | api | nein | manuell: neuen t()-Key hinzufuegen, Gateway neustarten | pending |
Mengengeruest
| Was | Anzahl | Dateien |
|---|---|---|
registerModelLabels() Aufrufe |
~115 | 29 |
HTTPException(detail=...) UI-sichtbar |
~150 | 21 |
TextMultilingual Pydantic-Felder |
~5 | 5 |
TextMultilingual Frontend-Renderer |
2 | FormGeneratorForm, FormGeneratorTable |
Links
- Frontend i18n:
frontend_nyla/src/providers/language/LanguageContext.tsx - Frontend Key-Scanner:
frontend_nyla/vite.config.ts(extractI18nKeys Plugin) - Gateway Model-Labels:
gateway/modules/shared/attributeUtils.py - Gateway i18n API:
gateway/modules/routes/routeI18n.py - UiLanguageSet Model:
gateway/modules/datamodels/datamodelUiLanguage.py - TextMultilingual:
gateway/modules/datamodels/datamodelUtils.py
Phase 7 (Vorschlag): RBAC-Labels & Quick Actions — Abgleich mit dem Code
Verifiziert (Stand Pruefung)
| Mechanismus | Code / Pfad | Befund |
|---|---|---|
| TextMultilingual | gateway/modules/datamodels/datamodelUtils.py |
Pydantic-Modell: en Pflicht, de/fr/it optional; get_text(lang) mit Fallback auf en. |
| Rollenbeschreibung in der DB | gateway/modules/datamodels/datamodelRbac.py — Role.description: TextMultilingual |
Feld ist explizit mehrsprachig; im Schema frontend_type: "multilingual". |
| Admin-Formular | FormGeneratorForm.tsx |
Felder mit Typ multilingual rendern pro Sprache Eingaben aus availableLanguages — passt zu TextMultilingual-Objekten (nicht zu einem einzelnen i18n-Key). |
| Template-Rollen (Code) | mainTrustee.py etc. — TEMPLATE_ROLES[].description |
Im Python Dict {en,de,fr}; beim Sync in die DB als TextMultilingual/Role-Record. |
| RBAC-Katalog (RAM) | rbacCatalog.py — registerDataObject / registerResourceObject |
Labels sind lose Dicts {en,de,...} im Speicher — kein Pydantic-TextMultilingual-Typ auf dem Katalog-Eintrag. |
| Feature-UI (Navigation) | UI_OBJECTS[].label |
Bereits auf deutschen String (Basis-Key) umgestellt; Navigation nutzt t() im Frontend. |
| Quick Actions | routeFeatureTrustee.py — GET .../quick-actions?language= |
Backend loest label/description-Dicts serverseitig mit language auf; Response = fertige Strings. |
| QuickActionBoard | QuickActionBoard.tsx |
Erwartet label: string, description: string — keine TextMultilingual-Objekte auf dem Draht. |
| TrusteeDashboardView | TrusteeDashboardView.tsx |
Laed Quick Actions neu bei currentLanguage-Wechsel (useEffect-Dependency). |
Kernpunkt: Es gibt zwei legitime Muster im gleichen Produkt:
- Persistierte Entitaeten (z. B.
Role) mit TextMultilingual — Bearbeitung im UI als Objekt mit Sprachfeldern (bereits vorhanden). - Statische Code-Listen (Katalog-Dicts, Quick-Action-Definitionen) — heute Dicts im Python oder serverseitige Aufloesung; Navigation/UI-Labels der zweiten Schicht sind auf String-Key +
t()migriert.
Phase 7 — angepasste Empfehlung
Nicht alles auf „ein deutscher String = einziger Key“ vereinheitlichen, wenn TextMultilingual und FormGenerator bereits passen:
Role.description(und gleichartige DB-Felder): Bei TextMultilingual bleiben: Übersetzungen koennen weiterhin pro Sprache in der DB gepflegt werden oder spaeter mit UiLanguageSet synchronisiert werden (separates Konzept: „Spiegelung“ vs. Duplikat vermeiden).DATA_OBJECTS/RESOURCE_OBJECTS: Entweder (a) Dicts beibehalten und zusaetzlich deutsche Basis-Texte als i18n-Keys in_REGISTRYregistrieren (context=rbac.*) fuer Admin/AI, oder (b) auf TextMultilingual-Shape im Katalog vereinheitlichen (Aufwand: API + Admin-Anzeige).- Quick Actions: Entweder (a) serverseitige Aufloesung beibehalten und Gateway
t()auf die deutschen Basis-Strings aus den Dicts anwenden (Cache pro Request-Sprache), oder (b) nur noch deutsche Keys ausliefern undt()im Frontend (wie MandateNavigation) — dannQuickActionBoardumt(action.label)erweitern.
Konkreter naechster technischer Schritt (klein): _registerRbacLabels() in i18nRegistry.py — aus allen DATA_OBJECTS/RESOURCE_OBJECTS die de-Texte (und optional Template-description-de) als Keys mit context rbac.data / rbac.resource / rbac.role ins xx-Set syncen, ohne das Datenmodell TextMultilingual zu ersetzen.
Nicht empfohlen: Role.description auf einen reinen String-Key ohne Migration umstellen — bricht FormGenerator-Erwartung und bestehende DB-Daten, solange TextMultilingual Pflicht bleibt.
Phase 7b (UX): TextMultilingual — Button „In alle Sprachen uebersetzen“
Ziel: Echten Mehrwert im Formular: Nutzer traegt den Text einmal in der gewuenschten Ausgangs-Sprache ein und kann per Klick die uebrigen Sprachfelder automatisch fuellen (statt fuenfmal manuell zu tippen).
Machbarkeit: Ja. Technisch gut andockbar:
| Baustein | Bemerkung |
|---|---|
| Gateway | routeI18n.py enthaelt bereits _translateBatch (KI, Batches, Kapitalisierung _matchCapitalization). Ergaenzung: schlanker Endpoint z. B. POST /api/i18n/translate-field mit Body { "sourceText": "...", "sourceLang": "de", "targetLangs": ["en","fr"] } und Response { "en": "...", "fr": "..." } — Auth + Billing analog zu bestehenden Uebersetzungs-Jobs. |
| Frontend | FormGeneratorForm.tsx — renderMultilingualField: neben dem Feld-Label oder unter den Inputs ein Button; onClick: API aufrufen, dann handleMultilingualChange fuer jede Zielsprache setzen (Quellsprache unveraendert lassen). Loading/Disabled waehrend der Anfrage; Fehler via Toast. |
| Quellsprache | Produktregel festlegen: (A) immer englisch (en), weil im Modell Pflicht, oder (B) aktuelle UI-Sprache (currentLanguage), oder (C) die erste nicht-leere Zeile unter den Sprach-Inputs. Empfehlung: (C) mit Fallback auf en, damit es intuitiv bleibt. |
| Kosten / Limits | Gleiche Policy wie Admin-AI-Uebersetzung (Billing-Callback, Rate-Limits). |
Abgrenzung: Kein Ersatz fuer professionelles Review; KI kann Fachbegriffe falsch setzen — Button-Tooltip mit Hinweis optional.
Status: ✅ Umgesetzt (Phase 7 + 7b).
_registerRbacLabels()registriert 118 Keys (rbac.data, rbac.resource, rbac.role, rbac.quickaction) im xx-Basisset.POST /api/i18n/translate-fieldEndpoint fuer On-Demand-Uebersetzung von TextMultilingual-Feldern.- FormGenerator: Button „In alle Sprachen uebersetzen" in multilingualen Feldern.
Konkreter Umsetzungsplan (Arbeitspakete, Sprints, Abnahme): siehe
c-work/1-plan/2026-04-gateway-i18n-phase-7-implementation.md.
Abschluss
- b-reference/ aktualisiert (gateway/architecture.md, frontend-nyla/architecture.md)
- TOPICS.md aktualisiert (neues Thema: i18n/Mehrsprachigkeit)
- Dieses Dokument -> z-archive/ verschoben (nach finaler Validierung)