Интернационализация PHP: от простых массивов до gettext
Локализация PHP приложений: ключевые подходы
Как обеспечить локализацию с помощью расширения gettext?
Расширение gettext является стандартным и наиболее эффективным решением для интернационализации в PHP. Оно поддерживает плюрализацию, категории (LC_MESSAGES, LC_TIME, LC_MONETARY) и кэширование переводов на уровне сервера.
Для работы требуется установить расширение (обычно включено в PHP, но может потребоваться пакет php-gettext). Код размещается в файле index.php:
<?php
putenv('LC_ALL=ru_RU.UTF-8');
setlocale(LC_ALL, 'ru_RU.UTF-8');
bindtextdomain('messages', './locale');
textdomain('messages');
echo _("Hello, world!");
?>Php loc index php (локализация в php)
Шаги: подготовка файлов .po (с помощью xgettext или Poedit), компиляция в .mo, расположение в директории locale/ru_RU/LC_MESSAGES/...
Типичные ошибки: локаль не установлена на сервере, неверный путь к файлам .mo, отсутствие нужной кодировки, проблемы с кэшем (требуется перезапуск веб-сервера или очистка кэша gettext). Решение: проверить setlocale через var_dump(setlocale(LC_ALL, 0)); использовать абсолютные пути.
Цель: профессиональные проекты с множеством языков, требования к производительности и поддержка плюрализации.
Как выполнить переводы без внешних расширений с помощью массивов PHP?
Самый простой способ: создать файлы с массивами, где ключи - строковые идентификаторы, значения - переводы. Файл lang/ru.php:
<?php
return [
'hello' => 'Привет, мир',
'welcome' => 'Добро пожаловать',
];
?>Index php en (локализация (язык en))
Функция перевода:
function t($key, $lang = 'ru') {
$translations = include "lang/$lang.php";
return $translations[$key] ?? $key;
}
Action php lang (язык в php (локализация))
Проблемы: нет поддержки множественного числа, сложно поддерживать большие массивы, каждый запрос загружает весь файл (можно кэшировать через include или OpCache). Решение: для плюрализации потребуется дополнительная логика.
Используется в небольших проектах, где нет требований к производительности и количеству языков.
Как хранить переводы в формате JSON для удобства редактирования?
JSON легко читается и редактируется внешними инструментами. Файл lang/ru.json:
{
"hello": "Привет",
"welcome": "Добро пожаловать"
}
PHP код:
function t($key, $lang = 'ru') {
$json = file_get_contents("lang/$lang.json");
$data = json_decode($json, true);
return $data[$key] ?? $key;
}
Проблемы: каждый вызов функции читает файл с диска - медленно. Решение: кэширование в статическую переменную или использование кэша (APCu, Redis). Также JSON не поддерживает комментарии.
Подходит для проектов, где переводы редактируются не разработчиками, и нужен простой текстовый формат.
Как интегрировать профессиональную систему переводов с помощью Symfony Translation компонента?
Компонент предоставляет мощные возможности: поддержка XLIFF, YAML, PHP, плюрализация, каталоги, загрузчики. Установка через Composer:
composer require symfony/translation
Пример использования:
<?php
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\ArrayLoader;
$translator = new Translator('ru');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', [
'hello' => 'Привет',
'apples_count' => '{0} Нет яблок|[1,Inf] %count% яблок',
], 'ru');
echo $translator->trans('hello');
echo $translator->trans('apples_count', ['%count%' => 5]);
?>
Проблемы: необходимость автозагрузки Composer, увеличение размера приложения, сложность начальной настройки. Решение: использовать в проектах, уже использующих Symfony, или если требуется дружественный интерфейс для переводчиков.
Применяется в больших коммерческих проектах с командой переводчиков и сложной логикой локализации.
Расширенные примеры локализации в PHP
Пример 1. Плюрализация с gettext (функция ngettext).
<?php
putenv('LC_ALL=ru_RU.UTF-8');
setlocale(LC_ALL, 'ru_RU.UTF-8');
bindtextdomain('messages', './locale');
textdomain('messages');
$count = 5;
echo ngettext('%d яблоко', '%d яблок', $count);
?>
5 яблок
Пример 2. Динамическое переключение языка через сессию.
<?php
session_start();
$lang = $_SESSION['lang'] ?? 'ru';
if (isset($_GET['lang'])) {
$lang = $_GET['lang'];
$_SESSION['lang'] = $lang;
}
putenv("LC_ALL={$lang}_RU.UTF-8");
setlocale(LC_ALL, "{$lang}_RU.UTF-8");
bindtextdomain('messages', './locale');
textdomain('messages');
echo _('Welcome');
?>
Добро пожаловать (при lang=ru)
Пример 3. Форматирование чисел и валют с помощью intl (NumberFormatter).
<?php
$number = 123456.78;
$fmt = new NumberFormatter('ru_RU', NumberFormatter::CURRENCY);
echo $fmt->formatCurrency($number, 'RUB');
$fmt2 = new NumberFormatter('en_US', NumberFormatter::DECIMAL);
echo $fmt2->format($number);
?>
123 456,78 ₽ 123,456.78
Пример 4. Сложные сообщения с MessageFormatter (плюрализация + подстановки).
<?php
$message = '{0, plural, one{# книга} few{# книги} other{# книг}}';
$fmt = new MessageFormatter('ru_RU', $message);
echo $fmt->format([0 => 1]);
echo $fmt->format([0 => 3]);
echo $fmt->format([0 => 10]);
?>
1 книга 3 книги 10 книг
Пример 5. JSON с кэшированием в статическую переменную.
<?php
function t($key, $lang = 'ru') {
static $cache = [];
if (!isset($cache[$lang])) {
$json = file_get_contents("lang/$lang.json");
$cache[$lang] = json_decode($json, true);
}
return $cache[$lang][$key] ?? $key;
}
echo t('hello');
?>
Привет
Пример 6. Работа с временными зонами и датами.
<?php
$date = new DateTime('2025-03-24 15:30:00', new DateTimeZone('Europe/Moscow'));
$fmt = new IntlDateFormatter('ru_RU', IntlDateFormatter::LONG, IntlDateFormatter::SHORT);
echo $fmt->format($date);
?>
24 марта 2025 г., 15:30