Reputation: 521
I'm using flask, pybabel for i18n. Sometimes I need to send emails to my users. And I want to send email in their own language. Language code stores in databases so the problem is to translate template with right language. Here is part of my sending function:
lang = user.get_lang()
subject = _('Subject')
for user in users:
if user.email:
body = render_template('emails/template.eml', user=user)
recipients = [user.email]
msg = Message(subject, html=body, recipients=recipients)
conn.send(msg)
And example of template:
{{ _('Hi {name}. How are you?').format(user.name) }}
All I need is something like set_langauge(lang)
which I can call before each template render. How can I do it?
Thanks.
Upvotes: 3
Views: 1410
Reputation: 521
Thanks to @tbicr I ended up with this solution.
In my app.py I have set_locale
function:
from flask import _request_ctx_stack as ctx_stack
# ...
def set_locale(lang):
ctx = ctx_stack.top
ctx.babel_locale = lang
# ...
and I call it before rendering email template.
The problem was I need to send emails to many users with different languages at the same time:
with app.test_request_context():
with mail.connect() as conn:
for user in users:
set_locale(user.get_lang())
subject = _(u'Best works').format(get_month_display(month))
body = render_template('emails/best_works.eml'
recipients = [user.email]
msg = Message(subject, html=body, recipients=recipients)
conn.send(msg)
and when I call set_locale
first time the value of locale was cached and all emails were rendered with language of first user.
The solution is to call flaskext.babel.refresh
each time after set_locale
Upvotes: 4
Reputation: 26070
I have next render_template
function for emails:
def render_template(template_name_or_list, **context):
# get request context
ctx = _request_ctx_stack.top
# check request context
# if function called without request context
# then call with `test_tequest_context`
# this because I send email from celery tasks
if ctx is None:
with current_app.test_request_context():
return render_template(template_name_or_list, **context)
# I have specific locale detection (from url)
# and also use `lang` variable in template for `url_for` links
# so you can just pass language parameter without context parameter
# and always set `babel_locate` with passed language
locale = getattr(ctx, 'babel_locale', None)
if locale is None:
ctx.babel_locale = Locale.parse(context['lang'])
# render template without additinals context processor
# because I don't need this context for emails
# (compare with default flask `render_template` function)
return _render(ctx.app.jinja_env.get_or_select_template(
template_name_or_list), context, ctx.app)
So if you need just change language in request context use next code (see get_locale
):
def set_langauge(lang)
ctx = _request_ctx_stack.top
ctx.babel_locale = Locale.parse(lang)
Upvotes: 4