Каким способом заложить многоязычность в движок?

Silentland

Новичок
Перечитал все статьи по этой теме, в т.ч. http://phpclub.ru/faq/multilang. В голове каша теперь. Объясню лучше свою ситуацию:

Делаю простой сайт с БД из 20 таблиц и тремя языками. Пишу под него движок. Большой соблазн не заморачиваться и просто расширить нужные столбцы дополнительными переводами, но внутренний голос подсказывает, что это не путь самурая.

Движок планирую использовать и в других проектах, поэтому хочется сделать сразу хорошо, но в любом случае это не будут проекты с сотней языков, иероглифов, поддержкой падежей, множественного числа и проч. Так же там точно не будет кучи статей на разных языках. В основном будут переводы интерфейса и нескольких информационных страниц.

Смотрел в сторону GetText, но не понял как она поможет в моем случае.
Движок большей частью клиентский. Т.е. абсолютно все данные он выдают в JSON, а JS уже рисует страницу. Все строки (кроме технических) хранятся в БД.

Наиболее приглянувшийся вариант создать одну табличку с переводами:
Strings
id, ru, en, de, fr, ...
и везде ссылаться на id строк из нее:
Article
id, iser_id, title_id, text_id
select Strings.$lang from Article, Strings where Article.title_id = Strings.id
ну и INSERT, DELETE, UPDATE придумать как записать.

Кажется достаточно просто. А способы с отдельной табличкой переводов для каждой таблички, каким-то идентификатором языка и проч.совсем не понятны.

Какую архитектуру посоветуете в моем случае?
 

Breeze

goshogun
Команда форума
Партнер клуба
языковые версии

id
article_id
lang_id
title
text

статьи

article_id
user_id
и т.д.

сделаешь join по article_id и lang_id и будет красота, и языков хоть 100
 

Silentland

Новичок
А зачем в языковых версиях отдельно article_id выносить? Получается, что он дублирует id, к которому можно напрямую привязать? А джойн быстрее работает чем выборка из двух таблиц (как в примере выше)?
 

Breeze

goshogun
Команда форума
Партнер клуба
затем, что id -- primary key автоинкремент, который может использоваться для всяких разных целей.
а article_id это article_id

можно конечно сделать только [primary] key по article_id + lang_id, но не нужно, т.к. практика показала, что наличие id сильно упрощает жизнь, особенно при усложнении системы.

по поводу join почитай в документации, что есть "выборка из двух таблиц (как в примере выше)"
 

fixxxer

К.О.
Партнер клуба
Это получится, что в статьях article_id + lang_id, в новостях news_id + lang_id итд. Нехорошо. По таблице на сущность, хотя нет никакой разницы на что ссылаться.

Мне больше нравится вариант с translation_id в таблицах article/news и таблица translations (translation_id, lang_id)
 

WMix

герр M:)ller
Партнер клуба
а давайте усложним немножко, чтоб поинтересней было

начальный вариант

article
id, title, intro, text

news
id, title, text

с переводом вариант Breeze

article
id , title, intro, text

article_trans
id, art_id, lang_id, title, intro, text

news
id ,title, text

news_trans
id, news_id, lang_id, title, text

fixxxer
а вот с твоим вариантом у меня затык, я не понимаю, как несколько различных полей засунуть в одну табличку
 

Breeze

goshogun
Команда форума
Партнер клуба
Это получится, что в статьях article_id + lang_id, в новостях news_id + lang_id итд. Нехорошо. По таблице на сущность, хотя нет никакой разницы на что ссылаться.

Мне больше нравится вариант с translation_id в таблицах article/news и таблица translations (translation_id, lang_id)
ну разумеется, что дальше уже вариации в зависимости от) потому что потом захочется еще версионность
человеку ж главное принцип, чтоб языковые поля не плодить.
 

WMix

герр M:)ller
Партнер клуба
а как же интро? да и типы полей разные....
или на каждый тип по табличке?

тогда можно даже и без translation_id

trans_text
id, lang, rel_table, rel_field, rel_id, trans_text

trans_varchar
id, lang, rel_table, rel_field, rel_id, trans_text

PHP:
select  n.id, t1.text as title, t2.text as text
from news n

left join trans_varchar t1 on
    t1.rel_table = 'news'  AND
    t1.rel_field = 'title' AND
    t1.lang      = 'ru'    AND
    t1.rel_id    = n.id


left join trans_text t2 on
    t2.rel_table = 'news' AND
    t2.rel_field = 'text' AND
    t2.lang      = 'ru'   AND
    t2.rel_id    = n.id
да на eav похоже....
 

Silentland

Новичок
Если следовать примеру WMix, получается так:

начальный вариант
article
id, title, intro, text

news
id, title, text

с переводом вариант Breeze
article
id

article_trans
id, art_id, lang_id, title, intro, text

news
id

news_trans
id, news_id, lang_id, title, text

Конечно, создавать по отдельной табличке не очень удобно, да и выборку как делать, если в article так же содержатся данные, не требующие перевода. Двумя запросами?

Вариант fixxxer
article
id, title_translation_id, intro_translation_id, text_translation_id

news
id, title_translation_id, text_translation_id

translations
id, lang_id, translation_text

Наиболее понятный вариант

