youtube/remotion/werkzeuge/texte.mts
Lara 60acaa9921 Sprechtexte nach Sprachen getrennt (DE/ES/FR), Folge 2 in allen drei Sprachen renderbar
- Texte stehen nur noch in remotion/src/folge02/texte/<sprache>.json,
  die Szenen enthalten nur Ablauf und Bewegung
- Sprache beim Rendern wählbar (render.mjs --sprache es, Studio über
  --props); Zeitplan richtet sich nach Textlänge bzw. Aufnahmen der Sprache
- Baukasten-Wörter je Sprache in baukasten/sprache.ts: Kanalname im Buch
  (mit festem Umbruch für ES/FR), „Heute: …“, Sprechernamen, Kalendertitel,
  „Hatschi!“; Kalenderbild für ES/FR vorberechnet
- Stimmen je Sprache: stimmen/<sprache>.json, public/stimmen/folge02/<sprache>/,
  stimmen.mts --sprache, eigene Stimmen je Sprache möglich
- neues werkzeuge/texte.mts: prüft fehlende Übersetzungen, --tabelle zum Gegenlesen
- README, CLAUDE.md und Drehbuch-Hinweise angepasst

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 10:25:52 +01:00

69 lines
3.2 KiB
TypeScript

// Prüft die Sprechtexte einer Folge (src/folgeNN/texte/<sprache>.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<string, string>};
const texte: Record<string, Texte> = 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);