Как использовать Pyramid i18n вне представлений и шаблонов?

документация Pyramid показывает как использовать внутренние представления i18n (и шаблоны). Но как его использовать вне представлений и шаблонов, где у нас нет доступа к текущим request (например, в forms и models)?

@Michael сказал передать request моделям и формам. Но так ли это? Я имею в виду, если поля формы определены до вызова метода __init__(), то же самое с моделями. Они не видят никаких параметров из представлений...

В Pylons мы могли бы просто использовать get_lang() и set_lang() и определить предпочтительный язык в родительском контроллере, а затем использовать ugettext() и ungettext() в любом месте, которое мы хотим, не вызывая их напрямую из request каждый раз (в представлениях).

Как это сделать в Пирамиде? Обратите внимание, что язык должен быть установлен в настройках пользователя (сеанс, файлы cookie, БД и т. д.).


person Vitalii Ponomar    schedule 29.04.2012    source источник


Ответы (4)


Мое решение состоит в том, чтобы создать класс формы, когда это необходимо, с локализатором в качестве параметра. Например

формы.py

class FormFactory(object):

    def __init__(self, localizer):
        self.localizer = localizer
        _ = self.localizer
        self.required_msg = _(u'This field is required.')
        self.invalid_email_msg = _(u'Invalid email address.')
        self.password_not_match_msg = _(u'Password must match')

    def make_contact_form(self):
        _ = self.localizer
        class ContactForm(Form):
            email = TextField(_(u'Email address'), [
                validators.Required(self.required_msg),
                validators.Email(self.invalid_email_msg)
            ])
            content = TextAreaField(_(u'Content'), [
                validators.Required(self.required_msg)
            ])
        return ContactForm

Когда нужно использовать форму

@view_config(route_name='front_pages.contact_us',
             renderer='myweb:templates/front_pages/contact_us.genshi')
def contact_us(request):
    """Display contact us form or send mail

    """
    _ = get_localizer(request)

    factory = FormFactory(_)
    ContactForm = factory.make_contact_form()
    form = ContactForm(request.params)
    return dict(form=form)

Как видите, мы получаем локализатор в представлении и передаем его в FormFactory, а затем создаем форму с этой фабрикой. При этом все сообщения в форме были заменены текущим языком локали.

Точно так же вы можете сделать то же самое с моделью.

person Fang-Pen Lin    schedule 12.05.2012

Вы уже нашли pyramid.18n.get_localizer?

http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/narr/i18n.html#using-a-localizer

person Michael Merickel    schedule 29.04.2012
comment
get_localizer() требуется параметр request, поэтому это означает, что я могу использовать его только внутри функции представления (или класса). Но как его использовать вне представлений? - person Vitalii Ponomar; 30.04.2012
comment
Ну, вы получаете доступ к request, передавая его в свои формы/модели, или используете get_current_request(), или создаете свой собственный локализатор, как говорится в этой ссылке, через make_localizer. request определяет, какую локаль должен использовать локализатор, и если вы хотите сохранить это поведение, то, очевидно, вам нужен request. - person Michael Merickel; 30.04.2012
comment
Похоже, вы только что изменили вопрос с того, как сделать перевод вне представлений/шаблонов, на как сделать перевод. Я чувствую, что ответил на ваш первоначальный вопрос в ответе и комментариях относительно того, как это делает Pyramid. Чтобы узнать, как Pyramid выполняет перевод, вы должны прочитать всю страницу, на которую я дал ссылку, потому что она действительно охватывает большую часть этого на практике. - person Michael Merickel; 30.04.2012

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

def my_get_localizer(locale=None):
    request = Request({})
    request._LOCALE_ = locale

    return get_localizer(request)

В качестве альтернативы, присоединяйтесь к irc-каналу #pyramid@freenode и приставайте к ребятам достаточно, чтобы разделить функциональность get_localizer на 2 отдельные документированные функции (get_localizer и get_localizer_for_locale_name) для нашего удовольствия ;)

Кроме того, обратите внимание, что Pyramid TranslationStrings ленивы, поэтому вы можете переводить их так поздно, как захотите, например.

class MyModel(Base):
    description = TranslationString("My model number ${number}")

...

def view(request):
    m = MyModel()
    localizer = get_localizer(request)
    description = localizer.translate(m.description, mapping={'number': 1})

Примечание: i18n пилонов был худшей банкой с червями, которую я открывал за многие годы. Взлом set_lang, get_lang был действительно ужасным и болезненным, поскольку нам нужно было отправлять электронные письма пользователям на их родных языках, а затем пытаться восстановить язык обратно... кроме того, было НЕВОЗМОЖНО перевести что-либо, кроме запроса в программы пилонов, как переводчика или реестра тогда не существовало.

person Antti Haapala    schedule 09.05.2012

Можно сделать локализатор, а потом соответственно перевести шаблон.

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

Для ясности я обозначу его как «fr» ниже.

from pyramid.i18n import make_localizer, TranslationStringFactory
from mako.template import Template
from mako.lookup import TemplateLookup
import os

absolute_path = os.path.dirname(os.path.realpath(__file__))
tsf = TranslationStringFactory('your_domain')
mako_lookup = TemplateLookup(directories=['/'])
template = Template(filename=template_path, lookup=mako_lookup)

localizer = make_localizer("fr", [absolute_path + '/../locale/'])

def auto_translate(*args, **kwargs):
    return localizer.translate(tsf(*args, **kwargs))

# Pass _ pointer (translate function) to the context
_ = auto_translate

context.update({
    "_": _
})

html = template.render(**context)

EDIT Вы также можете поместить эту логику в небольшую функцию

def get_translator(lang):
    """
    Useful when need to translate outside of queries (no pointer to request)
    :param lang:
    :return:
    """
    localizer = make_localizer(lang, [absolute_path + '/../locale/'])

    def auto_translate(*args, **kwargs):
        return localizer.translate(tsf(*args, **kwargs))

    _ = auto_translate

    return _
person eton_ceb    schedule 13.10.2016