mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 16:14:02 +00:00
i18n: Use the recipient's language when sending outgoing emails.
It appears that our i18n logic was only using the recipient's language for logged-in emails, so even properly tagged for translation and translated emails for functions like "Find my team" and "password reset" were being always sent in English. With great work by Vishnu Ks on the tests and the to_emails code path.
This commit is contained in:
@@ -264,6 +264,7 @@ class ZulipPasswordResetForm(PasswordResetForm):
|
||||
send_email('zerver/emails/password_reset', to_emails=[email],
|
||||
from_name="Zulip Account Security",
|
||||
from_address=FromAddress.tokenized_no_reply_address(),
|
||||
language=request.LANGUAGE_CODE,
|
||||
context=context)
|
||||
|
||||
class CreateUserForm(forms.Form):
|
||||
|
@@ -815,7 +815,7 @@ def do_start_email_change_process(user_profile: UserProfile, new_email: str) ->
|
||||
})
|
||||
send_email('zerver/emails/confirm_new_email', to_emails=[new_email],
|
||||
from_name='Zulip Account Security', from_address=FromAddress.tokenized_no_reply_address(),
|
||||
context=context)
|
||||
language=user_profile.default_language, context=context)
|
||||
|
||||
def compute_irc_user_fullname(email: str) -> str:
|
||||
return email.split("@")[0] + " (IRC)"
|
||||
@@ -4461,7 +4461,8 @@ def do_send_confirmation_email(invitee: PreregistrationUser,
|
||||
'activate_url': activation_url, 'referrer_realm_name': referrer.realm.name}
|
||||
from_name = "%s (via Zulip)" % (referrer.full_name,)
|
||||
send_email('zerver/emails/invitation', to_emails=[invitee.email], from_name=from_name,
|
||||
from_address=FromAddress.tokenized_no_reply_address(), context=context)
|
||||
from_address=FromAddress.tokenized_no_reply_address(),
|
||||
language=referrer.realm.default_language, context=context)
|
||||
|
||||
def email_not_system_bot(email: str) -> None:
|
||||
if is_cross_realm_bot_email(email):
|
||||
|
@@ -2,6 +2,7 @@ from django.conf import settings
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.template import loader
|
||||
from django.utils.timezone import now as timezone_now
|
||||
from django.utils.translation import override as override_language
|
||||
from django.template.exceptions import TemplateDoesNotExist
|
||||
from zerver.models import UserProfile, ScheduledEmail, get_user_profile_by_id, \
|
||||
EMAIL_TYPES, Realm
|
||||
@@ -12,7 +13,7 @@ import logging
|
||||
import ujson
|
||||
|
||||
import os
|
||||
from typing import Any, Dict, Iterable, List, Mapping, Optional
|
||||
from typing import Any, Dict, Iterable, List, Mapping, Optional, Tuple
|
||||
|
||||
from zerver.lib.logging_util import log_to_file
|
||||
from confirmation.models import generate_key
|
||||
@@ -36,7 +37,8 @@ class FromAddress:
|
||||
def build_email(template_prefix: str, to_user_ids: Optional[List[int]]=None,
|
||||
to_emails: Optional[List[str]]=None, from_name: Optional[str]=None,
|
||||
from_address: Optional[str]=None, reply_to_email: Optional[str]=None,
|
||||
context: Optional[Dict[str, Any]]=None) -> EmailMultiAlternatives:
|
||||
language: Optional[str]=None, context: Optional[Dict[str, Any]]=None
|
||||
) -> EmailMultiAlternatives:
|
||||
# Callers should pass exactly one of to_user_id and to_email.
|
||||
assert (to_user_ids is None) ^ (to_emails is None)
|
||||
if to_user_ids is not None:
|
||||
@@ -53,19 +55,32 @@ def build_email(template_prefix: str, to_user_ids: Optional[List[int]]=None,
|
||||
'email_images_base_uri': settings.ROOT_DOMAIN_URI + '/static/images/emails',
|
||||
'physical_address': settings.PHYSICAL_ADDRESS,
|
||||
})
|
||||
subject = loader.render_to_string(template_prefix + '.subject',
|
||||
context=context,
|
||||
using='Jinja2_plaintext').strip().replace('\n', '')
|
||||
message = loader.render_to_string(template_prefix + '.txt',
|
||||
context=context, using='Jinja2_plaintext')
|
||||
|
||||
try:
|
||||
html_message = loader.render_to_string(template_prefix + '.html', context)
|
||||
except TemplateDoesNotExist:
|
||||
emails_dir = os.path.dirname(template_prefix)
|
||||
template = os.path.basename(template_prefix)
|
||||
compiled_template_prefix = os.path.join(emails_dir, "compiled", template)
|
||||
html_message = loader.render_to_string(compiled_template_prefix + '.html', context)
|
||||
def render_templates() -> Tuple[str, str, str]:
|
||||
subject = loader.render_to_string(template_prefix + '.subject',
|
||||
context=context,
|
||||
using='Jinja2_plaintext').strip().replace('\n', '')
|
||||
message = loader.render_to_string(template_prefix + '.txt',
|
||||
context=context, using='Jinja2_plaintext')
|
||||
|
||||
try:
|
||||
html_message = loader.render_to_string(template_prefix + '.html', context)
|
||||
except TemplateDoesNotExist:
|
||||
emails_dir = os.path.dirname(template_prefix)
|
||||
template = os.path.basename(template_prefix)
|
||||
compiled_template_prefix = os.path.join(emails_dir, "compiled", template)
|
||||
html_message = loader.render_to_string(compiled_template_prefix + '.html', context)
|
||||
return (html_message, message, subject)
|
||||
|
||||
if not language and to_user_ids is not None:
|
||||
language = to_users[0].default_language
|
||||
if language:
|
||||
with override_language(language):
|
||||
# Make sure that we render the email using the target's native language
|
||||
(html_message, message, subject) = render_templates()
|
||||
else:
|
||||
(html_message, message, subject) = render_templates()
|
||||
logger.warning("Missing language for email template '{}'".format(template_prefix))
|
||||
|
||||
if from_name is None:
|
||||
from_name = "Zulip"
|
||||
@@ -94,9 +109,10 @@ class EmailNotDeliveredException(Exception):
|
||||
def send_email(template_prefix: str, to_user_ids: Optional[List[int]]=None,
|
||||
to_emails: Optional[List[str]]=None, from_name: Optional[str]=None,
|
||||
from_address: Optional[str]=None, reply_to_email: Optional[str]=None,
|
||||
context: Dict[str, Any]={}) -> None:
|
||||
mail = build_email(template_prefix, to_user_ids=to_user_ids, to_emails=to_emails, from_name=from_name,
|
||||
from_address=from_address, reply_to_email=reply_to_email, context=context)
|
||||
language: Optional[str]=None, context: Dict[str, Any]={}) -> None:
|
||||
mail = build_email(template_prefix, to_user_ids=to_user_ids, to_emails=to_emails,
|
||||
from_name=from_name, from_address=from_address,
|
||||
reply_to_email=reply_to_email, language=language, context=context)
|
||||
template = template_prefix.split("/")[-1]
|
||||
logger.info("Sending %s email to %s" % (template, mail.to))
|
||||
|
||||
|
@@ -8,13 +8,48 @@ from django.test import TestCase
|
||||
from django.utils import translation
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from django.core import mail
|
||||
from http.cookies import SimpleCookie
|
||||
|
||||
from zerver.lib.test_classes import (
|
||||
ZulipTestCase,
|
||||
)
|
||||
from zerver.management.commands import makemessages
|
||||
from zerver.lib.notifications import enqueue_welcome_emails
|
||||
|
||||
from django.utils.timezone import now as timezone_now
|
||||
|
||||
class EmailTranslationTestCase(ZulipTestCase):
|
||||
def test_email_translation(self) -> None:
|
||||
def check_translation(phrase: str, request_type: str, *args: Any, **kwargs: Any) -> None:
|
||||
if request_type == "post":
|
||||
self.client_post(*args, **kwargs)
|
||||
elif request_type == "patch":
|
||||
self.client_patch(*args, **kwargs)
|
||||
|
||||
email_message = mail.outbox[0]
|
||||
self.assertIn(phrase, email_message.body)
|
||||
|
||||
for i in range(len(mail.outbox)):
|
||||
mail.outbox.pop()
|
||||
|
||||
hamlet = self.example_user("hamlet")
|
||||
hamlet.default_language = "de"
|
||||
hamlet.save()
|
||||
realm = hamlet.realm
|
||||
realm.default_language = "de"
|
||||
realm.save()
|
||||
|
||||
self.login(hamlet.email)
|
||||
|
||||
check_translation("Viele Grüße", "patch", "/json/settings", {"email": "hamlets-new@zulip.com"})
|
||||
check_translation("Felicidades", "post", "/accounts/home/", {"email": "new-email@zulip.com"}, HTTP_ACCEPT_LANGUAGE="pt")
|
||||
check_translation("Danke, dass Du", "post", '/accounts/find/', {'emails': hamlet.email})
|
||||
check_translation("Viele Grüße", "post", "/json/invites", {"invitee_emails": "new-email@zulip.com", "stream": ["Denmark"]})
|
||||
|
||||
with self.settings(DEVELOPMENT_LOG_EMAILS=True):
|
||||
enqueue_welcome_emails(hamlet)
|
||||
check_translation("Viele Grüße", "")
|
||||
|
||||
class TranslationTestCase(ZulipTestCase):
|
||||
"""
|
||||
|
@@ -359,10 +359,10 @@ def prepare_activation_url(email: str, request: HttpRequest,
|
||||
request.session['confirmation_key'] = {'confirmation_key': activation_url.split('/')[-1]}
|
||||
return activation_url
|
||||
|
||||
def send_confirm_registration_email(email: str, activation_url: str) -> None:
|
||||
def send_confirm_registration_email(email: str, activation_url: str, language: str) -> None:
|
||||
send_email('zerver/emails/confirm_registration', to_emails=[email],
|
||||
from_address=FromAddress.tokenized_no_reply_address(),
|
||||
context={'activate_url': activation_url})
|
||||
language=language, context={'activate_url': activation_url})
|
||||
|
||||
def redirect_to_email_login_url(email: str) -> HttpResponseRedirect:
|
||||
login_url = reverse('django.contrib.auth.views.login')
|
||||
@@ -398,7 +398,7 @@ def create_realm(request: HttpRequest, creation_key: Optional[str]=None) -> Http
|
||||
return HttpResponseRedirect(activation_url)
|
||||
|
||||
try:
|
||||
send_confirm_registration_email(email, activation_url)
|
||||
send_confirm_registration_email(email, activation_url, request.LANGUAGE_CODE)
|
||||
except smtplib.SMTPException as e:
|
||||
logging.error('Error in create_realm: %s' % (str(e),))
|
||||
return HttpResponseRedirect("/config-error/smtp")
|
||||
@@ -439,7 +439,7 @@ def accounts_home(request: HttpRequest, multiuse_object: Optional[MultiuseInvite
|
||||
email = form.cleaned_data['email']
|
||||
activation_url = prepare_activation_url(email, request, streams=streams_to_subscribe)
|
||||
try:
|
||||
send_confirm_registration_email(email, activation_url)
|
||||
send_confirm_registration_email(email, activation_url, request.LANGUAGE_CODE)
|
||||
except smtplib.SMTPException as e:
|
||||
logging.error('Error in accounts_home: %s' % (str(e),))
|
||||
return HttpResponseRedirect("/config-error/smtp")
|
||||
|
@@ -46,7 +46,7 @@ def confirm_email_change(request: HttpRequest, confirmation_key: str) -> HttpRes
|
||||
context = {'realm_name': user_profile.realm.name, 'new_email': new_email}
|
||||
send_email('zerver/emails/notify_change_in_email', to_emails=[old_email],
|
||||
from_name="Zulip Account Security", from_address=FromAddress.SUPPORT,
|
||||
context=context)
|
||||
language=user_profile.default_language, context=context)
|
||||
|
||||
ctx = {
|
||||
'new_email': new_email,
|
||||
|
Reference in New Issue
Block a user