Настройка локализации на PHP через index.php: ключевые подходы и примеры

Раздел: Веб-разработка на PHP -> Локализация и интернационализация

Основы локализации в PHP: выбор механизма для index.php

Как организовать переключение языка сайта через параметр в URL (index.php?lang=ru)?

Наиболее распространённый и простой способ - передавать код языка через GET-параметр lang. Этот метод легко кэшируется, понятен поисковым системам и не требует работы с сессиями. Ниже приведён базовый сценарий для файла index.php.


<?php
// Определяем доступные языки
$available_langs = ['ru', 'en', 'de'];

// Получаем язык из запроса или устанавливаем по умолчанию
$lang = isset($_GET['lang']) && in_array($_GET['lang'], $available_langs) 
    ? $_GET['lang'] 
    : 'ru';

// Подключаем файл с переводами для выбранного языка
$lang_file = __DIR__ . "/lang/{$lang}.php";
if (file_exists($lang_file)) {
    $translations = require $lang_file;
} else {
    die("Language file not found: " . $lang_file);
}

// Функция для удобного вывода перевода
function __($key) {
    global $translations;
    return $translations[$key] ?? $key;
}

?>
<!DOCTYPE html>
<html lang="<?= $lang ?>">
<head>
    <title><?= __('site_title') ?></title>
</head>
<body>
    <h2><?= __('welcome_message') ?></h2>
    <nav>
        <a href="?lang=ru">Русский</a>
        <a href="?lang=en">English</a>
        <a href="?lang=de">Deutsch</a>
    </nav>
</body>
</html>
  

Index php lang (настройка локализации в php)

Файл перевода lang/ru.php может выглядеть так:


<?php
return [
    'site_title'      => 'Мой сайт',
    'welcome_message' => 'Добро пожаловать!',
];
  

Типичная ошибка и её решение

Если файл перевода отсутствует или в нём допущена синтаксическая ошибка, скрипт остановится с помощью die(). Лучше заменить это на graceful fallback - использовать язык по умолчанию и логировать ошибку.


if (!file_exists($lang_file)) {
    error_log("Missing language file: $lang_file");
    $translations = require __DIR__ . '/lang/ru.php'; // fallback
}
    

Как хранить выбор языка в сессии, чтобы не терять его между страницами?

Использование сессии позволяет запомнить язык пользователя на всё время его визита без необходимости передавать параметр в каждом URL. Этот подход удобен для административных панелей или одностраничных приложений.


<?php
session_start();

$available_langs = ['ru', 'en'];

// Если в GET передан новый язык, обновляем сессию
if (isset($_GET['lang']) && in_array($_GET['lang'], $available_langs)) {
    $_SESSION['lang'] = $_GET['lang'];
}

$lang = $_SESSION['lang'] ?? 'ru';

// Подключаем переводы
$lang_file = __DIR__ . "/lang/{$lang}.php";
if (file_exists($lang_file)) {
    $translations = require $lang_file;
} else {
    $translations = require __DIR__ . '/lang/ru.php';
}
?>
  

При таком подходе ссылки на переключение языка будут содержать GET-параметр, который обновляет сессию и затем происходит редирект на ту же страницу без параметра, чтобы URL был чистым. Либо можно использовать форму с POST.

Проблема: если пользователь закроет браузер, сессия потеряется. Для долговременного запоминания языка лучше использовать cookie.

Как реализовать локализацию через cookie без привязки к сессии?

Cookie позволяют хранить выбор языка даже после закрытия браузера. Пример ниже устанавливает cookie на 30 дней.


<?php
$available_langs = ['ru', 'en'];

if (isset($_GET['lang']) && in_array($_GET['lang'], $available_langs)) {
    setcookie('lang', $_GET['lang'], time() + 60*60*24*30, '/');
    $lang = $_GET['lang'];
} elseif (isset($_COOKIE['lang']) && in_array($_COOKIE['lang'], $available_langs)) {
    $lang = $_COOKIE['lang'];
} else {
    $lang = 'ru';
}

// Загрузка переводов...
?>
  

Важно помнить: cookie отправляются с каждым запросом, что может увеличить размер заголовков. Также нужно учитывать настройки безопасности (HttpOnly, Secure).

Какие преимущества даёт использование gettext (MO-файлы) для профессиональной локализации?

Gettext - стандартный инструмент UNIX для интернационализации. Он позволяет отделить переводы от кода, использовать плюральные формы и контексты. В PHP расширение gettext должно быть установлено.


<?php
// Устанавливаем локаль и домен
$locale = 'ru_RU.UTF-8';
putenv("LANG=$locale");
setlocale(LC_ALL, $locale);
bindtextdomain('messages', __DIR__ . '/locales');
textdomain('messages');

// Определяем язык из GET-параметра
$lang = $_GET['lang'] ?? 'ru';
if (in_array($lang, ['ru', 'en'])) {
    $locale = ($lang === 'en') ? 'en_US.UTF-8' : 'ru_RU.UTF-8';
    putenv("LANG=$locale");
    setlocale(LC_ALL, $locale);
}

// Вывод перевода
echo gettext('Hello World');
// или _('Hello World');
?>
  

Для работы с gettext необходимы файлы .po и скомпилированные .mo. Расположение: locales/ru_RU/LC_MESSAGES/messages.mo.

Распространённая проблема: gettext не работает в некоторых окружениях (например, в Windows без установки локалей). Решение - использовать альтернативную библиотеку-обёртку или перейти на собственные массивы.

Как организовать локализацию с автозагрузкой языковых файлов по заголовкам Accept-Language?

