diff --git a/weblate/accounts/models.py b/weblate/accounts/models.py
index 5a8a77512f48..41aff75186ae 100644
--- a/weblate/accounts/models.py
+++ b/weblate/accounts/models.py
@@ -46,6 +46,7 @@
from weblate.utils import messages
from weblate.utils.decorators import disable_for_loaddata
from weblate.utils.fields import EmailField
+from weblate.utils.html import mail_quote_value
from weblate.utils.render import validate_editor
from weblate.utils.request import get_ip_address, get_user_agent
from weblate.utils.token import get_token
@@ -352,9 +353,9 @@ def get_params(self):
}
for name, value in self.params.items():
if name in {"old", "new", "name", "email", "username"}:
- value = format_html("{}
", value)
+ value = format_html("{}
", mail_quote_value(value))
elif name in {"device", "project", "site_title", "method"}:
- value = format_html("{}", value)
+ value = format_html("{}", mail_quote_value(value))
result[name] = value
diff --git a/weblate/utils/html.py b/weblate/utils/html.py
index 4f4eae3e8eeb..9c7b0ebd547f 100644
--- a/weblate/utils/html.py
+++ b/weblate/utils/html.py
@@ -9,10 +9,12 @@
from typing import TYPE_CHECKING
import nh3
+from django.utils.html import format_html, format_html_join
from html2text import HTML2Text as _HTML2Text
from lxml.etree import HTMLParser
if TYPE_CHECKING:
+ from django.utils.safestring import SafeString
from lxml.etree import ParserTarget
from weblate.checks.flags import Flags
@@ -164,3 +166,25 @@ def handle_tag(self, tag: str, attrs: dict[str, str | None], start: bool) -> Non
self.o(WEBLATE_TAGS[tag][not start])
return
super().handle_tag(tag, attrs, start)
+
+
+def mail_quote_char(text: str) -> str | SafeString:
+ if text in {":", "."}:
+ return format_html("{}", text)
+ return text
+
+
+def mail_quote_value(text: str) -> str | SafeString:
+ """
+ Quote value to be used in e-mail notifications.
+
+ This tries to avoid automatic conversion to links by Gmail
+ and similar services.
+
+ Solution based on https://stackoverflow.com/a/23404042/225718
+ """
+ return format_html_join(
+ "",
+ "{}",
+ ((mail_quote_char(part),) for part in re.split("([.:])", text)),
+ )
diff --git a/weblate/utils/tests/test_html.py b/weblate/utils/tests/test_html.py
index 8dd276ea9a7e..69deed6020e1 100644
--- a/weblate/utils/tests/test_html.py
+++ b/weblate/utils/tests/test_html.py
@@ -7,7 +7,12 @@
from django.test import SimpleTestCase
from weblate.checks.flags import Flags
-from weblate.utils.html import HTML2Text, HTMLSanitizer, extract_html_tags
+from weblate.utils.html import (
+ HTML2Text,
+ HTMLSanitizer,
+ extract_html_tags,
+ mail_quote_value,
+)
class HTMLSanitizerTestCase(SimpleTestCase):
@@ -104,3 +109,23 @@ def test_html2text_diff(self) -> None:
html2text.handle("text "),
"text{+ +}\n\n",
)
+
+
+class MailQuoteTestCase(SimpleTestCase):
+ def test_plain(self):
+ self.assertEqual(
+ mail_quote_value("text"),
+ "text",
+ )
+
+ def test_dot(self):
+ self.assertEqual(
+ mail_quote_value("example.com"),
+ "example.com",
+ )
+
+ def test_url(self):
+ self.assertEqual(
+ mail_quote_value("https://test.example.com"),
+ "https://test.example.com",
+ )