Самый эффективный подход для многоязычного PHP-сайта

Я работаю над большим многоязычным веб-сайтом и рассматриваю различные подходы к тому, чтобы сделать его многоязычным. Я могу придумать следующие возможные альтернативы:

  1. Функция Gettext с генерацией файлов .po
  2. Одна таблица MySQL с переводами и уникальным строковым идентификатором для каждого текста
  3. PHP-файлы с массивами, содержащими разные переводы с уникальными строковыми идентификаторами

Насколько я понял, функции Gettext должны быть наиболее эффективными, но мое требование состоит в том, чтобы можно было изменить текстовую строку на исходном справочном языке (английском) без автоматического возврата других переводов этой строки обратно на английский только потому, что пара слов изменена. Возможно ли это с помощью Gettext?

Какое решение является наименее требовательным к ресурсам?
Использование функций Gettext или файлов PHP с массивами более или менее требовательно к ресурсам?
Есть ли другие предложения по более эффективным решениям?


person alexteg    schedule 10.05.2010    source источник


Ответы (3)


Несколько соображений:

1. Переводы
Кто будет делать переводы? Люди, которые тоже подключены к сайту? Бюро переводов? При использовании Gettext вы будете работать с файлами типа «pot» (.po). Эти файлы содержат идентификатор сообщения и строку сообщения (перевод). Пример:

msgid "A string to be translated would go here"  
msgstr ""

Теперь это выглядит прекрасно и понятно для всех, кому нужно это перевести. Но что происходит, когда вы используете ключевые слова, как предлагает Майк, вместо полных предложений? Если кому-то нужно перевести msgid с именем «address_home», он или она не имеет ни малейшего понятия, должен ли это быть заголовок «Домашний адрес» или это полное предложение. В этом случае не забудьте добавить комментарии к файлу прямо перед вызовом функции gettext, например:

/// This is a comment that will be included in the pot file for the translators
gettext("ready_for_lost_episode");

Использование xgettext --add-comments=/// при создании файлов .po добавит эти комментарии. Однако я не думаю, что Gettext можно использовать таким образом. Кроме того, если вам нужно добавить комментарии к каждому тексту, который вы хотите отобразить, вы: а) возможно, в какой-то момент сделаете ошибку, б) весь скрипт все равно будет заполнен текстами, только в форме комментариев; c) комментарии должны быть размещены непосредственно над функцией Gettext, что не всегда удобно, в зависимости от положения функции в вашем коде.

2. Техническое обслуживание
Когда ваш сайт разрастется (даже дальше), а вместе с ним и ваши языковые файлы, может стать довольно сложно поддерживать все различные переводы таким способом. Каждый раз, когда вы добавляете текст, вам нужно создавать новые файлы, отправлять файлы переводчикам, получать файлы обратно, следить за тем, чтобы структура оставалась нетронутой (нетерпеливые переводчики всегда рады перевести синтаксис, сделав весь файл непригодный для использования :)), и закончите с импортом новых переводов. Конечно, это выполнимо, но помните о возможных проблемах в этой связи с большими сайтами и множеством разных языков.


Другой вариант: объедините вторую и третью альтернативы:

Лично я считаю более полезным управлять переводом с помощью (простой) CMS, сохраняя переменные и переводы в базе данных и самостоятельно экспортируя соответствующие тексты в языковые файлы:

  1. добавить переменные в базу данных (например: id, page, variable);
  2. добавить переводы к этим переменным (например: id, varId, language, translation);
  3. выберите соответствующие переменные и переводы, запишите их в файл;
  4. включить соответствующий языковой файл на свой сайт;
  5. создайте свою собственную функцию для отображения текста переменных:

text('var'); или что-то вроде __('faq','register','lost_password_text');

Пункт 3 может заключаться в простом выборе всех соответствующих переменных и переводов из базы данных, помещении их в массив и записи сериализованного массива в файл.

Преимущества:

  1. Техническое обслуживание. Поддерживать тексты в больших проектах может быть намного проще. Вы можете группировать переменные по страницам, разделам или другим частям вашего сайта, просто добавив столбец в вашу базу данных, который определяет, к какой части сайта принадлежит эта переменная. Таким образом, вы можете быстро просмотреть список всех переменных, используемых, например, в страницу часто задаваемых вопросов.

  2. Идет перевод. Вы можете отобразить переменную со всеми переводами всех разных языков на одной странице. Это может быть полезно для людей, которые могут переводить тексты на несколько языков одновременно. И может быть полезно увидеть другие переводы, чтобы почувствовать контекст, чтобы перевод был как можно лучше. Вы также можете запросить базу данных, чтобы узнать, что было переведено, а что нет. Возможно, добавьте отметки времени, чтобы отслеживать возможные устаревшие переводы.

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

В целом, я думаю, вы обнаружите, что таким образом у вас будет гораздо больше контроля над переводами, особенно в долгосрочной перспективе. Я ничего не могу сказать о скорости или эффективности этого подхода по сравнению с собственной функцией gettext. Но, в зависимости от размера языковых файлов, я не думаю, что это будет большой разницей. Если вы группируете переменные по страницам или разделам, вы всегда можете включать только необходимые части.

