Почему интернационализация PHP — это не просто «перевод слов»
Представьте: вы написали крутой PHP-проект. Он работает, радует пользователей, приносит прибыль. Но однажды в статистике вы замечаете, что 30% трафика — из Германии, 15% — из Японии, а 10% — из ОАЭ. Ваш сайт говорит только по-русски. Пользователи уходят, не найдя кнопку «Купить» на своём языке.
Знакомая ситуация? Интернационализация (i18n) PHP-приложений — это не просто замена слов в шаблоне. Это архитектурный подход, который позволяет вашему коду «понимать» культуру, валюту, даты и даже направление текста (слева направо или справа налево).
В этой статье мы разберём, как превратить обычный PHP-сайт в мультиязычную платформу, используя нативные инструменты и лучшие практики. Вы узнаете, как работает gettext в PHP, чем отличается локализация от интернационализации, и как не наступить на грабли с плюрализацией.
i18n vs l10n: в чём разница (и почему это важно)
Прежде чем писать код, давайте договоримся о терминах. Интернационализация (i18n) — это подготовка кода к переводу: вынос строк, работа с форматами чисел, дат, валют. Локализация (l10n) — это сам процесс перевода и адаптации под конкретный регион.
Простая аналогия: интернационализация — это строительство дома с пустыми комнатами, куда можно поставить любую мебель. Локализация — это расстановка мебели под вкусы конкретного жильца.
Для PHP-разработчика это означает: не хардкодьте строки! Никогда. Даже если сейчас сайт только на русском. Каждая фраза должна быть обёрнута в функцию перевода.
Стек технологий для i18n в PHP
В экосистеме PHP есть несколько мощных инструментов. Мы рассмотрим два основных подхода: классический gettext и современный ICU MessageFormat через расширение intl.
1. Gettext — дедушка мультиязычности
Gettext в PHP — это стандарт де-факто для многих CMS (WordPress, Drupal). Он использует .po и .mo файлы для хранения переводов. Плюсы: простота, поддержка плюральных форм, интеграция с Poedit.
2. ICU MessageFormat — для сложной логики
Расширение PHP intl (Internationalization) предоставляет классы MessageFormatter и NumberFormatter. Это выбор для проектов, где нужны сложные шаблоны с выбором падежа, рода и числа.
3. Библиотеки-обёртки
- symfony/translation — компонент Symfony, работает как с YAML/XLIFF, так и с gettext.
- laravel-localization — для Laravel (пакет mcamara/laravel-localization).
- zend-i18n — для Laminas (бывший Zend Framework).
Практика: настраиваем gettext в PHP с нуля
Допустим, у нас есть простой скрипт index.php, который выводит приветствие. Сделаем его мультиязычным.
Шаг 1. Подготовка кода
Оберните все строки в функцию _() (алиас для gettext()):
<?php
// index.php
echo _("Добро пожаловать на наш сайт!");
$items = 5;
// Для плюральных форм используем ngettext
echo ngettext("%d товар", "%d товаров", $items);
?>
Шаг 2. Генерация .pot файла
Установите gettext на сервере или используйте xgettext:
xgettext -o locales/messages.pot index.php --from-code=UTF-8
Шаг 3. Создание перевода
Скопируйте messages.pot в locales/ru_RU/LC_MESSAGES/messages.po и отредактируйте:
msgid "Добро пожаловать на наш сайт!"
msgstr "Welcome to our site!"
msgid "%d товар"
msgid_plural "%d товаров"
msgstr[0] "%d item"
msgstr[1] "%d items"
Шаг 4. Компиляция и подключение
msgfmt locales/ru_RU/LC_MESSAGES/messages.po -o locales/ru_RU/LC_MESSAGES/messages.mo
В PHP-скрипте укажите локаль:
<?php
$locale = 'ru_RU'; // или 'en_US' по выбору пользователя
putenv("LC_ALL=$locale");
setlocale(LC_ALL, $locale);
bindtextdomain("messages", "locales");
textdomain("messages");
echo _("Добро пожаловать на наш сайт!");
?>
Сложные случаи: плюрализация и контекст
Русский язык славится сложными формами множественного числа. В PHP gettext справляется с этим через ngettext(). Но есть нюанс: для арабского, польского или чешского языков нужно больше двух форм. Интернационализация PHP приложений требует учитывать это на этапе проектирования.
Пример для русского (3 формы):
msgid "%d яблоко"
msgid_plural "%d яблока"
msgstr[0] "%d apple"
msgstr[1] "%d apples"
msgstr[2] "%d apples" // В русском 3 формы, но в английском — 2. Poedit подскажет.
Контекст перевода
Иногда одно и то же слово переводится по-разному в зависимости от контекста. Например, «Save» может быть «Сохранить» (действие) или «Кроме» (предлог). Используйте pgettext:
echo pgettext("Verb", "Save"); // Сохранить
echo pgettext("Preposition", "Save"); // Кроме
Современный подход: ICU MessageFormat через PHP intl
Расширение intl входит в стандартную поставку PHP (начиная с 5.3). Оно позволяет писать сложные шаблоны без множественных ngettext.
Пример: выбор рода и числа в одном шаблоне
<?php
$fmt = new MessageFormatter('ru_RU',
'{gender, select,
male {Пользователь {name} добавил {count, plural, one {# товар} few {# товара} other {# товаров}}}
female {Пользовательница {name} добавила {count, plural, one {# товар} few {# товара} other {# товаров}}}
other {Пользователь {name} добавил