Dostosuj preferencje dotyczące zgody

Używamy plików cookie, aby pomóc użytkownikom w sprawnej nawigacji i wykonywaniu określonych funkcji. Szczegółowe informacje na temat wszystkich plików cookie odpowiadających poszczególnym kategoriom zgody znajdują się poniżej.

Pliki cookie sklasyfikowane jako „niezbędne” są przechowywane w przeglądarce użytkownika, ponieważ są niezbędne do włączenia podstawowych funkcji witryny.... 

Zawsze aktywne

Niezbędne pliki cookie mają kluczowe znaczenie dla podstawowych funkcji witryny i witryna nie będzie działać w zamierzony sposób bez nich.Te pliki cookie nie przechowują żadnych danych umożliwiających identyfikację osoby.

Funkcjonalne pliki cookie pomagają wykonywać pewne funkcje, takie jak udostępnianie zawartości witryny na platformach mediów społecznościowych, zbieranie informacji zwrotnych i inne funkcje stron trzecich.

Brak plików cookie do wyświetlenia.

Analityczne pliki cookie służą do zrozumienia, w jaki sposób użytkownicy wchodzą w interakcję z witryną. Te pliki cookie pomagają dostarczać informacje o metrykach liczby odwiedzających, współczynniku odrzuceń, źródle ruchu itp.

Wydajnościowe pliki cookie służą do zrozumienia i analizy kluczowych wskaźników wydajności witryny, co pomaga zapewnić lepsze wrażenia użytkownika dla odwiedzających.

Brak plików cookie do wyświetlenia.

Reklamowe pliki cookie służą do dostarczania użytkownikom spersonalizowanych reklam w oparciu o strony, które odwiedzili wcześniej, oraz do analizowania skuteczności kampanii reklamowej.

Brak plików cookie do wyświetlenia.

Metody na przechowywanie tłumaczeń w bazie danych

Zawartość

