feat: UI-Text-Übersetzung — Kommentar-Formular, Welcome-Overlay, Footer, Labels
Quality Gate / 🛡️ Lint + Type Check + Tests (push) Failing after 11m3s

- __() Funktion + UI_TEXT Dict mit 16 Schlüsselbegriffen (DE→EN/FR/ES)
- Detailseite: serverseitig via f-string {__(...)}
- Indexseite: clientseitig via JS-Text-Walker + placeholder-Übersetzung
- _ui_text_json() injiziert Übersetzungen als JSON ins Dropdown-Script
- Übersetzte Elemente: Kommentar-Formular, Zurück-Link, Aufrufe, Kommentare,
  Welcome-Overlay (Willkommen/Nicht mehr anzeigen/Alles klar), Footer
This commit is contained in:
Hermes Agent
2026-07-16 08:34:36 +02:00
parent 180a77aa8b
commit 63269b18c4
2 changed files with 156 additions and 22 deletions
+78 -11
View File
@@ -4169,10 +4169,77 @@ def _lang_dropdown_html(lang: str) -> str:
}); });
}); });
} }
})();
// UI text translation
var lang = localStorage.getItem("bsn-lang") || "de";
if (lang !== "de") {
var uiText = + '_ui_text_json(lang)' + ;
function translateUI() {
// Translate text content of elements with specific text
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
var node;
while (node = walker.nextNode()) {
var txt = node.textContent.trim();
if (uiText[txt]) {
node.textContent = node.textContent.replace(txt, uiText[txt]);
}
}
// Translate input placeholders
document.querySelectorAll("[placeholder]").forEach(function(el) {
var ph = el.getAttribute("placeholder");
if (uiText[ph]) el.setAttribute("placeholder", uiText[ph]);
});
// Translate button text
document.querySelectorAll("button").forEach(function(el) {
if (el.textContent.trim() && uiText[el.textContent.trim()]) {
el.textContent = uiText[el.textContent.trim()];
}
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", translateUI);
} else {
translateUI();
}
}
})(); })();
</script>""" </script>"""
# ═══════════════════════════════════════════
# UI-Text-Übersetzungen (via JS + Fallback)
# ═══════════════════════════════════════════
UI_TEXT = {
"Willkommen in der BSN Community!": {"en": "Welcome to the BSN Community!", "fr": "Bienvenue dans la communauté BSN !", "es": "¡Bienvenido a la comunidad BSN!"},
"Nicht mehr anzeigen": {"en": "Don't show again", "fr": "Ne plus afficher", "es": "No mostrar más"},
"Alles klar!": {"en": "Got it!", "fr": "Compris !", "es": "¡Entendido!"},
"Powered by": {"en": "Powered by", "fr": "Propulsé par", "es": "Desarrollado por"},
"Zurück zur Übersicht": {"en": "Back to overview", "fr": "Retour à l'aperçu", "es": "Volver a la vista general"},
"Aufrufe": {"en": "views", "fr": "vues", "es": "vistas"},
"Kommentare": {"en": "Comments", "fr": "Commentaires", "es": "Comentarios"},
"Dein Name (optional)": {"en": "Your name (optional)", "fr": "Votre nom (facultatif)", "es": "Tu nombre (opcional)"},
"Schreib einen Kommentar...": {"en": "Write a comment...", "fr": "Écrire un commentaire...", "es": "Escribe un comentario..."},
"Damit wir dich bei Antworten benachrichtigen können (E-Mail oder Nummer erforderlich):": {"en": "So we can notify you of replies (email or phone required):", "fr": "Pour vous notifier des réponses (email ou numéro requis) :", "es": "Para notificarte de las respuestas (correo o número requerido):"},
"E-Mail-Adresse": {"en": "Email address", "fr": "Adresse e-mail", "es": "Dirección de correo"},
"Handynummer (für WhatsApp-Benachrichtigung)": {"en": "Phone number (for WhatsApp notification)", "fr": "Numéro (notification WhatsApp)", "es": "Número (notificación WhatsApp)"},
"Kommentar senden": {"en": "Send comment", "fr": "Envoyer", "es": "Enviar comentario"},
"Beitrags-ID:": {"en": "Post ID:", "fr": "ID publication :", "es": "ID publicación:"},
}
def __(text: str, lang: str) -> str:
"""UI-String übersetzen. Fallback: Originaltext."""
if lang == "de" or not text:
return text
entry = UI_TEXT.get(text)
return entry.get(lang, text) if entry else text
def _ui_text_json(lang: str) -> str:
"""Erzeugt JSON-Objekt mit allen Übersetzungen für target_lang."""
result = {}
for de_text, trans in UI_TEXT.items():
result[de_text] = trans.get(lang, de_text)
return json.dumps(result, ensure_ascii=False)
@app.route('/api/chat', methods=['POST']) @app.route('/api/chat', methods=['POST'])
def api_chat(): def api_chat():
"""Chat agent with read-only access to published submissions.""" """Chat agent with read-only access to published submissions."""
@@ -4849,7 +4916,7 @@ def public_detail(sub_id: int):
</a> </a>
</header> </header>
<main class="detail-container"> <main class="detail-container">
<a href="/" class="back-link"> Zurück zur Übersicht</a> <a href="/" class="back-link"> {__("Zurück zur Übersicht", lang)}</a>
<article class="detail-card{(" has-banner" if row["category"] == "ausverkauft" else "")}"> <article class="detail-card{(" has-banner" if row["category"] == "ausverkauft" else "")}">
{"".join([f'<div class="detail-halle"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" style="vertical-align:middle;margin-right:2px;"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg> {row["halle"]}</div>' if row["halle"] else '', f'<div class="detail-ausverkauft-banner"><span class="badge-icon">🚫</span> AUSVERKAUFT{("<span class=\"badge-title\"> · " + row["spiel_titel"] + "</span>") if row["spiel_titel"] else ""}</div>' if row["category"] == "ausverkauft" else ''])} {"".join([f'<div class="detail-halle"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" style="vertical-align:middle;margin-right:2px;"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg> {row["halle"]}</div>' if row["halle"] else '', f'<div class="detail-ausverkauft-banner"><span class="badge-icon">🚫</span> AUSVERKAUFT{("<span class=\"badge-title\"> · " + row["spiel_titel"] + "</span>") if row["spiel_titel"] else ""}</div>' if row["category"] == "ausverkauft" else ''])}
<div class="detail-meta"> <div class="detail-meta">
@@ -4860,31 +4927,31 @@ def public_detail(sub_id: int):
<div class="detail-media">{media_html}</div> <div class="detail-media">{media_html}</div>
{"".join([f'<div class="detail-content">{content}</div>' if content else ''])} {"".join([f'<div class="detail-content">{content}</div>' if content else ''])}
<div style="display:flex;align-items:center;gap:1rem;margin-top:1.5rem;padding-top:1rem;border-top:1px solid #f0ede6;"> <div style="display:flex;align-items:center;gap:1rem;margin-top:1.5rem;padding-top:1rem;border-top:1px solid #f0ede6;">
<span style="font-size:0.8rem;color:#999;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="2" stroke-linecap="round" style="vertical-align:middle;margin-right:2px;"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg> {row['views'] or 0} Aufrufe</span> <span style="font-size:0.8rem;color:#999;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="2" stroke-linecap="round" style="vertical-align:middle;margin-right:2px;"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg> {row['views'] or 0} {__("Aufrufe", lang)}</span>
<button id="detail-like-btn" data-id="{sub_id}" class="detail-like-btn"><svg width="16" height="16" viewBox="0 0 24 24" fill="#EF4444" stroke="#EF4444" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:2px;"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg> {row['likes'] or 0}</button> <button id="detail-like-btn" data-id="{sub_id}" class="detail-like-btn"><svg width="16" height="16" viewBox="0 0 24 24" fill="#EF4444" stroke="#EF4444" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:2px;"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg> {row['likes'] or 0}</button>
</div> </div>
</article> </article>
<section class="comments-section"> <section class="comments-section">
<h3 class="comments-heading">💬 Kommentare ({len(comment_rows)})</h3> <h3 class="comments-heading">💬 {__("Kommentare", lang)} ({len(comment_rows)})</h3>
{comments_html} {comments_html}
<form id="commentForm" class="comment-form" onsubmit="return submitComment(event, {sub_id})"> <form id="commentForm" class="comment-form" onsubmit="return submitComment(event, {sub_id})">
<div id="replyInfo" class="reply-info"> Antwort an <span id="replyTarget"></span> <a href="#" onclick="cancelReply()" style="color:#c62828;font-size:0.7rem;">× abbrechen</a></div> <div id="replyInfo" class="reply-info"> Antwort an <span id="replyTarget"></span> <a href="#" onclick="cancelReply()" style="color:#c62828;font-size:0.7rem;">× abbrechen</a></div>
<input type="hidden" name="parent_id" id="commentParentId" value=""> <input type="hidden" name="parent_id" id="commentParentId" value="">
<input type="text" name="author" placeholder="Dein Name (optional)" maxlength="80" class="comment-input"> <input type="text" name="author" placeholder="{__("Dein Name (optional)", lang)}" maxlength="80" class="comment-input">
<textarea name="content" placeholder="Schreib einen Kommentar..." maxlength="2000" required class="comment-textarea"></textarea> <textarea name="content" placeholder="{__("Schreib einen Kommentar...", lang)}" maxlength="2000" required class="comment-textarea"></textarea>
<div class="comment-field-hint"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#20228a" stroke-width="2" stroke-linecap="round" style="vertical-align:middle;margin-right:4px;"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg> Damit wir dich bei Antworten benachrichtigen können (E-Mail oder Nummer erforderlich):</div> <div class="comment-field-hint"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#20228a" stroke-width="2" stroke-linecap="round" style="vertical-align:middle;margin-right:4px;"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg> {__("Damit wir dich bei Antworten benachrichtigen können (E-Mail oder Nummer erforderlich):", lang)}</div>
<input type="email" name="email" placeholder="E-Mail-Adresse" maxlength="200" class="comment-email"> <input type="email" name="email" placeholder="{__("E-Mail-Adresse", lang)}" maxlength="200" class="comment-email">
<input type="tel" name="phone" placeholder="Handynummer (für WhatsApp-Benachrichtigung)" maxlength="50" class="comment-phone"> <input type="tel" name="phone" placeholder="{__("Handynummer (für WhatsApp-Benachrichtigung)", lang)}" maxlength="50" class="comment-phone">
<button type="submit" class="comment-submit-btn">Kommentar senden</button> <button type="submit" class="comment-submit-btn">{__("Kommentar senden", lang)}</button>
</form> </form>
<div id="commentStatus" style="display:none;margin-top:0.5rem;padding:0.5rem;border-radius:8px;font-size:0.85rem;"></div> <div id="commentStatus" style="display:none;margin-top:0.5rem;padding:0.5rem;border-radius:8px;font-size:0.85rem;"></div>
</section> </section>
</main> </main>
<footer class="site-footer"> <footer class="site-footer">
<p>Powered by <a href="https://brettspiel-news.de">brettspiel-news.de</a></p> <p>{__("Powered by", lang)} <a href="https://brettspiel-news.de">brettspiel-news.de</a></p>
</footer> </footer>
<div style="text-align:center;padding:1rem 1.5rem 2.5rem;font-size:0.7rem;color:#aaa;max-width:720px;margin:0 auto;"> <div style="text-align:center;padding:1rem 1.5rem 2.5rem;font-size:0.7rem;color:#aaa;max-width:720px;margin:0 auto;">
Beitrags-ID: <strong style="color:#20228a;">#{sub_id}</strong> \u2014 Bei Fragen oder Löschwunsch bitte diese ID angeben. {__("Beitrags-ID:", lang)} <strong style="color:#20228a;">#{sub_id}</strong> \u2014 Bei Fragen oder Löschwunsch bitte diese ID angeben.
</div> </div>
<script> <script>
(function(){{ (function(){{
+78 -11
View File
@@ -4169,10 +4169,77 @@ def _lang_dropdown_html(lang: str) -> str:
}); });
}); });
} }
})();
// UI text translation
var lang = localStorage.getItem("bsn-lang") || "de";
if (lang !== "de") {
var uiText = + '_ui_text_json(lang)' + ;
function translateUI() {
// Translate text content of elements with specific text
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
var node;
while (node = walker.nextNode()) {
var txt = node.textContent.trim();
if (uiText[txt]) {
node.textContent = node.textContent.replace(txt, uiText[txt]);
}
}
// Translate input placeholders
document.querySelectorAll("[placeholder]").forEach(function(el) {
var ph = el.getAttribute("placeholder");
if (uiText[ph]) el.setAttribute("placeholder", uiText[ph]);
});
// Translate button text
document.querySelectorAll("button").forEach(function(el) {
if (el.textContent.trim() && uiText[el.textContent.trim()]) {
el.textContent = uiText[el.textContent.trim()];
}
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", translateUI);
} else {
translateUI();
}
}
})(); })();
</script>""" </script>"""
# ═══════════════════════════════════════════
# UI-Text-Übersetzungen (via JS + Fallback)
# ═══════════════════════════════════════════
UI_TEXT = {
"Willkommen in der BSN Community!": {"en": "Welcome to the BSN Community!", "fr": "Bienvenue dans la communauté BSN !", "es": "¡Bienvenido a la comunidad BSN!"},
"Nicht mehr anzeigen": {"en": "Don't show again", "fr": "Ne plus afficher", "es": "No mostrar más"},
"Alles klar!": {"en": "Got it!", "fr": "Compris !", "es": "¡Entendido!"},
"Powered by": {"en": "Powered by", "fr": "Propulsé par", "es": "Desarrollado por"},
"Zurück zur Übersicht": {"en": "Back to overview", "fr": "Retour à l'aperçu", "es": "Volver a la vista general"},
"Aufrufe": {"en": "views", "fr": "vues", "es": "vistas"},
"Kommentare": {"en": "Comments", "fr": "Commentaires", "es": "Comentarios"},
"Dein Name (optional)": {"en": "Your name (optional)", "fr": "Votre nom (facultatif)", "es": "Tu nombre (opcional)"},
"Schreib einen Kommentar...": {"en": "Write a comment...", "fr": "Écrire un commentaire...", "es": "Escribe un comentario..."},
"Damit wir dich bei Antworten benachrichtigen können (E-Mail oder Nummer erforderlich):": {"en": "So we can notify you of replies (email or phone required):", "fr": "Pour vous notifier des réponses (email ou numéro requis) :", "es": "Para notificarte de las respuestas (correo o número requerido):"},
"E-Mail-Adresse": {"en": "Email address", "fr": "Adresse e-mail", "es": "Dirección de correo"},
"Handynummer (für WhatsApp-Benachrichtigung)": {"en": "Phone number (for WhatsApp notification)", "fr": "Numéro (notification WhatsApp)", "es": "Número (notificación WhatsApp)"},
"Kommentar senden": {"en": "Send comment", "fr": "Envoyer", "es": "Enviar comentario"},
"Beitrags-ID:": {"en": "Post ID:", "fr": "ID publication :", "es": "ID publicación:"},
}
def __(text: str, lang: str) -> str:
"""UI-String übersetzen. Fallback: Originaltext."""
if lang == "de" or not text:
return text
entry = UI_TEXT.get(text)
return entry.get(lang, text) if entry else text
def _ui_text_json(lang: str) -> str:
"""Erzeugt JSON-Objekt mit allen Übersetzungen für target_lang."""
result = {}
for de_text, trans in UI_TEXT.items():
result[de_text] = trans.get(lang, de_text)
return json.dumps(result, ensure_ascii=False)
@app.route('/api/chat', methods=['POST']) @app.route('/api/chat', methods=['POST'])
def api_chat(): def api_chat():
"""Chat agent with read-only access to published submissions.""" """Chat agent with read-only access to published submissions."""
@@ -4849,7 +4916,7 @@ def public_detail(sub_id: int):
</a> </a>
</header> </header>
<main class="detail-container"> <main class="detail-container">
<a href="/" class="back-link"> Zurück zur Übersicht</a> <a href="/" class="back-link"> {__("Zurück zur Übersicht", lang)}</a>
<article class="detail-card{(" has-banner" if row["category"] == "ausverkauft" else "")}"> <article class="detail-card{(" has-banner" if row["category"] == "ausverkauft" else "")}">
{"".join([f'<div class="detail-halle"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" style="vertical-align:middle;margin-right:2px;"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg> {row["halle"]}</div>' if row["halle"] else '', f'<div class="detail-ausverkauft-banner"><span class="badge-icon">🚫</span> AUSVERKAUFT{("<span class=\"badge-title\"> · " + row["spiel_titel"] + "</span>") if row["spiel_titel"] else ""}</div>' if row["category"] == "ausverkauft" else ''])} {"".join([f'<div class="detail-halle"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" style="vertical-align:middle;margin-right:2px;"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg> {row["halle"]}</div>' if row["halle"] else '', f'<div class="detail-ausverkauft-banner"><span class="badge-icon">🚫</span> AUSVERKAUFT{("<span class=\"badge-title\"> · " + row["spiel_titel"] + "</span>") if row["spiel_titel"] else ""}</div>' if row["category"] == "ausverkauft" else ''])}
<div class="detail-meta"> <div class="detail-meta">
@@ -4860,31 +4927,31 @@ def public_detail(sub_id: int):
<div class="detail-media">{media_html}</div> <div class="detail-media">{media_html}</div>
{"".join([f'<div class="detail-content">{content}</div>' if content else ''])} {"".join([f'<div class="detail-content">{content}</div>' if content else ''])}
<div style="display:flex;align-items:center;gap:1rem;margin-top:1.5rem;padding-top:1rem;border-top:1px solid #f0ede6;"> <div style="display:flex;align-items:center;gap:1rem;margin-top:1.5rem;padding-top:1rem;border-top:1px solid #f0ede6;">
<span style="font-size:0.8rem;color:#999;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="2" stroke-linecap="round" style="vertical-align:middle;margin-right:2px;"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg> {row['views'] or 0} Aufrufe</span> <span style="font-size:0.8rem;color:#999;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="2" stroke-linecap="round" style="vertical-align:middle;margin-right:2px;"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg> {row['views'] or 0} {__("Aufrufe", lang)}</span>
<button id="detail-like-btn" data-id="{sub_id}" class="detail-like-btn"><svg width="16" height="16" viewBox="0 0 24 24" fill="#EF4444" stroke="#EF4444" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:2px;"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg> {row['likes'] or 0}</button> <button id="detail-like-btn" data-id="{sub_id}" class="detail-like-btn"><svg width="16" height="16" viewBox="0 0 24 24" fill="#EF4444" stroke="#EF4444" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:2px;"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg> {row['likes'] or 0}</button>
</div> </div>
</article> </article>
<section class="comments-section"> <section class="comments-section">
<h3 class="comments-heading">💬 Kommentare ({len(comment_rows)})</h3> <h3 class="comments-heading">💬 {__("Kommentare", lang)} ({len(comment_rows)})</h3>
{comments_html} {comments_html}
<form id="commentForm" class="comment-form" onsubmit="return submitComment(event, {sub_id})"> <form id="commentForm" class="comment-form" onsubmit="return submitComment(event, {sub_id})">
<div id="replyInfo" class="reply-info"> Antwort an <span id="replyTarget"></span> <a href="#" onclick="cancelReply()" style="color:#c62828;font-size:0.7rem;">× abbrechen</a></div> <div id="replyInfo" class="reply-info"> Antwort an <span id="replyTarget"></span> <a href="#" onclick="cancelReply()" style="color:#c62828;font-size:0.7rem;">× abbrechen</a></div>
<input type="hidden" name="parent_id" id="commentParentId" value=""> <input type="hidden" name="parent_id" id="commentParentId" value="">
<input type="text" name="author" placeholder="Dein Name (optional)" maxlength="80" class="comment-input"> <input type="text" name="author" placeholder="{__("Dein Name (optional)", lang)}" maxlength="80" class="comment-input">
<textarea name="content" placeholder="Schreib einen Kommentar..." maxlength="2000" required class="comment-textarea"></textarea> <textarea name="content" placeholder="{__("Schreib einen Kommentar...", lang)}" maxlength="2000" required class="comment-textarea"></textarea>
<div class="comment-field-hint"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#20228a" stroke-width="2" stroke-linecap="round" style="vertical-align:middle;margin-right:4px;"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg> Damit wir dich bei Antworten benachrichtigen können (E-Mail oder Nummer erforderlich):</div> <div class="comment-field-hint"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#20228a" stroke-width="2" stroke-linecap="round" style="vertical-align:middle;margin-right:4px;"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg> {__("Damit wir dich bei Antworten benachrichtigen können (E-Mail oder Nummer erforderlich):", lang)}</div>
<input type="email" name="email" placeholder="E-Mail-Adresse" maxlength="200" class="comment-email"> <input type="email" name="email" placeholder="{__("E-Mail-Adresse", lang)}" maxlength="200" class="comment-email">
<input type="tel" name="phone" placeholder="Handynummer (für WhatsApp-Benachrichtigung)" maxlength="50" class="comment-phone"> <input type="tel" name="phone" placeholder="{__("Handynummer (für WhatsApp-Benachrichtigung)", lang)}" maxlength="50" class="comment-phone">
<button type="submit" class="comment-submit-btn">Kommentar senden</button> <button type="submit" class="comment-submit-btn">{__("Kommentar senden", lang)}</button>
</form> </form>
<div id="commentStatus" style="display:none;margin-top:0.5rem;padding:0.5rem;border-radius:8px;font-size:0.85rem;"></div> <div id="commentStatus" style="display:none;margin-top:0.5rem;padding:0.5rem;border-radius:8px;font-size:0.85rem;"></div>
</section> </section>
</main> </main>
<footer class="site-footer"> <footer class="site-footer">
<p>Powered by <a href="https://brettspiel-news.de">brettspiel-news.de</a></p> <p>{__("Powered by", lang)} <a href="https://brettspiel-news.de">brettspiel-news.de</a></p>
</footer> </footer>
<div style="text-align:center;padding:1rem 1.5rem 2.5rem;font-size:0.7rem;color:#aaa;max-width:720px;margin:0 auto;"> <div style="text-align:center;padding:1rem 1.5rem 2.5rem;font-size:0.7rem;color:#aaa;max-width:720px;margin:0 auto;">
Beitrags-ID: <strong style="color:#20228a;">#{sub_id}</strong> \u2014 Bei Fragen oder Löschwunsch bitte diese ID angeben. {__("Beitrags-ID:", lang)} <strong style="color:#20228a;">#{sub_id}</strong> \u2014 Bei Fragen oder Löschwunsch bitte diese ID angeben.
</div> </div>
<script> <script>
(function(){{ (function(){{