Wzorcowa strona wielojęzyczna
Zacznę od opisu, jak według mnie powinna wyglądać wzorcowa strona prowadzona w kilku językach. Po pierwsze, musi mieć ona przetłumaczony interfejs użytkownika. Ponadto w każdym z języków musi udostępniać jakieś treści w postaci artykułów, newsów i tak dalej. Ważne jest, że każdy z języków musi stanowić niejako osobną całość tak, jakby stanowił niezależną witrynę. W sekcji angielskiej nie ma prawa wyświetlić się news napisany po polsku. Nie ma też tam prawa pojawić się kategoria, tag, komentarz, cokolwiek, napisanego w języku innym, niż angielski (chyba że ktoś wpisze coś po polsku, twierdząc, że to jest po angielsku, ale od usuwania tego są moderatorzy :)). Podobna zależność zachodzi w drugą stronę. W ekstremalnym przypadku każda z wersji językowych może różnić się nawet ilością głównych działów. Przykładowo, gdyby taki system istniał na Zyxist.com, część polska mogłaby posiadać działy: Dzienniki zyxowe, Artykuły, Cenzura, Revomer oraz Hall of Fame, w angielskiej występowałyby tylko Chronicles of Zyxist, Revomer i Hall of Fame, zaś w słowackiej tylko Zyxové denníky oraz Hall of Fame.
Jednocześnie, możemy wydzielić tutaj kilka zasobów, które mogą i które powinny być współdzielone między wersjami. Zaliczyłbym do nich konta użytkowników. Jest to oczywiście bardzo wygodne. Rejestrujemy się w sekcji polskiej i udzielamy się tam, ale od czasu do czasu mamy też możliwość napisania czegoś po angielsku bez konieczności otwierania drugiego konta oraz przelogowywania się.
Grzechy istniejących stron
Zazwyczaj nietrudno jest przystosować skrypt do współpracy z jednym językiem, nawet innym, niż angielski. Upowszechnia się unicode i znika problem kodowań. Pozostaje już tylko kwestia podmienienia interfejsu i można zacząć działanie. Jednak praktycznie ciężko jest znaleźć coś, co choć trochę zbliżałoby się do opisanego wyżej modelu. Szwankujące elementy:
- Brak mechanizmu autodetekcji języka.
- Przekazywanie informacji o języku w postaci ciastka, a nie w adresie URL - nie można jednocześnie przeglądać sekcji polskiej i angielskiej.
- Brak logicznego oddzielenia treści napisanych w różnych językach. Użytkownik musi albo stosować jakieś prowizoryczne mechanizmy, albo pogodzić się na mieszanie wszystkiego. Jeżeli już coś takiego się pojawia, przeważnie jest mocno fragmentaryczne i dotyczy tylko niewielkiej części danych, które powinny być tym objęte.
- Brak eleganckich mechanizmów w sekcji administracyjnej umożliwiających łatwe zorientowanie się w tym, co edytujemy.
- Brak możliwości kojarzenia ze sobą zasobów napisanych w różnych językach.
- Błędy gramatyczne - to jest także mankament stron jednojęzycznych ze spolszczonym interfejsem. Najbardziej dobijający mnie kwiatek to wszędobylskie "23 styczeń" itd. zamiast "23 stycznia". Błąd niestety leży w implementacji systemowych funkcji daty, których autorzy zwyczajnie nie wiedzieli, że w innych językach rzeczownik może posiadać inną formę w zależności od tego, w której częsci zdania się znajduje.
- Brak implementacji Unicode - do wielojęzycznych stron jest to podstawa. Niestety, nie wszystkie skrypty wciąż prawidłowo sobie z nim radzą i do takich zastosowań trzeba je omijać szerokim łukiem.
Oczywiście niektóre skrypty wypadają pod tym względem lepiej, inne gorzej. Zastanawia mnie tylko, dlaczego do tych popularniejszych chociaż nie ma nawet modów, które rozwiązałyby resztę problemów. W przypadku forów dyskusyjnych w zasadzie można tylko natworzyć działów dla każdego z języków i wyświetlać je jednocześnie. Genialne rozwiązanie zastosowano na forum gry Wiedźmin, gdy jechało jeszcze na autorskim skrypcie. Jego twórcy zastosowali podział na sekcje językowe spełniający niemal wszystkie założenia, jakie wyżej podałem. Po wybraniu języka miałeś dostęp wyłącznie do treści w nim napisanych - działów, kategorii, tematów. Nie było tam miejsca na nieporządek.
Prosta implementacja
Okazuje się, że implementacja większości z założeń nie jest wcale trudna. Musimy jedynie wyodrębnić wszystkie tabele odpowiadające za treść i dołożyć do nich pole identyfikacji języka. Następnie dodajemy sobie jakiś filtr, który do odwołujących się do nich zapytań dołoży klauzulę AND `lang_id` = :lang_id, gdzie pod :lang_id podstawiamy identyfikator aktualnie używanej mowy. Stosunkowo prosto można rozwiązać też sprawę powiązań między wersjami. W optymistycznej wersji (o ile zrezygnujemy z kluczy obcych) wystarczy na to jedna tabela. Zapisujemy do niej typ zasobu oraz dwa identyfikatory, które pragniemy skojarzyć. Gdy chcemy wyświetlić powiązania, wprowadzamy odpowiedni typ zasobu i robimy proste skojarzenie:
SELECT z.id, z.title FROM `zasoby` z, `powiazania` p WHERE p.type = :typ AND p.src_id = :src_id AND p.dst_id = z.id
Zwróćmy uwagę na dwie rzeczy:
- W tej sytuacji jeden z identyfikatorów pełni rolę źródłowego, a drugi docelowego. Aby powiązanie działało w obie strony, musimy dla każdej skojarzonej pary utworzyć po dwa rekordy. Alternatywnym rozwiązaniem jest lekkie rozbudowanie powyższego zapytania.
- W podanym modelu możemy także kojarzyć zasoby znajdujące się w obrębie tego samego języka. Jeżeli nie chcemy takiego uogólnienia, musimy dbać o to przy tworzeniu powiązania. W końcu informacja o języku jest już zapisana w każdym zasobie i nie trzeba jej tu powielać. Wystarczy ograniczenie, że kojarzone zasoby muszą mieć różne języki i tyle - a gdzie to będzie przechowywane, to nie ma znaczenia, bo przy ręcznej manipulacji taki system zawsze da się oszukać, więc nie ma co sobie komplikować życia.
Zakończenie
Temat ten postanowiłem poruszyć przy okazji stawiania strony grupy Invenzzia. Nie mogę olać Polaków, bo będą narzekać, że oni angielskij nepanimaju, nawet gdy zupełnie dobrze radzą sobie z dokumentacjami do PHP, 1327 frameworków i do Smarty'ego. Nie mogę też olać nie-Polaków, gdyż wtedy mogę od razu między bajki włożyć wypromowanie bibliotek i innych rzeczy poza granicami kraju. Niestety - co skrypt, to problemy. Wielojęzyczne MediaWiki to porażka, jakich mało, a robienie osobnych instalacji trochę kosztuje. Forum - jakiegokolwiek bym nie wziął, i tak nie będzie tego, co być powinno. Blog - aktualnie z braku autodetekcji jest miks polsko-angielski, chyba że ktoś sobie wybierze z menu odpowiedni język. Jedynie z bugtrackerem nie ma problemów, gdyż on z definicji musi być w jednym języku prowadzony.
Nawiasem mówiąc w zeszłym roku prowadziłem eksperymenty, jak wyglądałoby wielojęzyczne Zyxist.com :). Niestety odbywało się to na silniku Terenzzia będącym w zasadzie lekko zmodyfikowanym Zend Frameworkiem, a projekt ten poszedł do piachu po jednokrotnym użyciu. Niemniej doświadczenia i idee pozostały.















