Мультиязычность — критически важная особенность современных веб-приложений, ориентированных на международную аудиторию. Реализация поддержки нескольких языков требует системного подхода как на клиентской, так и на серверной стороне. В данной статье рассмотрены архитектурные решения, принятые при разработке клиент-серверного приложения, включая проектирование базы данных, реализацию переводов и создание удобных инструментов для работы с ними.
1. Подходы к реализации мультиязычности
Перед выбором архитектурного решения необходимо рассмотреть основные подходы к локализации:
1.1 Использование онлайн-переводчиков (например, Google Translate API)
Плюсы:
– быстрая реализация (не требует настройки структуры хранения переводов);
– автоматическая локализация без хранения переводов (перевод выполняется «на лету» при помощи стороннего API).
– Минусы:
– отсутствие контроля над качеством перевода (машинный перевод может быть некорректным);
– ограниченная кастомизация (невозможно адаптировать фразы под терминологию продукта);
– возможны сбои из-за стороннего API.
1.2 Встраивание переводов в код (жестко закодированные строки)
Плюсы:
– простота реализации на раннем этапе (не требуется дополнительная архитектура, переводимые строки пишутся прямо в компонентах или контроллерах).
Минусы:
– трудности масштабирования (при добавлении новых языков нужно менять строки в каждом месте использования);
– увеличение дублирования;
– отсутствие централизованного управления переводами.
1.3 Централизованное хранение переводов и работа с ними через ключи
Плюсы:
– гибкость (добавление нового языка не требует переписывания компонентов);
– масштабируемость;
– централизованное управление (тексты собраны в одном месте, легко отслеживать и редактировать).
Минусы:
– более сложная реализация на ранних этапах (требуется предварительная настройка инфраструктуры: хранилища, загрузка файлов, внедрение вспомогательных хуков и утилит).
Вывод: несмотря на чуть более высокую сложность реализации на старте, ключевая локализация является оптимальным решением для клиент-серверных приложений. Она обеспечивает чистую архитектуру, простоту поддержки и расширения. Все текстовые элементы выносятся в отдельные языковые файлы (или таблицы БД), доступ к ним осуществляется по ключу. Далее рассмотрим, как реализовать данный подход на практике.
2. Проектирование БД для поддержки мультиязычности
Для хранения мультиязычных данных в реляционных базах данных применяются различные паттерны. В рамках проекта была использована связанная структура с таблицами перевода, привязанными по внешнему ключу к основной сущности.
На следующем изображении показана реализация на уровне базы данных:
В этом случае с основной таблицей связана таблица, хранящая переводы наименований для карточки (связь один-ко-многим). Такой подход обеспечивает:
– гибкость: можно добавлять новые языки без изменения схемы;
– безопасность: изолированное хранение переводов;
– возможность использования одного API для получения данных на нужном языке.
3. Работа с переводами на клиентской стороне (react)
a. Структура файлов с переводами
Все переводы можно организовать в структуру JSON, где ключи определяют расположение текста (в каждом файле будет хранится свой язык). Пример файла для хранения переводов:
Удобной структурой является хранение переводов по страницам (home, login и т.д.), а часто используемые слова – в общем разделе (common).
b. Создание кастомного хука
Для упрощения доступа к переводам реализован собственный хук. Ссылка на код: https://gitlab.com/MBC_Studio/guess-who-front/-/blob/master/src/hooks/translationHock.js
В кастомном хуке useTranslate используются три метода: t, tc и tv, каждый из которых предназначен для работы с переводами в определённом контексте:
– t — универсальный метод для доступа ко всему дереву переводов (например, t('common.start'));
– tc — используется для получения переводов, относящихся к конкретной странице или модулю (например, tc('welcome') при установленном префиксе 'pages.home'), что делает код более читаемым и локализованным;
– tv — предназначен для получения сообщений валидации (например, tv('required')), используется в правилах форм и помогает централизованно управлять ошибками ввода.
Данные методы позволяют удобно и единообразно обращаться к переводам во всём приложении, избавляя от дублирования ключей и повышая читаемость кода.
Пример использования хука в компоненте:
В данном примере:
– tc('welcome') подставляет перевод из раздела pages.home;
– t('common.start') обращается к глобальному разделу common для получения текста кнопки.
Чтобы отобразить перевод, достаточно передать соответствующий ключ, а значение будет автоматически подставлено из загруженных языковых файлов.
Использование хука в форме регистрации:
Объект validation, возвращаемый из хука, содержит заранее определённые правила валидации с сообщениями об ошибках, локализованными с помощью метода tv (например, tv('required') → "Поле обязательно для заполнения" и т. д.).
Таким образом, сообщения об ошибках при валидации (например, если пользователь ввёл слишком короткое имя) будут автоматически отображаться на выбранном языке.
c. Создание компонента переключения языка
Для повышения удобства пользователя и поддержки мультиязычности в интерфейсе рекомендуется реализовать отдельный элемент управления, например, плавающую кнопку, открывающую модальное окно с выбором языка. Такой подход позволяет быстро и интуитивно переключаться между доступными локализациями, не покидая текущую страницу.
Пример подобной реализации доступен по ссылке: https://gitlab.com/MBC_Studio/guess-who-front/-/blob/master/src/ui/buttons/HelpFloatButton/HelpFloatButton.jsx
Компонент HelpFloatButton представляет собой плавающую кнопку, по нажатию на которую раскрывается меню с двумя действиями: переход на Telegram для отправки отчёта об ошибке и вызов модального окна с выбором языка. Для работы с переводами используется кастомный хук useTranslate. Управление отображением модального окна осуществляется через состояние changeLanguage. Выбор языка выполняется с помощью компонента Select из библиотеки Ant Design и применяется через метод i18n.changeLanguage.
Такой подход обеспечивает централизованное и понятное переключение языка в приложении, повышая удобство пользователя и упрощая поддержку локализации.
4. Заключение
В результате применения вышеописанных архитектурных решений было создано масштабируемое и гибкое клиент-серверное приложение с полной поддержкой мультиязычности.
Такой подход позволяет легко расширять число поддерживаемых языков, централизованно управлять переводами и адаптировать интерфейс под разные локали без значительных изменений в логике приложения.
Исходный код (frontend-приложение): https://gitlab.com/MBC_Studio/guess-who-front
Исходный код (backend-приложение): https://gitlab.com/MBC_Studio/guess-who-game