Переводы хранятся в хранилище переводов. Оно должно быть просто молниеносным, к нему будут много и часто обращаться даже один пользователь при отрисовке одной страницы.
Простейшим способом делают - массив ключ-значение, а чтобы все сразу в оперативку не грузить (со временем переводов будет несколько тысяч) - разбивают на группы и подключают по мере необходимости еще одну группу и еще одну группу
Опять же - оперативка установлена на одном компьютере. А если у нас несколько сайтов - нужно либо на каждом в оперативку, либо очень быстро отвечающий сервер с переводами. Там меньше проблема будет найти по ключу, чем обеспечить минимальный пинг. А если оперативка - то обеспечить обновление на всех сайтов переводов и конфликты между обновлениями в разное время
В базе хранят ключи из этого хранилища. После доставания информации копируют поля с припиской _lang доставая из переводов нужные ключи и производя подстановку переменных во фразу
Мне очень понравился синтаксис одного из вордпрессовских модулей который я увидел много лет назад. Говорят он тянется еще из ruby, но я на руби не писал никогда.
"[:en]hello world[:ru]привет, мир[:by]прывет, мiр"
и функция _translate() понадобится
потом _translate_to_language() - когда нужно будет на русской версии показать английским что-нибудь
потом _translate_international() - когда нужно на любой версии показать базовым языком, который понимают большинство народов например
потом _get_translate_array() который отдает просто массив для фронта, чтобы туда отправить переводы пачкой и там делать _translate()
благодаря такой записи я могу в базу положить и ключ перевода и всю фразу в таком синтаксисе, например если у меня будет редактирование в админке заголовков при которых нет смысла менять глобальную базу переводов. я просто запишу в базу текст разделенный таким образом и всё будет работать ровно для этой записи
со временем возникает необходимость сделать переводы с поддержкой окончаний "22 рубля, 25 рублей, 21 рубль", наш синтаксис становится таким:
"[:en]рубль[:ru1]рубля[:ru2]рублей[:en]rouble[:en1]roubles[:en2]roubles"
и функция _getPluralIndexFromString() понадобится
потом нам понадобятся подстановки, мне понравился такой синтаксис
"[:en]hello, {=name}[:ru]привет, {=name}"
и функция _interpolate() понадобится. Почему равно? Чтобы искать можно было с помощью while (strpos()) а не регуляркой
и конечно же захочется чтобы в нее постоянно не скармливать массив параметров ключ-значение, а просто передавать их через запятую чтобы перевод выглядел как-то так
__('@path.type.key', $param1, $param2) и оно само понимало что первый параметр нужно в первый поставить
еще в определенный момент покажется что хранить текст на пару абзацев в переводах так себе идея, и возникнет необходимость сделать чтобы целые страницы можно было сразу делать на русском языке, то есть в папке шаблонов появится подпапка языка и шаблонизатор должен будет сначала поискать страницу на нужном языке
разобрать все эти фразы в большой массив со вложенностями это как минимум тысячу раз применить регулярку. так что преобразованные переводы лучше хранить в кеше. но есть и более сложные способы по реализации, но более быстрее по итогу - оседлать .pot файлы и подключать их, они вроде как внутри индексированы и еще изменения могут отслеживать если их нужной програмкой парсить из кода.
казалось бы можно делать сразу массивом, зачем эти регулярки, ведь единственная проблема - неудобно отдавать переводчикам тексты - они не видят смысла сообщения, читая его на одном каком-то языке, если у тебя файлы en.php, ru.php, а говорить им "откройте два вместе" - никто как правило не делает, только жалуются ходят что плохо. поэтому в таком синтаксисе они видят сразу
опять же в таком синтаксисе ключи переводов можно безболезненно сортировать по алфавиту в любом редакторе, т.к. переносов строк как таковых нет - мы используем \n. и можно нажимать F9 и все становится аккуратненько
но в принципе я слышал что делают на каком-нибудь быстром запрос языке, чтобы даже не в базе их хранить а в elasticsearch с собственной веб страницей куда можно долбить запросами много раз в секунду и ничего страшного, тогда там индексы вручную строятся при закачке текстов, но их обновление - тоже веселая работа
ключи удобнее всего именовать в следующей конвенции
path_to_module.type.key
сначала кажется что ключом может быть сама фраза на английском, но потом нужно получить по русскому тексту английский и облом. поэтому сразу ключи, без вариантов
site_categories_form.title.product => "[:ru]Продукт"
site_categories_form.option.product_empty => "[:ru]-- ВЫБЕРИТЕ ПРОДУКТ --"
К сожалению это не задачка типа "ща переводики сделаем" - это нормальный пласт. Каждый раз натыкаешься на то, что то переводчикам неудобно, то хрен обновишь, то мисматч происходит между просто текстом и ключом фразы, то языки пересеклись, то на нужном языке нельзя вывести, то редирект не работает. Чтобы все это собрать, придется замучаться
Это то как я сейчас делаю, кто знает умнее - пожалуйста укажите где можно лучше быстрее, с удовольствием улучшу