[Django]-Pass a lazy translation string including variable to function in Django

1đź‘Ť

âś…

Instead of passing the translated subject, just pass it non translated:

subject = '%(user)s has posted a comment'
context = {'user': user}

def send_notifications(request, subject, url, context):
    from django.utils.translation import activate
    for s in Subscription.objects.filter(url=url):
        activate(s.user.userprofile.lang)
        send_mail(_(subject) % context, render_to_string('notification_email.txt', locals()), settings.SERVER_EMAIL, [s.user.email])

If you’re not going to personalize the contents per user, then you might as well limit the number of renderings because that’s a little confusing:

# also do your imports at the top to catch import issues early
from django.utils.translation import activate
from django.utils.translation import ugettext as _

def send_notifications(request, url, 
    translatable_subject, context,
    body_template='notification_template.txt'):
    previous_lang = None
    for s in Subscription.objects.filter(url=url).order_by('user__userprofile__lang'):
        if s.user.userprofile.lang != previous_lang:
            activate(s.user.userprofile.lang)
            subject = _(translatable_subject) % context
            body = render_to_string(body_template, locals())
        send_mail(subject, body, settings.SERVER_EMAIL, [s.user.email])
        previous_lang = s.user.userprofile.lang

As such, it is much more obvious that you’re not going to render emails per usage.

This slight rewrite should make you doubt about the original choice of a couple of names (locals, notification_template).

The above sample code is barely an “educated guess” and you should double check it and make sure you understand everything before you paste it.

👤jpic

0đź‘Ť

Ok, found a solution myself. In case anybody runs into a similar problem:

from django.utils.translation import ugettext as _

# create subject as raw string in Django view
raw_subject = r"%(username)s has posted a comment"

# for the sake of generic variables, create a dictionary to pass to function
extra_context = { 'user': user }

# call function with raw string and dictionary as params
send_notifications(request, raw_subject, url, extra_context)

# translate the raw string inside send_notifications into the temporarily activated language
translated_subject = _(raw_subject) % extra_context

Appears to be working as desired 🙂 Since we are working with several different notifications, I tried to avoid an extra template for each kind. However, calling a template with extra_context is also a possible solution.

0đź‘Ť

I ended up creating a class that will handle the translation, passing the context of the string to this class.

from django.utils.translation import gettext_lazy as _

class Translatable(object):
    def __init__(self, text, context):
        self.text = text
        self.context = context

    def __str__(self):
        return _(self.text).format(**self.context)

In your function, just call str(subject):

def send_notifications(request, subject, url):
    from django.utils.translation import activate
    for s in Subscription.objects.filter(url=url):
        activate(s.user.userprofile.lang)
        send_mail(str(subject), render_to_string('notification_email.txt', locals()), settings.SERVER_EMAIL, [s.user.email])

And passes subject to your function as following:

subject = Translatable(_("{user} has posted a comment"), context={'user': user})
send_notifications(request, subject, url)

Leave a comment