Языковая поддержка в PHP: выбор и настройка локализации

Раздел: Программирование на PHP -> Интернационализация

Реализация локализации в PHP: подходы и примеры

Какой метод локализации является наиболее эффективным для production-проектов?

Наиболее надёжным и быстрым решением для интернационализации PHP-приложений остаётся расширение gettext. Оно встроено в PHP, использует компилированные файлы .mo и позволяет легко переключать язык через системные локали. Основная идея: все строки оборачиваются в функцию _() или gettext(), а затем создаются файлы перевода для каждого языка.


// Установка локали и домена
$locale = $_GET['lang'] ?? 'ru_RU';
putenv("LC_ALL=$locale");
setlocale(LC_ALL, $locale);
bindtextdomain("messages", "./locale/");
textdomain("messages");

// Использование
$greeting = _("Hello, world!");
echo $greeting;
  

Php loc index php (локализация в php)

Путь к файлам: ./locale/ru_RU/LC_MESSAGES/messages.mo. Файлы .po и .mo создаются через утилиты (xgettext, msgfmt).

Типичные ошибки:

  • Локаль не установлена в системе – проверьте через locale -a.
  • Неверный формат имени локали (ru_RU.utf8, ru_RU).
  • Забыли вызвать textdomain().
  • Проблемы с кодировкой – текст в .po должен быть в UTF-8.

Как реализовать локализацию без установки дополнительных расширений, только на PHP?

Самый простой вариант – хранить переводы в ассоциативных массивах. Подходит для небольших проектов или прототипов. Создаётся файл с языковыми константами.


// lang_ru.php
return [
  'greeting' => 'Привет, мир!',
  'farewell' => 'До свидания'
];

// index.php
$lang = $_GET['lang'] ?? 'ru';
$translations = include "lang_{$lang}.php";
echo $translations['greeting'];
  

Index php en (локализация (язык en))

Проблемы:

  • Ручное управление ключами – легко допустить опечатку.
  • Сложность с плюральными формами и контекстом.
  • Каждый запрос загружает весь массив – неэффективно для сотен строк.

Как организовать локализацию через внешние JSON-файлы?

Удобный способ для современных приложений, особенно если переводы редактируются не разработчиками. JSON легко читается и обрабатывается.


// locale/ru.json
{
  "app.title": "Моё приложение",
  "user.welcome": "Добро пожаловать, %s!"
}

// Загрузка
$lang = $_COOKIE['lang'] ?? 'ru';
$json = file_get_contents("locale/{$lang}.json");
$messages = json_decode($json, true);
$username = 'Иван';
echo sprintf($messages['user.welcome'], htmlspecialchars($username));
  

Action php lang (язык в php (локализация))

Недостатки:

  • Отсутствие поддержки плюральных форм без дополнительной логики.
  • Файл читается при каждом запросе – кеширование обязательно (APCu, file cache).
  • Сложность с плюральными формами для языков с множеством вариантов (арабский, славянские).

Как использовать базу данных для хранения переводов?

Подходит для больших динамических систем, где переводы меняются часто и нужен интерфейс управления. Обычно таблицы: translations (ключ, контекст), translation_values (id перевода, id языка, текст).


