Языковая поддержка в 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 файлах выводятся соответствующие переводы.