Ich entwickle eine mehrsprachige Software. In Bezug auf den Anwendungscode ist die Lokalisierbarkeit kein Problem. Wir können sprachspezifische Ressourcen verwenden und verfügen über alle Arten von Tools, die gut mit ihnen zusammenarbeiten.
Aber was ist der beste Ansatz bei der Definition eines mehrsprachigen Datenbankschemas? Angenommen, wir haben viele Tabellen (100 oder mehr), und jede Tabelle kann mehrere Spalten enthalten, die lokalisiert werden können (die meisten nvarchar-Spalten sollten lokalisierbar sein). Beispielsweise kann eine der Tabellen Produktinformationen enthalten:
CREATE TABLE T_PRODUCT (
NAME NVARCHAR(50),
DESCRIPTION NTEXT,
PRICE NUMBER(18, 2)
)
Ich kann mir drei Ansätze vorstellen, um mehrsprachigen Text in den Spalten NAME und DESCRIPTION zu unterstützen:
Separate Spalte für jede Sprache
Wenn wir dem System eine neue Sprache hinzufügen, müssen wir zusätzliche Spalten erstellen, um den übersetzten Text wie folgt zu speichern:
CREATE TABLE T_PRODUCT ( NAME_EN NVARCHAR(50), NAME_DE NVARCHAR(50), NAME_SP NVARCHAR(50), DESCRIPTION_EN NTEXT, DESCRIPTION_DE NTEXT, DESCRIPTION_SP NTEXT, PRICE NUMBER(18,2) )
Übersetzungstabelle mit Spalten für jede Sprache
Anstatt übersetzten Text zu speichern, wird nur ein Fremdschlüssel für die Übersetzungstabelle gespeichert. Die Übersetzungstabelle enthält eine Spalte für jede Sprache.
CREATE TABLE T_PRODUCT ( NAME_FK int, DESCRIPTION_FK int, PRICE NUMBER(18, 2) ) CREATE TABLE T_TRANSLATION ( TRANSLATION_ID, TEXT_EN NTEXT, TEXT_DE NTEXT, TEXT_SP NTEXT )
Übersetzungstabellen mit Zeilen für jede Sprache
Anstatt übersetzten Text zu speichern, wird nur ein Fremdschlüssel für die Übersetzungstabelle gespeichert. Die Übersetzungstabelle enthält nur einen Schlüssel, und eine separate Tabelle enthält eine Zeile für jede Übersetzung in eine Sprache.
CREATE TABLE T_PRODUCT ( NAME_FK int, DESCRIPTION_FK int, PRICE NUMBER(18, 2) ) CREATE TABLE T_TRANSLATION ( TRANSLATION_ID ) CREATE TABLE T_TRANSLATION_ENTRY ( TRANSLATION_FK, LANGUAGE_FK, TRANSLATED_TEXT NTEXT ) CREATE TABLE T_TRANSLATION_LANGUAGE ( LANGUAGE_ID, LANGUAGE_CODE CHAR(2) )
Jede Lösung hat Vor- und Nachteile, und ich würde gerne wissen, welche Erfahrungen Sie mit diesen Ansätzen gemacht haben, was Sie empfehlen und wie Sie ein mehrsprachiges Datenbankschema entwerfen würden.
LANGUAGE_CODE
sind natürliche Schlüssel, vermeidenLANGUAGE_ID
.Antworten:
Was halten Sie von einer zugehörigen Übersetzungstabelle für jede übersetzbare Tabelle?
Wenn Sie über mehrere übersetzbare Spalten verfügen, ist nur ein einziger Join erforderlich, um diese + zu erhalten. Da Sie eine Übersetzungs-ID nicht automatisch generieren, ist es möglicherweise einfacher, Elemente zusammen mit den zugehörigen Übersetzungen zu importieren.
Die negative Seite davon ist, dass Sie bei einem komplexen Sprach-Fallback-Mechanismus diesen möglicherweise für jede Übersetzungstabelle implementieren müssen - wenn Sie sich dazu auf eine gespeicherte Prozedur verlassen. Wenn Sie dies über die App tun, ist dies wahrscheinlich kein Problem.
Lassen Sie mich wissen, was Sie denken - ich bin auch dabei, dies für unsere nächste Bewerbung zu entscheiden. Bisher haben wir Ihren 3. Typ verwendet.
quelle
T_PRODUCT
hat 1 Million Zeilen,T_PRODUCT_tr
hätte 2 Millionen. Würde es die SQL-Effizienz stark reduzieren?Dies ist ein interessantes Thema, also lasst uns nekromantieren.
Beginnen wir mit den Problemen von Methode 1:
Problem: Sie denormalisieren, um Geschwindigkeit zu sparen.
In SQL (außer PostGreSQL mit hstore) können Sie keine Parametersprache übergeben und sagen:
Also musst du das machen:
Das heißt, Sie müssen ALLE Ihre Abfragen ändern, wenn Sie eine neue Sprache hinzufügen. Dies führt natürlich zur Verwendung von "dynamischem SQL", sodass Sie nicht alle Ihre Abfragen ändern müssen.
Dies führt normalerweise zu so etwas (und kann übrigens nicht in Ansichten oder Funktionen mit Tabellenwerten verwendet werden, was wirklich ein Problem ist, wenn Sie das Berichtsdatum tatsächlich filtern müssen).
Das Problem dabei ist:
a) Die Datumsformatierung ist sehr sprachspezifisch, sodass Sie dort ein Problem erhalten, wenn Sie nicht im ISO-Format eingeben (was der durchschnittliche Programmierer für Gartensorten normalerweise nicht tut, und im Fall von Ein Bericht, den der Benutzer verdammt noch mal nicht für Sie tun wird, auch wenn er ausdrücklich dazu aufgefordert wird.
und
b) am wichtigsten ist , dass Sie jede Art von Syntaxprüfung verlieren . Wenn
<insert name of your "favourite" person here>
sich das Schema ändert, weil sich plötzlich die Anforderungen für den Flügel ändern und eine neue Tabelle erstellt wird, die alte übrig bleibt, aber das Referenzfeld umbenannt wird, erhalten Sie keine Warnung. Ein Bericht funktioniert auch, wenn Sie ihn ausführen, ohne den Wing-Parameter (==> guid.empty) auszuwählen. Aber plötzlich, wenn ein tatsächlicher Benutzer tatsächlich einen Flügel auswählt ==>Boom . Diese Methode unterbricht jede Art von Test vollständig.Methode 2:
Kurz gesagt: "Großartige" Idee (Warnung - Sarkasmus), kombinieren wir die Nachteile von Methode 3 (langsame Geschwindigkeit bei vielen Einträgen) mit den ziemlich schrecklichen Nachteilen von Methode 1.
Der einzige Vorteil dieser Methode besteht darin, dass Sie sie beibehalten Alle Übersetzungen in einer Tabelle und vereinfachen daher die Wartung. Dasselbe kann jedoch mit Methode 1 und einer gespeicherten dynamischen SQL-Prozedur sowie einer (möglicherweise temporären) Tabelle erreicht werden, die die Übersetzungen und den Namen der Zieltabelle enthält (und ist recht einfach, vorausgesetzt, Sie haben alle Ihre Textfelder mit benannt gleich).
Methode 3:
Eine Tabelle für alle Übersetzungen: Nachteil: Sie müssen n Fremdschlüssel in der Produkttabelle für n Felder speichern, die Sie übersetzen möchten. Daher müssen Sie n Verknüpfungen für n Felder ausführen. Wenn die Übersetzungstabelle global ist, enthält sie viele Einträge und Verknüpfungen werden langsam. Außerdem müssen Sie für n Felder immer n-mal der Tabelle T_TRANSLATION beitreten. Dies ist ein ziemlicher Aufwand. Was tun Sie nun, wenn Sie benutzerdefinierte Übersetzungen pro Kunde vornehmen müssen? Sie müssen weitere 2x n Verknüpfungen zu einer zusätzlichen Tabelle hinzufügen. Wenn Sie beitreten müssen, sagen wir 10 Tabellen, mit 2x2xn = 4n zusätzlichen Verknüpfungen, was für ein Durcheinander! Dieses Design ermöglicht es auch, dieselbe Übersetzung mit 2 Tabellen zu verwenden. Wenn ich den Elementnamen in einer Tabelle ändere, möchte ich dann wirklich JEDES MAL einen Eintrag in einer anderen Tabelle ändern?
Außerdem können Sie die Tabelle nicht mehr löschen und erneut einfügen, da es jetzt Fremdschlüssel in den Produkttabellen gibt. Sie können natürlich das Festlegen der FKs weglassen und dann
<insert name of your "favourite" person here>
die Tabelle löschen und erneut einfügen alle Einträge mit newid () [oder durch Angabe der ID in der Einfügung, aber Identitätseinfügung ], und das würde (und wird) sehr bald ) führen.Methode 4 (nicht aufgeführt): Speichern aller Sprachen in einem XML-Feld in der Datenbank. z.B
Dann können Sie den Wert durch XPath-Query in SQL erhalten, wo Sie die String-Variable eingeben können
Und Sie können den Wert folgendermaßen aktualisieren:
Wo Sie ersetzen können
/lang/de/...
mit'.../' + @in_language + '/...'
Ähnlich wie der PostGre-Hstore, nur dass er aufgrund des Overheads beim Parsen von XML (anstatt einen Eintrag aus einem assoziativen Array im PG-Hstore zu lesen) viel zu langsam wird und die XML-Codierung ihn zu schmerzhaft macht, um nützlich zu sein.
Methode 5 (wie von SunWuKung empfohlen, die Sie auswählen sollten): Eine Übersetzungstabelle für jede "Produkt" -Tabelle. Das bedeutet eine Zeile pro Sprache und mehrere "Text" -Felder, sodass nur EIN (linker) Join für N Felder erforderlich ist. Dann können Sie einfach ein Standardfeld in die "Produkt" -Tabelle einfügen, die Übersetzungstabelle einfach löschen und erneut einfügen und eine zweite Tabelle für benutzerdefinierte Übersetzungen (bei Bedarf) erstellen, die Sie auch löschen können und erneut einfügen), und Sie haben immer noch alle Fremdschlüssel.
Lassen Sie uns ein Beispiel machen, um diese WERKE zu sehen:
Erstellen Sie zunächst die Tabellen:
Dann geben Sie die Daten ein
Und dann die Daten abfragen:
Wenn Sie faul sind, können Sie auch den ISO-TwoLetterName ('DE', 'EN' usw.) als Primärschlüssel für die Sprachtabelle verwenden. Dann müssen Sie die Sprach-ID nicht nachschlagen. Aber wenn Sie dies tun, möchten Sie vielleicht stattdessen das IETF-Sprach-Tag verwenden, was besser ist, weil Sie de-CH und de-DE erhalten, was in Bezug auf die Ortographie wirklich nicht gleich ist (doppelte s statt ß überall). , obwohl es die gleiche Basissprache ist. Dies ist nur ein kleines Detail, das für Sie wichtig sein kann, insbesondere wenn man bedenkt, dass en-US und en-GB / en-CA / en-AU oder fr-FR / fr-CA ähnliche Probleme haben.
Quote: Wir brauchen es nicht, wir machen unsere Software nur auf Englisch.
Antwort: Ja - aber welches?
Wenn Sie eine Ganzzahl-ID verwenden, sind Sie auf jeden Fall flexibel und können Ihre Methode zu einem späteren Zeitpunkt ändern.
Und Sie sollten diese Ganzzahl verwenden, denn nichts ist ärgerlicher, destruktiver und problematischer als ein verpfuschtes Db-Design.
Siehe auch RFC 5646 , ISO 639-2 ,
Und wenn Sie immer noch sagen „wir“ nur unsere Anwendung machen für „nur eine Kultur“ (wie en-US in der Regel) - deshalb brauche ich nicht , dass zusätzliche integer, das ist eine gute Zeit und Ort wäre das zu erwähnen , IANA-Sprach-Tags , nicht wahr?
Weil sie so gehen:
und
(1996 gab es eine Rechtschreibreform ...) Versuchen Sie, ein Wort in einem Wörterbuch zu finden, wenn es falsch geschrieben ist. Dies wird sehr wichtig bei Anwendungen, die sich mit rechtlichen und öffentlich-rechtlichen Portalen befassen.
Noch wichtiger ist, dass es Regionen gibt, die von kyrillischen zu lateinischen Alphabeten wechseln. Dies ist möglicherweise problematischer als das oberflächliche Ärgernis einer obskuren Rechtschreibreform, weshalb dies je nach Land, in dem Sie leben, ebenfalls eine wichtige Überlegung sein kann. Auf die eine oder andere Weise ist es besser, diese ganze Zahl dort zu haben, nur für den Fall ...
Bearbeiten:
Und durch Hinzufügen
ON DELETE CASCADE
nachman kann einfach sagen:
DELETE FROM T_Products
und erhalten keine Fremdschlüsselverletzung.Die Zusammenstellung würde ich folgendermaßen machen:
A) Haben Sie Ihren eigenen DAL.
B) Speichern Sie den gewünschten Sortiernamen in der Sprachtabelle
Möglicherweise möchten Sie die Kollatierungen in eine eigene Tabelle einfügen, z.
C) Halten Sie den Kollatierungsnamen in Ihren auth.user.language-Informationen bereit
D) Schreiben Sie Ihre SQL wie folgt:
E) Dann können Sie dies in Ihrem DAL tun:
Damit erhalten Sie diese perfekt zusammengestellte SQL-Abfrage
quelle
Die dritte Option ist aus mehreren Gründen die beste:
-Adam
quelle
Schauen Sie sich dieses Beispiel an:
Ich denke, es gibt keinen Grund zu erklären, die Struktur beschreibt sich selbst.
quelle
Normalerweise würde ich diesen Ansatz wählen (nicht die tatsächliche SQL), dies entspricht Ihrer letzten Option.
Weil alle übersetzbaren Texte an einem Ort sind, was die Wartung so viel einfacher macht. Manchmal werden Übersetzungen an Übersetzungsbüros ausgelagert. Auf diese Weise können Sie ihnen nur eine große Exportdatei senden und sie genauso einfach zurück importieren.
quelle
Translation
Tabelle oder dieTranslationItem.translationitemid
Spalte?Bevor Sie zu technischen Details und Lösungen gehen, sollten Sie eine Minute innehalten und einige Fragen zu den Anforderungen stellen. Die Antworten können einen großen Einfluss auf die technische Lösung haben. Beispiele für solche Fragen wären:
- Werden alle Sprachen ständig verwendet?
- Wer und wann füllt die Spalten mit den verschiedenen Sprachversionen?
- Was passiert, wenn ein Benutzer eine bestimmte Sprache eines Textes benötigt und keine im System vorhanden ist?
- Nur die Texte sollen lokalisiert werden oder es gibt auch andere Elemente (zum Beispiel kann PREIS in $ und € gespeichert werden, weil sie unterschiedlich sein können)
quelle
Ich habe nach Tipps zur Lokalisierung gesucht und dieses Thema gefunden. Ich habe mich gefragt, warum dies verwendet wird:
Sie erhalten also so etwas wie user39603 vorschlägt:
Kannst du nicht einfach die Tabelle weglassen?
quelle
ProductItem
Tisch soProductTexts
oder so nennenProductL10n
. Macht mehr Sinn.Ich stimme dem Randomizer zu. Ich verstehe nicht, warum Sie eine Tabelle "Übersetzung" benötigen.
Ich denke, das ist genug:
quelle
Wäre der folgende Ansatz praktikabel? Angenommen, Sie haben Tabellen, in denen mehr als eine Spalte übersetzt werden muss. Für das Produkt können Sie also sowohl den Produktnamen als auch die Produktbeschreibung haben, die übersetzt werden müssen. Könnten Sie Folgendes tun:
quelle
"Welches ist das Beste" basiert auf der Projektsituation. Die erste ist einfach auszuwählen und zu warten, und auch die Leistung ist am besten, da bei der Auswahl der Entität keine Tabellen verknüpft werden müssen. Wenn Sie bestätigt haben, dass Ihr Projekt nur 2 oder 3 Sprachen unterstützt und es nicht zunimmt, können Sie es verwenden.
Der zweite ist ok, aber schwer zu verstehen und zu pflegen. Und die Leistung ist schlechter als die erste.
Der letzte ist gut skalierbar, aber schlecht in der Leistung. Die Tabelle T_TRANSLATION_ENTRY wird immer größer. Es ist schrecklich, wenn Sie eine Liste von Entitäten aus einigen Tabellen abrufen möchten.
quelle
Dieses Dokument beschreibt die möglichen Lösungen sowie die Vor- und Nachteile der einzelnen Methoden. Ich bevorzuge die "Zeilenlokalisierung", da Sie das DB-Schema beim Hinzufügen einer neuen Sprache nicht ändern müssen.
quelle