Автоматическое определение языка браузера полезно для первого визита. Алгоритм: проверяем cookie/сессию, если нет - анализируем HTTP-заголовок.


function detect_language() {
    $available = ['ru', 'en', 'de'];
    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
        $browser_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
        foreach ($browser_langs as $lang) {
            $lang_code = substr($lang, 0, 2);
            if (in_array($lang_code, $available)) {
                return $lang_code;
            }
        }
    }
    return 'ru'; // по умолчанию
}

$lang = $_GET['lang'] ?? $_COOKIE['lang'] ?? $_SESSION['lang'] ?? detect_language();
  

Выбор подходящего решения

Каждый вариант имеет свою область применения. GET-параметр - для публичных сайтов, важен для SEO. Сессия - для приложений с авторизацией. Cookie - для постоянных пользователей, не требующих регистрации. Gettext - для больших проектов с командами переводчиков. Определение по браузеру - для первого контакта с пользователем.

Расширенные примеры реализации локализации

Пример 1: Использование gettext с плюральными формами и контекстом

Gettext поддерживает правила множественного числа, которые зависят от локали. Для русского языка три формы: 1, 2-4, 5-20. Пример кода с функциями ngettext и pgettext.

Пример

<?php
// Установка локали и домена (аналогично базовому примеру)
$locale = 'ru_RU.UTF-8';
putenv("LANG=$locale");
setlocale(LC_ALL, $locale);
bindtextdomain('app', __DIR__ . '/locale');
textdomain('app');

// Вывод с учётом количества (плюральные формы)
$count = 5;
echo ngettext('{n} item', '{n} items', $count);
// В .po файле должно быть: msgid "{n} item" / msgid_plural "{n} items" / msgstr[0] "{n} элемент" / msgstr[1] "{n} элемента" / msgstr[2] "{n} элементов"

// Контекстный перевод (например, "bank" как финансовое учреждение и "bank" как берег реки)
echo pgettext('finance', 'bank');
echo pgettext('geography', 'bank');
?>
5 элементов
(в зависимости от .po файла)

Пример 2: Загрузка переводов из базы данных с кэшированием в файл

Для динамических приложений удобно хранить переводы в таблице MySQL. Чтобы не делать запрос при каждом вызове, можно кэшировать результат в PHP-файл или memcached.

Пример

<?php
function load_translations_from_db($lang) {
    $cache_file = __DIR__ . "/cache/lang_{$lang}.php";
    if (file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) {
        return require $cache_file;
    }
    
    // Запрос к БД (пример с PDO)
    $pdo = new PDO('mysql:host=localhost;dbname=site', 'user', 'pass');
    $stmt = $pdo->prepare("SELECT `key`, `value` FROM translations WHERE lang = ?");
    $stmt->execute([$lang]);
    $translations = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
    
    // Сохраняем кэш
    $content = '<?php return ' . var_export($translations, true) . ';';
    file_put_contents($cache_file, $content, LOCK_EX);
    
    return $translations;
}

$lang = $_GET['lang'] ?? 'ru';
$translations = load_translations_from_db($lang);
function __($key) { global $translations; return $translations[$key] ?? $key; }
echo __('welcome');
?>
Добро пожаловать

Пример 3: Сборка строк с подстановками (sprintf) в переводах

Часто нужно вставлять динамические данные в перевод. Нельзя склеивать строки, так как порядок слов в разных языках различается. Используем sprintf с именованными плейсхолдерами (собственная реализация).

Пример

// lang/ru.php
return [
    'greeting' => 'Привет, %s!',
    'items_count' => 'У вас %d новых сообщений.',
];

// index.php
function __($key, ...$args) {
    global $translations;
    $text = $translations[$key] ?? $key;
    return count($args) ? sprintf($text, ...$args) : $text;
}

echo __('greeting', 'Иван');
echo __('items_count', 3);
?>
Привет, Иван!
У вас 3 новых сообщений.

Пример 4: Многоуровневый fallback языков (например, en как запасной для de)

Если перевод для выбранного языка не найден, можно использовать английский, а если и его нет - сам ключ.

Пример

$lang = 'de';
$available_fallback = ['de' => 'en', 'en' => 'en', 'ru' => 'ru'];

$translations = [];
$fallback_lang = $available_fallback[$lang] ?? 'en';
foreach ([$lang, $fallback_lang] as $try_lang) {
    $file = __DIR__ . "/lang/{$try_lang}.php";
    if (file_exists($file)) {
        $translations = array_merge($translations, require $file);
    }
}

function __($key) {
    global $translations;
    return $translations[$key] ?? $key;
}
?>
Если в de.php нет ключа 'hello', но есть в en.php, то будет выведено 'Hello'.

Пример 5: JavaScript-локализация в связке с PHP (передача переводов на клиент)

Для динамических элементов интерфейса может потребоваться передать массив переводов на JavaScript. Делаем это через JSON.

Пример

<?php
$lang = $_GET['lang'] ?? 'ru';
$translations = require __DIR__ . "/lang/{$lang}.php";
?>
<!DOCTYPE html>
<html>
<head>
    <script>
        var i18n = <?= json_encode($translations, JSON_UNESCAPED_UNICODE) ?>;
        function __(key) { return i18n[key] || key; }
        console.log(__('js_alert'));
    </script>
</head>
<body>
    <button onclick="alert(__('click_me'))">Click</button>
</body>
</html>
В консоли будет выведено значение ключа 'js_alert' из языкового файла.

Настройка локализации в PHP - comments

En
Index php lang (php)