person Alec    schedule 11.05.2010
comment
Спасибо за исчерпывающий ответ! Пару переводов мы сделаем сами, остальное сделает бюро переводов. Я знал о проблеме с использованием ключей в gettext, но если бы я выбрал этот подход, я бы, скорее всего, создал свой собственный парсер для создания PO-файлов, используемых переводчиками, из PO-файлов, созданных xgettext, чтобы он сочетал английский с переведенным языком вместо ключей. Я рассматривал возможность использования БД, но мне кажется неэффективным делать запросы к БД для того, что практически можно рассматривать как статический контент. - person alexteg; 12.05.2010
comment
Я думал о решении с БД в бэкэнде для создания интерфейса для переводчиков, но затем создания статических PO-файлов - ›MO-файлов или PHP-массивов в языковых файлах, включенных в зависимости от запрошенного языка. Что касается производительности, из моих исследований по-прежнему кажется, что PO-файлы наиболее эффективны, поскольку они кэшируются, переводятся машинным переводом (MO-файлы) и находятся на более низком уровне, чем то, что может быть написано на PHP. Тем не менее, я сам проведу несколько тестов производительности по различным методам и скоро опубликую их здесь. - person alexteg; 12.05.2010

После некоторого тестирования я, наконец, решил более или менее использовать линии комбинации Алека второй и третьей альтернативы.

Проблема с Gettext
Я попытался сначала настроить всю систему gettext, чтобы опробовать ее, но оказалось, что это намного сложнее, чем я думал. Проблема в том, что системы Windows и Unix используют сокращенные имена на разных языках для setlocale (). На данный момент я запускаю свой dev-сервер в Windows с Wamp, а последний сайт будет запустить в Linux. После того, как я просмотрел пару дюжина руководств, форумы, questions и т. д. и перезапускать сервер после каждого изменения. Я не мог настроить его должным образом каким-либо простым способом. Кроме того, gettext не является потокобезопасным, для обновления языкового файла необходимо перезапустить сервер или необходимо использовать хак, нет простого способа обработки различных версий языковых файлов или обработки исходного английского текста без изменения источника или использования предложения Майка, что, как указал Алек, не оптимально.

Решение
Итак, я пришел к тому, что считаю лучшим решением, основанным на ответе Алека:

  • Сохраните все переводы в БД с полями; language, page, var_key, version, revision и last_modified_time - где версия соответствует версиям исходного перевода (английский), а revision позволяет переводчику изменять / исправлять окончательные переводы в версии.
  • Используйте для перевода своего рода CMS, которая подключена к БД, обрабатывает разные версии и позволяет легко просматривать, на какие языки переведены, на какие версии и насколько полными являются переводы.
  • Когда ревизия версии завершается, создаются файлы кеша - каждый файл содержит массив только с var_key и текстовым переводом для одного языка и одной страницы и назван с помощью ISO 639-1 названия языков и название страницы, например: lang / en_index.php Эти языковые файлы затем просто включаются и заключаются в функция t ($ var_key), которая позволяет использовать БД во время разработки, а затем изменена на использование только файлов кеша.

Производительность
Мне так и не удалось протестировать gettext, но согласно ссылка, которую опубликовал Майк, разница в производительности между использованием массива и gettext для меня полностью приемлема из-за преимуществ, которые дает настраиваемая система, как описано выше. Однако я сравнил использование массива с 20 переведенными текстовыми строками в массиве с получением тех же 20 текстовых строк из MySQL БД. Оказалось, что использование массива, включенного из файла, оказалось в 6 раз быстрее, чем получение всех 20 строк одновременно из базы данных MySQL. Это не был действительно научный тест, и результаты, безусловно, могут отличаться в разных системах и настройках, но он четко показывает именно то, что я ожидал - что с использованием БД будет намного медленнее, чем с использованием массива напрямую, поэтому я решил создать кеш -файлы для массива вместо использования БД.

Для сравнения я также проверил, насколько быстро выводятся только простые эхо с одним и тем же текстом. Это оказалось примерно в 20 раз быстрее, чем использование массивов из включенного файла, но что ж, тогда невозможно выполнить перевод, не имея разных версий страницы для разных языков, что противоречит цели динамических страниц. Тогда лучше также использовать хорошую систему кэширования.

Исходные файлы теста производительности:
PHP: http://pastie.org/964082
Таблица MySQL: http://pastie.org/964115
Это точно не идеально, но, по крайней мере, дает представление о различиях в производительности.

person alexteg    schedule 17.05.2010
comment
Маленькие файлы будут более эффективными, чем чтение из базы данных. Это потому, что они кешируются на нескольких уровнях, от системного кеша до кеша php. Запросы к базе данных не кэшируются, поскольку предполагается, что база данных изменяется. - person Michał Leon; 26.08.2015
comment
@ Михал Леон: Очень полезный комментарий! Сколько кб или мб в «маленьком» файле? Где я могу увидеть / установить эти значения кеширования файлов? - person CoR; 12.12.2015
comment
Все ссылки мертвы :( - person The Godfather; 31.05.2020

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

Ключ gettext - "привет"

Затем у вас есть различные языковые переводы этого и английский перевод этого, который также является «привет», тогда, если вы хотите обновить английскую версию строки, вы можете оставить ключ в покое и просто обновить английский перевод.

person Pollett    schedule 10.05.2010
comment
Это звучит как хорошая идея. Но является ли функция gettext наименее ресурсоемким подходом или есть какое-то лучшее решение? - person alexteg; 11.05.2010
comment
Я никогда особо не тратил на это много времени, так как gettext всегда был достаточно быстрым даже для некоторых крупных сайтов, с которыми я работал, но беглый взгляд на статью о тестировании на: mel.melaxis.com/devblog/2006/04/10/, кажется, предлагает некоторые идеи об эффективности использования ресурсов. - person Pollett; 11.05.2010
comment
Что ж, спасибо за помощь и ссылку. Я думаю, что тогда я буду использовать подход gettext с ключами в качестве оригинального перевода. - person alexteg; 11.05.2010