Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Django_-_podrobnoe_rukovodstvo

.pdf
Скачиваний:
330
Добавлен:
01.03.2016
Размер:
4.88 Mб
Скачать

380

Глава 19. Интернационализация

Например, если приложение Django содержит переводимую строку

“Welcome to my site.”:

_(“Welcome to my site.”)

то django-admin.py makemessages создаст po-файл, в котором будет такое сообщение:

#: path/to/python/module.py:23 msgid “Welcome to my site.” msgstr “”

Поясним:

•• msgid – это переводимая строка, взятая из текста оригинала. Ее изменять не следует.

•• msgstr – место, где должен быть перевод. Изначально там ничего нет,

апереводчик должен ввести текст. Не забывайте заключать перевод

вкавычки.

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

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

Чтобы заново просмотреть исходный код и шаблоны­ после модификации и обновить файлы сообщений для всех языков, выполните команду:

django-admin.py makemessages -a

Компиляция файлов сообщений

После создания (и после каждого изменения) файла сообщений его необходимо откомпилировать, преобразовав в эффективный формат, который понимает gettext. Для этого служит утилита django-admin.py compilemessages.

Она перебирает все имеющиеся po-файлы и создает из них mo-файлы – двоичные файлы, оптимизированные для gettext. Команду django-admin. py compilemessages следует запускать из того же каталога, что и djangoadmin.py makemessages:

django-admin.py compilemessages

Вот и все. Перевод готов.

Как Django определяет языковые предпочтения

381

Как Django определяет языковые предпочтения

После подготовки собственных переводов (или если вы просто хотите использовать переводы, входящие в комплект поставки Django) необходимо активировать перевод приложения.

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

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

Если вы всего лишь хотите, чтобы Django разговаривал на вашем родном языке и для этого языка имеется перевод, то, кроме установки LANGUAGE_CODE, больше ничего делать не нужно.

Если же требуется, чтобы каждый пользователь мог определить предпочтительный для себя язык, то понадобится дополнительный процессор LocaleMiddleware. Он выбирает язык в зависимости от данных, пришедших в запросе.

Чтобы воспользоваться процессором LocaleMiddleware, добавьте строку

‘django.middleware.locale.LocaleMiddleware’ в параметр MIDDLEWARE_CLASSES. Так как порядок следования процессоров важен, придерживайтесь следующих рекомендаций:

•• Этот процессор должен быть как можно ближе к началу списка.

•• Он должен располагаться после SessionMiddleware, так как для его работы необходимы данные сеанса.

•• Если используется процессор CacheMiddleware, то LocaleMiddleware должен располагаться после него.

Например, параметр MIDDLEWARE_CLASSES мог бы выглядеть так:

MIDDLEWARE_CLASSES = ( ‘django.contrib.sessions.middleware.SessionMiddleware’, ‘django.middleware.locale.LocaleMiddleware’, ‘django.middleware.common.CommonMiddleware’,

)

Подробнее о дополнительных процессорах см. в главе 17.

Процессор LocaleMiddleware пытается определить языковые предпочтения пользователя, применяя следующий алгоритм:

1.Сначала он отыскивает ключ django_language в сеансе текущего пользователя.

2.Если такого ключа нет, производится попытка отыскать cookie.

3.Если cookie не найден, анализируется HTTP-заголовок AcceptLanguage. Этот заголовок посылается броузером, чтобы сообщить сер-

382

Глава 19. Интернационализация

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

4.Если подходящий язык не найден, используется значение парамет­ ра LANGUAGE_CODE.

Отметим следующие моменты:

•• На каждом из этих шагов ожидается, что язык будет определен в виде стандартной строки. Например, бразильский диалект португальского языка определяется строкой pt-br.

•• Если имеется перевод на основной язык, а на диалект отсутствует, то Django будет использовать основной язык. Например, если пользователь указал язык de-at (австрийский диалект немецкого), но имеется только перевод на язык de, то будет использован de.

•• Допускаются только языки, перечисленные в параметре LANGUAGES. Если вы хотите ограничить выбор языка некоторым подмножеством языков (потому что приложение переведено не на все языки), то оставьте в списке LANGUAGES только разрешенные языки. Например:

LANGUAGES = (

(‘de’, _(‘German’)), (‘en’, _(‘English’)),

)

