Kundenportal: Oberflaeche (Masterplan S.12)

Vier Ansichten in einer Seite: Anmeldung, Passwort festlegen ueber den
Einladungslink, Uebersicht, Projektdetail. 71 Textbausteine in fuenf
Sprachen.

Korrektur an einer frueheren Entscheidung: Ich hatte das Portal als "nur
Deutsch" eingestuft wie die Verwaltung. Denkfehler -- die Verwaltung
nutzen Dogfather und VanVan, das PORTAL nutzen Kunden, und die koennen
Franzosen oder Portugiesen sein. Jetzt fuenfsprachig, und die Sprache des
Kunden wird bei der Anmeldung automatisch uebernommen.

Gestaltungsentscheidungen:
* Eine Phasenleiste zeigt auf einen Blick, wo das Projekt steht. Das ist
  die haeufigste Frage ueberhaupt und der Grund, warum Kunden anrufen.
  Bei pausierten oder abgebrochenen Projekten wird KEINE Leiste gezeigt --
  ein Fortschrittsbalken waere dort irrefuehrend.
* Eine Marke sagt, wer gerade am Zug ist ("Wir warten auf dich" /
  "Dogfather ist dran"). Das beendet die haeufigste Unklarheit im
  Projektverlauf.
* Offene Zahlungen stehen ganz oben und in Gold -- das Einzige, was den
  Kunden wirklich zum Handeln auffordert.
* Rueckfrage nur beim ANNEHMEN eines Zusatzangebots, nicht beim Ablehnen.
  Annehmen erzeugt eine Zahlungspflicht, Ablehnen ist folgenlos.
* Abgelaufene Sitzung fuehrt zur Anmeldung mit klarer Ansage statt zu
  einer leeren Seite, die wie "du hast keine Projekte" aussaehe.
* Der Einladungslink wird nach Gebrauch aus der Adresszeile entfernt --
  er ist verbraucht und hat in der Chronik nichts verloren.
* Die Zahlungsknoepfe sagen ehrlich, dass PayPal noch eingerichtet wird,
  statt so zu tun als wuerde etwas passieren.

Pruefskript zweimal geschaerft, beide Male waren es Fehlalarme:
* Die Heuristik fuer Nachschlagetabellen hielt gewoehnliche Woerter wie
  "abnahme" und "uebergeben" fuer Woerterbuch-Schluessel. Jetzt muss ein
  Unterstrich im Namen sein, so wie bei allen echten Schluesseln.
* Eine Ueberschrift gilt jetzt auch als uebersetzt, wenn die Uebersetzung
  INNEN sitzt (fester Teil + dynamischer Teil).

i18n vollstaendig, 45/45 Handy-Abnahme.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-08-22 21:17:22 +02:00
co-authored by Claude Opus 5
parent 04a1af1253
commit 1b432f456f
4 changed files with 804 additions and 3 deletions
+18 -3
View File
@@ -42,7 +42,13 @@ for (const datei of seiten) {
einen einzigen Nutzer, der sie braucht. Genau wie der bestehende
interne Bereich des Universe, der ebenfalls nur Deutsch spricht.
Die Tag-Balance wird trotzdem geprüft (siehe unten). */
const NUR_DEUTSCH = ["verwaltung", "portal"];
/* NUR die Verwaltung ist einsprachig. Das Kundenportal stand hier
zunaechst auch drin -- das war ein Denkfehler: die Verwaltung nutzen
Dogfather und VanVan, das Portal nutzen KUNDEN. Und Kunden koennen
Franzosen oder Portugiesen sein. Ein Portal, das die Sprachwahl der
oeffentlichen Seite ignoriert, waere genau der Bruch, den ein Kunde
sofort bemerkt. */
const NUR_DEUTSCH = ["verwaltung"];
const einsprachig = NUR_DEUTSCH.includes(name);
if (!existsSync(i18nDatei) && !einsprachig) {
@@ -87,8 +93,12 @@ for (const datei of seiten) {
if (existsSync(seitenSkript)) quellen.push(readFileSync(seitenSkript, "utf8"));
for (const quelle of quellen) {
for (const m of quelle.matchAll(/WD\.t\("([a-z0-9_]+)"\)/g)) benutzt.add(m[1]);
// Schlüssel aus Nachschlagetabellen, z.B. { ja: "af3_ja", ... }
for (const m of quelle.matchAll(/["']((?:af|pk|zg|pf|ue|fq|ab|ls|pr|s\d)[a-z0-9_]*)["']\s*[,}\]]/g)) {
/* Schlüssel aus Nachschlagetabellen, z.B. { ja: "af3_ja", ... }.
Der Unterstrich im Muster ist wichtig: ohne ihn hielt die Prüfung
auch gewöhnliche Wörter wie "abnahme" oder "uebergeben" (aus der
Phasenliste im Portal) für Wörterbuch-Schlüssel und meldete sie als
fehlend. Alle echten Schlüssel haben die Form praefix_name. */
for (const m of quelle.matchAll(/["']([a-z]{2}\d?_[a-z0-9_]+)["']\s*[,}\]]/g)) {
benutzt.add(m[1]);
}
}
@@ -108,6 +118,11 @@ for (const datei of seiten) {
const ohneI18n = [];
for (const m of html.matchAll(/<(h[1-4])\b([^>]*)>([\s\S]*?)<\/\1>/g)) {
if (m[2].includes("data-i18n")) continue;
/* Auch übersetzt, wenn die Übersetzung INNEN sitzt — etwa
<h1><span data-i18n="po_hallo">Hallo</span> <span id="name"></span></h1>,
wo ein Teil fest und ein Teil dynamisch ist. Ohne diese Ausnahme
meldet die Prüfung eine korrekt übersetzte Überschrift als Fehler. */
if (m[3].includes("data-i18n")) continue;
const text = m[3].replace(/<[^>]*>/g, "").trim();
if (!text) continue; // rein dekorativ
if (/^\$\{/.test(text)) continue; // dynamisch aus JS