Реализация языковых файлов в PHP для конфигурирования сайта

Раздел: Конфигурирование PHP -> Конфигурация и настройка

Языковые файлы в PHP: подходы к реализации мультиязычности

Как организовать языковые файлы с помощью PHP-массивов?

Наиболее эффективным решением является хранение переводов в виде PHP-массивов, возвращаемых из отдельных файлов. Такой подход обеспечивает максимальную производительность, так как массивы загружаются напрямую интерпретатором без дополнительного разбора. Для реализации создается папка lang/, внутри которой располагаются файлы ru.php, en.php и т.д. Каждый файл возвращает ассоциативный массив. Пример файла lang/ru.php:


<?php
return [
    'welcome' => 'Добро пожаловать',
    'login' => 'Войти',
    'logout' => 'Выйти',
];
  

Files php lang (языковые файлы в php)

Загрузка происходит через функцию, определяющую текущую локаль пользователя:


function loadLang(string $locale): array {
    $file = __DIR__ . '/lang/' . $locale . '.php';
    if (!file_exists($file)) {
        throw new RuntimeException('Языковой файл не найден: ' . $file);
    }
    return require $file;
}
  

Files php ini (файлы php.ini в php)

Типичные ошибки и их решения

Ошибка: Файл не найден из-за неправильного пути. Решение: использовать абсолютный путь через __DIR__ или задать константу LANG_DIR.

Проблема: Отсутствие кеширования при большом количестве запросов. Решение: сохранять загруженный массив в статической переменной или использовать apcu_store().

Как хранить переводы в JSON и загружать их в PHP?

JSON-файлы удобны для редактирования не-программистами и легко интегрируются с фронтенд-библиотеками. Каждый языковой файл содержит объект с парами ключ-значение. Пример lang/en.json:


{
    "welcome": "Welcome",
    "login": "Log in",
    "logout": "Log out"
}
  

Загрузка выполняется через file_get_contents() и json_decode():


function loadLangJson(string $locale): array {
    $file = __DIR__ . '/lang/' . $locale . '.json';
    if (!file_exists($file)) {
        throw new InvalidArgumentException('Отсутствует файл: ' . $file);
    }
    $content = file_get_contents($file);
    $data = json_decode($content, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new RuntimeException('Ошибка парсинга JSON: ' . json_last_error_msg());
    }
    return $data;
}
  

Возможные проблемы

Проблема: Некорректная кодировка файла. Решение: убедиться, что JSON сохранен в UTF-8 без BOM.

Ошибка: Дублирование ключей. JSON не допускает дубликатов, но последний перезапишет предыдущий. Проверять структуру перед использованием.

Можно ли использовать INI-файлы для языковых констант?

INI-формат подходит для плоских переводов без вложенности. Функция parse_ini_file() возвращает массив. Пример lang/de.ini:


welcome = "Willkommen"
login = "Anmelden"
logout = "Abmelden"
  

Загрузка:


function loadLangIni(string $locale): array {
    $file = __DIR__ . '/lang/' . $locale . '.ini';
    if (!file_exists($file)) {
        throw new RuntimeException('INI файл не найден: ' . $file);
    }
    $data = parse_ini_file($file);
    if ($data === false) {
        throw new RuntimeException('Не удалось разобрать INI файл');
    }
    return $data;
}
  

Ограничения

Проблема: Не поддерживаются многомерные массивы. Решение: использовать секции ([section]) для группировки, но это требует дополнительной обработки.

Ошибка: Спецсимволы в значениях (например, кавычки). Решение: экранировать или использовать двойные кавычки.

Как использовать gettext для профессиональной локализации?

Расширение gettext стандартно для Unix-систем и позволяет использовать .po/.mo файлы с поддержкой плюральных форм и контекста. Требуется установка пакета gettext в ОС. Пример настройки:


putenv('LC_ALL=ru_RU');
setlocale(LC_ALL, 'ru_RU.UTF-8');
bindtextdomain('messages', __DIR__ . '/locale');
textdomain('messages');
  

Файлы переводов размещаются по пути locale/ru_RU/LC_MESSAGES/messages.po. Команда для компиляции: msgfmt messages.po -o messages.mo. В коде используется функция gettext('welcome').

Распространенные сложности

Проблема: Расширение gettext не включено в PHP. Решение: установить php-gettext или использовать библиотеку-эмуляцию.

Ошибка: Несовпадение локали в системе и в вызове setlocale. Решение: проверить список доступных локалей командой locale -a.

Когда целесообразно хранить переводы в базе данных?

База данных (MySQL, SQLite) удобна для динамических сайтов с возможностью редактирования переводов через админ-панель. Схема таблицы: id, locale, key, value. Пример запроса:


function loadLangDb(string $locale, PDO $pdo): array {
    $stmt = $pdo->prepare('SELECT `key`, `value` FROM translations WHERE locale = ?');
    $stmt->execute([$locale]);
    return $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
}
  

Недостатки

Проблема: Лишние запросы к БД при каждом вызове. Решение: кешировать результат в памяти с помощью Memcached или Redis.

Ошибка: Долгое выполнение при большом количестве ключей. Решение: использовать кеширование на уровне приложения.

Расширенные примеры работы с языковыми файлами

Пример с PHP-массивами со вложенными ключами

Файл lang/ru.php содержит группы:

Пример

<?php
return [
    'menu' => [
        'home' => 'Главная',
        'about' => 'О нас',
        'contact' => 'Контакты',
    ],
    'errors' => [
        'not_found' => 'Страница не найдена',
    ],
];
  

Функция получения перевода с поддержкой точечной нотации:

Пример

function trans(string $key, array $lang): string {
    $keys = explode('.', $key);
    $value = $lang;
    foreach ($keys as $k) {
        if (!isset($value[$k])) {
            throw new OutOfBoundsException('Ключ не найден: ' . $key);
        }
        $value = $value[$k];
    }
    return $value;
}

$lang = loadLang('ru');
echo trans('menu.home', $lang); // Выведет: Главная
  
Главная
  

Пример с JSON и поддержкой плюральных форм

В JSON можно добавить специальный ключ для чисел:

Пример

{
    "apples": {
        "one": "{count} яблоко",
        "few": "{count} яблока",
        "many": "{count} яблок"
    }
}
  

Функция выбора формы:

Пример

function pluralize(int $count, array $forms): string {
    $mod10 = $count % 10;
    $mod100 = $count % 100;
    if ($mod10 == 1 && $mod100 != 11) {
        $form = $forms['one'];
    } elseif ($mod10 >= 2 && $mod10 <= 4 && ($mod100 < 10 || $mod100 >= 20)) {
        $form = $forms['few'];
    } else {
        $form = $forms['many'];
    }
    return str_replace('{count}', $count, $form);
}

$lang = loadLangJson('ru');
echo pluralize(5, $lang['apples']); // Выведет: 5 яблок
  
5 яблок
  

Пример с INI-файлами и секциями

INI-файл lang/fr.ini:

Пример

[menu]
home = "Accueil"
about = "À propos"
[errors]
not_found = "Page non trouvée"
  

Разбор с группами:

Пример

function loadLangIniGrouped(string $locale): array {
    $file = __DIR__ . '/lang/' . $locale . '.ini';
    $data = parse_ini_file($file, true); // true - создает вложенные массивы по секциям
    if ($data === false) throw new RuntimeException();
    return $data;
}

$lang = loadLangIniGrouped('fr');
echo $lang['menu']['home']; // Accueil
  
Accueil
  

Пример с gettext и плюральными формами

Содержимое locale/ru_RU/LC_MESSAGES/messages.po:

Пример

msgid "apple"
msgid_plural "apples"
msgstr[0] "яблоко"
msgstr[1] "яблока"
msgstr[2] "яблок"
  

Компиляция: msgfmt messages.po -o messages.mo. PHP-код:

Пример

putenv('LC_ALL=ru_RU');
setlocale(LC_ALL, 'ru_RU.UTF-8');
bindtextdomain('messages', __DIR__ . '/locale');
textdomain('messages');

echo ngettext('apple', 'apples', 5); // Выведет: 5 яблок (если правильно настроено)
  
5 яблок
  

Примечание: Результат зависит от наличия скомпилированного .mo файла и настроенной локали в системе.

Пример с базой данных и динамическим кешированием

Таблица translations содержит записи: (1, 'ru', 'welcome', 'Добро пожаловать'), (2, 'en', 'welcome', 'Welcome'). Функция с использованием статического кеша:

Пример

function loadLangCached(string $locale, PDO $pdo): array {
    static $cache = [];
    if (isset($cache[$locale])) {
        return $cache[$locale];
    }
    $stmt = $pdo->prepare('SELECT `key`, `value` FROM translations WHERE locale = ?');
    $stmt->execute([$locale]);
    $cache[$locale] = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
    return $cache[$locale];
}

$lang = loadLangCached('ru', $pdo);
echo $lang['welcome']; // Добро пожаловать
  
Добро пожаловать
  

Для продакшена рекомендуется использовать внешний кеш, например Redis, чтобы избежать роста памяти процесса.

Языковые файлы в PHP - comments

En
Files php lang (php)