TL;DR: Multi-Language-Datenbankdesign bedeutet die Wahl zwischen Translation Tables, Locale-Spalten und JSON-Speicherung. Dieser Leitfaden behandelt Schema-Muster, Fallback-Strategien und Abfrage-Optimierung für SaaS-i18n. Beispiele von tanstackship.com.
Einführung
Wenn Ihre SaaS-Anwendung mehrere Sprachen unterstützen muss, ist das Datenbankschema das Fundament. Ein schlechtes Design führt zu komplexen Abfragen, Performance-Problemen und Wartungsaufwand. Der richtige Ansatz hängt von den Inhaltstypen und den Zugriffsmustern ab. Für die übergreifende i18n-Architektur empfehlen wir i18n für SaaS: Architekturmuster im Vergleich.
Schema-Design-Muster
| Muster | Flexibilität | Abfragekomplexität | Performance | Optimal für |
|---|---|---|---|---|
| Translation Table | Hoch | Mittel | Gut | Dynamische Inhalte |
| Locale-Spalten | Gering | Gering | Ausgezeichnet | Fixe wenige Locales |
| JSON/JSONB-Spalte | Mittel | Gering | Gut | Einfache Übersetzungen |
| EAV (Entity-Attribute-Value) | Sehr hoch | Hoch | Schlecht | Nicht empfohlen |
| Separate DB pro Locale | Maximal | Hoch | Variiert | Enterprise-Multi-Region |
Translation-Table-Muster (Empfohlen)
-- Basis-Content-Tabelle (sprachunabhängig)
CREATE TABLE blog_posts (
id TEXT PRIMARY KEY,
slug TEXT NOT NULL UNIQUE,
author_id TEXT NOT NULL,
published_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW()
);
-- Übersetzungstabelle (eine Zeile pro Locale)
CREATE TABLE blog_post_translations (
blog_post_id TEXT NOT NULL REFERENCES blog_posts(id),
locale TEXT NOT NULL,
title TEXT NOT NULL,
excerpt TEXT,
body TEXT NOT NULL,
meta_description TEXT,
is_auto_translated BOOLEAN DEFAULT false,
updated_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (blog_post_id, locale)
);
-- Abfrage mit Fallback-Kette
SELECT
COALESCE(t.title, f.title) as title,
COALESCE(t.body, f.body) as body
FROM blog_posts p
LEFT JOIN blog_post_translations t
ON t.blog_post_id = p.id AND t.locale = 'de'
LEFT JOIN blog_post_translations f
ON f.blog_post_id = p.id AND f.locale = 'en'
WHERE p.slug = 'getting-started';
Locale-Spalten-Muster (Einfach, wenige Locales)
-- Locale-Spalten: einfach, aber nicht skalierbar
CREATE TABLE products (
id TEXT PRIMARY KEY,
name_en TEXT NOT NULL,
name_zh TEXT,
name_de TEXT,
name_ja TEXT,
description_en TEXT NOT NULL,
description_zh TEXT,
description_de TEXT,
description_ja TEXT,
price DECIMAL(10, 2) NOT NULL
);
Geeignet für 2–3 Locales mit einfachen Schemata. Bei mehr Sprachen wird es unhandlich.
JSONB-Muster (Flexibel)
-- JSONB-Übersetzungen: flexibel, aber schwerer abzufragen
CREATE TABLE pages (
id TEXT PRIMARY KEY,
slug TEXT NOT NULL,
translations JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMP DEFAULT NOW()
);
-- Beispiel-JSONB-Inhalt
{
"en": {
"title": "About Us",
"body": "<h1>Our Story</h1><p>...</p>"
},
"de": {
"title": "Über Uns",
"body": "<h1>Unsere Geschichte</h1><p>...</p>"
},
"zh": {
"title": "关于我们",
"body": "<h1>我们的故事</h1><p>...</p>"
}
}
Performance-Optimierung
| Strategie | Wirkung | Implementierung |
|---|---|---|
| Zusammengesetzter Index auf (locale, slug) | Hoch | CREATE INDEX idx_locale_slug ON blog_post_translations(locale, blog_post_id) |
| Materialisierte View für aktive Locales | Mittel | Häufige Locale-Paare vorab verknüpfen |
| Redis-Cache für Übersetzungsabfragen | Hoch | Nach erstem Datenbank-Lookup cachen |
| Fallback-Tiefe begrenzen | Mittel | Max. 2-stufiger Fallback (Ziel → en) |
| Alle Übersetzungen gebündelt laden | Hoch | Einzelne Abfrage für alle Locales, in der App filtern |
Fazit
Das Translation-Table-Muster ist für die meisten SaaS-Anwendungen optimal – es skaliert auf beliebig viele Locales, unterstützt Fallback-Ketten und integriert sich gut mit ORMs. Locale-Spalten nur für einfache Fälle mit 2–3 Sprachen verwenden. EAV-Muster vollständig vermeiden. Indizes auf Locale- + Lookup-Key-Kombinationen immer setzen.
Für Datenmodellierung über i18n hinaus empfiehlt sich Datenmodellierung: Benutzer, Organisationen und Berechtigungen. Best Practices für SQL-Indizierung behandelt SQL-Indizierungsstrategien für Webanwendungen, und Datenbank-Migrationen: Strategien für Zero-Downtime bietet Orientierung für Schema-Änderungen im Deployment.