// Пример запроса (PDO)
$stmt = $pdo->prepare("
  SELECT v.text
  FROM translations t
  JOIN translation_values v ON t.id = v.translation_id
  JOIN languages l ON l.id = v.language_id
  WHERE t.key = :key AND l.code = :lang
");
$stmt->execute(['key' => 'button.save', 'lang' => $lang]);
echo $stmt->fetchColumn();
  

Сложности:

  • Каждый вызов – запрос к БД, требуется кеширование (Memcached, Redis).
  • Необходимость миграций при изменении структуры.
  • Сложнее поддерживать плюральные формы – нужна отдельная логика.

Как использовать расширение intl для форматирования чисел, дат и строк с учётом локали?

Расширение intl (Internationalization) предоставляет классы NumberFormatter, MessageFormatter и Locale. Оно не заменяет gettext, но дополняет его для форматирования.


$fmt = new NumberFormatter('de_DE', NumberFormatter::DECIMAL);
echo $fmt->format(1234567.89); // 1.234.567,89

$msg = new MessageFormatter('ru_RU', '{0,plural,one{# яблоко}few{# яблока}other{# яблок}}');
echo $msg->format([5]); // 5 яблок
  

Замечания:

  • Расширение должно быть установлено (часто включено, но не всегда).
  • Для сложных правил множественного числа требуется корректный шаблон ICU.

Расширенные примеры кода с результатами

1. Комплексный пример с gettext и выбором языка через параметр action php lang

Пример

// init.php
function initLocale($langCode) {
  $locales = [
    'en' => 'en_US.utf8',
    'ru' => 'ru_RU.utf8',
    'de' => 'de_DE.utf8'
  ];
  $locale = $locales[$langCode] ?? 'en_US.utf8';
  putenv("LC_ALL=$locale");
  setlocale(LC_ALL, $locale);
  bindtextdomain("messages", __DIR__ . "/locale");
  textdomain("messages");
  bind_textdomain_codeset("messages", "UTF-8");
}

// index.php
require 'init.php';
$lang = $_GET['lang'] ?? 'ru';
initLocale($lang);
echo _("Welcome") . "\n";
echo _("Total items: %d") . "\n";
При lang=ru: Добро пожаловать
Всего элементов: %d

Пояснение: Создан общий файл инициализации. Язык получается из GET-параметра. Файлы переводов подготовлены заранее.

2. Локализация через JSON с кешированием в APCu

Пример

function loadTranslations($lang) {
  $cacheKey = "lang_{$lang}";
  if (apcu_exists($cacheKey)) {
    return apcu_fetch($cacheKey);
  }
  $file = __DIR__ . "/locales/{$lang}.json";
  if (!file_exists($file)) {
    return [];
  }
  $data = json_decode(file_get_contents($file), true);
  apcu_store($cacheKey, $data, 3600);
  return $data;
}

$lang = $_SESSION['lang'] ?? 'ru';
$t = loadTranslations($lang);
echo $t['nav.home'] ?? 'Home';
При наличии файла locales/ru.json с ключом nav.home: Главная

3. Плюральные формы с помощью intl MessageFormatter

Пример

$formatter = new MessageFormatter('ru_RU', '{0,plural,one{Ваш запрос вернул # результат}few{Ваш запрос вернул # результата}other{Ваш запрос вернул # результатов}}');
echo $formatter->format([1]) . "\n";
echo $formatter->format([3]) . "\n";
echo $formatter->format([10]) . "\n";
Ваш запрос вернул 1 результат
Ваш запрос вернул 3 результата
Ваш запрос вернул 10 результатов

4. Динамическая загрузка переводов из БД с использованием PDO и кеширования Memcached

Пример

$memcached = new Memcached();
$memcached->addServer('localhost', 11211);

function getTranslation($key, $lang) {
  global $memcached, $pdo;
  $cacheKey = "trans_{$lang}_{$key}";
  $val = $memcached->get($cacheKey);
  if ($memcached->getResultCode() === Memcached::RES_SUCCESS) {
    return $val;
  }
  $stmt = $pdo->prepare("
    SELECT v.text
    FROM translations t
    JOIN translation_values v ON t.id = v.translation_id
    JOIN languages l ON l.id = v.language_id
    WHERE t.key = :key AND l.code = :lang
    LIMIT 1
  ");
  $stmt->execute(['key' => $key, 'lang' => $lang]);
  $text = $stmt->fetchColumn() ?: $key;
  $memcached->set($cacheKey, $text, 86400);
  return $text;
}

echo getTranslation('form.submit', 'ru');
Если в БД есть запись: Отправить

5. Использование gettext с множественными доменами и контекстами

Пример

bindtextdomain('errors', './locale');
bindtextdomain('messages', './locale');

// Переключение домена
textdomain('errors');
echo dgettext('messages', "Hello");

// Контекст (pgettext)
echo pgettext("menu", "File");
При корректно подготовленных .mo файлах выводятся соответствующие переводы.

Язык в PHP (локализация) - comments

En
Action php lang (php)