diff --git a/src/agents/orchestrator.py b/src/agents/orchestrator.py index 1211d10..a81efc0 100644 --- a/src/agents/orchestrator.py +++ b/src/agents/orchestrator.py @@ -341,6 +341,10 @@ async def _send_email_notifications_for_incident( from email_utils.sender import send_email from email_utils.templates import incident_notification_email from config import MAGIC_LINK_BASE_URL + from services.org_settings import get_org_language + + # Sprache der Org bestimmen (die Lage gehoert genau einer Org) + org_lang_iso = await get_org_language(db, tenant_id) if tenant_id else "de" # Alle Nutzer mit aktiven Abos fuer diese Lage laden cursor = await db.execute( @@ -386,6 +390,7 @@ async def _send_email_notifications_for_incident( notifications=filtered_notifications, dashboard_url=dashboard_url, incident_type=incident_type, + lang=org_lang_iso, ) try: await send_email(prefs["email"], subject, html) @@ -1753,27 +1758,41 @@ class AgentOrchestrator: }, }, visibility, created_by, tenant_id) - # DB-Notifications erzeugen + # DB-Notifications erzeugen (Texte org-sprach-relativ) + is_en = output_language_iso == "en" parts = [] - if new_count > 0: - parts.append(f"{new_count} neue Meldung{'en' if new_count != 1 else ''}") - if confirmed_count > 0: - parts.append(f"{confirmed_count} bestätigt") - if contradicted_count > 0: - parts.append(f"{contradicted_count} widersprochen") - summary_text = ", ".join(parts) if parts else "Keine neuen Entwicklungen" + if is_en: + if new_count > 0: + parts.append(f"{new_count} new article{'s' if new_count != 1 else ''}") + if confirmed_count > 0: + parts.append(f"{confirmed_count} confirmed") + if contradicted_count > 0: + parts.append(f"{contradicted_count} contradicted") + summary_text = ", ".join(parts) if parts else "No new developments" + research_prefix = "Research" + new_articles_msg = f"{new_count} new article{'s' if new_count != 1 else ''} found" + else: + if new_count > 0: + parts.append(f"{new_count} neue Meldung{'en' if new_count != 1 else ''}") + if confirmed_count > 0: + parts.append(f"{confirmed_count} bestätigt") + if contradicted_count > 0: + parts.append(f"{contradicted_count} widersprochen") + summary_text = ", ".join(parts) if parts else "Keine neuen Entwicklungen" + research_prefix = "Recherche" + new_articles_msg = f"{new_count} neue Meldung{'en' if new_count != 1 else ''} gefunden" db_notifications = [{ "type": "refresh_summary", "title": title, - "text": f"Recherche: {summary_text}", + "text": f"{research_prefix}: {summary_text}", "icon": "warning" if contradicted_count > 0 else "success", }] if new_count > 0: db_notifications.append({ "type": "new_articles", "title": title, - "text": f"{new_count} neue Meldung{'en' if new_count != 1 else ''} gefunden", + "text": new_articles_msg, "icon": "info", }) for sc in status_changes: diff --git a/src/email_utils/templates.py b/src/email_utils/templates.py index b855a4c..455e844 100644 --- a/src/email_utils/templates.py +++ b/src/email_utils/templates.py @@ -1,13 +1,40 @@ -"""HTML-E-Mail-Vorlagen für Magic Links, Einladungen und Benachrichtigungen.""" +"""HTML-E-Mail-Vorlagen für Magic Links, Einladungen und Benachrichtigungen. + +Sprache pro Empfaenger-Org gesteuert (Default 'de'). +""" -def magic_link_login_email(username: str, link: str) -> tuple[str, str]: +def magic_link_login_email(username: str, link: str, lang: str = "de") -> tuple[str, str]: """Erzeugt Login-E-Mail mit Magic Link. + Args: + username: Empfaenger-Anzeigename + link: Magic-Link-URL + lang: ISO-Sprachcode ('de' | 'en') + Returns: (subject, html_body) """ - subject = f"AegisSight Monitor - Anmeldung" + if lang == "en": + subject = "AegisSight Monitor - Sign in" + body = ( + "Hi {username},", + "Click the button below to sign in:", + "Sign in", + "Or copy this link into your browser:", + "This link is valid for 10 minutes. If you did not request this sign-in, simply ignore this email.", + ) + else: + subject = "AegisSight Monitor - Anmeldung" + body = ( + "Hallo {username},", + "Klicken Sie auf den Button, um sich anzumelden:", + "Jetzt anmelden", + "Oder kopieren Sie diesen Link in Ihren Browser:", + "Dieser Link ist 10 Minuten gültig. Falls Sie diese Anmeldung nicht angefordert haben, ignorieren Sie diese E-Mail.", + ) + + greeting, intro, button_label, copy_hint, validity = body html = f""" @@ -15,18 +42,18 @@ def magic_link_login_email(username: str, link: str) -> tuple[str, str]:

AegisSight Monitor

-

Hallo {username},

+

{greeting.format(username=username)}

-

Klicken Sie auf den Button, um sich anzumelden:

+

{intro}

- Jetzt anmelden + {button_label}
-

Oder kopieren Sie diesen Link in Ihren Browser:

+

{copy_hint}

{link}

-

Dieser Link ist 10 Minuten gültig. Falls Sie diese Anmeldung nicht angefordert haben, ignorieren Sie diese E-Mail.

+

{validity}

""" @@ -39,6 +66,7 @@ def incident_notification_email( notifications: list[dict], dashboard_url: str, incident_type: str = "adhoc", + lang: str = "de", ) -> tuple[str, str]: """Erzeugt Benachrichtigungs-E-Mail für Lagen-Updates. @@ -48,13 +76,30 @@ def incident_notification_email( notifications: Liste von {"text": ..., "icon": ...} Dicts dashboard_url: Link zum Dashboard incident_type: "adhoc" oder "research" + lang: ISO-Sprachcode ('de' | 'en') Returns: (subject, html_body) """ is_research = incident_type == "research" - type_label = "Recherche" if is_research else "Lagebild" - type_label_lower = "Recherche" if is_research else "Lage" + + if lang == "en": + type_label = "Research" if is_research else "Situation" + type_label_lower = "research" if is_research else "situation" + notification_word = "notification" + greeting = f"Hi {username}," + intro = f"There is news on the {type_label_lower}" + button_label = "Open in dashboard" + footer = "You can disable these notifications in your dashboard settings." + else: + type_label = "Recherche" if is_research else "Lagebild" + type_label_lower = "Recherche" if is_research else "Lage" + notification_word = "Benachrichtigung" + greeting = f"Hallo {username}," + intro = f"es gibt Neuigkeiten zur {type_label_lower}" + button_label = "Im Dashboard ansehen" + footer = "Diese Benachrichtigung kann in den Einstellungen im Dashboard deaktiviert werden." + subject = f"AegisSight - {incident_title}" icon_map = { @@ -87,20 +132,20 @@ def incident_notification_email(

AegisSight Monitor

-

{type_label} - Benachrichtigung

+

{type_label} - {notification_word}

-

Hallo {username},

-

es gibt Neuigkeiten zur {type_label_lower} {incident_title}:

+

{greeting}

+

{intro} {incident_title}:

{items_html}
- Im Dashboard ansehen + {button_label}
-

Diese Benachrichtigung kann in den Einstellungen im Dashboard deaktiviert werden.

+

{footer}

""" diff --git a/src/routers/auth.py b/src/routers/auth.py index 629aaec..968b690 100644 --- a/src/routers/auth.py +++ b/src/routers/auth.py @@ -96,9 +96,11 @@ async def request_magic_link( ) await db.commit() - # E-Mail senden + # E-Mail senden -- Sprache aus Org-Settings des Users link = f"{MAGIC_LINK_BASE_URL}/?token={token}" - subject, html = magic_link_login_email(user["email"].split("@")[0], link) + from services.org_settings import get_org_language + org_lang_iso = await get_org_language(db, user["organization_id"]) + subject, html = magic_link_login_email(user["email"].split("@")[0], link, lang=org_lang_iso) await send_email(email, subject, html) magic_link_limiter.record(email, ip)