Question

Pyramid documentation shows us how to use i18n inside views (and templates as well). But how to does one use it outside of views and templates where we have no access to current request (for example, in forms and models)?

@Michael said to pass request to models and forms. But is it right? I mean if form fields defines before __init__() method calls, the same with models. They don't see any parameters from views...

In Pylons we could simply use get_lang() and set_lang() and define preferable language in parent controller and then use ugettext() and ungettext() in any place we want without calling it from request directly every possible time (in views).

How to do that in Pyramid? Note that the language must be set from user's settings (session, cookies, db, etc).

Was it helpful?

Solution

My solution is to create the form class when it's needed with localizer as parameter. For example

forms.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

When you need to use the form

@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)

As you can see, we get the localizer in the view, and pass it to the FormFactory, then create a form with that factory. By doing that, all messages in the form was replaced with current locale language.

Likewise, you can do the same with model.

OTHER TIPS

Actually I had this very same problem. What I ended up doing was to see how the default locale negotiator works - it looks for a LOCALE property on the given request object. So just use a dummy to create the localizer. You may cache this value too, if you want

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

    return get_localizer(request)

Alternatively, join the irc channel #pyramid @ freenode and pester the guys enough there to split the functionality of get_localizer in 2 separate documented functions (get_localizer and get_localizer_for_locale_name) for us to enjoy ;)

Also, notice that Pyramid TranslationStrings are lazy, so you can translate them as late as you want, e.g.

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})

Sidenote: pylons' i18n was the worst can of worms I had opened in ages. The set_lang, get_lang hack was really awful, and pain in the ass as we needed to send emails to users in their native languages and then tried to restore the language back... also, it was IMPOSSIBLE to translate anything outside of a request in a pylons program, as a translator or the registry did not exist then.

You can make a localizer, and then translate a template accordingly.

When making the localizer, you can pass the lang you want (whether you have it from db or else). Hope it can help.

For the sake of clarity, I will set it as 'fr' below

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 You can also put this logic into a small function

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 _
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top