Реализация мультиязычных текстовых сообщений в веб приложении

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

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

Физические файлы сообщений

В общем есть файл в котором структурировано лежат сообщения на разных. Обычно в этом файле программный код, который и организовывает сообщения для быстрого доступа. Сообщения могут храниться как в переменных так и в массиве, что встречается наиболее часто. Всё это закрыто в класс/функцию/структуру/перечисление/пространство имён. Файлы распределены по языкам дабы было легко понять сообщение на каком языке нужно выводить.

Как это организовано у меня

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

Теория

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

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

Разумеется присутствуют все проверки. Есть некая иерархия замен сообщений. Т.е. если запрашиваемого файла или сообщения в нём нет, то система попытается подключить файл или выдать сообщение из файла языка заменителя. Например, если была запрошена страница на американском, а такого файла языка нет, то будет подключен файл английского языка и так по нарастающей по иерархии замен. Или если в подключённом файле языке для конкретного сообщения нет текста или вообще нет этого сообщения в файле, то так же будут произведены замены на другие сообщения. Это очень полезно т.к. обычно по мере развития приложения не успеваешь добавлять в другие языковые файлы новые сообщения, в этом случае функция замен очень удобна. Например, если в файле сообщений английского языка нет нужного сообщения т.к. файл устарел и новые сообщения туда ещё не добавлены, то вместо английского система выведет сообщение на русском, ну или на более приемлемое по цепочке замен, которая легко редактируется в конфигурации приложения.

Практика

Разбором URL, т.е. пользовательской информацией, как и положено, занимается главный контроллер, который подключает в свою очередь, если нужно, дополнительные контроллеры, а те вызывают нужные модели и отдают информацию в виды.

Многие не знают где именно лучше подключать файлы сообщений. Если рассуждать логически, то файл сообщений это некая текстовая база данных, которая хранит в себе все нужные тексты приложения. Как известно со всеми базами данных в MVC приложении работает модель, поэтому и подключать языковые файлы нужно в модели. В моих приложениях в классе модели есть специальный метод, который и занимается работой с файлами сообщений. Он их подключает, комбинирует и вытаскивает нужные данные. В общем несомненно файлами сообщений должна заниматься исключительно модель и никто другой т.к. это прямые обязанности модели, другие пласты приложения вообще не должны знать откуда модель вытаскивает данные т.к. это не их дело, модель вообще может вытаскивать сообщения хоть из текстовых файлов, это её дело. Метод/функция получения нужного сообщения находится в главной модели. В неё от видов и других моделей передаётся нужное сообщение, текст которого надо получить и указание откуда его брать, из файла ядра приложения (нужно для сообщений ошибок моделей) или из файла текущей темы приложения. Этот метод проверяет есть ли в запрошенном файле такое сообщение и возвращает его. Если сообщение пустое или такого ключа в массиве сообщений текущего языкового файла не оказалось но включается механизм замен иерархия которого описана в файле конфигурации. По цепи подключаются дополнительные языковые файлы и проверяются на наличие искомого сообщения. Если нигде нужное сообщение не встречается, то в конечном итоге доходим до коренного языкового файла, обычно это файл того языка на котором говорят разработчики приложения и из этого файла вытаскивается нужный текст. С такой функцией замен очень удобно поддерживать приложение, т.е. оно само регулируется и можно не заботиться о переводе во время разработки, т.е. не обязательно поддерживать в актуальном состоянии все языковые файлы которых может быть несколько десятков.