В этом примере разрешено выбирать только английский и немецкий языки (и их диалекты, например, de-ch или en-us).

•• Если вы задали LANGUAGES, как описано в предыдущем пункте, то будет правильно отмечать названия языков как переводимые строки, но при этом следует пользоваться функцией-заглушкой ugettext(), а не функцией из пакета django.utils.translation. Никогда не следует импортировать пакет django.utils.translation из файла параметров, поскольку он сам зависит от параметров, и импортирование этого пакета создаст циклическую­ зависимость.

Чтобы решить эту проблему, следует использовать функцию-за­ глушку­ ugettext(), как это сделано в следующем примере файла параметров:

ugettext = lambda s: s

LANGUAGES = (

(‘de’, ugettext(‘German’)), (‘en’, ugettext(‘English’)),

)

При этом сценарий django-admin.py makemessages все равно найдет и извлечет эти строки в файл перевода, но на этапе выполнения они переводиться не будут. Не забудьте обернуть названия языков на-

Применение механизма перевода в собственных проектах

383

стоящей функцией ugettext() всюду, где во время выполнения используется параметр LANGUAGES.

•• Процессор LocaleMiddleware может выбрать только те языки, на которые переведено ядро Django. Если вы хотите перевести свое приложение на еще не поддерживаемый язык, то нужно будет перевести на него хотя бы минимальное подмножество строк ядра Django. Например, в Django используются технические сообщения для перевода форматов времени и даты, поэтому их обязательно нужно перевести, чтобы система­ работала правильно.

Для начала достаточно просто скопировать po-файл для английского языка и перевести хотя бы технические сообщения (и, может быть, сообщения о результатах контроля данных).

Распознать технические сообщения несложно – они набраны заглавными буквами. Переводить их, как другие сообщения, не нужно; требуется лишь ввести правильный местный вариант для предложенного английского значения. Например, для строки DATETIME_ FORMAT (или DATE_FORMAT, или TIME_FORMAT) это будет строка формата, принятая в вашем родном языке. Формат определяется так же, как в шаблонном­ теге now.

После того как процессор LocaleMiddleware определит язык, он сохранит его в атрибуте request.LANGUAGE_CODE объекта HttpRequest. Вы можете обращаться к этому атрибуту в своих представлениях. Например:

def hello_world(request):

if request.LANGUAGE_CODE == ‘de-at’:

return HttpResponse(“You prefer to read Austrian German.”) else:

return HttpResponse(“You prefer to read another language.”)

Обратите внимание, что статический код языка (который не был обработан дополнительным процессором) можно получить из атрибута settings.LANGUAGE_CODE, тогда как динамический – из атрибута request. LANGUAGE_CODE.

Применение механизма перевода в собственных проектах

При поиске переводов Django применяет следующий алгоритм:

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

2.Далее просматривается подкаталог locale в каталоге проекта. Если перевод найден там, используется он.

3.Наконец, просматривается основной каталог переводов django/conf/ locale.

384

Глава 19. Интернационализация

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

Структура всех репозиториев файлов сообщений одинакова:

•• $APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)

•• $PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)

•• Поиск всех файлов <language>/LC_MESSAGES/django.(po|mo) производится во всех каталогах, перечисленных в параметре LOCALE_PATHS, в порядке их следования в списке

•• $PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)

Для создания файлов сообщений используется та же утилита djangoadmin.py makemessages, что и для файлов сообщений самого Django. От вас требуется только запустить ее из нужного места – из каталога, где находится подкаталог conf/locale (в случае дерева исходных текстов) или locale/ (в случае сообщений приложения или проекта). А для создания mo-файлов, с которыми работает gettext, запустите ту же утилиту django-admin.py compilemessages, о которой упоминалось выше.

Можно также выполнить команду django-admin.py compilemessagessettings=path.to.settings, которая откомпилирует po-файлы, находящиеся во всех каталогах, перечисленных в параметре LOCALE_PATHS.

Поиск файлов сообщений приложения организован несколько сложнее, для этого требуется дополнительный процессор LocaleMiddleware. Если вы не хотите его использовать, то будут обрабатываться только файлы сообщений самого Django и проекта.

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

