import fs from 'fs' import path from 'path' type Verse = { verseNum: number; text: string } type Chapter = { chapterNum: number; verses: Verse[] } type Book = { name: string; chapters: Chapter[] } // Mapping to determine testament + order, reused to split OT / NT const BOOK_MAPPINGS: Record = { // Old Testament 'Geneza': { testament: 'Old Testament', orderNum: 1 }, 'Exodul': { testament: 'Old Testament', orderNum: 2 }, 'Leviticul': { testament: 'Old Testament', orderNum: 3 }, 'Numerii': { testament: 'Old Testament', orderNum: 4 }, 'Numeri': { testament: 'Old Testament', orderNum: 4 }, 'Deuteronomul': { testament: 'Old Testament', orderNum: 5 }, 'Deuteronom': { testament: 'Old Testament', orderNum: 5 }, 'Iosua': { testament: 'Old Testament', orderNum: 6 }, 'Judecătorii': { testament: 'Old Testament', orderNum: 7 }, 'Judecători': { testament: 'Old Testament', orderNum: 7 }, 'Rut': { testament: 'Old Testament', orderNum: 8 }, '1 Samuel': { testament: 'Old Testament', orderNum: 9 }, '2 Samuel': { testament: 'Old Testament', orderNum: 10 }, '1 Împăraţi': { testament: 'Old Testament', orderNum: 11 }, '2 Împăraţi': { testament: 'Old Testament', orderNum: 12 }, '1 Imparati': { testament: 'Old Testament', orderNum: 11 }, '2 Imparati': { testament: 'Old Testament', orderNum: 12 }, '1 Cronici': { testament: 'Old Testament', orderNum: 13 }, '2 Cronici': { testament: 'Old Testament', orderNum: 14 }, 'Ezra': { testament: 'Old Testament', orderNum: 15 }, 'Neemia': { testament: 'Old Testament', orderNum: 16 }, 'Estera': { testament: 'Old Testament', orderNum: 17 }, 'Iov': { testament: 'Old Testament', orderNum: 18 }, 'Psalmii': { testament: 'Old Testament', orderNum: 19 }, 'Proverbele': { testament: 'Old Testament', orderNum: 20 }, 'Proverbe': { testament: 'Old Testament', orderNum: 20 }, 'Eclesiastul': { testament: 'Old Testament', orderNum: 21 }, 'Cântarea Cântărilor': { testament: 'Old Testament', orderNum: 22 }, 'Isaia': { testament: 'Old Testament', orderNum: 23 }, 'Ieremia': { testament: 'Old Testament', orderNum: 24 }, 'Plângerile': { testament: 'Old Testament', orderNum: 25 }, 'Ezechiel': { testament: 'Old Testament', orderNum: 26 }, 'Daniel': { testament: 'Old Testament', orderNum: 27 }, 'Osea': { testament: 'Old Testament', orderNum: 28 }, 'Ioel': { testament: 'Old Testament', orderNum: 29 }, 'Amos': { testament: 'Old Testament', orderNum: 30 }, 'Obadia': { testament: 'Old Testament', orderNum: 31 }, 'Iona': { testament: 'Old Testament', orderNum: 32 }, 'Mica': { testament: 'Old Testament', orderNum: 33 }, 'Naum': { testament: 'Old Testament', orderNum: 34 }, 'Habacuc': { testament: 'Old Testament', orderNum: 35 }, 'Ţefania': { testament: 'Old Testament', orderNum: 36 }, 'Țefania': { testament: 'Old Testament', orderNum: 36 }, 'Hagai': { testament: 'Old Testament', orderNum: 37 }, 'Zaharia': { testament: 'Old Testament', orderNum: 38 }, 'Maleahi': { testament: 'Old Testament', orderNum: 39 }, // New Testament 'Matei': { testament: 'New Testament', orderNum: 40 }, 'Marcu': { testament: 'New Testament', orderNum: 41 }, 'Luca': { testament: 'New Testament', orderNum: 42 }, 'Ioan': { testament: 'New Testament', orderNum: 43 }, 'Faptele Apostolilor': { testament: 'New Testament', orderNum: 44 }, 'Romani': { testament: 'New Testament', orderNum: 45 }, '1 Corinteni': { testament: 'New Testament', orderNum: 46 }, '2 Corinteni': { testament: 'New Testament', orderNum: 47 }, 'Galateni': { testament: 'New Testament', orderNum: 48 }, 'Efeseni': { testament: 'New Testament', orderNum: 49 }, 'Filipeni': { testament: 'New Testament', orderNum: 50 }, 'Coloseni': { testament: 'New Testament', orderNum: 51 }, '1 Tesaloniceni': { testament: 'New Testament', orderNum: 52 }, '2 Tesaloniceni': { testament: 'New Testament', orderNum: 53 }, '1 Timotei': { testament: 'New Testament', orderNum: 54 }, '2 Timotei': { testament: 'New Testament', orderNum: 55 }, 'Tit': { testament: 'New Testament', orderNum: 56 }, 'Titus': { testament: 'New Testament', orderNum: 56 }, 'Filimon': { testament: 'New Testament', orderNum: 57 }, 'Evrei': { testament: 'New Testament', orderNum: 58 }, 'Iacov': { testament: 'New Testament', orderNum: 59 }, '1 Petru': { testament: 'New Testament', orderNum: 60 }, '2 Petru': { testament: 'New Testament', orderNum: 61 }, '1 Ioan': { testament: 'New Testament', orderNum: 62 }, '2 Ioan': { testament: 'New Testament', orderNum: 63 }, '3 Ioan': { testament: 'New Testament', orderNum: 64 }, 'Iuda': { testament: 'New Testament', orderNum: 65 }, 'Apocalipsa': { testament: 'New Testament', orderNum: 66 }, 'Revelaţia': { testament: 'New Testament', orderNum: 66 }, 'Revelația': { testament: 'New Testament', orderNum: 66 } } function parseRomanianBibleMarkdown(md: string): Book[] { const lines = md.split(/\r?\n/) const books: Book[] = [] let currentBook: Book | null = null let currentChapter: Chapter | null = null // The MD file uses a pattern of a centered title line with ellipses like: … GENEZA … // We detect those as book delimiters. const isBookHeader = (line: string) => /^…\s*.+\s*…$/.test(line.trim()) const normalizeBookName = (raw: string) => { // Strip leading/trailing ellipsis and extra spaces const name = raw.replace(/^…\s*|\s*…$/g, '').trim() // Try to map known variants to our mapping keys // The MD sometimes uses slightly different diacritics or spacing; leave as-is if not found return name } for (let i = 0; i < lines.length; i++) { const raw = lines[i].trim() if (!raw) continue if (isBookHeader(raw)) { // Save previous chapter and book if they have content if (currentChapter && currentChapter.verses.length > 0 && currentBook) { currentBook.chapters.push(currentChapter) } if (currentBook && currentBook.chapters.length > 0) { books.push(currentBook) } const bookName = normalizeBookName(raw) currentBook = { name: bookName, chapters: [] } currentChapter = null continue } // Detect chapter markers like: Capitolul X (case-insensitive, diacritics tolerant) const chapterMatch = raw.match(/^[cC][aA][pP][iI][tT][oO][lL][uU][lL]\s+(\d+)$/) if (chapterMatch && currentBook) { // Save previous chapter if exists if (currentChapter && currentChapter.verses.length > 0) { currentBook.chapters.push(currentChapter) } currentChapter = { chapterNum: parseInt(chapterMatch[1], 10), verses: [] } continue } // Detect verses that begin with a number: "1 text..." const verseMatch = raw.match(/^(\d+)\s+(.+)$/) if (verseMatch && currentChapter) { const verseNum = parseInt(verseMatch[1], 10) let verseText = verseMatch[2].trim() // Remove paragraph markers if present verseText = verseText.replace(/^¶\s*/, '') // Merge continuation lines that don't start with a verse or chapter/book markers let j = i + 1 while (j < lines.length) { const lookahead = lines[j].trim() if (!lookahead) break if (/^(\d+)\s+/.test(lookahead)) break // next verse if (isBookHeader(lookahead)) break // next book if (/^[cC][aA][pP][iI][tT][oO][lL][uU][lL]\s+\d+$/.test(lookahead)) break // next chapter verseText += ' ' + lookahead j++ } i = j - 1 verseText = verseText.replace(/\s+/g, ' ').trim() if (verseText) { currentChapter.verses.push({ verseNum, text: verseText }) } } } // Save last chapter and book if (currentChapter && currentChapter.verses.length > 0 && currentBook) { currentBook.chapters.push(currentChapter) } if (currentBook && currentBook.chapters.length > 0) { books.push(currentBook) } return books } function writeTestamentJson(books: Book[], outPath: string, testament: 'Old Testament' | 'New Testament') { const data = { testament, books: books.map(b => ({ name: b.name, chapters: b.chapters })) } fs.mkdirSync(path.dirname(outPath), { recursive: true }) fs.writeFileSync(outPath, JSON.stringify(data, null, 2), 'utf-8') console.log(`Wrote ${outPath} with ${books.length} books.`) } async function main() { const mdPath = path.join(process.cwd(), 'bibles', 'Biblia-Fidela-limba-romana.md') if (!fs.existsSync(mdPath)) { throw new Error(`Markdown not found at ${mdPath}`) } console.log(`Reading: ${mdPath}`) const md = fs.readFileSync(mdPath, 'utf-8') const parsedBooks = parseRomanianBibleMarkdown(md) console.log(`Parsed ${parsedBooks.length} books from Markdown`) // Filter and sort into OT and NT using the mapping const otBooks: Book[] = [] const ntBooks: Book[] = [] for (const b of parsedBooks) { const meta = BOOK_MAPPINGS[b.name] if (!meta) { console.warn(`Skipping unknown book in mapping: ${b.name}`) continue } if (meta.testament === 'Old Testament') otBooks.push(b) else ntBooks.push(b) } // Sort by canonical order otBooks.sort((a, b) => BOOK_MAPPINGS[a.name].orderNum - BOOK_MAPPINGS[b.name].orderNum) ntBooks.sort((a, b) => BOOK_MAPPINGS[a.name].orderNum - BOOK_MAPPINGS[b.name].orderNum) // Write JSON outputs compatible with import-romanian-versioned.ts const otOut = path.join(process.cwd(), 'data', 'old_testament.json') const ntOut = path.join(process.cwd(), 'data', 'new_testament.json') writeTestamentJson(otBooks, otOut, 'Old Testament') writeTestamentJson(ntBooks, ntOut, 'New Testament') } main().catch(err => { console.error('Conversion failed:', err) process.exit(1) })