четверг, 11 апреля 2013 г.

Интернационализация Django

Сразу хочу сказать, ничего нового я не напишу. Более того, не претендую на всеобъемлющее и полное описание. Наконец, касаюсь только одной версии Django - 1.5.0. С другими не требовалось делать интернационализированные сайты. Ну и последнее замечание - статья больше для себя, даже не для новичков: все давно можно найти на просторах Интернет, но если кому-то пригодится будет здорово.

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

Итак. Задача стоит следующим образом: необходимо сделать сайт на, например, двух языках: русском и английском. И сразу первый сюрприз: основной язык необходимо считать английским, а русский - локализацией. Этого требует американское происхождение Django. Итак. Основной язык en, локализация ru.

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

  • Нужны переводы текста в Python-коде: всякого рода "Error!", "Page NOT found" надо корректно уметь переводить.
  • Нужны переводы текста в HTML-шаблонах: "News" в заголовке при переключении на русский язык должно стать "Новости"
  • Нужен и перевод текста, хранящегося в БД.
  • URI уникальные для каждого языка.

Рассмотрим средства для решения трех последних - локализацию в коде пока не использовал, потому тонкости не смогу рассказать: по этим граблям ещё предстоит пройтись в будущем.

Когда сложная и непонятная задача разбивается на заведомо более простые и конкретные подзадачи, дальше становится проще.

  1. Перевод в шаблонах. Используется средство gettext. Файл проекта settings.py надо модифицировать так:

    import os
    LANGUAGE_CODE = 'ru'

    LANGUAGES = (
    ('en', ('English')),
    ('ru', ('Russian')),
    )

    LOCALE_PATHS = (
    os.path.join(os.path.dirname(__file__), 'locale'),
    )
    MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    )

    Из замечаний хочется сказать, что django.middleware.locale.LocaleMiddleware должна быть повыше и после django.contrib.sessions.middleware.SessionMiddleware. Так же, и главное, что этого нет в официальной доке, указываем LOCALE_PATHS. Это ложь, конечно. Есть, но особого акцента на этом не ставится. В свое время пару дней потратил на борьбу с "мельницами" - лучше указать, ведь "Явное, лучше неявного.".

    Подготовка к переводам шаблонов (на самом деле и переводов в Python-коде, если этот перевод готовить) завершена. Как же процесс перевода этой части проходит? В шаблонах все фразы отмечаем шаблонными тэгами {% trans "English" %} и {% blocktrans %}"English"{% endblocktrans %}, а в самом начале, после, если имеется, команды extend каждого шаблона указываем, что данный шаблон подлежит переводу - {% load i18n %}. Создаем каталог locale в месте, указанном в LOCALE_PATHS, обычно - корень проекта. Запускаем команду

    django-admin.py makemessages -l ru -a

    и Django создает локализацию: генерируется файл django.po. В нем много групп по три строки

    #: abi/templates/base_event.html:13
    msgid "Main"
    msgstr ""

    где первая строка — места, в которых встретилась фраза, помеченная для перевода, вторая строка — фраза для перевода ("English"), а третья — перевод, который надо вписать ("Английский"). Специфику перевода, зависящего от числа рассматривать не буду. После того как будут заполнены строки переводов, выполняем команду по сборке интернационализации:

    django-admin.py compilemessages.

    При добавлении в будущем новых фраз для перевода - так же запускаем эти две команды: старые переводы никуда не пропадут, и придется лишь новые описывать.

    Сейчас при переключении языков на сайте фразы на английском и русском должны так же соответственно языку меняться. Идем дальше.

  2. Перевод текста, хранящегося в БД. Это никак не описывается в официальной доке Django: так что интернационализировать данные в БД будем сторонним решением. Для начала могу сказать, что очень много решений для перевода БД устарели и не работают в версии Django 1.5.0, хотя в сети много инструкций и статей по их использованию. Соответственно выбираем работающий инструмент django-transmeta. Каждый класс в моделях подготавливается для работы с django-transmeta отдельно. Допустим у нас есть такая модель:

    class EventRecord(models.Model):
           title = models.CharField(max_length = 160)
           title_side = models.CharField(max_length = 160)
           descript = models.CharField(max_length = 120, blank = True)
           body = models.TextField()
           report = models.BooleanField()

           def __unicode__(self):
                 return self.title

           class Meta:
                 ordering = ['id']

    Положим, мы хотим, чтобы поля title, title_side, descript и body имели перевод. Для этого модель надо изменить следующим образом:

    from transmeta import TransMeta


    class EventRecord(models.Model):
           __metaclass__ = TransMeta
           title = models.CharField(max_length = 160)
           title_side = models.CharField(max_length = 160)
           descript = models.CharField(max_length = 120, blank = True)
           body = models.TextField()
           report = models.BooleanField()

           def __unicode__(self):
                 return self.title

           class Meta:
                 ordering = ['id']
                 translate = ('title', 'title_side', 'descript', 'body')

    Теперь необходимо выполнить команду ./manage.py sync_transmeta_db[1] и transmeta изменит поля в БД. Конкретнее, вместо поля body, будут созданы поля body_ru и поле body_en. Еще конкретнее: body будет переименовано в body_ru (основной язык). Эти поля необходимо, например, в админке, заполнить. Теперь в зависимости от языка[2] будут выбираться поля БД с соответствующим языковым префиксом.

  3. URI. Тут довольно просто, чтобы URI зависел от языка, используйте from django.conf.urls.i18n import i18n_patterns и

    urlpatterns += i18n_patterns('event.views',
           url(r'^event/(\d+)/$', 'event', name = 'event_anon'),
    )

    вместо patterns.

    Что ещё полезно, так это не конструировать URI вручную, а использовать шаблонный тэг {% url %} - он поддерживает интернационализацию и будет указывать на страницу с тем же языком.

