Почему ваш PHP-сайт должен заговорить на разных языках?
Представьте: вы создали идеальный продукт — быстрый, удобный, решающий проблемы. Но есть одна загвоздка: ваш интерфейс говорит только по-русски. Потенциальные клиенты из Германии, Японии или Бразилии просто закрывают вкладку, не понимая ни кнопки. Это не просто упущенная прибыль — это потеря целых рынков.
Интернационализация (i18n — от «internationalization», где 18 букв между i и n) и локализация (l10n) — это не «галочка» для стартапа, а стратегический инструмент. Многие разработчики боятся этого слова, думая, что придется переписывать весь код. На самом деле, грамотная организация переводов в PHP — это элегантный процесс, который делает код чище, а бизнес — глобальнее.
В этой статье мы разберем три реальных подхода к интернационализации PHP приложений: от простых массивов до мощного расширения intl и классического gettext. Вы узнаете, как не просто перевести текст, а правильно работать с датами, числами и плюральными формами — ведь в арабском, польском или русском языках свои правила склонения.
Основные подходы к i18n в PHP
Прежде чем писать код, нужно выбрать архитектуру. Условно все методы делятся на три категории. Какую выбрать — зависит от масштаба проекта и требований к производительности.
1. Массивы и ini-файлы (DIY-подход)
Самый простой способ — хранить переводы в ассоциативных массивах или .ini файлах. Отлично подходит для небольших проектов или MVP.
// lang/ru.php
return [
'welcome' => 'Добро пожаловать на сайт!',
'logout' => 'Выйти',
'cart_empty' => 'Корзина пуста'
];
// lang/en.php
return [
'welcome' => 'Welcome to the website!',
'logout' => 'Logout',
'cart_empty' => 'Cart is empty'
];
Использовать это просто: подключаем нужный файл в зависимости от сессии или куки пользователя.
function __($key) {
static $lang = null;
if ($lang === null) {
$locale = $_SESSION['lang'] ?? 'ru';
$lang = include "lang/{$locale}.php";
}
return $lang[$key] ?? $key;
}
echo __('welcome'); // Добро пожаловать на сайт!
Плюсы: простота, не требует расширений, легко кэшируется (можно даже в Redis сохранить).
Минусы: нет поддержки плюральных форм (придется писать велосипед), неудобно для больших проектов (100500 ключей в одном файле).
2. Gettext — классика жанра
Gettext — стандарт де-факто для интернационализации в мире Unix. В PHP он доступен через расширение gettext. Это мощный инструмент, который используется в WordPress, Drupal и Symfony.
Сначала создаем .po файлы (например, для русского языка):
# ru_RU/LC_MESSAGES/messages.po
msgid "Hello, %s!"
msgstr "Здравствуйте, %s!"
msgid "You have %d new message"
msgid_plural "You have %d new messages"
msgstr[0] "У вас %d новое сообщение"
msgstr[1] "У вас %d новых сообщения"
msgstr[2] "У вас %d новых сообщений"
Затем компилируем в .mo файл (инструментом msgfmt). В PHP-коде это выглядит так:
putenv('LC_ALL=ru_RU.UTF-8');
setlocale(LC_ALL, 'ru_RU.UTF-8');
bindtextdomain('messages', './locale');
textdomain('messages');
echo gettext('Hello, %s!', 'Алексей'); // Здравствуйте, Алексей!
// Плюральные формы — магия:
echo ngettext('%d message', '%d messages', 3); // 3 сообщения
Кстати, для ускорения разработки используйте Poedit — он подсветит непереведенные строки.
3. Расширение Intl (PHP 5.3+)
Если вам нужно не просто перевести текст, а корректно отформатировать число, дату или валюту под стандарты страны — используйте Intl. Это надстройка над библиотекой ICU (International Components for Unicode).
// Форматирование чисел
$fmt = new NumberFormatter('de_DE', NumberFormatter::DECIMAL);
echo $fmt->format(1234567.89); // 1.234.567,89
// Форматирование валюты
$fmt = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
echo $fmt->formatCurrency(1234.56, 'EUR'); // €1,234.56
// Сравнение строк с учетом локали
$coll = new Collator('ru_RU');
$result = $coll->compare('ёлка', 'ежик'); // корректное сравнение буквы "ё"
Но самое полезное — класс MessageFormatter, который умеет подставлять переменные и плюральные формы без написания условий:
$msg = '{0, plural,
one {# сообщение}
few {# сообщения}
many {# сообщений}
other {# сообщений}
}';
$fmt = new MessageFormatter('ru_RU', $msg);
echo $fmt->format([3]); // 3 сообщения
Практический чек-лист: как внедрить i18n без боли
Теперь, когда мы знаем инструменты, давайте составим план действий. Я рекомендую последовательность, которая спасет вас от рефакторинга.
Шаг 1. Выделите все строки в отдельные функции
Даже если вы пока не знаете, какой метод выберете, начните с обертки. Вместо прямого вывода текста:
// Плохо
echo "Привет, мир!";
// Хорошо
echo t('Привет, мир!');
Функция t() может быть заглушкой, которая просто возвращает переданную строку. Потом вы легко замените её на вызов gettext или поиск в массиве.
Шаг 2. Определите, как узнавать язык пользователя
- Через URL:
example.com/ru/about— самый надежный способ (SEO-friendly). - Через сессию: пользователь выбрал язык в настройках.
- Через заголовок Accept-Language: автоопределение, но не стоит доверять ему на 100%.
// Определение из URL
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$segments = explode('/', trim($uri, '/'));
$locale = $segments[0] ?? 'ru';
if (!in_array($locale, ['ru', 'en', 'de'])) {
$locale = 'ru'; // fallback
}
$_SESSION['lang'] = $locale;
Шаг 3. Настройте плюральные формы (самое сложное)
В русском языке три формы: 1 (сообщение), 2-4 (сообщения), 5-20 (сообщений).