Интернационализация PHP: как заговорить с миром на одном языке

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему ваш 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 (сообщений).

Блоги

Рекомендаций по книгам