Простейший выход из этой ситуации – разместить приложения, не являющиеся частью проекта (и следовательно, сопровождаемые собственными переводами), вне дерева проекта. В таком случае при запуске django-admin.py makemessages из каталога проекта будут переведены только строки, явно связанные с проектом, а строки, распространяемые независимо от него, останутся непереведенными.

Представление set_language

385

Представление set_language

В качестве дополнительного удобства в состав Django включено представление django.views.i18n.set_language, которое принимает языковую настройку пользователя и выполняет переадресацию на предыдущую страницу.

Для активации этого представления добавьте в конфигурацию URL такую строку:

(r’^i18n/’, include(‘django.conf.urls.i18n’)),

Примечание

В этом примере представление будет доступно по адресу /i18n/setlang/.

Ожидается, что это представление будет вызываться методом POST и в запросе будет присутствовать параметр language. Если включена поддержка сеансов, то представление сохранит выбранный язык в сеансе пользователя. В противном случае выбранный язык по умолчанию будет сохраняться в cookie с именем django_language. (Имя можно изменить, определив параметр LANGUAGE_COOKIE_NAME.)

После того как язык будет определен, Django произведет переадресацию пользователя, руководствуясь следующим алгоритмом:

•• Django проверит наличие параметра next в POST-запросе.

•• Если такого параметра не существует или он пуст, то Django проверит наличие URL в заголовке Referer.

•• Если этот заголовок отсутствует (например, его формирование подавлено в броузере), то пользователь будет переадресован в корень сайта (/).

Ниже приводится пример HTML-разметки в шаблоне:­

<form action=”/i18n/setlang/” method=”post”>

<input name=”next” type=”hidden” value=”/next/page/” /> <select name=”language”>

{% for lang in LANGUAGES %}

<option value=”{{ lang.0 }}”>{{ lang.1 }}</option> {% endfor %}

</select>

<input type=»submit» value=»Go» /> </form>

Переводы и JavaScript

Добавление переводов в JavaScript-сценарии порождает некоторые проблемы:

386

Глава 19. Интернационализация

•• Программный код на языке JavaScript не обладает доступом к реализации gettext.

•• Программный код на языке JavaScript не обладает доступом к po- и mo-файлам; они должны быть предоставлены сервером.

•• Каталоги переводов для JavaScript должны иметь как можно меньший размер.

Django предлагает интегрированное решение этих проблем: он передает переводы в JavaScript таким способом, что внутри сценария JavaScript можно использовать gettext и все остальное.

Представление javascript_catalog

Основой решения является представление javascript_catalog; оно отправляет­ библиотеку JavaScript-кода, которая содержит функции, имитирующие­ интерфейс gettext, а также массив переводимых строк. Эти строки извлекаются из приложения, проекта или ядра Django в зависимости от того, что задано в словаре info_dict или в URL.

Интерфейс с этой библиотекой устроен так:

js_info_dict = {

‘packages’: (‘your.app.package’,),

}

urlpatterns = patterns(‘’,

(r’^jsi18n/$’, ‘django.views.i18n.javascript_catalog’, js_info_dict),

)

Все строки в массиве packages должны быть записаны с соблюдением синтаксиса путей к пакетам в Python (через точку, точно так же строки записываются в параметре INSTALLED_APPS) и должны ссылаться на пакеты, которые содержат каталог locale. Если указано несколько пакетов, то все каталоги объединяются в один. Это полезно, когда в JavaScriptсценарии используются строки из разных приложений.

Представление можно сделать динамическим, поместив пакеты в образец URL:

urlpatterns = patterns(‘’,

(r’^jsi18n/(?P<packages>\S+)/$’, ‘django.views.i18n.javascript_catalog’),

)

В этом случае пакеты определяются в виде списка имен пакетов, разделенных в URL знаком +. Это особенно полезно, когда на страницах используется код из разных приложений, эти страницы часто изменяются, а сводить все в один большой файл вам не хочется. В качестве меры безопасности элементом списка может быть только django.conf или путь к пакету, присутствующему в параметре INSTALLED_APPS.

Переводы и JavaScript

387

Использование каталога переводов в JavaScript

Чтобы воспользоваться каталогом, достаточно загрузить динамически сгенерированный сценарий:

<script type=”text/javascript” src=”/path/to/jsi18n/”></script>

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

document.write(gettext(‘this is to be translated’));

Имеется также функция ngettext:

var object_cnt = 1 // или 0, или 2, или 3, ...

