8.6 KiB
Coding-Konventionen
Naming
- Alle internen Funktionen beginnen mit
_Prefix (nicht exportierbar) - camelCase fuer Variablen und Funktionsnamen (kein snake_case)
- PascalCase fuer Klassen und Pydantic-Models
- Dateien:
camelCasefuer Module (z.B.mainServiceAi.py,routeBilling.py)
Frontend (React/TypeScript)
- Keine Browser-Dialoge (
alert,confirm,prompt) -- stattdessenuseConfirm()/usePrompt()Hooks - CSS Modules fuer Styling
- Hooks-Pattern fuer State und API-Zugriffe (
useApiRequest,useBilling, etc.) - Fehler propagieren -- keine stillen Fallbacks bei kritischen Pfaden
i18n-Pflicht: t() fuer alle UI-Texte
Jeder sichtbare Text im UI (Labels, Buttons, Placeholders, Tooltips, Fehlermeldungen) muss mit t() getaggt werden. Hardcodierte deutsche Strings im JSX sind nicht erlaubt.
import { useLanguage } from '../../providers/language/LanguageContext';
const { t } = useLanguage();
// Einfacher Text
t('Speichern')
// Mit Variablen-Interpolation
t('{count} Eintraege gefunden', { count: String(total) })
// Gleicher Text, anderer Kontext → Klammer als Kontext-Hinweis
t('Offen (Status)') // vs. t('Offen (Zustand)')
- Key = deutscher Klartext (kein Dot-Notation-Schema)
- Kein Plural-Framework -- separate Keys verwenden:
t('1 Eintrag')vs.t('{count} Eintraege', { count }) - Fehlende Keys erscheinen als
[Text](eckige Klammern = unuebersetzt) - Admin synchronisiert Sprachsets ueber Administration → System → UI-Sprachen → "Alle aktualisieren"
Backend (FastAPI/Python)
- Pydantic-Models als einzige Quelle fuer UI-Feld-Definitionen
PowerOnModelals Basis mit System-Audit-Feldern (sysCreatedAt,sysCreatedBy,sysModifiedAt,sysModifiedBy)- Fehler propagieren -- Exceptions explizit werfen, nicht schlucken
- Config ueber
APP_CONFIG(ausmodules/shared/configuration.py)
i18n-Pflicht: t() fuer alle UI-sichtbaren Gateway-Texte
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.
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.
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.
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.
// 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:
from modules.shared.i18nRegistry import apiRouteContext
routeApiMsg = apiRouteContext("routeBilling")
raise HTTPException(status_code=403, detail=routeApiMsg("Zugriff verweigert"))
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")-- AI-Kontext kommt automatisch aus dem Class-Docstringjson_schema_extra={"label": "Deutscher Feldname"}-- Pflicht fuer jedes UI-sichtbare FeldField(description=...)-- wird als AI-Kontext fuer die Uebersetzung verwendet- Kein
registerModelLabels()mehr verwenden (entfernt)
Feature-Module: Labels als deutsche Basis-Strings
Neue DATA_OBJECTS, RESOURCE_OBJECTS und UI_OBJECTS Labels verwenden deutsche Klartext-Strings als Basis-Key. Bestehende {en, de, fr}-Dicts werden beim Boot automatisch ueber _registerRbacLabels() registriert (Kontext rbac.data, rbac.resource, rbac.role, rbac.quickaction). Fuer neue Eintraege: de-Text als Basis-Key verwenden, en/fr im Dict fuer Legacy-Anzeige bis vollstaendige Migration.
DATA_OBJECTS = [
{
"objectKey": "data.uam.UserInDB",
"label": {"en": "User", "de": "Benutzer", "fr": "Utilisateur"},
"meta": {"table": "UserInDB", "namespace": "uam"}
},
]
Die de-Texte erscheinen automatisch im xx-Basisset und koennen per AI uebersetzt werden.
Projektstruktur Gateway
gateway/
app.py # FastAPI-App, Middleware, Startup
config.ini # Statische Konfiguration
modules/
auth/ # JWT, OAuth, CSRF, Authentication
datamodels/ # Pydantic-Models (zentrale Quelle)
features/ # Feature-Module (workspace, automation, ...)
<name>/
main<Name>.py # FEATURE_CODE, Registrierung
routeFeature<Name>.py # HTTP-Endpunkte
interfaceFeature<Name>.py # DB-Interface
datamodelFeature<Name>.py # Feature-spezifische Models
interfaces/ # DB-Interfaces (CRUD, Queries)
routes/ # Core-Routes (billing, admin, GDPR, ...)
security/ # RBAC-Engine
serviceCenter/
core/ # serviceSecurity, serviceUtils, serviceStreaming
services/ # serviceAi, serviceChat, serviceAgent, ...
registry.py # Service-Registrierung und Dependencies
shared/ # configuration.py, Utilities
system/ # registry.py (Feature-Discovery)
workflows/
methods/ # Unified Action Library
processing/ # WorkflowProcessor, Modes
automation/ # v1 Runtime
automation2/ # v2 Engine
connectors/ # Externe Systeme (DB, SharePoint, Jira, ...)
aicore/ # AI-Provider-Plugins, Model-Selector
Anti-Patterns
- Keine impliziten Type-Conversions in API-Responses
- Keine DB-Queries in Route-Handlern (immer ueber Interfaces)
- Kein direkter
os.environ-Zugriff -- immerAPP_CONFIG - Keine hartkodierten Secrets -- verschluesselt in Env-Dateien