Metody na przechowywanie tłumaczeń w bazie danych

      W tym wpisie przedstawię kilka możliwych podejść do rozwiązania problemu przechowywania wielojęzycznych dokumentów w bazie danych i dostępu do nich.

      Wyobraźmy sobie, że mamy aplikację internetową, przechowującą dane o produktach w kilku językach – na przykład w języku polskim i angielskim.

      Pierwsze, najprostsze rozwiązanie, to utworzenie dodatkowych kolumn w tabeli dla każdego tekstu, który powinien być przetłumaczony, tak jak poniżej:

      idcreationDatecreatedBypricetitle_entitle_pldesc_endesc_pl
      12025-05-14 13:0911.99ApplesJabłkaTasty applesSmaczne jabłka
      22025-05-14 13:0912.19PearsGruszkiSour pearsKwaśne gruszki

      Dane z bazy możemy uzyskać przy pomocy trywialnego zapytania

      SELECT * FROM products WHERE id = 1
      

      To podejście ma niewątpliwe zalety, w postaci łatwej implementacji, prostych zapytań (brak konieczności użycia JOIN-ów), nie występuje też redundancja. Ma jednak istotne wady – zarządzanie staje się trudne, gdy języków jest więcej niż kilka, a dodanie nowego języka wymaga skomplikowanych zmian w strukturze. Trzeba ponadto pamiętać, które kolumny mają własne tłumaczenia, a które pozostają niezmienione w różnych językach.

      Kolejne podejście to modyfikacja poprzedniego, jednak tutaj zamiast dodawać kolejne kolumny, informacje o tłumaczeniu umieszczamy w jednej kolumnie z zawartością w postaci JSONa.

      idcreationDatecreatedBypricetranslation
      12025-05-14 13:0911.99[{“language”:”en”,”translate”:{“title”:”Apples”,”description”:”Tasty apples”}},{“language”:”pl”,”translate”:{“title”:”Jabłka”,”description”:”Smaczne jabłka”}}]
      22025-05-14 13:0912.19[{“language”:”en”,”translate”:{“title”:”Pears”,”description”:”Sour pears”}},{“language”:”pl”,”translate”:{“title”:”Gruszki”,”description”:”Kwaśne gruszki”}}]

      Zapytanie pobierające pierwszy rekord z bazy z opisem w języku angielskim:

      SELECT id, creationDate, createdBy, price,
        JSON_UNQUOTE(JSON_EXTRACT(translation, '$[0].translate.title')) AS title,
        JSON_UNQUOTE(JSON_EXTRACT(translation, '$[0].translate.description')) AS description
      FROM products
      WHERE id = 1;
      

      Zalety tego rozwiązania pokrywają się z zaletami poprzednika – jest łatwość implementacji, proste zapytania (bez JOIN), nie trzeba duplikować zawartości i wygląda sensowniej niż pierwsze rozwiązanie. Niestety, taka baza w dalszym ciągu robi się trudna do utrzymania przy wielu językach, zapytania robią się bardziej skomplikowane (starsze bazy MySQL nie rozpoznają typu JSON), a ponadto większość edytorów WYSIWYG (np. CKEdit) nie będzie chciało łatwo współpracować przy edycji danych z takiej tabeli.

      Rozwiązanie numer 3 to podejście przypominające to pierwsze, ale zamiast dodawać kolumny, dodajemy kolejne wiersze.

      idcreationDatecreatedBypricelangtitledesc
      12025-05-14 13:0911.99enApplesTasty apples
      12025-05-14 13:0911.99plJabłkaSmaczne jabłka
      22025-05-14 13:0912.19enPearsSour pears
      22025-05-14 13:0912.19plGruszkiKwaśne gruszki
      SELECT * FROM products WHERE lang = 'en' AND id = 1;
      

      Ponownie, do zalet tego rozwiązania należy łatwa implementacja i proste układanie zapytań, natomiast pojawiają się nowe trudności – przy zmianie danych niezależnych od języka (np. ceny) należy zaktualizować je we wszystkich wersjach językowych rekordu, aby użytkownik wersji polskiej nie zapłacił innej ceny niż użytkownik wersji angielskiej. Skomplikowana staje się również operacja dodania nowego języka.

      Czwarte rozwiązanie to najczystsze podejście, jakie do tej pory rozważaliśmy. Wszystkie teksty będziemy przechowywać w jeden tabeli z tłumaczeniami. Ułatwi nam to zarządzanie tekstami w wielu językach, zwłaszcza gdy będziemy potrzebowali dodać jeszcze kolejny.

      Tabela products

      idcreationDatecreatedBypricetitle_trans_iddesc_trans_id
      12025-05-14 13:0911.9912
      22025-05-14 13:0922.1934

      Tabela translations

      idlangvalue
      1enApples
      1plJabłka
      2enTasty apples
      2plSmaczne jabłka
      3enPears
      3plGruszki
      4enSour pears
      4plKwaśne gruszki

      Zapytanie do bazy danych:

      SELECT p.id, p.creationDate, p.createdBy, p.price,
        title_en.value AS title, desc_en.value AS description
      FROM products p
      LEFT JOIN translations title_en ON p.title_trans_id = title_en.id AND title_en.lang = 'en'
      LEFT JOIN translations desc_en ON p.desc_trans_id = desc_en.id AND desc_en.lang = 'en'
      WHERE p.id = 1;
      

      Zaletą tego rozwiązania będzie z pewnością normalizacja i podejście relacyjne do problemu, dodawanie nowych języków również wydaje się proste (nie zmieniają się kolumny). Wszystkie tłumaczenia wędrują do jednej tabeli i są czytelne. Do wad należą natomiast dość skomplikowane zapytania i trudność w utrzymaniu (zwykłe operacje typu INSERT, DELETE czy UPDATE robią się nadmiernie skomplikowane).

      Piąta opcja to modyfikacja poprzedniego rozwiązania, które wydaje się utrzymywać jego zalety, jak i eliminować niektóre wady. Dla każdej tabeli przechowującej tekstowe informacje, które można przetłumaczyć tworzymy dodatkową tabelę.

      Tabela products

      idcreationDatecreatedByprice
      12025-05-14 13:0911.99
      22025-05-14 13:0922.19

      Tabela productTranslations

      idproductIdlangtitledesc
      11enApplesTasty apples
      21plJabłkaSmaczne jabłka
      32enPearsSour pears
      42plGruszkiKwaśne gruszki

      Zapytanie SQL:

      SELECT p.id, p.creationDate, p.createdBy, p.price,
        pt.title, pt.desc AS description
      FROM products p
      JOIN productTranslations pt ON p.id = pt.productId AND pt.lang = 'en'
      WHERE p.id = 1;
      

      Zalety tego rozwiązania są podobne do zalet rozwiązania numer 4: dane są znormalizowane, łatwo można dodać nowe języki. Kolumny zachowują ponadto swoje oryginalne nazwy, a zapytania są dość proste (tylko jeden JOIN). Wadą jest natomiast znaczne zwiększenie liczby tabel (w najgorszym przypadku ich podwojenie).

      Najlepsze rozwiązanie problemu przechowywania tłumaczeń w bazie danych MySQL zależy od czasu jaki możemy poświęcić, typu projektu, zakładanego czasu na realizację projektu i wielu różnych czynników. Projekty amatorskie i proste witryny mogą opierać się na jednym z pierwszych rozwiązań, natomiast profesjonalne aplikacje powinny raczej być projektowane w bardziej elastyczny i przyszłościowy sposób, jak na przykład w propozycji nr 5.