Napisał Komentator Newsa w wtorek, 5 lutego 2008 o 23:57
Ad 1. Autodetekcja języka jest dość łatwa. Trzeba pamiętać o sprawdzaniu, czy uzyskana wartość nie jest pusta, gdyż Google i Validator nie wysyłają Accept-Language. Mimo tego funkcję autodetekcji powinien udostępniać PHP. :)
Ad 2. Z jednej strony popieram. Z drugiej zaś dobrze jest zapamiętać wybrany przez użytkownika język i nie opierać się tylko na jego autowykrywaniu. Niektórzy nie potrafią nawet wejść w ustawienia przeglądarki. xD
Ad 7. Na razie stosuję Latin2 (iso-8859-2) i zastanawiam się nad przejściem na UTF-8. Zaletą kodowania ISO jest mniejszy rozmiar plików, co może liczyć się w przypadku kont o małej pojemności.
Wielojęzykowość implementuję w ten sposób - kategorie posiadają pole "access" w bazie, które może przybrać wartości: 1 (wszystkie języki), "pl", "en", (...), 2 (ukryta), 3 (niedostępna). Podobnie np. dla bloków menu.
Jeśli aplikacja jest przeznaczona przede wszystkim dla wielojęzycznych stron, powiązania są na pewno użyteczne, w tym: "wyświetl ten artykuł w języku X". Trzeba przy tym pamiętać o wydajności całego skryptu. :)