Handy-Bedienung in 27 Sprachen, Kopf im Einrichtungs-Modus, schmale Handys

- ui-texte.js: 74 Bedientexte je Sprache (uebersetzt + lektoriert), t() in
  demo.html; am PC bleibt alles deutsch, am Handy Sprache der Person,
  RTL fuer ar/fa/ur/ps, verbundene Schriften ohne Sperrsatz
- Servermeldungen am Handy nach Code in der Bediensprache
- Seitenkopf "Fuer Sie kostenlos ueber ..." statt "2 Analysen gratis"
- Turnstile passt auf schmale Handys (Abstaende, compact unter 350 px)
- Knopf fuer Mitarbeitende eindeutig: "Zugang dauerhaft auf ein Handy geben"

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
JUSNandClaude Opus 5.5 committed 2026-10-22 22:30:33 +02:00
1 parent 6dc0207716
commit 82235dc529
2 files changed
+2282 -79

No files matched your search

+144 -79
View File
@@ -68,6 +68,17 @@
} }
.mono { font-family: 'Courier New', ui-monospace, monospace; letter-spacing: 0.02em; } .mono { font-family: 'Courier New', ui-monospace, monospace; letter-spacing: 0.02em; }
.wrap { max-width: 720px; margin: 0 auto; padding: 0 20px 72px; } .wrap { max-width: 720px; margin: 0 auto; padding: 0 20px 72px; }
/* Schriften mit verbundenen Buchstaben (Arabisch, Devanagari …): kein
Buchstabenabstand und keine Schreibmaschine — sonst reißen Wörter auseinander */
html.verbundene-schrift .formular-kopf, html.verbundene-schrift .betreff-label,
html.verbundene-schrift h2, html.verbundene-schrift .karten-label {
font-family: inherit; letter-spacing: 0; text-transform: none;
}
@media (max-width: 420px) {
/* Cloudflare-Prüfung ist fest 300 px breit — sonst ragt sie aus dem Kasten */
.wrap { padding: 0 14px 56px; }
.antrag { padding: 22px 14px; }
}
.briefkopf { .briefkopf {
display: flex; justify-content: space-between; align-items: baseline; display: flex; justify-content: space-between; align-items: baseline;
@@ -265,23 +276,23 @@
<div class="briefkopf"> <div class="briefkopf">
<span class="absender"><a href="index.html">📬 BehördenKlar</a> <span style="font-weight:400;font-size:.62em;opacity:.6;letter-spacing:.04em;">by Webklar</span></span> <span class="absender"><a href="index.html">📬 BehördenKlar</a> <span style="font-weight:400;font-size:.62em;opacity:.6;letter-spacing:.04em;">by Webklar</span></span>
<span class="az mono">Formular D-01 · Gratis-Demo</span> <span class="az mono" id="kopfAz">Formular D-01 · Gratis-Demo</span>
</div> </div>
<header> <header>
<p class="betreff-label">Betreff</p> <p class="betreff-label" data-t="betreff">Betreff</p>
<h1>Probieren Sie es aus —<br><span class="markiert">mit Ihrem eigenen Brief.</span></h1> <h1><span data-t="titel1">Probieren Sie es aus —</span><br><span class="markiert" data-t="titel2">mit Ihrem eigenen Brief.</span></h1>
<p class="anrede"> <p class="anrede">
Kein Konto. Keine App-Installation. Laden Sie ein Foto Ihres <span data-t="anrede">Kein Konto. Keine App-Installation. Laden Sie ein Foto Ihres
Behördenbriefs hoch — in etwa einer Minute lesen Sie ihn in einfacher Behördenbriefs hoch — nach ein bis zwei Minuten lesen Sie ihn in einfacher
Sprache. <strong>2 Analysen gratis.</strong> Sprache.</span> <strong id="anredeAngebot" data-t="anredeGratis">2 Analysen gratis.</strong>
</p> </p>
</header> </header>
<section id="demo"> <section id="demo">
<h2><span class="nr">§ 1</span> Brief hochladen</h2> <h2><span class="nr">§ 1</span> <span data-t="abschnittHochladen">Brief hochladen</span></h2>
<div class="antrag"> <div class="antrag">
<p class="formular-kopf">Antrag auf Klartext · 2 Gratis-Analysen</p> <p class="formular-kopf" data-t="formKopfGratis">Brief erklären lassen · 2 kostenlose Analysen</p>
<p class="verbunden" id="verbundenBanner"></p> <p class="verbunden" id="verbundenBanner"></p>
@@ -290,7 +301,7 @@
✅ Kauf erfolgreich! Ihr Guthaben wurde aktiviert. ✅ Kauf erfolgreich! Ihr Guthaben wurde aktiviert.
</p> </p>
<p id="guthabenBanner" class="bonus-zeile" style="display: none;"> <p id="guthabenBanner" class="bonus-zeile" style="display: none;">
<span id="guthabenText">💳 Ihr Guthaben:</span> <strong id="guthabenZahl">0</strong> Analysen — laden Sie den nächsten Brief hoch. <span id="guthabenText">💳 Ihr Guthaben:</span> <strong id="guthabenZahl">0</strong> <span data-t="bannerRest">Analysen — laden Sie den nächsten Brief hoch.</span>
<br><button type="button" class="link-knopf" id="guthabenHandyKnopf" style="font-size: 14.5px;">📱 Auch auf dem Handy nutzen</button> <br><button type="button" class="link-knopf" id="guthabenHandyKnopf" style="font-size: 14.5px;">📱 Auch auf dem Handy nutzen</button>
</p> </p>
<div class="qr-box" id="guthabenQrBox"> <div class="qr-box" id="guthabenQrBox">
@@ -299,20 +310,20 @@
<p class="klein" style="margin-top: 0;">Mit der Handy-Kamera scannen. Danach können Sie Briefe auch direkt am Handy erklären lassen.</p> <p class="klein" style="margin-top: 0;">Mit der Handy-Kamera scannen. Danach können Sie Briefe auch direkt am Handy erklären lassen.</p>
</div> </div>
<p style="margin-bottom: 14px; font-size: 15.5px;"> <p style="margin-bottom: 14px; font-size: 15.5px;">
🌐 <label for="sprachWahl">Erklärung in dieser Sprache:</label> 🌐 <label for="sprachWahl" data-t="spracheLabel">Erklärung in dieser Sprache:</label>
<select id="sprachWahl" style="font-family: inherit; font-size: 15.5px; padding: 5px 8px; border: 1.5px solid var(--tinte); background: #fff; margin-inline-start: 6px;"></select> <select id="sprachWahl" style="font-family: inherit; font-size: 15.5px; padding: 5px 8px; border: 1.5px solid var(--tinte); background: #fff; margin-inline-start: 6px;"></select>
</p> </p>
<label class="konsens"> <label class="konsens">
<input type="checkbox" id="konsens"> <input type="checkbox" id="konsens">
<span> <span>
Ich bin einverstanden, dass das Foto dieses Briefs zur Erklärung <span data-t="konsens">Ich bin einverstanden, dass das Foto dieses Briefs zur Erklärung
an den Server von BehördenKlar und an den KI-Dienst Anthropic an den Server von BehördenKlar und an den KI-Dienst Anthropic
(USA) geschickt wird. Briefe können sehr persönliche Angaben (USA) geschickt wird. Briefe können sehr persönliche Angaben
enthalten, zum Beispiel zu Gesundheit, Herkunft oder Aufenthalt. enthalten, zum Beispiel zu Gesundheit, Herkunft oder Aufenthalt.
BehördenKlar speichert den Brief nicht. Anthropic löscht ihn in BehördenKlar speichert den Brief nicht. Anthropic löscht ihn in
der Regel nach 30 Tagen und nutzt ihn nicht zum Training. der Regel nach 30 Tagen und nutzt ihn nicht zum Training.</span>
<a href="datenschutz.html" target="_blank">Datenschutzerklärung</a> <a href="datenschutz.html" target="_blank" data-t="datenschutzLink">Datenschutzerklärung</a>
</span> </span>
</label> </label>
@@ -330,7 +341,7 @@
</div> </div>
<input type="file" id="datei" accept="image/jpeg,image/png,application/pdf" style="display: none;"> <input type="file" id="datei" accept="image/jpeg,image/png,application/pdf" style="display: none;">
<button class="cta" id="startKnopf">📷 Brief auswählen &amp; erklären lassen</button> <button class="cta" id="startKnopf" data-t="startKnopf">📷 Brief auswählen &amp; erklären lassen</button>
<button class="cta cta-zwei" id="handyKnopf" type="button" style="display: none;">📱 Lieber mit dem Handy fotografieren</button> <button class="cta cta-zwei" id="handyKnopf" type="button" style="display: none;">📱 Lieber mit dem Handy fotografieren</button>
<div class="qr-box" id="handyBox"> <div class="qr-box" id="handyBox">
<strong>Mit dem Handy fotografieren</strong> <strong>Mit dem Handy fotografieren</strong>
@@ -355,36 +366,38 @@
if (/^(localhost|127\.0\.0\.1)$/.test(location.hostname)) { if (/^(localhost|127\.0\.0\.1)$/.test(location.hostname)) {
document.querySelector('.cf-turnstile').setAttribute('data-sitekey', '1x00000000000000000000AA'); document.querySelector('.cf-turnstile').setAttribute('data-sitekey', '1x00000000000000000000AA');
} }
// Sehr schmale Handys (< 350 px): kompakte Cloudflare-Prüfung
if (window.innerWidth < 350) document.querySelector('.cf-turnstile').setAttribute('data-size', 'compact');
</script> </script>
<p class="klein"> <p class="klein" data-t="tippFoto">
Geht mit Foto (JPG/PNG) oder PDF, max. 5 MB. Tipp: gutes Licht, Geht mit Foto (JPG/PNG) oder PDF, max. 5 MB. Tipp: gutes Licht,
Brief von oben, ganzer Text im Bild. Brief von oben, ganzer Text im Bild.
</p> </p>
<p class="klein"> <p class="klein">
📱 <strong>Am Handy:</strong> Wählen Sie „Galerie" oder „Dateien", <strong data-t="tippHandyTitel">📱 Am Handy:</strong> <span data-t="tippHandy">Wählen Sie „Galerie" oder „Dateien",
wenn Sie den Brief schon fotografiert haben. Tippen Sie auf wenn Sie den Brief schon fotografiert haben. Tippen Sie auf
„Kamera", öffnet sich die Kamera-App Ihres Handys — und die fragt „Kamera", öffnet sich die Kamera-App Ihres Handys — und die fragt
nach ihren eigenen Berechtigungen. <strong>Diese Abfrage kommt von nach ihren eigenen Berechtigungen. Diese Abfrage kommt von
Ihrem Handy, nicht von uns:</strong> Diese Seite greift weder auf Ihrem Handy, nicht von uns: Diese Seite greift weder auf
Kamera noch auf Mikrofon zu. Kamera noch auf Mikrofon zu.</span>
</p> </p>
</div> </div>
<div class="status" id="status"> <div class="status" id="status">
<span class="sanduhr">⏳</span> <span class="sanduhr">⏳</span>
<p class="gross">Ihr Brief wird analysiert…</p> <p class="gross">Ihr Brief wird erklärt …</p>
<p class="sub">Das dauert bis zu einer Minute. Bitte lassen Sie die Seite geöffnet.</p> <p class="sub" data-t="statusDauer">Das dauert ein bis zwei Minuten. Bitte lassen Sie die Seite geöffnet.</p>
</div> </div>
<div class="fehlerbox" id="fehler"></div> <div class="fehlerbox" id="fehler"></div>
<details id="codeTeil" style="margin-top: 16px; border-top: 1px solid rgba(33, 33, 29, 0.2);"> <details id="codeTeil" style="margin-top: 16px; border-top: 1px solid rgba(33, 33, 29, 0.2);">
<summary>🤝 Sie haben einen Code von einer Beratungsstelle?</summary> <summary data-t="codeFrage">🤝 Sie haben einen Code von einer Beratungsstelle?</summary>
<p>Dann ist BehördenKlar für Sie kostenlos. Geben Sie den Code hier ein:</p> <p data-t="codeText">Dann ist BehördenKlar für Sie kostenlos. Geben Sie den Code hier ein:</p>
<input class="wl-input" id="codeFeld" placeholder="z. B. MBE-K7QX" autocapitalize="characters" autocomplete="off" spellcheck="false" style="margin-top: 10px; text-transform: uppercase;"> <input class="wl-input" id="codeFeld" placeholder="z. B. MBE-K7QX" autocapitalize="characters" autocomplete="off" spellcheck="false" style="margin-top: 10px; text-transform: uppercase;">
<button class="cta" id="codeKnopf" type="button">Code einlösen</button> <button class="cta" id="codeKnopf" type="button" data-t="codeKnopf">Code einlösen</button>
</details> </details>
</div> </div>
</section> </section>
@@ -435,7 +448,7 @@
</section> </section>
<section id="ergebnis"> <section id="ergebnis">
<h2><span class="nr">§ 3</span> Ihr Brief in Klartext</h2> <h2><span class="nr">§ 3</span> <span data-t="ergebnisTitel">Ihr Brief in Klartext</span></h2>
<div class="umschalter" id="sprachUmschalter" role="group" aria-label="Sprache der Erklärung"></div> <div class="umschalter" id="sprachUmschalter" role="group" aria-label="Sprache der Erklärung"></div>
<div class="brief-karte"> <div class="brief-karte">
@@ -447,31 +460,31 @@
<div class="brief-karte" id="ergKalenderKarte" style="display: none;"> <div class="brief-karte" id="ergKalenderKarte" style="display: none;">
<span class="karten-label" id="ergKalenderLabel">Nicht vergessen</span> <span class="karten-label" id="ergKalenderLabel">Nicht vergessen</span>
<div id="ergKalenderText" style="margin-top: 6px;"></div> <div id="ergKalenderText" style="margin-top: 6px;"></div>
<button class="cta" id="kalenderKnopf" type="button" style="margin-top: 12px;">📅 In meinen Kalender eintragen</button> <button class="cta" id="kalenderKnopf" type="button" style="margin-top: 12px;" data-t="kalenderKnopf">📅 In meinen Kalender eintragen</button>
<p class="klein" id="ergKalenderHinweis"> <p class="klein" id="ergKalenderHinweis" data-t="kalenderHinweis">
Ihr Handy erinnert Sie <strong>3 Tage</strong> und <strong>1 Tag</strong> vorher. Ihr Handy erinnert Sie 3 Tage und 1 Tag vorher.
Der Eintrag landet nur in Ihrem eigenen Kalender — wir speichern nichts. Der Eintrag landet nur in Ihrem eigenen Kalender — wir speichern nichts.
</p> </p>
</div> </div>
<div class="brief-karte"> <div class="brief-karte">
<span class="karten-label">Einfach erklärt</span> <span class="karten-label" data-t="labelEinfach">Einfach erklärt</span>
<p id="ergErklaerung" dir="auto" style="white-space: pre-line;"></p> <p id="ergErklaerung" dir="auto" style="white-space: pre-line;"></p>
</div> </div>
<div class="brief-karte" id="ergChecklisteKarte" style="display: none;"> <div class="brief-karte" id="ergChecklisteKarte" style="display: none;">
<span class="karten-label neutral">Das müssen Sie tun</span> <span class="karten-label neutral" data-t="labelTun">Das müssen Sie tun</span>
<div id="ergCheckliste"></div> <div id="ergCheckliste"></div>
</div> </div>
<div class="brief-karte" id="ergAnrufKarte" style="display: none;"> <div class="brief-karte" id="ergAnrufKarte" style="display: none;">
<span class="karten-label neutral">Anrufen</span> <span class="karten-label neutral" data-t="labelAnrufen">Anrufen</span>
<div id="ergAnrufKontakt" style="margin-top: 6px;"></div> <div id="ergAnrufKontakt" style="margin-top: 6px;"></div>
<div id="ergAnrufSaetze"></div> <div id="ergAnrufSaetze"></div>
</div> </div>
<div class="brief-karte" id="ergBegriffeKarte" style="display: none;"> <div class="brief-karte" id="ergBegriffeKarte" style="display: none;">
<span class="karten-label neutral">Schwere Wörter erklärt</span> <span class="karten-label neutral" data-t="labelWoerter">Schwere Wörter erklärt</span>
<div id="ergBegriffe"></div> <div id="ergBegriffe"></div>
</div> </div>
@@ -484,9 +497,9 @@
</div> </div>
</div> </div>
<p class="hinweis-recht"> <p class="hinweis-recht" data-t="rechtHinweis">
⚖️ BehördenKlar erklärt Briefe, ersetzt aber keine Rechtsberatung. Bei ⚖️ BehördenKlar erklärt Briefe, ersetzt aber keine Rechtsberatung. Bei
wichtigen Entscheidungen hilft eine Beratungsstelle oder ein Anwalt. wichtigen Entscheidungen hilft eine Beratungsstelle oder eine Anwältin bzw. ein Anwalt.
</p> </p>
</section> </section>
@@ -536,6 +549,7 @@
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<script src="qrcode.js"></script> <script src="qrcode.js"></script>
<script src="uebergabe.js"></script> <script src="uebergabe.js"></script>
<script src="ui-texte.js"></script>
<script> <script>
(function () { (function () {
'use strict'; 'use strict';
@@ -639,6 +653,42 @@
if (name) speicher.set(EINRICHTUNG_SCHLUESSEL, name); if (name) speicher.set(EINRICHTUNG_SCHLUESSEL, name);
else { speicher.remove(EINRICHTUNG_SCHLUESSEL); speicher.remove(ABLAUF_SCHLUESSEL); speicher.remove(CODE_SCHLUESSEL); } else { speicher.remove(EINRICHTUNG_SCHLUESSEL); speicher.remove(ABLAUF_SCHLUESSEL); speicher.remove(CODE_SCHLUESSEL); }
} }
// ── Bediensprache ─────────────────────────────────────────────────
// Am PC (Beratung) bleibt alles deutsch. Am Handy richtet sich die
// Bedienung nach der gewählten Erklärungssprache (Texte: ui-texte.js).
var UI = window.BK_UI_TEXTE || { de: {} };
var RTL = { ar: 1, fa: 1, ur: 1, ps: 1 };
var VERBUNDEN = { ar: 1, fa: 1, ur: 1, ps: 1, hi: 1, ti: 1, zh: 1 };
var uiSprache = 'de';
function t(schluessel, werte) {
var text = (UI[uiSprache] && UI[uiSprache][schluessel]) || (UI.de && UI.de[schluessel]) || schluessel;
if (werte) for (var k in werte) text = text.split('{' + k + '}').join(werte[k]);
return text;
}
function wendeUiSpracheAn() {
var amPc = window.matchMedia && window.matchMedia('(hover: hover) and (pointer: fine)').matches;
var gewuenscht = amPc ? 'de' : zielsprache();
uiSprache = UI[gewuenscht] ? gewuenscht : 'de';
document.documentElement.lang = uiSprache;
document.documentElement.dir = RTL[uiSprache] ? 'rtl' : 'ltr';
document.documentElement.classList.toggle('verbundene-schrift', !!VERBUNDEN[uiSprache]);
var elemente = document.querySelectorAll('[data-t]');
for (var i = 0; i < elemente.length; i++) {
var el = elemente[i], key = el.getAttribute('data-t');
if (UI.de && UI.de[key]) el.textContent = t(key);
}
// Was gerade dynamisch angezeigt wird, in der neuen Sprache neu setzen
var stelle = (typeof handyModus !== 'undefined' && handyModus && handyModus.e) || (einrichtung() && hatGuthaben() ? einrichtung() : '');
if (stelle) zeigeEinrichtungImKopf(stelle);
if (typeof handyModus !== 'undefined' && handyModus && !handyModus.fertig) verbundenBanner.textContent = t('verbunden');
}
/** Meldung des Servers: am Handy in der Bediensprache, sonst die genaue deutsche. */
function serverMeldung(daten) {
var zuordnung = { turnstile: 'srvTurnstile', ki: 'srvKi', ki_konfig: 'srvDienst', dienst: 'srvDienst', datei: 'srvDatei', inhalt: 'srvInhalt', aufgebraucht: 'srvAufgebraucht' };
if (uiSprache !== 'de' && zuordnung[daten.code]) return t(zuordnung[daten.code]);
return daten.meldung || t('srvKi');
}
// Handy ↔ PC: Ist dieses Gerät gerade das Handy, das für einen PC // Handy ↔ PC: Ist dieses Gerät gerade das Handy, das für einen PC
// fotografiert? Ein Guthaben aus dem QR-Code (w) gilt nur für diese // fotografiert? Ein Guthaben aus dem QR-Code (w) gilt nur für diese
// Sitzung und wird hier NICHT gespeichert. // Sitzung und wird hier NICHT gespeichert.
@@ -665,8 +715,11 @@
} }
function zeigeGuthabenBanner(n) { function zeigeGuthabenBanner(n) {
var bis = einrichtungBis(); var bis = einrichtungBis();
document.getElementById('guthabenHandyKnopf').textContent = einrichtung()
? '🔑 Zugang dauerhaft auf ein Handy geben (nur für Mitarbeitende)'
: '📱 Auch auf dem Handy nutzen';
guthabenText.textContent = einrichtung() guthabenText.textContent = einrichtung()
? '🤝 Kostenlos über ' + einrichtung() + (bis ? ' (bis ' + deDatum(bis) + ')' : '') + ':' ? t('bannerKostenlos', { name: einrichtung() + (bis ? ' ' + t('bannerBis', { datum: deDatum(bis) }) : '') })
: '💳 Ihr Guthaben:'; : '💳 Ihr Guthaben:';
guthabenZahl.textContent = n; guthabenZahl.textContent = n;
guthabenBanner.style.display = 'block'; guthabenBanner.style.display = 'block';
@@ -678,8 +731,19 @@
function benutzt() { return parseInt(speicher.get(SPEICHER_SCHLUESSEL) || '0', 10); } function benutzt() { return parseInt(speicher.get(SPEICHER_SCHLUESSEL) || '0', 10); }
function setzeBenutzt(n) { speicher.set(SPEICHER_SCHLUESSEL, String(n)); } function setzeBenutzt(n) { speicher.set(SPEICHER_SCHLUESSEL, String(n)); }
var LEER_HINWEIS = 'Die kostenlosen Analysen sind aufgebraucht oder abgelaufen. Ratsuchende: Bitte fragen Sie bei Ihrer Beratungsstelle nach. Beratungsstelle: Ein Anruf genügt, Justin Klein, 0170 4969375. Nach dem Aufladen einfach diese Seite neu laden oder den Code unten noch einmal eingeben.'; /** Text mit {name}: der Name kommt in ein <bdi>, damit „(Test)." in arabischem Text richtig steht. */
var KEIN_ZUGANG_HANDY = 'Auf diesem Handy sind die kostenlosen Analysen verbraucht. Bitte am Computer den Code der Beratungsstelle einlösen und dann einen neuen QR-Code erzeugen.'; function mitName(el, vorlage, name) {
var teile = vorlage.split('{name}');
el.textContent = teile[0];
var b = document.createElement('bdi'); b.textContent = name; el.appendChild(b);
if (teile[1]) el.appendChild(document.createTextNode(teile[1]));
}
/** Einrichtungs-Zugang: oben nicht mehr „2 Analysen gratis" anpreisen. */
function zeigeEinrichtungImKopf(name) {
mitName(document.querySelector('#demo .formular-kopf'), t('formKopfKostenlos'), name);
mitName(document.getElementById('anredeAngebot'), t('anredeKostenlos'), name);
document.getElementById('kopfAz').textContent = 'Formular D-01 · ' + name;
}
function zeigeStand() { function zeigeStand() {
if (handyModus && handyModus.fertig) return; if (handyModus && handyModus.fertig) return;
// Handy-Modus: nie Kaufangebot oder Code-Feld (die Beratung ist dabei) // Handy-Modus: nie Kaufangebot oder Code-Feld (die Beratung ist dabei)
@@ -690,7 +754,7 @@
if (benutzt() >= 2) { if (benutzt() >= 2) {
eingabe.style.display = 'none'; eingabe.style.display = 'none';
fehlerBox.style.display = 'block'; fehlerBox.style.display = 'block';
fehlerBox.textContent = KEIN_ZUGANG_HANDY; fehlerBox.textContent = t('keinZugangHandy');
meldeAnPc({ schritt: 'fehler', code: 'kein_zugang', meldung: 'Am Handy sind die kostenlosen Analysen verbraucht.' }); meldeAnPc({ schritt: 'fehler', code: 'kein_zugang', meldung: 'Am Handy sind die kostenlosen Analysen verbraucht.' });
} else { } else {
eingabe.style.display = 'block'; eingabe.style.display = 'block';
@@ -706,12 +770,10 @@
kaufBlock.style.display = 'none'; kaufBlock.style.display = 'none';
codeTeil.style.display = 'block'; codeTeil.style.display = 'block';
fehlerBox.style.display = 'block'; fehlerBox.style.display = 'block';
fehlerBox.textContent = LEER_HINWEIS; fehlerBox.textContent = t('leerHinweis');
return; return;
} }
if (einrichtung() && hatGuthaben()) { if (einrichtung() && hatGuthaben()) zeigeEinrichtungImKopf(einrichtung());
document.querySelector('#demo .formular-kopf').textContent = 'Antrag auf Klartext · kostenlos über ' + einrichtung();
}
if (handyModus && handyModus.w) { if (handyModus && handyModus.w) {
eingabe.style.display = 'block'; eingabe.style.display = 'block';
guthabenBanner.style.display = 'none'; guthabenBanner.style.display = 'none';
@@ -752,7 +814,7 @@
startKnopf.addEventListener('click', function () { startKnopf.addEventListener('click', function () {
fehlerBox.style.display = 'none'; fehlerBox.style.display = 'none';
if (!konsens.checked) { if (!konsens.checked) {
zeigeFehler('Bitte zuerst oben das Häkchen bei der Einwilligung setzen.'); zeigeFehler(t('fehlerKonsens'));
fehlerBox.dataset.art = 'konsens'; fehlerBox.dataset.art = 'konsens';
konsens.closest('label').scrollIntoView({ behavior: 'smooth', block: 'center' }); konsens.closest('label').scrollIntoView({ behavior: 'smooth', block: 'center' });
konsens.focus(); konsens.focus();
@@ -781,16 +843,14 @@
function zeigeFertigBanner(angekommen, ohneUebersetzung, paket) { function zeigeFertigBanner(angekommen, ohneUebersetzung, paket) {
verbundenBanner.innerHTML = ''; verbundenBanner.innerHTML = '';
verbundenBanner.appendChild(document.createTextNode(angekommen verbundenBanner.appendChild(document.createTextNode(angekommen ? t('fertig') : t('nichtAngekommen')));
? '✅ Fertig. Die Erklärung steht hier und auf dem Computer. Für einen weiteren Brief tippt die Beratung am Computer noch einmal auf „Lieber mit dem Handy fotografieren".' if (ohneUebersetzung) verbundenBanner.appendChild(document.createTextNode(' ' + t('ohneUebersetzung')));
: '⚠️ Die Erklärung steht hier, ist aber am Computer nicht angekommen.'));
if (ohneUebersetzung) verbundenBanner.appendChild(document.createTextNode(' Die Übersetzung hat diesmal nicht geklappt, deshalb steht die Erklärung auf Deutsch.'));
if (!angekommen) { if (!angekommen) {
var knopf = document.createElement('button'); var knopf = document.createElement('button');
knopf.type = 'button'; knopf.className = 'cta cta-zwei'; knopf.type = 'button'; knopf.className = 'cta cta-zwei';
knopf.textContent = '🔁 Noch einmal an den Computer senden'; knopf.textContent = t('nochmalSenden');
knopf.addEventListener('click', async function () { knopf.addEventListener('click', async function () {
knopf.disabled = true; knopf.textContent = 'Wird gesendet …'; knopf.disabled = true; knopf.textContent = t('wirdGesendet');
var ok = await ergebnisAnPc(paket); var ok = await ergebnisAnPc(paket);
zeigeFertigBanner(ok, ohneUebersetzung, paket); zeigeFertigBanner(ok, ohneUebersetzung, paket);
}); });
@@ -840,17 +900,17 @@
async function starteAnalyse(datei) { async function starteAnalyse(datei) {
fehlerBox.style.display = 'none'; fehlerBox.style.display = 'none';
if (datei.size > 5 * 1024 * 1024 && datei.type === 'application/pdf') { if (datei.size > 5 * 1024 * 1024 && datei.type === 'application/pdf') {
zeigeFehler('Die PDF-Datei ist zu groß (max. 5 MB). Tipp: Fotografieren Sie den Brief stattdessen.'); zeigeFehler(t('fehlerPdfGross'));
return; return;
} }
var token = turnstileToken(); var token = turnstileToken();
if (!token) { if (!token) {
zeigeFehler('Die Sicherheitsprüfung lädt noch — bitte warten Sie 2 Sekunden und versuchen Sie es erneut.'); zeigeFehler(t('fehlerTurnstileLaedt'));
return; return;
} }
eingabe.style.display = 'none'; eingabe.style.display = 'none';
status.querySelector('.gross').textContent = 'Ihr Brief wird analysiert…'; status.querySelector('.gross').textContent = t('statusLaeuft');
status.style.display = 'block'; status.style.display = 'block';
try { try {
@@ -884,7 +944,7 @@
meldeAnPc({ schritt: 'fehler', code: daten.code || '', meldung: daten.meldung || '' }); meldeAnPc({ schritt: 'fehler', code: daten.code || '', meldung: daten.meldung || '' });
// Guthaben vom PC: Fehler nur anzeigen, am Handy nichts umstellen // Guthaben vom PC: Fehler nur anzeigen, am Handy nichts umstellen
if (handyModus && handyModus.w) { if (handyModus && handyModus.w) {
zeigeFehler(daten.meldung || 'Etwas ist schiefgelaufen. Bitte erneut versuchen.'); zeigeFehler(serverMeldung(daten));
return; return;
} }
// Handy ohne Zugang vom PC: Kontingent-Fehler nicht als Kaufangebot zeigen // Handy ohne Zugang vom PC: Kontingent-Fehler nicht als Kaufangebot zeigen
@@ -892,7 +952,7 @@
if (daten.code === 'aufgebraucht') setzeBenutzt(2); if (daten.code === 'aufgebraucht') setzeBenutzt(2);
eingabe.style.display = 'none'; eingabe.style.display = 'none';
fehlerBox.style.display = 'block'; fehlerBox.style.display = 'block';
fehlerBox.textContent = KEIN_ZUGANG_HANDY; fehlerBox.textContent = t('keinZugangHandy');
meldeAnPc({ schritt: 'fehler', code: 'kein_zugang', meldung: 'Am Handy sind die kostenlosen Analysen verbraucht.' }); meldeAnPc({ schritt: 'fehler', code: 'kein_zugang', meldung: 'Am Handy sind die kostenlosen Analysen verbraucht.' });
return; return;
} }
@@ -922,7 +982,7 @@
kaufBlock.scrollIntoView({ behavior: 'smooth' }); kaufBlock.scrollIntoView({ behavior: 'smooth' });
return; return;
} }
zeigeFehler(daten.meldung || 'Etwas ist schiefgelaufen. Bitte erneut versuchen.'); zeigeFehler(serverMeldung(daten));
return; return;
} }
@@ -972,7 +1032,7 @@
eingabe.style.display = 'block'; eingabe.style.display = 'block';
if (window.turnstile) window.turnstile.reset(); if (window.turnstile) window.turnstile.reset();
meldeAnPc({ schritt: 'fehler', code: 'verbindung', meldung: 'Verbindung fehlgeschlagen.' }); meldeAnPc({ schritt: 'fehler', code: 'verbindung', meldung: 'Verbindung fehlgeschlagen.' });
zeigeFehler('Verbindung fehlgeschlagen. Bitte prüfen Sie Ihr Internet und versuchen Sie es erneut.'); zeigeFehler(t('fehlerVerbindung'));
} }
} }
@@ -1001,11 +1061,11 @@
var farbe = tage <= 7 ? 'var(--rot)' : tage <= 21 ? 'var(--gelb)' : 'var(--gruen)'; var farbe = tage <= 7 ? 'var(--rot)' : tage <= 21 ? 'var(--gelb)' : 'var(--gruen)';
var punkt = tage <= 7 ? '🔴' : tage <= 21 ? '🟡' : '🟢'; var punkt = tage <= 7 ? '🔴' : tage <= 21 ? '🟡' : '🟢';
var datum = deDatum(erstes.iso); var datum = deDatum(erstes.iso);
var wann = erstes.art === 'Termin' ? 'Termin am ' + datum : 'Frist bis ' + datum; var wann = erstes.art === 'Termin' ? t('ampelTerminAm', { datum: datum }) : t('ampelFristBis', { datum: datum });
var text = tage < 0 ? (erstes.art === 'Termin' ? 'Der Termin war am ' + datum + '!' : 'Die Frist ist womöglich schon vorbei (' + datum + ')!') : var text = tage < 0 ? (erstes.art === 'Termin' ? t('ampelTerminWar', { datum: datum }) : t('ampelFristVorbei', { datum: datum })) :
tage === 0 ? 'HEUTE: ' + wann + '!' : tage === 0 ? t('ampelHeute') + ': ' + wann + '!' :
tage === 1 ? 'Morgen: ' + wann : tage === 1 ? t('ampelMorgen') + ': ' + wann :
'Noch ' + tage + ' Tage · ' + wann; t('ampelNoch', { n: tage }) + ' · ' + wann;
return { farbe: farbe, text: punkt + ' ' + text, istFrist: erstes.art === 'Frist' }; return { farbe: farbe, text: punkt + ' ' + text, istFrist: erstes.art === 'Frist' };
} }
@@ -1240,9 +1300,9 @@
// oder bedingte Fristen ("bis zum 10. Tag nach jedem Vierteljahr"), // oder bedingte Fristen ("bis zum 10. Tag nach jedem Vierteljahr"),
// aus denen sich kein einzelnes Datum machen laesst. Ein Haekchen // aus denen sich kein einzelnes Datum machen laesst. Ein Haekchen
// wuerde dann "alles erledigt" signalisieren — gefaehrlich falsch. // wuerde dann "alles erledigt" signalisieren — gefaehrlich falsch.
label.textContent = 'Kalender'; label.textContent = t('labelKalender');
zeile('ℹ️ Kein festes Datum zum Eintragen gefunden.'); zeile(t('keinDatum1'));
zeile('Wichtig: Manche Fristen haben kein festes Datum, zum Beispiel „jedes Vierteljahr" oder nur unter bestimmten Bedingungen. Lesen Sie dazu die Erklärung oben.'); zeile(t('keinDatum2'));
knopf.style.display = 'none'; knopf.style.display = 'none';
hinweis.style.display = 'none'; hinweis.style.display = 'none';
karte.style.border = ''; karte.style.border = '';
@@ -1250,12 +1310,12 @@
return; return;
} }
letzteAnalyse = a; letzteAnalyse = a;
label.textContent = 'Nicht vergessen'; label.textContent = t('labelNichtVergessen');
knopf.style.display = ''; knopf.style.display = '';
hinweis.style.display = ''; hinweis.style.display = '';
karte.style.border = '2px solid var(--amtsblau)'; karte.style.border = '2px solid var(--amtsblau)';
if (frist) zeile('⚠️ Frist: ' + deDatum(frist.datum), frist.aktion); if (frist) zeile(t('fristZeile', { datum: deDatum(frist.datum) }), frist.aktion);
if (termin) zeile('📅 Termin: ' + deDatum(termin.datum) + (termin.uhrzeit ? ', ' + termin.uhrzeit + ' Uhr' : ''), termin.ort); if (termin) zeile(t('terminZeile', { datum: deDatum(termin.datum) }) + (termin.uhrzeit ? ', ' + t('uhrzeit', { zeit: termin.uhrzeit }) : ''), termin.ort);
karte.style.display = 'block'; karte.style.display = 'block';
} }
@@ -1293,22 +1353,23 @@
if (hervorheben) w.className = 'az-wert'; if (hervorheben) w.className = 'az-wert';
p.appendChild(b); p.appendChild(w); box.appendChild(p); p.appendChild(b); p.appendChild(w); box.appendChild(p);
} }
angabe('Ansprechpartner', k.ansprechpartner); angabe(t('ansprechpartner'), k.ansprechpartner);
angabe('Erreichbar', k.sprechzeiten); angabe(t('erreichbar'), k.sprechzeiten);
angabe('Danach werden Sie gefragt', k.aktenzeichen, true); angabe(t('gefragt'), k.aktenzeichen, true);
var liste = document.getElementById('ergAnrufSaetze'); var liste = document.getElementById('ergAnrufSaetze');
liste.innerHTML = ''; liste.innerHTML = '';
if (saetze.length) { if (saetze.length) {
var kopf = document.createElement('p'); var kopf = document.createElement('p');
kopf.style.marginTop = '12px'; kopf.style.marginTop = '12px';
kopf.innerHTML = '<strong>Das können Sie am Telefon sagen:</strong>'; var stark = document.createElement('strong'); stark.textContent = t('amTelefon'); kopf.appendChild(stark);
liste.appendChild(kopf); liste.appendChild(kopf);
var angezeigt = versionen.length ? versionen[aktiveVersion].code : zielsprache(); var angezeigt = versionen.length ? versionen[aktiveVersion].code : zielsprache();
var mitBedeutung = angezeigt !== 'de'; var mitBedeutung = angezeigt !== 'de';
saetze.forEach(function (s) { saetze.forEach(function (s) {
var d = document.createElement('div'); d.className = 'anruf-satz'; var d = document.createElement('div'); d.className = 'anruf-satz';
var de = document.createElement('p'); de.className = 'anruf-de'; var de = document.createElement('p'); de.className = 'anruf-de';
de.setAttribute('dir', 'ltr'); // wird auf Deutsch gesagt — auch auf arabischer Seite links-nach-rechts
de.textContent = '„' + s.deutsch + '“'; de.textContent = '„' + s.deutsch + '“';
d.appendChild(de); d.appendChild(de);
if (mitBedeutung && s.bedeutung && s.bedeutung !== s.deutsch) { if (mitBedeutung && s.bedeutung && s.bedeutung !== s.deutsch) {
@@ -1442,7 +1503,7 @@
zeichneHandyQr(); zeichneHandyQr();
zeigeStand(); zeigeStand();
if (res.d.guthaben <= 0) return; // zeigeStand zeigt den Leer-Hinweis mit Telefonnummer if (res.d.guthaben <= 0) return; // zeigeStand zeigt den Leer-Hinweis mit Telefonnummer
kaufOk.textContent = '✅ Code eingelöst. BehördenKlar ist für Sie kostenlos, über ' + res.d.einrichtung + '.'; kaufOk.textContent = t('codeEingeloest', { name: res.d.einrichtung });
kaufOk.style.display = 'block'; kaufOk.style.display = 'block';
}) })
.catch(function () { .catch(function () {
@@ -1625,7 +1686,7 @@
if (h.a === 'g') { uebernehmeGuthaben(h); return; } if (h.a === 'g') { uebernehmeGuthaben(h); return; }
var ue = await BKUebergabe.oeffne(h.s, h.k).catch(function () { return null; }); var ue = await BKUebergabe.oeffne(h.s, h.k).catch(function () { return null; });
if (!ue) { if (!ue) {
zeigeFehler('Dieser QR-Code ist ungültig. Bitte am Computer einen neuen erzeugen.'); zeigeFehler(t('qrUngueltig'));
return; return;
} }
@@ -1636,10 +1697,11 @@
sitzungsSprache = h.l; sitzungsSprache = h.l;
document.getElementById('sprachWahl').value = h.l; document.getElementById('sprachWahl').value = h.l;
} }
verbundenBanner.textContent = '🔗 Mit dem Computer verbunden. Setzen Sie das Häkchen und fotografieren Sie Ihren Brief. Die Erklärung erscheint hier und auf dem Bildschirm.'; wendeUiSpracheAn();
verbundenBanner.textContent = t('verbunden');
verbundenBanner.style.display = 'block'; verbundenBanner.style.display = 'block';
handyKnopf.style.display = 'none'; handyKnopf.style.display = 'none';
if (handyModus.e) document.querySelector('#demo .formular-kopf').textContent = 'Antrag auf Klartext · kostenlos über ' + handyModus.e; if (handyModus.e) zeigeEinrichtungImKopf(handyModus.e);
// „verbunden" zuerst melden — zeigeStand meldet danach ggf. „kein Zugang", // „verbunden" zuerst melden — zeigeStand meldet danach ggf. „kein Zugang",
// und das darf nicht von „verbunden" überschrieben werden // und das darf nicht von „verbunden" überschrieben werden
await meldeAnPc({ schritt: 'verbunden' }); await meldeAnPc({ schritt: 'verbunden' });
@@ -1653,7 +1715,7 @@
leseModus = true; leseModus = true;
eingabe.style.display = 'none'; eingabe.style.display = 'none';
kaufBlock.style.display = 'none'; kaufBlock.style.display = 'none';
status.querySelector('.gross').textContent = 'Das Ergebnis wird geladen …'; status.querySelector('.gross').textContent = t('statusLaden');
status.style.display = 'block'; status.style.display = 'block';
var f = null; var f = null;
try { f = await BKUebergabe.holen(ue); } catch (e) {} try { f = await BKUebergabe.holen(ue); } catch (e) {}
@@ -1661,13 +1723,13 @@
if (!f || !f.ergebnis) { if (!f || !f.ergebnis) {
leseModus = false; leseModus = false;
zeigeStand(); zeigeStand();
zeigeFehler('Dieser QR-Code ist abgelaufen oder wurde schon benutzt (er gilt ' + BKUebergabe.LEBENSDAUER_MIN + ' Minuten und nur einmal). Bitte am Computer einen neuen erzeugen.'); zeigeFehler(t('qrAbgelaufen', { min: BKUebergabe.LEBENSDAUER_MIN }));
return; return;
} }
BKUebergabe.loeschen(ue); BKUebergabe.loeschen(ue);
codeTeil.style.display = 'none'; codeTeil.style.display = 'none';
kaufBlock.style.display = 'none'; kaufBlock.style.display = 'none';
verbundenBanner.textContent = '📱 Vom Computer übernommen. Tragen Sie die Frist gleich in Ihren Kalender ein, dann vergessen Sie nichts.'; verbundenBanner.textContent = t('mitgenommen');
verbundenBanner.style.display = 'block'; verbundenBanner.style.display = 'block';
zeigeAnalyse(bereiteVersionen(f.ergebnis, false)); zeigeAnalyse(bereiteVersionen(f.ergebnis, false));
document.getElementById('ergebnis').scrollIntoView({ behavior: 'smooth' }); document.getElementById('ergebnis').scrollIntoView({ behavior: 'smooth' });
@@ -1683,7 +1745,7 @@
setzeWallet(h.w); setzeWallet(h.w);
setzeGuthaben(parseInt(h.n, 10) || 1); setzeGuthaben(parseInt(h.n, 10) || 1);
setzeEinrichtung(h.e || null); setzeEinrichtung(h.e || null);
kaufOk.textContent = h.e ? '✅ Übernommen. BehördenKlar ist für Sie kostenlos, über ' + h.e + '.' : '✅ Guthaben übernommen.'; kaufOk.textContent = h.e ? t('uebernommen', { name: h.e }) : '✅ Guthaben übernommen.';
kaufOk.style.display = 'block'; kaufOk.style.display = 'block';
zeigeStand(); zeigeStand();
} }
@@ -1731,6 +1793,8 @@
feld.addEventListener('change', function () { feld.addEventListener('change', function () {
speicher.set(SPRACH_SCHLUESSEL, feld.value); speicher.set(SPRACH_SCHLUESSEL, feld.value);
sitzungsSprache = null; sitzungsSprache = null;
wendeUiSpracheAn();
zeigeStand();
if (/[?&]sprache=/.test(window.location.search) && window.history.replaceState) { if (/[?&]sprache=/.test(window.location.search) && window.history.replaceState) {
var u = new URL(window.location.href); var u = new URL(window.location.href);
u.searchParams.delete('sprache'); u.searchParams.delete('sprache');
@@ -1741,6 +1805,7 @@
})(); })();
rendrePakete(); rendrePakete();
wendeUiSpracheAn();
var startHash = BKUebergabe.ausAdresse(); var startHash = BKUebergabe.ausAdresse();
var startSuche = window.location.search; // vor pruefeRueckkehr, das die Adresse aufräumt var startSuche = window.location.search; // vor pruefeRueckkehr, das die Adresse aufräumt
// Handy-Verbindung übersteht ein Neuladen (z. B. wenn Android den Tab beim // Handy-Verbindung übersteht ein Neuladen (z. B. wenn Android den Tab beim
+2138
View File
File diff suppressed because it is too large. Load diff