Как сделать структуру БД многоязычного сайта? (1) Предполагается много связанных таблиц: тарифы, уровни доступа, названия объектов, классификаторы объектов и пр. (2) И есть общая для всех языков графическая контентная часть (графических картинок-диаграмм), где язык не важен (картинка — есть картинка… какой там язык). (3) И есть немного текстового контента «а ля» блог.
Как делать многоязычным блог более менее понятно. Делаем поле с признак языка, и формирование ленты на нужным языке делаем через этот признак.
Делать многоязычным контентно-графическую часть не нужно.
А как сделать многоязыковыми таблицы связей и признаков? В них заложены признаки состояния объектов, классификаторы, описание классов, уровни доступа, биллинг, состояние аккаунтов и прочее, прочее, прочее?.. Но во всех этих таблицах связей-признаков, есть поля описательного характера (например, название типа аккаунтов, или описание класса объекта). И эти описательные поля тоже надо бы сделать многоязычным. Число языков — произвольное (но конечно).
Первой приходит идея — сделать отдельные поля для каждого языка. Но такая схема не расширяема. Т.е. если появится новый язык, придется создавать новые поля (а языков может появится много). На уровне существующих приложения появление новых полей ничего сильно не изменит. Но зато придется писать новые приложения для каждого языка (впрочем, если поля называть умно, то можно просто на уровне приложения «вычислять» какие поля дергать для какого языка.
Вторая идея — сделать отдельные таблицы с описательными характеристиками для каждой таблицы связей, и связать эту таблицу через многое-к-одному с таблицей языков и собственно связей. Все красиво, но структура БД усложняется и может начать тормозить. Теперь при формировании запросов типа JOIN придется сливать вместе больше таблиц (на две дольше в каждый запрос), общее число запросов увеличится, а это может оказаться чревато.
Третья идея — писать в описательные поля на некотором языке разметки и разбирать эту разметку на уровне приложения. Например, описание типов аккаунта будет "[ru::Золотой][en::Gold][jp::ゴールド]"… Плюсы очевидны, но может начать тормозить приложение. Ведь некоторые описательные поля реально большие (например, описание прав и возможностей доступных обладателям «золотого» аккаунта). К тому же это потребует ВСЕ описательные поля сделать избыточной или плавающей длинны, а это замедляет работу СУБД…
Есть ли еще идеи? Может есть традиционные приемы разрешения многоязычности опробованные на CMS?
Простейший расширяемый способ — это одна БД на каждый язык. Пусть в оригинале у вас хранится фраза «Logout», тогда, скажем, в файле БД для русского языка помещаете запись «Logout» → «Выход».
Для ускорения можно сделать иначе, чем в геттексте: указывать английской фразе некий ключ или хеш, которым и помечать соответствующий перевод в каждом языковом файле.
Помещать все языки в одну БД будет быстрей, но совершенно не расширяемо.
Не понимаю, зачем все так усложнять, когда все укладывается в простую схему. Весь контент сайта условно можно разделить на 3 части:
1) редко меняющиеся данные (они обычно зашиты в файла шаблона);
2) часто меняющие данные либо контент генерируемый пользователями/администраторами/модераторами;
3) бинарные данные в духе картинок, прикрепляемых файлов.
Вся статика находится в папке skin/имя_скина/цветовая_схема/язык (например, /skin/modern/red/ru, у меня по умолчанию /skin/default/default/ru), в базе любой текстовой контент требующий перевода имеет столбцы под каждый требуемый целевой язык. Это могут быть столбцы с постфиксом оригинальное_имя_язык (к примеру, topic_content, topic_content_ru) для mysql или же наследование таблиц из postgresql.
Реализация:
1) вся текстовая информация сосредотачивается в файлах шаблона с использованием gettext-а.
2) постфиксные столбцы во всех таблицах.
3) статические картинки связанные со скином распределены по языковым папкам, адреса же до динамически добавляемых картинок хранятся в базе и при запросе согласно п.2 получаем адрес до нужной языковой картинки.
Единственная трудность с которой тут можно столкнуться, это gettext и переводы требующие контекста. Поскольку переводчик на руки получает po файл, то он не знает, на какой странице сайта используется то или иное слово и какой у него контекст. Но это не очень частый случай и просто требует привлечение к работе разработчика.
Хорошее решение. Спасибо. Шаблон еще переделать для нового языка не сложно, но вот вспомнить контекст каждого употребления реально не просто. Ведь есть падежи, спряжения, склонения, герундии… Англоязычное слово может быть одно, а вот русских или венгерских соответствий много.
Кроме того, кажется добавление нового языка будет не человеколюбиво. Вспомнить все контексты употребления слов и сочетаний не просто.
В gettext-е есть возможность задавать числительные через plural forms. Тема с падежами там вроде тоже как была, хотя для русского языка вроде не так все однозначно. Тема с контекстом на самом деле не сильно актуальна и возникает крайне редко. Ведь в gettext храниться кусок текста полностью как есть. Целая фраза или предложение. Я в своей практике не припомню случая, когда переводчику понадобился бы контекст. Поэтому приведенную схему считаю оптимальный и удобной для поддержки в проекте. И ни чего более удобного я пока еще не видел.
«Первая идея» — вполне себе расширяема, поля добавляются спокойно, выбор грамотного именования позволит все сделать на автомате. Плюс для языков можно сделать отдельную таблицу, что бы не трогать основные (но при этом класть все языки в одну строку).
«Вторая идея» — хороша в плане «сделать таблицы», но можно не усложнять ее «джоинами» (большого усложнения в прямых выборках нет, но тем не менее)… Джоины в основном нужны, когда инфа из джоинящейся таблицы «нужна прямо сейчас». В случае с языками Вам достаточно в течении приложения просто записывать в массив языковые ИД для джоина (вместо джоина языковой таблицы по ним), а потом в конце приложения сделать запрос в таблицу вытаскивая сразу все языковые переменные по ИД из этого массива и подставляя их в шаблон.
Что касается картинок, не будьте так однозначны. Они тоже бывают многоязычные, например та же инфографика или кнопки.
Просто вынесите все «переводимые» вещи в отдельные таблицы с полем языка. Скажем если это будет «блог» и его контент должен быть переводимым, то будет 2 таблицы: Заголовок, Контент. У каждой таблицы будет поле языка (language = en/ru/de/jp). Потом соответственно делайте выборку по ним.
p.s.: Так реализованы мультиязычные контенты в Drupal CMS. Посмотрите детально как они там это сделали.
А как? Ведь это таблицы связей?! Допустим у меня есть таблица с URL на картинки… картинкам язык не нужен, они для всех языков одинаковые. И есть таблица с классификатором картинок: Котики. Сиськи, Самодельные взрывные устройства… Не теги, а жестко заданная классификация… при чем для каждого класса есть еще и развернутое описалово. Не то чтобы этим описаловом будут пользоваться, но его надо иметь. Ну чтобы любопытные могли узнать «что есть сиськи и почему рудиментарные мужские молочные железы к таковым не относятся»… Так вот. в вашем случае все связи одно к многим превратятся к много ко многим (ведь к каждой картинке будет несколько связей с записями к разделе классификации (каждая запись на одном языке)… Кроме того. в админке придется все эти связи зафиксировать. Т.е. каждую картинку с котиком атрибутировать как «кот», «cat» и "猫"… учитывая что контент менеджер не знает всех языков… Это избыточно, и не юзабильно со стороны админки.
Я пока склоняюсь к третьему варианту. Там на каждый вариант классификации только один вариант связи.
Так это же не слово… это атрибут или описание классификатора… например, если есть атрибут единица измерения, и на всех языках это будет $ (знак доллара) то будет что-то типа [ru:$][en:$][jp:$]… эту же строку обработает фронт энд и из нее добудет нужный символ для конкретного языка… Мало того, можно фронт-энд запрограммить на поведение когда не удалось сделать разбор строки на принадлежность к языку…
Шутка в том, что сами атрибуты довольно жестко заданы. Их не надо часто редактировать, менять и новые создавать. Что-то типа рубрикатора в журнале или классифайд. Классифайд задан жестко, но многоязычен.