Tworzę oprogramowanie wielojęzyczne. Jeśli chodzi o kod aplikacji, lokalizacja nie jest problemem. Możemy korzystać z zasobów specyficznych dla języka i mieć wszelkiego rodzaju narzędzia, które dobrze z nimi współpracują.
Ale jakie jest najlepsze podejście do definiowania wielojęzycznego schematu bazy danych? Załóżmy, że mamy wiele tabel (100 lub więcej) i każda tabela może mieć wiele kolumn, które można zlokalizować (większość kolumn nvarchar powinna być zlokalizowana). Na przykład jedna z tabel może zawierać informacje o produkcie:
CREATE TABLE T_PRODUCT (
NAME NVARCHAR(50),
DESCRIPTION NTEXT,
PRICE NUMBER(18, 2)
)
Mogę wymyślić trzy podejścia do obsługi tekstu wielojęzycznego w kolumnach NAME i DESCRIPTION:
Oddzielna kolumna dla każdego języka
Kiedy dodajemy nowy język do systemu, musimy utworzyć dodatkowe kolumny do przechowywania przetłumaczonego tekstu, takie jak to:
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) )
Tabela tłumaczeń z kolumnami dla każdego języka
Zamiast przechowywać przetłumaczony tekst, przechowywany jest tylko klucz obcy do tabeli tłumaczeń. Tabela tłumaczeń zawiera kolumnę dla każdego języka.
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 )
Tabele tłumaczeń z wierszami dla każdego języka
Zamiast przechowywać przetłumaczony tekst, przechowywany jest tylko klucz obcy do tabeli tłumaczeń. Tabela tłumaczeń zawiera tylko klucz, a osobna tabela zawiera wiersz dla każdego tłumaczenia na język.
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) )
Każde rozwiązanie ma wady i zalety. Chciałbym wiedzieć, jakie są twoje doświadczenia z tymi podejściami, co polecasz i jak poszedłbyś na temat projektowania wielojęzycznego schematu bazy danych.
LANGUAGE_CODE
są naturalnym kluczem, unikajLANGUAGE_ID
.Odpowiedzi:
Co sądzisz o powiązanej tabeli tłumaczeń dla każdej tabeli możliwej do przetłumaczenia?
W ten sposób, jeśli masz wiele kolumn do przetłumaczenia, wystarczy tylko jedno połączenie, aby je uzyskać +, ponieważ nie generujesz automatycznie translacji, może być łatwiej zaimportować elementy wraz z powiązanymi z nimi tłumaczeniami.
Negatywną stroną tego jest to, że jeśli masz złożony mechanizm zastępczy języka, może być konieczne zaimplementowanie go dla każdej tabeli tłumaczeń - jeśli polegasz na jakiejś procedurze przechowywanej, aby to zrobić. Jeśli zrobisz to z aplikacji, prawdopodobnie nie będzie to problemem.
Daj mi znać, co myślisz - zamierzam również podjąć decyzję w tej sprawie przy kolejnej aplikacji. Do tej pory używaliśmy twojego 3. typu.
źródło
T_PRODUCT
ma 1 milion wierszy,T_PRODUCT_tr
miałby 2 miliony. Czy to znacznie zmniejszyłoby wydajność sql?To interesująca kwestia, więc zróbmy nekrologię.
Zacznijmy od problemów z metody 1:
Problem: Jesteś denormalizowany, aby zaoszczędzić prędkość.
W SQL (oprócz PostGreSQL z hstore) nie można przekazać języka parametrów i powiedzieć:
Musisz to zrobić:
Co oznacza, że musisz zmienić WSZYSTKIE swoje zapytania, jeśli dodasz nowy język. To oczywiście prowadzi do korzystania z „dynamicznego SQL”, więc nie musisz zmieniać wszystkich swoich zapytań.
Zwykle powoduje to coś takiego (i nie można jej użyć w widokach lub funkcjach wycenianych w tabeli, co jest naprawdę problemem, jeśli faktycznie trzeba filtrować datę raportu)
Problem polega na tym, że
a) Formatowanie daty jest bardzo specyficzne dla języka, więc pojawia się problem, jeśli nie wprowadzisz formatu ISO (czego zwykle nie robi przeciętny programista odmiany ogrodu, a w przypadku zgłoś użytkownika, który z całą pewnością nie zrobi dla ciebie piekła, nawet jeśli zostanie to wyraźnie polecone).
i
b) co najważniejsze , tracisz jakiekolwiek sprawdzanie składni . Jeśli
<insert name of your "favourite" person here>
zmieni się schemat, ponieważ nagle zmieniają się wymagania dotyczące zmiany skrzydła, i tworzona jest nowa tabela, stara w lewo, ale zmieniono nazwę pola referencyjnego, nie pojawi się żadne ostrzeżenie. Raport działa nawet po uruchomieniu bez wybrania parametru wing (==> guid.empty). Ale nagle, gdy faktyczny użytkownik faktycznie wybiera skrzydło ==> boom boom . Ta metoda całkowicie przełamuje wszelkie testy.Metoda 2:
W skrócie: „Świetny” pomysł (ostrzeżenie - sarkazm), połączmy wady metody 3 (niska prędkość przy wielu wejściach) z dość okropnymi wadami metody 1.
Jedyną zaletą tej metody jest zachowanie wszystkie tłumaczenia w jednej tabeli, dzięki czemu konserwacja jest prosta. To samo można jednak osiągnąć za pomocą metody 1 i dynamicznej procedury składowanej SQL oraz (ewentualnie tymczasowej) tabeli zawierającej tłumaczenia oraz nazwy tabeli docelowej (i jest to dość proste, zakładając, że wszystkie pola tekstowe zostały nazwane podobnie).
Metoda 3:
Jedna tabela dla wszystkich tłumaczeń: Wada: Musisz przechowywać n kluczy obcych w tabeli produktów dla n pól, które chcesz przetłumaczyć. Dlatego musisz wykonać n połączeń dla n pól. Gdy tabela translacji jest globalna, zawiera wiele pozycji, a sprzężenia stają się wolne. Ponadto zawsze musisz dołączyć do tabeli T_TRANSLATION n razy dla n pól. To jest dość narzut. Co teraz robisz, gdy musisz uwzględnić niestandardowe tłumaczenia dla każdego klienta? Będziesz musiał dodać kolejne 2x n złączenia do dodatkowego stołu. Jeśli musisz się przyłączyć, powiedzmy 10 tabel, z 2x2xn = 4n dodatkowych złączeń, co za bałagan! Ponadto ten projekt umożliwia użycie tego samego tłumaczenia z 2 tabelami. Jeśli zmienię nazwę elementu w jednej tabeli, czy naprawdę chcę zmienić wpis w innej tabeli KAŻDY JEDEN CZAS?
Ponadto nie można już usuwać i ponownie wstawiać tabeli, ponieważ w tabelach produktów znajdują się teraz klucze obce ... można oczywiście pominąć ustawienie FK, a następnie
<insert name of your "favourite" person here>
usunąć tabelę i ponownie wstawić wszystkie wpisy za pomocą newid () [lub poprzez podanie identyfikatora we wstawce, ale z wyłączonym identyfikatorem wstawiania ], a to (i doprowadzi) do śmieci (i wyjątków zerowych) naprawdę wkrótce.Metoda 4 (nie wymieniona): Przechowywanie wszystkich języków w polu XML w bazie danych. na przykład
Następnie możesz uzyskać wartość przez XPath-Query w SQL, gdzie możesz umieścić zmienną łańcuchową
Możesz zaktualizować wartość w następujący sposób:
Gdzie można zastąpić
/lang/de/...
z'.../' + @in_language + '/...'
Coś w rodzaju magazynu PostGre, z wyjątkiem tego, że z powodu narzutu podczas analizowania XML (zamiast odczytywania wpisu z tablicy asocjacyjnej w PG hstore) staje się on zbyt wolny, a kodowanie xml sprawia, że jest zbyt bolesne, aby było przydatne.
Metoda 5 (zalecana przez SunWuKung, ta, którą należy wybrać): Jedna tabela tłumaczeń dla każdej tabeli „Produkt”. Oznacza to jeden wiersz na język i kilka pól „tekstowych”, więc wymaga tylko JEDNEGO (lewego) łączenia na N polach. Następnie możesz łatwo dodać pole domyślne w tabeli „Produkt”, możesz łatwo usunąć i ponownie wstawić tabelę tłumaczeń, a także utworzyć drugą tabelę dla tłumaczeń niestandardowych (na żądanie), którą możesz również usunąć i włóż ponownie), a nadal masz wszystkie klucze obce.
Zróbmy przykład, aby zobaczyć to DZIAŁA:
Najpierw utwórz tabele:
Następnie wprowadź dane
Następnie prześlij zapytanie do danych:
Jeśli jesteś leniwy, możesz również użyć ISO-TwoLetterName („DE”, „EN” itp.) Jako klucza podstawowego tabeli językowej, nie musisz szukać identyfikatora języka. Ale jeśli to zrobisz, być może chcesz zamiast tego użyć tagu języka IETF , co jest lepsze, ponieważ dostajesz de-CH i de-DE, co tak naprawdę nie jest takie samo pod względem ortografii (wszędzie podwójne s zamiast ß) , chociaż jest to ten sam język podstawowy. To tak mały drobiazg, który może być dla ciebie ważny, szczególnie biorąc pod uwagę, że en-US i en-GB / en-CA / en-AU lub fr-FR / fr-CA ma podobne problemy.
Cytat: nie potrzebujemy tego, robimy nasze oprogramowanie tylko w języku angielskim.
Odpowiedź: Tak - ale który?
W każdym razie, jeśli użyjesz identyfikatora liczb całkowitych, będziesz elastyczny i możesz zmienić metodę w dowolnym momencie.
I powinieneś użyć tej liczby całkowitej, ponieważ nie ma nic bardziej irytującego, destrukcyjnego i kłopotliwego niż nieudany projekt Db.
Zobacz także RFC 5646 , ISO 639-2 ,
A jeśli nadal mówiąc „my” tylko uczynić nasz wniosek o „tylko jednej kultury” (jak en-US zazwyczaj) - więc nie muszę, że dodatkowy całkowitą, to będzie czas i miejsce, aby wspomnieć o dobrym Tagi językowe IANA , prawda?
Ponieważ idą w ten sposób:
i
(w 1996 r. przeprowadzono reformę ortografii). Spróbuj znaleźć słowo w słowniku, jeśli jest ono błędne; staje się to bardzo ważne w aplikacjach związanych z portalami prawnymi i publicznymi.
Co ważniejsze, istnieją regiony, które zmieniają się z alfabetu cyrylicy na alfabety łacińskie, co może być po prostu bardziej kłopotliwe niż powierzchowne utrudnienia związane z jakąś niejasną reformą ortografii, dlatego może to być również ważna kwestia, w zależności od kraju, w którym mieszkasz. Tak czy inaczej, lepiej mieć tam liczbę całkowitą, na wszelki wypadek ...
Edycja:
i dodając
ON DELETE CASCADE
pomożesz po prostu powiedzieć:
DELETE FROM T_Products
i nie uzyskać naruszenia klucza obcego.Jeśli chodzi o zestawienie, zrobiłbym to w ten sposób:
A) Miej swój własny DAL
B) Zapisz żądaną nazwę sortowania w tabeli językowej
Możesz umieścić zestawienia w osobnej tabeli, np .:
C) Miej nazwę sortowania dostępną w informacji o języku auth.user.language
D) Napisz swój SQL w ten sposób:
E) Następnie możesz to zrobić w swoim DAL:
Który da ci to doskonale skomponowane zapytanie SQL
źródło
Trzecia opcja jest najlepsza z kilku powodów:
-Adam
źródło
Spójrz na ten przykład:
Myślę, że nie trzeba wyjaśniać, struktura sama się opisuje.
źródło
Zwykle wybrałbym takie podejście (nie rzeczywiste sql), to odpowiada twojej ostatniej opcji.
Ponieważ posiadanie wszystkich tekstów do przetłumaczenia w jednym miejscu znacznie ułatwia konserwację. Czasami tłumaczenia są zlecane biurom tłumaczeń, w ten sposób możesz wysłać im tylko jeden duży plik eksportu i równie łatwo zaimportować go z powrotem.
źródło
Translation
tabela lubTranslationItem.translationitemid
kolumna?Zanim przejdziesz do szczegółów technicznych i rozwiązań, powinieneś zatrzymać się na chwilę i zadać kilka pytań na temat wymagań. Odpowiedzi mogą mieć ogromny wpływ na rozwiązanie techniczne. Przykładami takich pytań są:
- Czy wszystkie języki będą używane przez cały czas?
- Kto i kiedy wypełni kolumny różnymi wersjami językowymi?
- Co się stanie, gdy użytkownik będzie potrzebował określonego języka tekstu i nie będzie go w systemie?
- Tylko teksty mają być zlokalizowane lub są też inne elementy (na przykład CENA może być przechowywana w $ i €, ponieważ mogą być różne)
źródło
Szukałem wskazówek dotyczących lokalizacji i znalazłem ten temat. Zastanawiałem się, dlaczego jest to używane:
Otrzymujesz coś takiego, co sugeruje user39603:
Czy nie możesz po prostu pominąć tłumaczenia Tłumaczenie, aby uzyskać:
źródło
ProductItem
stolik czymś podobnymProductTexts
lubProductL10n
choć. Ma więcej sensu.Zgadzam się z randomizatorem. Nie rozumiem, dlaczego potrzebujesz tabeli „tłumaczenie”.
Myślę, że to wystarczy:
źródło
Czy poniższe podejście byłoby wykonalne? Załóżmy, że masz tabele, w których więcej niż 1 kolumna wymaga tłumaczenia. Tak więc dla produktu możesz mieć zarówno nazwę produktu, jak i opis produktu, które wymagają tłumaczenia. Czy możesz wykonać następujące czynności:
źródło
„Który jest najlepszy” zależy od sytuacji w projekcie. Pierwszy z nich jest łatwy do wybrania i utrzymania, a także wydajność jest najlepsza, ponieważ nie trzeba łączyć tabel przy wyborze encji. Jeśli potwierdziłeś, że twój poemat obsługuje tylko 2 lub 3 języki i nie wzrośnie, możesz go użyć.
Drugi jest w porządku, ale jest trudny do zrozumienia i utrzymania. A wydajność jest gorsza niż pierwsza.
Ten ostatni jest dobry w skalowalności, ale zły w wydajności. Tabela T_TRANSLATION_ENTRY będzie się powiększać, to okropne, gdy chcesz pobrać listę bytów z niektórych tabel.
źródło
W tym dokumencie opisano możliwe rozwiązania oraz zalety i wady każdej metody. Wolę „lokalizację wiersza”, ponieważ podczas dodawania nowego języka nie trzeba modyfikować schematu DB.
źródło