// Prüft die Sprechtexte einer Folge (src/folgeNN/texte/.json): // Fehlt in einer Sprache eine Zeile, die eine Szene braucht, oder steht dort // eine, die keine Szene mehr benutzt? // // npx tsx werkzeuge/texte.mts F02 prüfen // npx tsx werkzeuge/texte.mts F02 --tabelle alle Sprachen nebeneinander (Markdown) // // Kostet nichts und ändert nichts. import {spawnSync} from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import {fileURLToPath} from 'node:url'; const WURZEL = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); process.chdir(WURZEL); const args = process.argv.slice(2); const kuerzel = args.find((a) => !a.startsWith('--')); if (!kuerzel) throw new Error('Folge angeben, z. B. F02'); const nummer = kuerzel.replace(/\D/g, ''); const SPRACHEN = ['de', 'es', 'fr']; type Texte = {titel: string; lied?: string; zeilen: Record}; const texte: Record = Object.fromEntries( SPRACHEN.map((s) => [s, JSON.parse(fs.readFileSync(`src/folge${nummer}/texte/${s}.json`, 'utf8')) as Texte]), ); // Welche Zeilen sprechen die Szenen? (Zeilen, die der Baukasten selbst // mitbringt, z. B. das Intro, stehen nicht in den Textdateien.) const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx'; const ausgabe = spawnSync(npx, ['tsx', 'werkzeuge/szenenliste.mts', `./src/folge${nummer}/Folge${nummer}.tsx`, kuerzel], { encoding: 'utf8', shell: process.platform === 'win32', env: {...process.env, SPRACHE: 'de'}, }); const szenen = JSON.parse(ausgabe.stdout.split('\n').filter((z) => z.startsWith('[')).pop()!) as {szene?: string; zeilen: {id: string; wer?: string; text: string}[]}[]; const gebraucht: {schl: string; wer?: string}[] = []; for (const s of szenen) { if (!s.szene) continue; for (const z of s.zeilen) { const schl = `${s.szene}/${z.id}`; if (schl in texte.de.zeilen || z.text.startsWith('[fehlt:')) gebraucht.push({schl, wer: z.wer}); } } if (args.includes('--tabelle')) { const zelle = (t?: string) => (t ?? '**fehlt**').replaceAll('|', '\\|'); console.log(`| Zeile | Wer | ${SPRACHEN.join(' | ')} |`); console.log(`| --- | --- | ${SPRACHEN.map(() => '---').join(' | ')} |`); console.log(`| Titel | | ${SPRACHEN.map((s) => zelle(texte[s].titel)).join(' | ')} |`); for (const g of gebraucht) console.log(`| ${g.schl} | ${g.wer ?? ''} | ${SPRACHEN.map((s) => zelle(texte[s].zeilen[g.schl])).join(' | ')} |`); process.exit(0); } let fehler = 0; const schluessel = new Set(gebraucht.map((g) => g.schl)); for (const s of SPRACHEN) { const fehlt = gebraucht.filter((g) => !(g.schl in texte[s].zeilen)).map((g) => g.schl); const uebrig = Object.keys(texte[s].zeilen).filter((k) => !schluessel.has(k)); const leer = Object.entries(texte[s].zeilen).filter(([, t]) => !t.trim()).map(([k]) => k); if (!texte[s].titel) fehlt.unshift('titel'); for (const k of fehlt) console.log(`✗ ${s}: fehlt ${k}`); for (const k of leer) console.log(`✗ ${s}: leer ${k}`); for (const k of uebrig) console.log(`? ${s}: nicht benutzt ${k}`); fehler += fehlt.length + leer.length; if (!fehlt.length && !leer.length && !uebrig.length) console.log(`✓ ${s}: ${Object.keys(texte[s].zeilen).length} Zeilen`); } process.exit(fehler ? 1 : 0);