Переключение языков в PHP: подходы и примеры
Основные методы смены языка в PHP
Как организовать смену языка с помощью расширения gettext?
Цель: стандартная локализация с поддержкой множественных форм и контекстов.
Расширение gettext обеспечивает наиболее эффективное решение для многоязычных приложений. Оно использует файлы .po (редактируемые) и .mo (скомпилированные).
// Установка локали
setlocale(LC_ALL, 'ru_RU.UTF-8');
// Привязка домена
bindtextdomain('messages', './locale');
textdomain('messages');
// Вывод перевода
echo _('Hello');
Пояснение: bindtextdomain указывает путь к папке с переводами, textdomain задаёт домен (имя файла .mo без расширения). Функция _() alias gettext.
Типичные проблемы:
- Расширение gettext может быть не установлено на сервере. Решение: установка через менеджер пакетов (apt install php-gettext) или использование альтернатив.
- Неправильная настройка локали на сервере (локаль не сгенерирована). Решение: выполнить
locale-gen ru_RU.UTF-8. - Файлы .mo не обновляются после изменения .po. Решение: запускать
msgfmt messages.po -o messages.mo.
Случаи использования: средние и крупные проекты, требующие поддержки множественных чисел, контекстов и высокой производительности.
Как переключать язык с помощью PHP-массивов?
Цель: простейший способ для небольших проектов.
Создаётся массив с переводами для каждого языка.
$lang = [
'ru' => [
'hello' => 'Привет',
'goodbye' => 'До свидания'
],
'en' => [
'hello' => 'Hello',
'goodbye' => 'Goodbye'
]
];
$currentLang = 'ru';
echo $lang[$currentLang]['hello'];
Можно использовать include файлов с массивами.
Проблемы: сложно поддерживать множественные формы, нет стандартного инструмента для редактирования переводов, всё в коде.
Случаи использования: прототипы, простые сайты с 2-3 языками.
Как применить JSON-файлы для хранения переводов?
Цель: структурированное хранение переводов, удобное для редактирования.
// ru.json
{"hello": "Привет", "goodbye": "До свидания"}
// PHP
$translations = json_decode(file_get_contents("{$lang}.json"), true);
echo $translations['hello'];
Поддерживается кэширование в памяти.
Проблемы: нет поддержки множественных чисел (требуется дополнительная логика), каждый запрос читает файл или нужно кэшировать.
Как использовать расширение intl (MessageFormatter) для локализации?
Цель: форматирование сообщений с плюрализацией и подстановками.
$fmt = new MessageFormatter('ru_RU', '{0, plural, one{# книга} few{# книги} other{# книг}}');
echo $fmt->format([5]); // 5 книг
Требует установленного intl.
Проблемы: сложный синтаксис шаблонов, не заменяет полный перевод текстов.
Как применить сторонние библиотеки (Symfony Translation, Laravel Lang)?
Цель: использование готовых решений с поддержкой множества форматов.
Пример с Symfony Translation:
use Symfony\Component\Translation\Translator;
$translator = new Translator('ru');
$translator->addLoader('yaml', new YamlFileLoader());
$translator->addResource('yaml', 'messages.ru.yaml', 'ru');
echo $translator->trans('hello');
Аналогично для Laravel: файлы в resources/lang.
Проблемы: увеличение зависимостей, требуется знание фреймворка.
Как хранить язык пользователя в сессии или куки?
Цель: сохранение выбранного языка между запросами.
session_start();
if (isset($_GET['lang'])) {
$_SESSION['lang'] = $_GET['lang'];
setcookie('lang', $_GET['lang'], time()+3600*24);
}
$lang = $_SESSION['lang'] ?? $_COOKIE['lang'] ?? 'ru';
Комбинируется с любым методом перевода.
Проблемы: безопасность (не доверять cookie), необходимость синхронизации.
Как использовать базу данных для переводов?
Цель: динамическое управление переводами через админку.
$stmt = $pdo->prepare("SELECT translation FROM translations WHERE lang = ? AND key = ?");
$stmt->execute([$currentLang, 'hello']);
echo $stmt->fetchColumn();
Можно кэшировать в Memcached/Redis.
Проблемы: нагрузка на БД, сложность миграций.
Расширенные примеры смены языка в PHP
Реализация gettext с множественными формами и контекстами
Структура папок:
locale/
ru_RU/
LC_MESSAGES/
messages.po
messages.mo
en_US/
LC_MESSAGES/
messages.po
messages.mo
Пример содержимого messages.po для русского:
msgid "Hello"
msgstr "Привет"
msgid "You have %d message"
msgid_plural "You have %d messages"
msgstr[0] "У вас %d сообщение"
msgstr[1] "У вас %d сообщения"
msgstr[2] "У вас %d сообщений"
msgctxt "greeting"
msgid "Hello"
msgstr "Здравствуйте"
Код PHP:
setlocale(LC_ALL, 'ru_RU.UTF-8');
bindtextdomain('messages', './locale');
textdomain('messages');
bind_textdomain_codeset('messages', 'UTF-8');
echo _('Hello') . "\n";
echo sprintf(ngettext('You have %d message', 'You have %d messages', 5), 5) . "\n";
echo pgettext('greeting', 'Hello');
Результат:
Привет У вас 5 сообщений Здравствуйте
Автоматическое определение языка браузера
Использование HTTP_ACCEPT_LANGUAGE:
$browserLangs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
$lang = substr($browserLangs[0], 0, 2); // "ru", "en" и т.д.
$available = ['ru', 'en'];
$lang = in_array($lang, $available) ? $lang : 'en';
Результат: язык выбирается в соответствии с предпочтениями браузера.
Кэширование JSON-переводов с помощью APCu
function loadTranslations($lang) {
$cacheKey = "translations_$lang";
$data = apcu_fetch($cacheKey);
if ($data === false) {
$data = json_decode(file_get_contents("lang/$lang.json"), true);
apcu_store($cacheKey, $data, 3600);
}
return $data;
}
$translations = loadTranslations('ru');
echo $translations['hello'];
Результат: ускорение за счёт кэширования.
Интеграция с Symfony Translation Component
require 'vendor/autoload.php';
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\PhpFileLoader;
$translator = new Translator('ru');
$translator->addLoader('php', new PhpFileLoader());
$translator->addResource('php', 'messages.ru.php', 'ru');
echo $translator->trans('hello');
Файл messages.ru.php:
return ['hello' => 'Привет', 'goodbye' => 'До свидания'];
Результат: Привет
Работа с базой данных через PDO с кэшированием Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = "translation:ru:hello";
$translation = $redis->get($key);
if ($translation === false) {
$stmt = $pdo->prepare("SELECT translation FROM translations WHERE lang='ru' AND key='hello'");
$stmt->execute();
$translation = $stmt->fetchColumn();
$redis->setex($key, 3600, $translation);
}
echo $translation; // Привет
Результат: Привет (из кэша или БД).