From f8679a41c81a45b9f9c60fa398d004fc0bae780b Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Thu, 9 Apr 2026 21:55:19 +0200
Subject: [PATCH] i18n gateway planung
---
c-work/2-build/gateway-i18n-unified.md | 438 +++++++++++++++++++++++++
1 file changed, 438 insertions(+)
create mode 100644 c-work/2-build/gateway-i18n-unified.md
diff --git a/c-work/2-build/gateway-i18n-unified.md b/c-work/2-build/gateway-i18n-unified.md
new file mode 100644
index 0000000..4a47e2d
--- /dev/null
+++ b/c-work/2-build/gateway-i18n-unified.md
@@ -0,0 +1,438 @@
+
+
+
+
+# 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:** `@i18nModel` Decorator wird bei Import-Time ausgefuehrt (~29 Klassen). Die `t()`-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()` und `getModelLabel()` muessen weiterhin funktionieren (werden von `routeAttributes.py`, `routeSystem.py` etc. aufgerufen). Die neue Implementierung ersetzt die interne Logik, nicht die API.
+- **Context-Namensraum:** Gateway-Texte bekommen `context = "api.*"`, Model-Labels `context = "table..*"`. Kein Konflikt mit Frontend `context = "ui"`.
+- **AI-Kontext (value):** Wie im Frontend wird der `value` im 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 `@i18nModel` Decorator 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-Sync
+ - `modules/shared/attributeUtils.py` -- `getModelLabels()`/`getModelLabel()` auf neues System umstellen, `registerModelLabels()` entfernen
+ - `modules/routes/routeI18n.py` -- Erweiterung: Gateway-Keys in sync-xx integrieren
+ - `modules/datamodels/*.py` (~29 Dateien) -- `@i18nModel` Decorator + `json_schema_extra["label"]`, `registerModelLabels()` entfernen
+ - `modules/routes/*.py` (~21 Dateien) -- `HTTPException(detail=...)` auf `t()` umstellen
+ - `app.py` -- Boot-Hook fuer Gateway-Key-Scan
+- **Frontend:**
+ - `FormGeneratorForm.tsx` -- `TextMultilingual`-Renderer dynamisch machen
+ - `FormGeneratorTable.tsx` -- `formatTextMultilingual` dynamisch machen
+ - `AdminLanguagesPage.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.` 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.` | Automatisch aus Route-Funktion + HTTP-Status | `key="Zugriff verweigert", value="HTTP 403 in routeSecurityLocal.login"` |
+| **API-UI-Text** | `api.` | Manuell uebergeben oder aus Funktionsname | `key="Datei hochgeladen", value="Erfolgsmeldung nach Datei-Upload"` |
+
+### Neues Modul: `i18nRegistry.py`
+
+```python
+# 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 `_label` kommen
+- **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):
+```python
+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):
+```python
+@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:**
+```python
+def i18nModel(modelLabel: str, aiContext: str = ""):
+ """Class-Decorator: registriert Model- und Feld-Labels fuer i18n.
+
+ 1. Registriert t(modelLabel, "table.", aiContext or docstring)
+ 2. Fuer jedes Field mit json_schema_extra["label"]:
+ Registriert t(label, "table..", 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 `registerModelLabels` mehr 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:
+1. `registerModelLabels(...)` Aufruf loeschen
+2. `@i18nModel("Deutscher Modelname")` Decorator hinzufuegen
+3. `json_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):
+```python
+raise HTTPException(status_code=403, detail="Zugriff verweigert")
+```
+
+**Nachher** (mit AI-Kontext):
+```python
+raise HTTPException(
+ status_code=403,
+ detail=t("Zugriff verweigert", "api.routeSecurityLocal", "Fehlermeldung bei fehlendem Zugriff auf eine Ressource")
+)
+```
+
+Der Kontext `api.` wird automatisch aus dem Dateinamen abgeleitet. Der `value` beschreibt die Situation fuer die AI.
+
+### `TextMultilingual` UI-Renderer -- Vorher/Nachher
+
+**Vorher** (4 hardcoded Sprachen):
+```tsx
+// FormGeneratorForm.tsx
+const LANGS = ['en', 'ge', 'fr', 'it'];
+// 4 fixe Input-Felder
+```
+
+**Nachher** (dynamisch aus availableLanguages):
+```tsx
+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
+
+```python
+# 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.py` erstellen: `t()`, `@i18nModel`, `_REGISTRY`, `_CACHE`, `_setLanguage`, `_syncRegistryToDb`, `_loadCache`
+- [ ] `app.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 registerModelLabels` Imports 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=t("Text", "api.routeName", "Beschreibung")`. Klares Pattern, in Batches von 5-7 Dateien.
+- [ ] Alle UI-sichtbaren `HTTPException(detail="...")` in `routes/*.py` auf `t()` umstellen
+- [ ] Interne/technische Fehlermeldungen (die nie im UI erscheinen) NICHT umstellen
+
+### 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`: `renderMultilingualField` dynamisch aus `availableLanguages`
+- [ ] `FormGeneratorTable.tsx`: `formatTextMultilingual` dynamisch
+- [ ] `TextMultilingual` Backend-Typ pruefen: `ge` -> `de` Mapping ggf. anpassen
+
+### Phase 5: Integration + Test -- Cursor: **Opus 4.6**
+End-to-End Pruefung, Debugging, Log-Analyse, ggf. Fixes. Braucht das staerkste Modell fuer Fehleranalyse.
+- [ ] Gateway starten, pruefen ob Keys in `UiLanguageSet(xx)` erscheinen (context=api, table.*)
+- [ ] Admin-UI: Sprache synchronisieren, pruefen ob Gateway-Keys uebersetzt werden
+- [ ] UI mit anderer Sprache testen: Fehlermeldungen, Tabellen-Labels, TextMultilingual-Felder
+
+### 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 hinzufuegen (t(), @i18nModel, json_schema_extra["label"])
+- [ ] `d-guides/coding-conventions.md`: Regel fuer HTTPException-Texte mit t()
+- [ ] `d-guides/coding-conventions.md`: Regel fuer neue Pydantic-Models (@i18nModel Pflicht)
+- [ ] `b-reference/gateway/architecture.md`: i18n-Architektur dokumentieren (i18nRegistry, Boot-Sync, Cache)
+- [ ] `b-reference/frontend-nyla/architecture.md`: TextMultilingual dynamisch dokumentieren
+- [ ] `TOPICS.md`: Thema "i18n / Mehrsprachigkeit" hinzufuegen mit Verweis auf relevante Seiten
+
+### 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
+
+```markdown
+### 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
+
+```markdown
+### 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
+
+```markdown
+### TextMultilingual: Dynamische Sprachen
+
+TextMultilingual-Felder rendern automatisch Eingabefelder fuer alle verfuegbaren Sprachen
+(aus `availableLanguages`). Keine hardcodierten Sprach-Codes (en/ge/fr/it) mehr verwenden.
+```
+
+## 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`
+
+## Abschluss
+
+- [ ] b-reference/ aktualisiert (gateway/architecture.md, frontend-nyla/architecture.md)
+- [ ] TOPICS.md aktualisiert (neues Thema: i18n/Mehrsprachigkeit)
+- [ ] Dieses Dokument -> z-archive/ verschoben