Примечания
  1. При использовании sqlite3 команда не выполняется: из-за ограниченных возможностей ALTER таблицы не могут измениться так, как требуется для django-transmeta. Самый просто вариант - проделать указанные процедуры вручную. Для этого создаем дамп в формате SQL и модифицируем CREATE TABLES и INSERT для таблиц, модели которых должны иметь перевод. Сохраняем и пересоздаем sqlite из дампа - в админке все поля должны быть доступны для внесения перевода.
  2. И в самой официальной документации, и во многих статьях по теме было много сказано об изменении переменной django-language. Например, в статье, внизу создается форма... В общем всё странно и непонятно (было мне). Много читал, думал. Решил сделать сам. (с) Идея такая: язык зависит от текущего урла, в котором всегда строго в данный момент прописан язык. Используется функция, модифицирующая, согласно этой идее, request:

    def set_lang(request):

           '''Set 'django_language' in request.session'''

           url = request.META['PATH_INFO'].split('/')[1]
           request.session['django_language'] = url
           return request

    Для меня решение подошло. Для ваших целей может не подойти. Но так понятнее, верно? ;)

8 комментариев:

  1. Спасибо, помогло, особенно про явное указание LOCALE_PATHS

    ОтветитьУдалить
  2. А я в свою очередь могу всем настоятельно рекомендовать html5 шаблон , улучшает и рационализирует разметку документов, и много прочего!

    ОтветитьУдалить
  3. Этот комментарий был удален администратором блога.

    ОтветитьУдалить
  4. Этот комментарий был удален администратором блога.

    ОтветитьУдалить
  5. Этот комментарий был удален администратором блога.

    ОтветитьУдалить
  6. Roulette Rules -Play begins when the gamers have placed their bets by placing their chips on the numbered format. There are two forms of bets 메리트카지노 that you should be|you must be|you should be} acquainted with as a result of|as a end result of} that is what determines your roulette payouts. Rich features such as the Roulette favorite bets menu add comfort, pace and fun to our world-leading Roulette. This private menu allows gamers to customise and save as much as} 15 of their favorite bets, whether it's one bet or quantity of} bets, particular or neighbouring bets. This characteristic hastens betting for everybody and makes more complicated betting really simple. A player’s favorite bets can be found on any Roulette desk that means that gamers can place their saved bets time and time again, at whichever desk they're taking part in}.

    ОтветитьУдалить
  7. For manufacturers, installing web-enabled shop-wide machine monitoring systems is that first and most essential step to harness, construction, and take motion on production information. Using MachineMetrics, manufacturers can finally convey good manufacturing to the factory ground with software program that’s simple to combine, straightforward to use, and produces highly effective outcomes. MachineMetrics user-friendly platform simply integrates with any machine type including Precision CNC, Stamping, Grinding, Die or Mold, and extra. In CNC milling operations, the workpiece remains stationary and a high-speed rotating cutting CNC machining tool comes down onto the workpiece to take away excess materials. This type of machine may be very useful for forming basic geometric shapes.

    ОтветитьУдалить