Проблемы с PHP/Gettext

Я помню, как несколько месяцев назад тестировал несколько тестов с gettext, и следующий код работал отлично:

putenv('LANG=l33t');
putenv('LANGUAGE=l33t');
putenv('LC_MESSAGES=l33t');

if (defined('LC_MESSAGES')) // available if PHP was compiled with libintl
{
    setlocale(LC_MESSAGES, 'l33t');
}

else
{
    setlocale(LC_ALL, 'l33t');
}

bindtextdomain('default', './locale'); // ./locale/l33t/LC_MESSAGES/default.mo
bind_textdomain_codeset('default', 'UTF-8');
textdomain('default');

echo _('Hello World!'); // h3110 w0r1d!

Это работало отлично (под Windows XP и CentOS, если я правильно помню), что было хорошо, потому что я мог использовать произвольные "локали", не беспокоясь о том, были ли они установлены в системе или нет. Однако, похоже, это больше не работает, мне интересно, почему...


Red Hat + PHP 5.2.11:

Я могу переключаться назад и вперед из разных локалей, и переводы отображают соответствие, пока вызов setlocale() не возвращает false (если локаль доступна/установлена ​​в системе).

Это не идеально (было бы здорово, если бы я мог просто указать gettext в любой произвольный каталог переводов без проверки на наличие локали), но это приемлемо. В дальнейшем я проведу еще несколько тестов.

Windows 7 + PHP 5.3.1 (XAMPP):

setlocale() всегда возвращает false (даже при использовании LC_ALL вместо LC_MESSAGES), если я не использую какой-либо допустимый локаль Windows, например eng, deu или ptg - в этом случае локаль кажется для правильной установки, но переводы все еще не отображаются. Я не могу проверить прямо сейчас, потому что у меня открыто несколько вкладок, но я думаю, что самый первый вызов этого script дает правильный перевод (перезапуск Apache не будет делать трюк).

Я не уверен, связано ли это с PHP Bug # 49349. Я проверю это на пару часов.


Можно ли использовать расширение gettext (а не чистые реализации PHP, такие как php-gettext или Zend Translate Adapter) надежно в разных операционных системах (возможно, с пользовательскими локалями, такими как l33t)?

Кроме того, абсолютно необходимо использовать setlocale(LC_ALL, ...)? Я предпочел бы оставить настройки TIME, NUMERIC и MONETARY (специально) локали нетронутыми (по умолчанию для языкового стандарта POSIX).


У меня была идея... Можно ли вызвать setlocale() с очень распространенной локалью (например, C, POSIX или en_US) и указать язык через домен? Что-то вроде этого:

/lang/C/LC_MESSAGES/domain.pt.mo
/lang/C/LC_MESSAGES/domain.de.mo
/lang/C/LC_MESSAGES/domain.en.mo
/lang/C/LC_MESSAGES/domain2.pt.mo
/lang/C/LC_MESSAGES/domain2.de.mo
/lang/C/LC_MESSAGES/domain2.en.mo

Будет ли это работать на * nix и Windows plataforms без проблем?

Ответ 1

Gettext не слишком практичен для webapps.

  • Как например, он не соблюдает/не использует предпочтения стиля Accept-Language сам по себе.
  • Как правило, некоторые проблемы кэширования возникают на общих веб-хостах (mod_php SAPI).

Поэтому мне иногда хочется, чтобы PHP-модуль не существовал, и удобное выражение для функции _() было доступно для пользовательских реализаций.
(Имел мой собственный gettext.php, который работал более надежно.)

Ваши варианты:

  • В любом случае, согласно нескольким сообщениям об ошибках, порт Windows gettext имел некоторые недостатки с UTF-8. Возможно, ваша версия снова затронута. Поэтому попробуйте bind_textdomain_codeset('default', 'ISO-8859-1'); для стартеров. Кроме того, он предпочитает переменные среды в Windows IIRC, поэтому putenv("LC_ALL", "fr_FR"); может работать лучше, чем setlocale(). Особенно эффективен, если вы позже (gettext.dll).

    Также дайте ему возможность включить кодировку прямо там LANG=en_GB.ISO-8859-1. (Так как ваш исходный текст по-английски в любом случае, забота о кодировке здесь не очень похожа, но, вероятно, распространенный случай, когда gettext срабатывает над собой.) О, и иногда это UTF8 не UTF-8; также попробуйте ASCII.

  • Альтернативно обходить gettext. Идея вашего домена близка, но я бы просто использовал предопределенный. /locale/subdir для языков:

    ./lang/en/locale/C/LC_MESSAGES/domain.mo
    

    Затем просто вызовите bindtextdomain("default", "./lang/{$APP_LANG}/locale"), не давая gettext-комнате для интерпретации. Он всегда будет искать /C/, но правильный каталог локали уже был введен. Но попробуйте иметь символическую ссылку от $LANG на/C/там, в любом случае.

  • Укусить в gnu. Откажитесь от gettext. "У PhpWiki" было обычное преобразование awk script. Он преобразует файлы .po в скрипты массива .php(да, очень oldschool), и вместо этого использует вместо этого функцию __(). Закрыть. И более надежным.

Ответ 2

Этот код не будет отлично работать в каждой системе, потому что каждый репозиторий языковых версий системы + php отличается, в частности, между прочим.

Если вы хотите согласованности, вам нужно использовать что-то вроде Zend_Translate, которое, если вы устанавливаете Zend в каждой системе (одна и та же версия), все они будут соответствовать друг другу, потому что они используют одни и те же данные локализации, имена локалей и codebase.

Существует множество ошибок с setlocale, это просто ненадежно. См. Комментарии @http://php.net/manual/en/function.setlocale.php