Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% if complete_html %}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en">
<html lang="{{ lang | default('en', true) | e }}">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Expand Down
10 changes: 10 additions & 0 deletions notifications_utils/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ def __init__(
allow_html=False,
alt_text_en=None,
alt_text_fr=None,
lang=None,
):
super().__init__(template, values, jinja_path=jinja_path)
self.fip_banner_english = fip_banner_english
Expand All @@ -403,6 +404,11 @@ def __init__(
self.alt_text_en = alt_text_en
self.alt_text_fr = alt_text_fr
self.text_direction_rtl = template.get("text_direction_rtl", False)
# BCP 47 language tag for the rendered <html lang="..."> attribute. Defaults to
# "en" so existing callers keep their current behaviour, but French / bilingual
# callers should pass "fr" or "und" so screen readers announce the document
# language correctly (WCAG 3.1.1).
self.lang = lang or "en"

# set this again to make sure the correct either utils / downstream local jinja is used
# however, don't set if we are in a test environment (to preserve the above mock)
Expand Down Expand Up @@ -447,6 +453,7 @@ def __str__(self):
"alt_text_en": self.alt_text_en,
"alt_text_fr": self.alt_text_fr,
"text_direction_rtl": self.text_direction_rtl,
"lang": self.lang,
}
)

Expand Down Expand Up @@ -493,6 +500,7 @@ def __init__(
alt_text_en=None,
alt_text_fr=None,
user_language="en",
lang=None,
):
super().__init__(
template,
Expand All @@ -517,6 +525,7 @@ def __init__(
self.alt_text_fr = alt_text_fr
self.user_language = user_language
self.text_direction_rtl = template.get("text_direction_rtl", False)
self.lang = lang or "en"

def __str__(self):
return Markup(
Expand Down Expand Up @@ -544,6 +553,7 @@ def __str__(self):
"alt_text_en": self.alt_text_en,
"alt_text_fr": self.alt_text_fr,
"text_direction_rtl": self.text_direction_rtl,
"lang": self.lang,
}
)
)
Expand Down
35 changes: 35 additions & 0 deletions tests/test_template_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,41 @@ def test_fip_banner_french(renderer, show_banner):
assert "canada-logo.png" not in str(email)


def test_html_email_lang_defaults_to_english():
rendered = str(HTMLEmailTemplate({"content": "hello world", "subject": ""}))
assert '<html lang="en">' in rendered


@pytest.mark.parametrize(
"lang,expected",
[
("fr", '<html lang="fr">'),
("fr-CA", '<html lang="fr-CA">'),
("en-CA", '<html lang="en-CA">'),
("und", '<html lang="und">'),
],
)
def test_html_email_lang_is_threaded_into_html_tag(lang, expected):
rendered = str(HTMLEmailTemplate({"content": "hello world", "subject": ""}, lang=lang))
assert expected in rendered


def test_html_email_lang_falls_back_to_english_when_none():
rendered = str(HTMLEmailTemplate({"content": "hello world", "subject": ""}, lang=None))
assert '<html lang="en">' in rendered


def test_html_email_bilingual_blocks_get_inline_lang_attributes():
# add_language_divs already wraps [[en]]/[[fr]] blocks with lang attributes;
# for bilingual content the document-level lang should be set by the caller
# (e.g. to "und") so screen readers pick up the lang on the inline divs.
bilingual_content = "[[en]]\nHello\n[[/en]]\n[[fr]]\nBonjour\n[[/fr]]"
rendered = str(HTMLEmailTemplate({"content": bilingual_content, "subject": ""}, lang="und"))
assert '<html lang="und">' in rendered
assert '<div lang="en-ca">' in rendered
assert '<div lang="fr-ca">' in rendered


def test_logo_with_background_colour_shows():
email = str(
HTMLEmailTemplate(
Expand Down
Loading