Files
webmail-client/backend/tests/test_translation_prep.py
T
lukasandClaude Opus 5.5 8a1b9979e9 Productivity features, UI translations, local email translation
- Snooze (with a Snoozed folder), keyboard shortcuts with a Settings
  section, mail templates (Compose > More > Templates), a Subscriptions
  folder with bulk unsubscribe/delete, and tracker blocking with a
  Privacy toggle (on by default)
- Own dialogs instead of browser popups; emoji picker in compose; compose
  windows survive a visit to Settings
- Sentence completion learned from the user's sent mail (no AI)
- Interface in English and Polish (one file per language, English as the
  fallback), picked from the browser on first run, then kept
- Email translation on the server via LibreTranslate: Translate button
  and "Translate to...", messages in several languages translated part
  by part, names/addresses/emoji/file names/phone numbers kept, an
  animated line while translating
- Antispam "Mark as spam" loads with the message, not when the menu opens

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 20:00:31 +02:00

129 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Getting a message ready for translation (app/translation_prep.py)."""
from __future__ import annotations
from app.translation_prep import (
find_names,
find_names_in_signature,
join_wrapped_lines,
keep_symbols,
keep_terms,
split_pieces,
tidy,
translate_phrases,
)
KEPT = '<span translate="no">'
def test_wrapped_lines_are_joined_but_paragraphs_kept() -> None:
assert join_wrapped_lines("<p>ask you about<br>the schedule.</p>") == (
"<p>ask you about the schedule.</p>"
)
assert join_wrapped_lines("<div>ask you about</div><div>the schedule.</div>") == (
"<div>ask you about the schedule.</div>"
)
text = '<div class="wm-plaintext">ask you about\nthe schedule.\n\nThanks,\nAnna</div>'
assert join_wrapped_lines(text) == (
'<div class="wm-plaintext">ask you about the schedule.\n\nThanks,\nAnna</div>'
)
# A new sentence, or a capital after the break: left alone.
for html in ("<p>Done.<br>then more</p>", "<p>Hi Lukas,<br>Natalia</p>"):
assert join_wrapped_lines(html) == html
def test_sign_offs_come_from_the_phrase_table() -> None:
assert translate_phrases("<p>Best,<br>Natalia</p>", "en", "pl") == (
f"<p>{KEPT}Pozdrawiam,</span><br>Natalia</p>"
)
assert translate_phrases("<div>Mit freundlichen Grüßen</div>", "de", "en") == (
f"<div>{KEPT}Kind regards</span></div>"
)
assert "Pozdrawiam" not in translate_phrases("<p>Best results so far</p>", "en", "pl")
assert translate_phrases("<p>Best,</p>", "fr", "pl") == "<p>Best,</p>" # no table
def test_names_mid_sentence_but_not_headlines_or_german() -> None:
html = "<p>I came across Imaginary Pixels and Trusted Partner Network, in Spain.</p>"
assert find_names(html, "en") == {"Imaginary Pixels", "Trusted Partner Network"}
assert find_names("<p>Why Matchmoving Matters, Editing Digger And More</p>", "en") == set()
assert find_names("<p>Wir haben das Neue Angebot gesehen.</p>", "de") == set()
# Not across a sentence's end.
assert find_names("<p>working mostly in NukeX. My work includes paint.</p>", "en") == set()
assert find_names_in_signature("Imaginary Pixels – visual effects studio") == [
"Imaginary Pixels"
]
def test_kept_terms_and_spacing() -> None:
out = keep_terms(
"<p>Hi Natalia Raz, see Imaginary Pixels.</p>", ["Natalia Raz", "Imaginary Pixels"]
)
assert out == f"<p>Hi {KEPT}Natalia Raz</span>, see {KEPT}Imaginary Pixels</span>.</p>"
assert keep_terms("<style>.Natalia{}</style>", ["Natalia"]) == "<style>.Natalia{}</style>"
assert tidy(f"nad {KEPT}Bruton</span>i dalej") == f"nad {KEPT}Bruton</span> i dalej"
assert tidy("<span>a</span>b") == "<span>a</span>b" # the sender's own spans
def test_emoji_kept() -> None:
html = (
'<p>nice to e-meet you <span style="font-family:Segoe UI Emoji">\n😊</span> &nbsp;</p>'
"<p>Great 👍🏽 work ❤️</p>"
)
out = keep_symbols(html)
assert f"{KEPT}😊</span>" in out
assert f"{KEPT}👍🏽</span>" in out
assert f"{KEPT}❤️</span>" in out
assert "nice to e-meet you" in out # the words still go to translation
def test_file_names_kept() -> None:
html = (
"<li>A_0023C017_260521_130157_p1CAC.mov</li>"
"<p>See final_v2 and plate-03.exr, e.g. the U.S cut.</p>"
)
out = keep_symbols(html)
assert f"{KEPT}A_0023C017_260521_130157_p1CAC.mov</span>" in out
assert f"{KEPT}final_v2</span>" in out
assert f"{KEPT}plate-03.exr</span>" in out
assert "e.g. the U.S cut" in out # no file names there
def test_symbols_not_marked_twice_or_in_attributes() -> None:
html = (
f'<a href="https://x.io/a_b.mov">{KEPT}https://x.io/a_b.mov</span></a>'
"<style>.a_b{}</style>"
)
assert keep_symbols(html) == html
def test_text_without_letters_is_kept() -> None:
# A byte-order mark alone between tags came back as "Uzasadnienie".
out = keep_symbols(
'<div dir="ltr">\ufeff<div>Dear Lukas,</div><div> &nbsp; </div><p> – </p></div>'
)
assert f"{KEPT}\ufeff</span>" in out
assert "<div> &nbsp; </div>" in out # only spaces: nothing to keep
assert f"<p> {KEPT}–</span> </p>" in out
assert "<div>Dear Lukas,</div>" in out
def test_handles_and_phone_numbers_kept() -> None:
out = keep_symbols(
"<p>WeChat: Jacquelineeeee_ WhatsApp: +48 780 549 877, for 1300 - 2500 Euro</p>"
)
assert f"{KEPT}Jacquelineeeee_</span>" in out
assert f"{KEPT}+48 780 549 877</span>" in out
assert "1300 - 2500 Euro" in out # plain numbers stay in their sentence
def test_paragraphs_split_at_block_tags() -> None:
html = (
f"<style>p{{}}</style><div>Dear <b>Anna</b>,<br>hello {KEPT}x.mov</span> there</div>"
"<p> &nbsp; </p><li>Item</li>"
)
pieces, paragraphs = split_pieces(html)
assert [p.text for p in paragraphs] == ["Dear Anna,", "hello x.mov there", "Item"]
# The kept file name counts for the language but is not translated.
assert [pieces[i] for i in paragraphs[1].pieces] == ["hello ", " there"]