Переключение языков в 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; // Привет

Результат: Привет (из кэша или БД).

Смена языка в PHP - comments

En
Change lang php lang (php)