s = ngettext(‘literal for the singular case’, ‘literal for the plural case’, object_cnt);

Идаже функция строковой интерполяции: function interpolate(fmt, obj, named);

Синтаксис интерполяции заимствован у Python, поэтому функция interpolate поддерживает как позиционные, так и именованные аргументы:

•• Позиционная интерполяция: obj содержит JavaScript-объект Array, элементы которого используются для замещения шаблонных­ символов в порядке их следования. Например:

fmts = ngettext(‘There is %s object. Remaining: %s’, ‘There are %s objects. Remaining: %s’, 11);

s = interpolate(fmts, [11, 20]);

// s – строка ‘There are 11 objects. Remaining: 20’

•• Именованная интерполяция: этот режим устанавливается, когда необязательный параметр named равен true. В этом случае obj содержит объект JavaScript или ассоциативный массив. Например:

d = {

count: 10 total: 50

};

fmts = ngettext(‘Total: %(total)s, there is %(count)s object’, ‘there are %(count)s of a total of %(total)s objects’, d.count); s = interpolate(fmts, d, true);

Однако не следует злоупотреблять строковой интерполяцией; это всетаки JavaScript, а подстановка фактических значений выполняется

спомощью регулярных выражений. Они не такие быстрые, как строковая интерполяция в Python, так что приберегите эту технику для случаев, когда она действительно необходима (например, в сочетании

сngettext для правильного образования множественного числа).

388

Глава 19. Интернационализация

Создание каталогов переводов для JavaScript

Каталоги переводов создаются и обновляются точно так же, как все остальные каталоги переводов в Django: с помощью утилиты djangoadmin.py makemessages. Единственная разница заключается в том, что нужно указать флаг -d djangojs:

django-admin.py makemessages -d djangojs -l de

В результате этой команды будет создан каталог переводов на немецкий язык для использования в JavaScript. Обновив каталоги переводов, запустите команду django-admin.py compilemessages точно так же, как для обычных каталогов переводов в Django.

Замечания для пользователей, знакомых с gettext

Если вы знакомы с gettext, то, вероятно, обратили внимание на следующие особенности переводов в Django:

•• Домен строк определен как django или djangojs. Домен необходим, чтобы различать программы, которые хранят данные в общей библиотеке файлов сообщений (обычно /usr/share/locale/). Домен django применяется для переводимых строк в коде на Python и в шаблонах,­ а строки, принадлежащие этому домену, загружаются в глобальный каталог переводов. Домен djangojs используется только для каталогов переводов JavaScript, поэтому старайтесь, чтобы их размер был минимален.

•• В Django xgettext не используется напрямую. Для удобства применяются обертки вокруг xgettext и msgfmt, написанные на Python.

gettext для Windows

Это необходимо только тем, кто хочет извлечь идентификаторы сообщений или откомпилировать файлы сообщений (po-файлы). Собственно перевод сводится к редактированию существующих po-файлов, но если вы захотите создать собственные файлы сообщений, протестировать или откомпилировать измененный файл сообщений, то вам понадобится набор утилит gettext.

1.Загрузите следующие ZIP-файлы со страницы http://sourceforge.net/ projects/gettext:

•• gettext-runtime-X.bin.woe32.zip

•• gettext-tools-X.bin.woe32.zip

•• libiconv-X.bin.woe32.zip

2.Распакуйте все три файла в один каталог (C:\Program Files\gettextutils).

Что дальше?

389

3.Измените системную­ переменную окружения PATH:

a.Панель управления Система → Дополнительно → Переменные среды.

b.В списке Системные переменные выберите переменную Path и затем щелкните на кнопке Изменить.

c.Добавьте в конец поля Значение переменной строку ;C:\Program Files\ gettext-utils\bin.

Загрузить двоичный код gettext можно и из других мест, лишь бы команда xgettext --version работала правильно. Известно, что некоторые двоичные дистрибутивы версии 0.14.4 не поддерживают эту команду. Не пытайтесь использовать утилиты перевода Django в сочетании с пакетом gettext, если после ввода команды xgettext --version в окне команд Windows появляется окно с сообщением «Приложение xgettext. exe допустило ошибку и будет закрыто».

Что дальше?

Последняя глава посвящена безопасности: как защитить свои сайты и пользователей от злоумышленников.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]