Während einige Softwaresysteme von einer begrenzten Anzahl von Benutzern verwendet werden, die dieselbe Sprache sprechen, müssen die meisten Organisationen ihre Anwendungen vereinheitlichen und zentralisieren, damit sie von Personen verwendet werden können, die auf der ganzen Welt verschiedene Sprachen sprechen. Mehrsprachige Datenbanken stellen eine zusätzliche Schwierigkeitsstufe beim Entwerfen und Implementieren von Datenmodellen dar. In diesem Artikel schlagen wir einige Ansätze vor, um mit dieser Herausforderung umzugehen.
Welche Informationen müssen wir in mehreren Sprachen speichern?
Oberflächlich betrachtet mögen alle Zeichenfolgeninformationen für die Übersetzung in mehrere Sprachen plausibel erscheinen. Dies ist jedoch in der Regel nicht der Fall. Kundenbezogene Informationen wie CompanyName
oder Address
kann übersetzt werden, aber das ist vielleicht keine gute Idee.
Nehmen Sie einen Geschäftskunden in Großbritannien namens „Riverside Trucks“ mit einem Büro in „123 Upper Castle Road“. Sie möchten nicht, dass ein spanischsprachiger Benutzer einen Brief ausdruckt und an „Camiones Orilla“ in „123 Calle Castillo Superior“ sendet. Royal Mail (der britische Postdienst) findet es nicht! Wahrscheinlich möchten Sie nur die Spalten übersetzen, die beschreibende Informationen enthalten, keine Eigennamen.
Beim Entwerfen eines Systems zum Handhaben von Übersetzungen ist nicht immer im Voraus genau bekannt, welche Spalten übersetzbar sind und welche keine Übersetzungen erfordern. Die Wahl eines flexiblen Ansatzes spart viel Zeit in Design und Entwicklung. Werfen Sie einen Blick auf den Artikel „How to Design a Localization-Ready System“, um einige Beispiele zu sehen.
Welche Ansätze ziehen wir in Betracht?
In diesem Artikel beschreiben wir drei Ansätze für das mehrsprachige Datenbankdesign. Wir beginnen mit der einfachsten, die nicht so flexibel ist, und ziehen dann andere Optionen in Betracht, wobei wir jeweils die Vor- und Nachteile erläutern.
Sowohl die Syntax als auch die Datenbankmodelle (verfügbar auf dem webbasierten Datenmodellierer von Vertabelo), die in diesem Artikel verwendet werden, gelten für SQL Server. Sie lassen sich jedoch leicht an jede Datenbank-Engine anpassen.
Ansatz 1:Zusätzliche Spalten für übersetzte Inhalte erstellen
Dies ist der am einfachsten zu implementierende Ansatz, wenn auch nicht sehr flexibel. Es besteht aus dem Hinzufügen einer Spalte für jede Spalte und Sprache, die wir in unserem System verwenden müssen, wie im folgenden Vertabelo-Diagramm gezeigt:
Obwohl dies wie eine sehr einfache Lösung erscheinen mag, hat es einige Nachteile. Wir erklären es weiter unten.
Con:Code-Komplexität
Dieser Ansatz macht den Code komplexer. Es erfordert, dass wir entweder für jede Sprache eine andere Abfrage schreiben oder einen CASE
verwenden Konstrukt zum Abrufen der Übersetzung für die entsprechende Sprache basierend auf der Benutzerkonfiguration. Siehe zum Beispiel den folgenden Code:
SELECT ProductID, CASE @Language WHEN 'ES' THEN ProductName_ES WHEN 'DE' THEN ProductName_DE WHEN 'FR' THEN ProductName_FR ELSE ProductName END AS ProductName, CASE @Language WHEN 'ES' THEN ProductDescription_ES WHEN 'DE' THEN ProductDescription_DE WHEN ' FR' THEN ProductDescription_FR ELSE ProductDescription END AS ProductDescription, Price, Weight, ProductCategoryIDFROM ProductWHERE …
Hinweis: Im Beispiel verwenden wir die Variable @Language, um die gewünschte Sprache beizubehalten. Sie können SESSION_CONTEXT() (oder Anwendungskontext in Oracle) verwenden, um die Sprache für jeden Benutzer einzustellen und zu lesen.
Contra:Mangel an Flexibilität
Diesem Ansatz mangelt es an Flexibilität. Wenn wir eine neue Sprache implementieren müssen, müssen wir unser Datenmodell ändern, indem wir für jede übersetzbare Spalte in unserem System eine Spalte für die neue Sprache hinzufügen. Außerdem müssen wir für jede Tabelle eine neue Sprachabfrage erstellen (oder die vorhandene bearbeiten, indem wir einen neuen CASE WHEN
hinzufügen Klausel, die die neue Sprache für jede übersetzbare Spalte verwendet).
Con:Herausforderungen beim Umgang mit unbekannten Informationen
Stellen Sie sich Folgendes vor:Ein Benutzer fügt ein Produkt hinzu, weiß aber nicht, wie er es übersetzen soll, und lässt die übersetzten Spalten leer. Benutzer, die diese Sprachen sprechen, sehen NULL
oder leere Informationen in den Spalten, die möglicherweise erforderlich sind.
Ansatz 2:Übersetzbare Spalten in einer separaten Tabelle isolieren
Dieser Ansatz gruppiert die Spalten in einer Tabelle in übersetzbare und nicht übersetzbare Spalten. Nicht übersetzbare Spalten bleiben in der ursprünglichen Tabelle. Im Gegensatz dazu befinden sich die übersetzbaren in einer separaten Tabelle mit einem Fremdschlüssel zur ursprünglichen Tabelle und einem Sprachindikator. Siehe unten:
Das Diagramm zeigt die Originaltabellen (ohne übersetzbare Daten) in Weiß. Die Tabellen mit den Übersetzungen sind hellblau, die Haupttabelle mit den Sprachinformationen gelb.
Dies hat enorme Flexibilitätsvorteile im Vergleich zur Verwendung mehrerer Spalten, wie zuvor besprochen. Diese Methode erfordert keine Änderung des Datenmodells, wenn eine neue Sprache benötigt wird. Außerdem ist die Syntax zum Abfragen der Informationen einfacher:
SELECT p.ProductID, pt.ProductName, pt.ProductDescription, p.Price, p.Weight, p.ProductCategoryIDFROM Product pLEFT JOIN ProductTranslation pt ON pt.ProductID =p.ProductID AND pt.LanguageID =@LanguageWHERE …Es gibt jedoch noch einige Nachteile, wie wir weiter unten besprechen werden.
Con:Herausforderungen, wenn zusätzliche Spalten übersetzt werden müssen
Wenn wir eine nicht übersetzbare Spalte in eine übersetzbare umwandeln müssen (oder umgekehrt), müssen wir unser Datenmodell ändern und die Spalte von einer Tabelle in die andere verschieben. Dies bedeutet normalerweise höhere Kosten, sobald das System implementiert und in Gebrauch ist.
Con:Herausforderungen beim Umgang mit unbekannten Informationen
Wie der erste Ansatz hat auch dieser Ansatz Herausforderungen beim Umgang mit unbekannten Informationen. Wenn ein Benutzer ein Produkt hinzufügt, aber nicht weiß, wie er es übersetzen soll, und die übersetzten Spalten leer lässt, sehen Benutzer, die diese Sprachen sprechen,
NULL
oder leere Informationen in Spalten, die erforderlich sein können. Außerdem erfordert die Abfrage einenLEFT JOIN
für den Fall, dass die Übersetzung für die Sprache des aktuellen Benutzers noch nicht erstellt wurde, sodass die nicht übersetzbaren Daten weiterhin angezeigt werden.Ansatz 3:Hinzufügen eines Übersetzungssubsystems
Die Übersetzung kann als eine Funktion betrachtet werden, die völlig unabhängig von dem zu übersetzenden Datenmodell ist. In einem idealen System können wir die Übersetzung für jede Spalte aktivieren oder deaktivieren, ohne dass eine Änderung des Datenmodells erforderlich ist. Leider ist das leichter gesagt als getan.
Wir stellen eine Methode vor, die keinen Einfluss auf das bestehende Datenmodell hat und vollkommen flexibel ist. Obwohl es zum Zeitpunkt der Datenabfrage die Komplexität erhöht, erfordert es außer einigen Tabellen keine zusätzlichen Änderungen am Datenmodell. Dies kann eine gute Wahl sein, wenn Sie die Fähigkeit zum Speichern von Übersetzungen zu einem vorhandenen Datenmodell hinzufügen müssen.
Werfen wir einen Blick auf das Modell und sehen, wie es funktioniert:
Als erstes fällt auf, dass das ursprüngliche Datenmodell überhaupt keine Änderungen aufweist. Außerdem besteht keine direkte Beziehung zwischen diesem Modell und dem Übersetzungssubsystem.
Das Übersetzungssubsystem enthält ein kleines Datenwörterbuch mit den zu übersetzenden Tabellen und Spalten. Dieses Datenwörterbuch kann geändert werden, indem einfach Zeilen hinzugefügt/entfernt werden, ohne das Datenmodell zu ändern. Übersetzungen werden in einer separaten Tabelle gespeichert, wobei jeder Wert durch die folgenden 3 Spalten identifiziert wird:
ColumnID
:Identifiziert eindeutig die Spalte (und die Tabelle), die wir übersetzen.KeyID
:Speichert die ID (Primärschlüssel) der bestimmten Zeile, die wir übersetzen.LanguageID
:Identifiziert die Sprache der Übersetzung.
Dieses Design ermöglicht die Eingabe und Speicherung von Daten in den Originaltabellen, wobei Übersetzungen nur bei Bedarf hinzugefügt werden. Die übersetzten Informationen werden beim Abrufen von Daten verwendet, wobei die Originaldaten (in der Originalsprache) unverändert bleiben.
Daten können mit einer komplexeren Syntax als in den obigen Beispielen abgefragt werden. Es erfordert ein zusätzliches JOIN
für jede übersetzbare Spalte wie unten gezeigt:
SELECT p.ProductID, ISNULL(t1.TranslationValue, p.ProductName) AS ProductName, ISNULL(t2.TranslationValue, p.ProductDescription) AS ProductDescription, p.Price, p.Weight, p.ProductCategoryIDFROM Product pLEFT JOIN Translation t1 ON t1.ColumnID =<> AND t1.Key =p.ProductID AND t1.LanguageID =@LanguageLEFT JOIN Translation t2 ON t2.ColumnID =< > AND t2.Key =p.ProductID AND t2.LanguageID =@LanguageWHERE …;
Hinweis: „<<ProductName_ColumnID>>
“ und „<<ProductDescription_ColumnID>>
” muss durch die IDs der zu übersetzenden Spalten ersetzt werden, wie sie in ColumnInformation
gespeichert sind Tisch. Erwägen Sie, Übersetzungsansichten für jede zu übersetzende Tabelle zu generieren, um die Komplexität der JOINs für die Endbenutzer zu verbergen. Sie können diesen Schritt sogar mit einem Skript automatisieren, das jede Ansicht generiert. Dieses Skript kann das Datenwörterbuch der Datenbank abfragen, um die Tabellen und Spalten auszuwählen, und die Übersetzungslogik für die Spalten hinzufügen, die in ColumnInformation
vorhanden sind Tabelle.
Extra-Tipp Nr. 1
Sie können die Syntax auch vereinfachen. Ersetzen Sie jeden JOIN durch einen Aufruf einer Funktion, die den Übersetzungsaspekt behandelt (und verbirgt), wie unten gezeigt:
SELECT p.ProductID, ISNULL(fn_translate('Product','ProductName',ProductID), p.ProductName) AS ProductName, ISNULL(fn_translate('Product','ProductDescription',ProductID), p.ProductDescription) AS ProductName, p.Price, p.Weight, p.ProductCategoryIDFROM Product pWHERE …;
Die gewünschte Sprache kann die Funktion aus dem Kontext lesen oder als zusätzlichen Parameter hinzufügen. In diesem Beispiel verwendet die Funktion die als Parameter bereitgestellten Tabellen- und Spaltennamen sowie den Zeilenschlüssel (ebenfalls als Parameter bereitgestellt), um die gewünschte Übersetzung zu suchen und zurückzugeben.
Das Aufrufen einer Funktion impliziert eine zusätzliche Auswirkung auf die Leistung aufgrund des Kontextwechsels zwischen SQL und prozeduraler Sprache. Es kann jedoch eine einfachere Lösung für Datenbanken oder Tabellen sein, wenn die zu übersetzende Datenmenge dies zulässt.
Beide Beispiele – das mit einem JOIN und das mit einer Funktion – verwenden die SQL Server-Funktion ISNULL(). Wenn also die Übersetzung in die gewünschte Sprache nicht vorhanden ist, wird immer noch der ursprüngliche Wert angezeigt, der in den Spalten ProductName und ProductDescription gespeichert ist, anstelle von Leerzeichen oder NULL.
Allgemeine Überlegungen
Der dritte Ansatz ist normalerweise der beste für die Implementierung größerer Designs. Es ermöglicht Flexibilität sowohl beim Design als auch nach der Verwendung des Systems. Es gibt jedoch bestimmte Überlegungen, die die anderen Ansätze nützlich machen können. Unabhängig von Ihrer Wahl sollten Sie Folgendes berücksichtigen, um sowohl beim Entwurf als auch bei der Entwicklung/Implementierung Zeit zu sparen.
Fügen Sie eine Abstraktionsebene hinzu
Wie bereits erwähnt, sollten Sie Ansichten erstellen, die sich um die Übersetzungslogik kümmern, z. B. das Auswählen einer Spalte aus mehreren Übersetzungsspalten oder das Verbinden mit bestimmten Zeilen. Dies hält spezifische Implementierungsdetails vor Programmierern verborgen. Sie verwenden einfach diese Ansichten, anstatt jedes Mal komplexe SQL-Sätze erstellen zu müssen, wenn sie auf eine Tabelle mit übersetzbaren Informationen zugreifen müssen.
Kontext zum Filtern von Daten verwenden
Wie im ersten Beispiel erwähnt, sollten Sie die Verwendung von Kontextfunktionen in Betracht ziehen, die in den meisten Datenbank-Engines verfügbar sind. Verwenden Sie sie, um die Sprachinformationen der Benutzer zu speichern, sobald Sie sich im System angemeldet haben, und filtern Sie die Ergebnisse dann automatisch in den Ansichten, die sich um die Übersetzung kümmern.
Automatisieren
Moderne Systeme können Hunderte und sogar Tausende von Tabellen haben. Nehmen Sie sich Zeit, um die Übersetzungsansichten zu automatisieren, anstatt die Abfragen einzeln zu schreiben. Es kann einige Zeit dauern, bis Sie zu einem funktionierenden Skript gelangen, aber dann können Sie jederzeit neue Ansichten erstellen oder vorhandene in weniger als einer Sekunde neu erstellen!