Inhaltsverzeichnis
Datenbanklokalisation: Strategien zur Speicherung mehrsprachiger Daten
Beim Entwickeln einer mehrsprachigen Anwendung gehört die Entscheidung, wie übersetzte Inhalte in der Datenbank gespeichert werden, zu den ersten Architekturentscheidungen. Der gewählte Ansatz beeinflusst die Abfrageleistung, die Schemakomplexität und wie einfach neue Sprachen hinzugefügt werden können.
Es gibt kein einzelnes „richtiges" Design — die beste Wahl hängt von der Anzahl der Sprachen, dem Volumen übersetzbarer Inhalte, den Abfragemustern und den Teamvorlieben ab. Dieser Leitfaden behandelt die drei häufigsten Strategien mit praktischen Beispielen.
Das Problem
Betrachten Sie einen Produktkatalog mit Namen, Beschreibungen und Kategorien. In einer einsprachigen Anwendung ist das Schema unkompliziert:
CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT, price DECIMAL(10,2), created_at TIMESTAMP DEFAULT NOW() );
Sobald mehrere Sprachen benötigt werden, wird jede Textspalte zu einer Lokalisierungsherausforderung. Die Spalten price und created_at sind sprachunabhängig, aber name und description benötigen separate Werte für jedes Locale.
Strategie 1: Spalte pro Locale
Der einfachste Ansatz fügt eine Spalte für jede Sprache hinzu:
CREATE TABLE products ( id SERIAL PRIMARY KEY, name_en VARCHAR(255) NOT NULL, name_de VARCHAR(255), name_fr VARCHAR(255), name_ja VARCHAR(255), description_en TEXT, description_de TEXT, description_fr TEXT, description_ja TEXT, price DECIMAL(10,2), created_at TIMESTAMP DEFAULT NOW() );
Abfragen
-- Produkt mit deutschen Übersetzungen abrufen, Fallback auf Englisch SELECT id, COALESCE(name_de, name_en) AS name, COALESCE(description_de, description_en) AS description, price FROM products WHERE id = 42;
Vorteile
- Einfache Abfragen — Keine Joins erforderlich. Alle Daten befinden sich in einer einzigen Zeile.
- Leicht verständlich — Das Schema ist selbstdokumentierend.
- Gute Leseleistung — Einzelner Tabellenscan ohne Joins.
Nachteile
- Schemaänderungen für neue Sprachen — Das Hinzufügen einer Sprache erfordert ein
ALTER TABLEfür jede übersetzbare Spalte. Bei einer Tabelle mit 5 übersetzbaren Feldern und 10 Sprachen sind das 50 Spalten. - Dünn besetzte Daten — Die meisten Zeilen enthalten NULL-Werte für Sprachen ohne Übersetzungen.
- Schwer skalierbar — Die Abfragekomplexität wächst linear mit der Anzahl der Sprachen.
Am besten geeignet für
Anwendungen mit einem kleinen, festen Sprachsatz (2–4 Sprachen), der sich selten ändert. Einfache Inhaltsmodelle mit wenigen übersetzbaren Feldern.
Strategie 2: Separate Übersetzungstabelle
Dieser Ansatz normalisiert Übersetzungen in eine dedizierte Tabelle:
CREATE TABLE products ( id SERIAL PRIMARY KEY, price DECIMAL(10,2), created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE product_translations ( id SERIAL PRIMARY KEY, product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE, locale VARCHAR(10) NOT NULL, name VARCHAR(255) NOT NULL, description TEXT, UNIQUE (product_id, locale) ); CREATE INDEX idx_product_translations_lookup ON product_translations (product_id, locale);
Abfragen
-- Produkt mit französischen Übersetzungen abrufen, Fallback auf Englisch SELECT p.id, COALESCE(t.name, fallback.name) AS name, COALESCE(t.description, fallback.description) AS description, p.price FROM products p LEFT JOIN product_translations t ON t.product_id = p.id AND t.locale = 'fr' LEFT JOIN product_translations fallback ON fallback.product_id = p.id AND fallback.locale = 'en' WHERE p.id = 42;
Vorteile
- Keine Schemaänderungen für neue Sprachen — Das Hinzufügen einer Sprache ist nur das Einfügen neuer Zeilen.
- Saubere Trennung — Sprachunabhängige Daten verbleiben in der Haupttabelle.
- Flexibel — Einfach abzufragen, welche Sprachen für eine Entität verfügbar sind.
- Standardmuster — Von ORMs und Frameworks gut verstanden. Rails, Django und Laravel verfügen alle über Bibliotheken, die dieses Muster unterstützen.
Nachteile
- Join erforderlich — Jede Abfrage für übersetzte Daten benötigt mindestens einen JOIN.
- Fallback-Komplexität — Sprach-Fallback-Ketten (z. B.
pt-BR→pt→en) erfordern mehrere JOINs oder Anwendungslogik. - Mehr Tabellen — Jede übersetzbare Entität benötigt eine eigene Übersetzungstabelle.
Am besten geeignet für
Anwendungen mit vielen Sprachen, Inhaltsmodellen, die im Laufe der Zeit wachsen, und Teams, die eine saubere Datenbanknormalisierung schätzen.
Strategie 3: JSON-Spalte
Moderne Datenbanken (PostgreSQL 9.4+, MySQL 5.7+, SQLite 3.38+) unterstützen JSON-Spalten:
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name JSONB NOT NULL DEFAULT '{}',
description JSONB DEFAULT '{}',
price DECIMAL(10,2),
created_at TIMESTAMP DEFAULT NOW()
);
-- Mit Übersetzungen einfügen
INSERT INTO products (name, description, price)
VALUES (
'{"en": "Wireless Headphones", "de": "Kabellose Kopfhörer", "fr": "Écouteurs sans fil"}',
'{"en": "Premium wireless headphones with noise cancellation.", "de": "Premium kabellose Kopfhörer mit Geräuschunterdrückung."}',
79.99
);
Abfragen (PostgreSQL)
-- Produkt mit französischen Übersetzungen abrufen, Fallback auf Englisch SELECT id, COALESCE(name->>'fr', name->>'en') AS name, COALESCE(description->>'fr', description->>'en') AS description, price FROM products WHERE id = 42;
Vorteile
- Keine Schemaänderungen — Neue Sprachen sind einfach neue JSON-Schlüssel.
- Keine Joins — Alle Übersetzungen befinden sich in derselben Zeile.
- Flexibel — Verschiedene Zeilen können unterschiedliche Sprachsets haben.
- GIN-Indizierung — PostgreSQL JSONB-Spalten unterstützen GIN-Indizes für effiziente Abfragen.
Nachteile
- Schwächere Typsicherheit — Die Datenbank kann nicht erzwingen, dass jedes JSON-Objekt die erforderlichen Schlüssel oder Werttypen aufweist.
- Keine referenzielle Integrität — Im Gegensatz zum Übersetzungstabellenansatz gibt es keinen Foreign Key zur Validierung von Locale-Codes.
- ORM-Unterstützung variiert — Einige ORMs behandeln JSON-Spalten nicht nativ und erfordern benutzerdefinierte Serialisierung.
- Volltextsuch-Komplexität — Die Suche über alle Sprachen in einer JSON-Spalte erfordert spezialisierte Indizierung.
Am besten geeignet für
Anwendungen, die PostgreSQL oder MySQL mit moderatem Übersetzungsvolumen verwenden, Teams, die ein minimales Schema bevorzugen, und Projekte, bei denen die Vollständigkeit der Übersetzungen sprachübergreifend erheblich variiert.
Vergleichszusammenfassung
| Kriterium | Spalte pro Locale | Übersetzungstabelle | JSON-Spalte |
|---|---|---|---|
| Kosten für neue Sprache | Schemamigration | Zeilen einfügen | Keine Änderung |
| Abfragekomplexität | Einfach | JOIN erforderlich | Mittel |
| Leseleistung | Ausgezeichnet | Gut (mit Index) | Gut |
| Typsicherheit | Vollständig | Vollständig | Teilweise |
| ORM-Unterstützung | Eingebaut | Weit verbreitet | Variiert |
| Am besten für | Wenige feste Sprachen | Viele Sprachen | Flexible Modelle |
Umgang mit Locale-Fallbacks
Unabhängig davon, welche Strategie Sie wählen, benötigt Ihre Anwendung eine Fallback-Kette für fehlende Übersetzungen. Ein gängiges Muster:
- Exakte Übereinstimmung (
fr-CAfür kanadisches Französisch) - Sprachübereinstimmung (
frfür generisches Französisch) - Standardsprache (
enfür Englisch)
Diese Fallback-Logik wird typischerweise in der Anwendungsschicht statt in SQL implementiert, da sie Benutzerpräferenzen und Locale-Aushandlung umfassen kann.
Häufig gestellte Fragen
Sollten übersetzbare und nicht übersetzbare Spalten in derselben Tabelle sein?
Das hängt von Ihren Abfragemustern ab. Wenn Sie häufig übersetzte Inhalte abfragen, reduziert das Halten in der Nähe der Entität die Anzahl der Joins. Wenn Sie häufig nur nicht übersetzbare Daten (Preise, Inventar) abfragen, vermeidet das Trennen von Übersetzungen das unnötige Laden von Daten.
Wie gehe ich mit der Volltextsuche über Sprachen hinweg um?
PostgreSQL unterstützt sprachspezifische tsvector-Konfigurationen. Erstellen Sie separate Textsuchindizes für jede Sprache oder verwenden Sie die simple-Konfiguration für sprachunabhängige Suche. Mit dem Übersetzungstabellenansatz können Sie eine tsvector-Spalte pro Zeile erstellen, da jede Zeile bereits eine Sprache repräsentiert.
Was ist mit NoSQL-Datenbanken?
Dokumentdatenbanken wie MongoDB unterstützen den JSON-Ansatz auf natürliche Weise — jedes Dokument bettet Übersetzungen als verschachtelte Objekte ein. Dies funktioniert gut für inhaltsintensive Anwendungen, fehlt aber den Garantien zur referenziellen Integrität von relationalen Datenbanken.
Wie verhält sich das zur Lokalisierung von UI-Strings?
Datenbanklokalisation behandelt benutzergenerierte oder vom Administrator verwaltete Inhalte (Produktnamen, Seitentitel, CMS-Inhalte). Die Lokalisierung von UI-Strings — Schaltflächenbeschriftungen, Fehlermeldungen, Navigationstext — wird typischerweise von i18n-Bibliotheken (react-intl, vue-i18n usw.) mit JSON oder anderen dateibasierten Formaten gehandhabt. Die meisten Anwendungen benötigen beides.