Zum Inhalt

Internationalisierung (i18n)

Übersicht

Die xynap Platform bietet ein globales i18n-System mit automatischer Übersetzung, Redis-Caching und Admin-Verwaltung.

Komponente Technologie
Übersetzungs-API FastAPI /api/v1/i18n/
Cache Redis Hash (i18n:bundle:{locale}, TTL 1h)
Auto-Übersetzer LibreTranslate (lokal)
Source-of-Truth en.json (englische Quelltexte)
Frontend vue-i18n mit dynamischem Laden
Admin-UI Settings → Sprachen

Architektur

Frontend (Vue)                Backend API                    Storage
┌─────────────┐  GET /i18n   ┌──────────────┐    ┌──────────┐
│ vue-i18n     │ ──────────→ │ i18n Router  │ ──→│ Redis    │ (Cache)
│ loadMessages │  /{locale}  │              │    └──────────┘
└─────────────┘              │              │        ↓ miss
                             │              │    ┌──────────┐
                             │              │ ──→│ DB       │ (translations)
                             │              │    └──────────┘
                             │              │        ↓ miss
                             │              │    ┌──────────┐
                             │              │ ──→│ en.json  │ + Auto-Translate
                             └──────────────┘    └──────────┘

Lookup-Kette

  1. RedisHGETALL i18n:bundle:{locale} → sofort zurück
  2. DBtranslations-Tabelle → in Redis cachen
  3. en.json + LibreTranslate → übersetzen, in DB + Redis speichern
  4. Fallback — Key selbst zurückgeben

Öffentliche API-Endpoints

Kein Auth erforderlich.

Sprachen auflisten

GET /api/v1/i18n/languages

Response:

[
  {"code": "de", "label": "Deutsch"},
  {"code": "en", "label": "English"},
  {"code": "es", "label": "Español"}
]

Übersetzungs-Bundle laden

GET /api/v1/i18n/{locale}

Liefert alle Übersetzungs-Keys als flaches JSON. Unterstützt ETag/If-None-Match für Conditional Requests (304 Not Modified).

Response-Header:

ETag: "9e1c7de849edd567e026cc559512f9d3"
Cache-Control: public, max-age=300

Response-Body:

{
  "error.not_found": "Nicht gefunden",
  "auth.login_success": "Anmeldung erfolgreich",
  "invoice.created": "Rechnung {number} erstellt"
}

Einzelnen Key abrufen

GET /api/v1/i18n/{locale}/{key}

Admin-Endpoints

Erfordern settings.manage Permission.

Sprache hinzufügen

POST /api/v1/i18n/admin/languages
Content-Type: application/json

{"code": "es", "label": "Español"}

Beim ersten Bundle-Abruf der neuen Sprache werden alle Keys automatisch via LibreTranslate übersetzt.

Sprache entfernen

DELETE /api/v1/i18n/admin/languages/{locale}

Einschränkung

de und en können nicht entfernt werden.

Manuelle Übersetzung setzen

PUT /api/v1/i18n/admin/translations/{locale}/{key}
Content-Type: application/json

{"value": "Benutzer nicht gefunden"}

Überschreibt automatische Übersetzungen. Wird als auto_translated=false markiert.

Übersetzungen neu generieren (Rebuild)

POST /api/v1/i18n/admin/rebuild
POST /api/v1/i18n/admin/rebuild/{locale}

Generiert alle veralteten (stale) Übersetzungen neu via LibreTranslate.

Auto-Übersetzungen verwerfen (Forget)

POST /api/v1/i18n/admin/forget/{locale}

Löscht alle Auto-Übersetzungen einer Sprache. Werden beim nächsten Abruf frisch erzeugt.

Statistiken

GET /api/v1/i18n/admin/stats
{
  "total_keys": 111,
  "languages": [
    {
      "code": "de", "label": "Deutsch",
      "total": 111, "translated": 111,
      "auto": 0, "manual": 111, "stale": 0
    }
  ]
}

Veraltete Übersetzungen

GET /api/v1/i18n/admin/stale

Stale-Detection

Jede Übersetzung speichert den MD5-Hash des englischen Quelltexts (source_hash). Wenn sich en.json ändert:

  1. Hash stimmt nicht mehr überein
  2. Übersetzung wird als stale=true markiert
  3. Beim nächsten Bundle-Abruf oder Rebuild wird sie automatisch neu übersetzt

Frontend-Integration

Statische Dateien (Fallback)

de.ts und en.ts sind sofort verfügbar beim Laden — kein Flash of Untranslated Content.

Dynamisches Laden

Nach dem Mount lädt das Frontend die Übersetzungen von der API:

import { loadMessages } from '@/i18n/loader'

const msgs = await loadMessages('de')
// → API-Call mit ETag-Cache → localStorage-Cache

Sprachwechsel

import { useLocale } from '@/shell/composables/useLocale'

const { locale, languages, setLocale, toggleLocale } = useLocale()

// Verfügbare Sprachen (dynamisch von API)
// languages.value → [{code: 'de', label: 'Deutsch'}, ...]

setLocale('es')  // Lädt automatisch das spanische Bundle

Dateien

Datei Beschreibung
app/core_models/translation.py DB-Model (translations-Tabelle)
app/i18n/cache.py Redis-Cache-Layer
app/i18n/translator.py LibreTranslate-Client
app/i18n/service.py Translation-Service (Kern-Logik)
app/i18n/router.py API-Endpoints
app/i18n/__init__.py t(key, locale) Funktion
frontend/src/i18n/loader.ts API-basierter Loader
frontend/src/shell/composables/useLocale.ts Locale-Composable
frontend/src/shell/settings/SettingsLanguages.vue Admin-UI

Backend-Nutzung

from app.i18n import t

# In Endpoints
message = t("invoice.created", locale, number="2024-001")

# In E-Mail-Templates
subject = t("email.subject.invoice", user_locale, number=inv.number)