Вариант WMix с самодостаточными таблицами переводов для разных типов
trans_text
id, lang, rel_table, rel_field, rel_id, trans_text

trans_varchar
id, lang, rel_table, rel_field, rel_id, trans_text

Интересный подход, но получается, если нужно сделать выборку из таблички news, то кол-во комментариев или id пользователя я выбираю напрямую из news, а все тексты отдельным запросом из trans_*. Как-то не очень удобно

Вариант с таблицами переводов для разных типов
article
id, user_id, title_transvarchar_id, intro_transtext_id, text_transtext_id

news
id, title_transvarchar_id, text_transtext_id

transtext
id, lang, trans_text

transvarchar
id, lang, trans_text

Пока склоняюсь к такому варианту (почти как у fixxxer). По крайней мере, можно делать выборки-вставки одним запросом

Возникли еще такие вопросы.

Разделять типы данных так:
transtext и transvarchar (в двух таблицах)
id, lang, text

или так:

translation (таблица с полями для разных типов)
id, lang, transtext, transvarchar

Языки записывать так:
translation
id, lang, text

или так:

translation
id, text_ru, text_en, text_fr

Напомню, что в переводе будут нуждаться только интерфейс и десяток статей, ну максимум сотня. Если вдруг захочу адаптировать движок под интернет-магазин, то возможно сотня тысяч товаров... Но это уже маловероятно. 100 языков точно никогда не будет. Максимум 10. Поэтому желательно, чтобы просто и поудобнее было поддерживать.

по поводу join почитай в документации, что есть "выборка из двух таблиц (как в примере выше)"
поиск по фразе «выборка из двух таблиц» один шлак находит, а в документации так и не нашел, как она реализуется движком БД...
 

WMix

герр M:)ller
Партнер клуба
на мой вкус как у breeze самый удобный в смысле читабельности... остальное сложновастые запросы, или об этом программа думает..
 

Breeze

goshogun
Команда форума
Партнер клуба
я попрошу моему варианту не приписывать то, чего я не писал =)
 

Silentland

Новичок
на мой вкус как у breeze самый удобный в смысле читабельности... остальное сложновастые запросы, или об этом программа думает..
Видимо у меня как-то мозг по-другому работает...

article
id, user_id

article_trans
id, article_id, lang_id, title, intro, text

Получается, что табличка со статьями содержит в себе не всю информацию о статьях, в ней даже ссылок на тексты нет, которые ютятся в совершенно другой таблице. Получается, что главная таблица теперь article_trans и запрос уже нужно делать по ней и подгружать тот же user_id из таблицы статей. Вынос мозга просто... Более того, придется дублировать все таблицы.

Если все это переосмыслить, можно записать так

article
id, info_id, lang_id, title, intro, text

article_info
id, user_id

Можно сократить кол-во таблиц и свести всю инфу в одну, хотя получится вымучено:

infos
id, user_id, data, comments

С точки зрения структуры запросов, варианты breeze и fixxxer идентичны. Разве нет? В одном случае надо перевод джойнить, в другом — инфу об авторе и проч. Если рассматривать переводы фраз интерфейса, то неплохой вариант, т.к. никакого user_id и проч. там не будет и джойнить ничего не придется. Подводных камней нет?
 

Breeze

goshogun
Команда форума
Партнер клуба
join можно делать в обе стороны, так что по барабану как что называется, главное чтоб селективность по первой таблице была лучшей
 

WMix

герр M:)ller
Партнер клуба
я делал так, только не кидайте помидорами

article
id, pid, lang, title, text
pid указывает на оригинал статьи
выбрать все статьи
PHP:
select * from article where lang="ru"
переводы
PHP:
select id,lang from article where id=$pid or pid=$pid or pid=$id
 

Silentland

Новичок
я делал так, только не кидайте помидорами
Черт знает. Получается, что нужно писать отдельный запрос для переводов и все запросы будут раздваиваться, вместо того, чтобы в один запрос подставлять переменную $lang
 

Silentland

Новичок
Глянул свою БД,
menu_items
id
parent_id
position
level
title
res_id
res_type
css_class
show

Там только title нуждается в переводе, то же касается и других элементов интерфейса. Многоязычных столбцов 20% от силы во всех таблицах. Получается вариант fixxxer пердпочтительней...
 

WMix

герр M:)ller
Партнер клуба
да я понимаю, что это получается 2 строки, но каждый язык это уже по сути другие данные, пользователь - переводчик, дата - дата создания перевода... если подумать то в минималистическом виде это совсем даже неплохая база... конечно важно понимать что пишешь, как далеко это зайдет, какие затраты нужны... вариант с одной таблицей на тип, имеет один недостаток - таблица переводов уже не знает где используется то или другое слово... пойск можно делать только начиная с основной таблицы...
 

Silentland

Новичок
... вариант с одной таблицей на тип, имеет один недостаток - таблица переводов уже не знает где используется то или другое слово... пойск можно делать только начиная с основной таблицы...
А зачем ей это знать и зачем с нее поиск делать?

Еще вопрос, а что, если использовать вместо id языка его iso-код и сделать его первичным индексом? Тогда и отдельную табличку соответствия индексов языку не надо. Вообще, если смотреть шире, такой подход оправдан, например в таблице настроек `settings`, где каждая настройка имеет уникальное название?
 
Сверху