From d68feb04f71c3dea3b59726e2ffca225d99eaa35 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 20 Aug 2026 17:14:42 +0200 Subject: feat(lang): Latin and English book names, and the shipped sigla styles la.ini and en.ini both gain [sigla] (the current Vulgate/Latin punctuation convention, byte-identical to Render.default_style) and [bible] (a .full and .abbr row for every id in Book.all -- 45 cited ids plus the 7 tradition targets, 52 total). Shipping [sigla] changes no output, asserted by the full test run. Shipping [bible] does change rendered book names, which is the point. la.ini's titles are sourced from docs/research/scan1.txt/scan2.txt (the 1962 Missal scans), each row citing the line its incipit pattern was read from. Six pairs (kings_3/4, corinthians_1/2, thessalonians_1/2, timothy_1/2, peter_1/2) share one Missal incipit and differ only in the sourced .abbr, matching what the primary text itself does. Three ids (proverbs, song_of_songs, ecclesiasticus) and the seven tradition targets are marked UNSOURCED and fall back to the data's own spelling, per this project's central rule against inventing a Latin title. Re-running the task's own sourcing note against the scans, counting every hit rather than eyeballing a frequency-sorted list, found nine of its NOT-sourced verdicts were undercounted (a single clean hit, buried under higher-frequency matches): Galatians, Colossians, both Thessalonians, both Peter, Malachi, Numbers, Jonas, Osee and Esdras all have a clean incipit in the scans and are sourced here. The note's other three verdicts stand, confirmed independently. Ecclesiasticus is not simply unfound: this Missal reuses Wisdom's own Lectio libri Sapientiae incipit for Ecclesiasticus readings too (both were anciently classed as one Sapiential group), so using it for Ecclesiasticus would misidentify the book, not merely abbreviate it -- recorded in that row's own comment. en.ini's [bible] is filled in full, not left partial the way [celebration] is: without it, a Vulgate-numbered id would fall through the [meta] fallback chain to la.ini's Latin name, not merely a less complete English one. Traditional Douay-Rheims names for the Vulgate ids (3 Kings, Osee, Ecclesiasticus, Isaias, Apocalypse), modern names for the seven tradition targets, since --sigla-tradition modern is the reader asking for modern numbering. test_lang_coverage.ml gains test_every_book_named, asserting every Book.all id has both forms in la.ini -- the check that catches a forgotten tradition target, since nothing else in the suite ever names them. Shipping real book names changes 11 golden template renders (Render/golden) and several test/cli.t examples that used to demonstrate the pre-Task-10 default-spelling fallback; both are updated to the new, correct output, each line checked against a fresh render before promoting. --- test/test_lang_coverage.ml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'test/test_lang_coverage.ml') diff --git a/test/test_lang_coverage.ml b/test/test_lang_coverage.ml index b4de5be..b2d32a4 100644 --- a/test/test_lang_coverage.ml +++ b/test/test_lang_coverage.ml @@ -95,6 +95,30 @@ let test_every_rite_has_a_latin_name () = (fun id -> if L.rite t id = id then Alcotest.failf "no Latin display name for rite %S" id) [ Rite_ef.Temporal_ef.id ] +(* Task 10: every id Colitur_citation.Book.all knows must have BOTH a + `.full` and an `.abbr` row in lang/la.ini's own [bible] section -- this is + the check that would have caught a forgotten tradition target (the seven + ids in book.ml's own `tradition_targets`, never cited by the data itself, + so nothing else here would ever notice one missing -- see book.mli's own + note on why `default_spelling` falls back to the bare id for exactly + this case). The total-lookup contract makes this a one-line miss test: + `Lang.bible la key = key` IS "no entry", the same pattern + `test_every_slug_has_a_latin_name` above already uses for [celebration]. *) +let test_every_book_named () = + let la = la () in + let missing = + List.concat_map + (fun id -> + let n = Colitur_citation.Book.to_string id in + List.filter_map + (fun form -> + let key = n ^ "." ^ form in + if L.bible la key = key then Some key else None) + [ "full"; "abbr" ]) + Colitur_citation.Book.all + in + Alcotest.(check (list string)) "every book named, both forms" [] missing + (* lang/en.ini is DELIBERATELY partial (see its own header note): it declares [meta] fallback = la, so a slug it does not carry itself should still resolve through the chain to la.ini's name rather than degrade to the bare @@ -136,4 +160,5 @@ let suite = [ Alcotest.test_case "every slug has a Latin name" `Slow test_every_slug_has_a_latin_name; Alcotest.test_case "vocabularies complete" `Quick test_vocabularies_are_complete; Alcotest.test_case "every rite has a Latin name" `Quick test_every_rite_has_a_latin_name; + Alcotest.test_case "every book named, both forms" `Quick test_every_book_named; Alcotest.test_case "en.ini falls back to Latin" `Quick test_en_falls_back_to_latin ] ) -- cgit v1.3 From 46ffa91fd4fcc2249cd097b3b3a639d37a7d592e Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 20 Aug 2026 17:33:26 +0200 Subject: fix(lang): stop internal book ids reaching output, and finish the docs Three defects, all found by running the documented examples rather than by reading code. All seven modern-numbering targets carried their own internal id as their Latin name (kings_1.full = kings_1), so --sigla-tradition modern printed "kings_1 19:3-8" -- leaking a key book.mli states is never shown to a reader. They now carry Vulgate-derived Latin names marked CONSTRUCTED: the 1962 Missal uses Vulgate numbering throughout, so it can contain no incipit for a book that exists only under a later convention. In Latin the modern tradition therefore only moves Kings and Esdras; Osee, Ionas, Ecclesiasticus and the Apocalypse keep their names either way, since modern numbering is a vernacular convention. The coverage test could not catch this. It compares a name against its KEY ("kings_1.full"), and "kings_1" differs from that, so the row passed. A second check now asserts no name IS its own id. The two are complementary: one catches a missing row, the other a row present but filled with the wrong thing. Mutation-proved. --help still claimed sigla settings were resolved "but not yet rendered", stale since the render wiring landed. colitur-config.5 documented none of the three settings even though config --show prints them. --- bin/main.ml | 7 +++--- lang/la.ini | 51 ++++++++++++++++++++++++++----------- man/colitur-config.5 | 63 ++++++++++++++++++++++++++++++++++++++++++++++ test/test_lang_coverage.ml | 28 +++++++++++++++++++++ 4 files changed, 132 insertions(+), 17 deletions(-) (limited to 'test/test_lang_coverage.ml') diff --git a/bin/main.ml b/bin/main.ml index 5ad3132..c76a082 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1267,9 +1267,10 @@ naming: `abbr`), the same discipline as an unknown `--lang`; an unrecognised `--sigla-tradition` is not -- it degrades to the Vulgate with a stderr warning, because asking for a renumbering - is optional the way asking for a language is not. Settings - only, for now: this build resolves and reports them but does - not yet render a citation through them. + is optional the way asking for a language is not. `--raw` + bypasses all of it and emits each citation exactly as stored, + byte for byte, so it stays usable for diffing and does not + depend on the citation parser being correct. colitur lang --list which language files this build can find, and each one's own declared fallback, if any. diff --git a/lang/la.ini b/lang/la.ini index 369d6b3..3783482 100644 --- a/lang/la.ini +++ b/lang/la.ini @@ -2157,17 +2157,40 @@ apocalypse.abbr = Apoc ; render one of these bare ids straight into a citation. ; UNSOURCED (all seven rows below -- see this section's own header ; immediately above for why: a category mismatch, not a search gap). -kings_1.full = kings_1 -kings_1.abbr = kings_1 -kings_2.full = kings_2 -kings_2.abbr = kings_2 -nehemiah.full = nehemiah -nehemiah.abbr = nehemiah -sirach.full = sirach -sirach.abbr = sirach -hosea.full = hosea -hosea.abbr = hosea -jonah.full = jonah -jonah.abbr = jonah -revelation.full = revelation -revelation.abbr = revelation +; The seven MODERN-NUMBERING targets. All CONSTRUCTED, and necessarily so: +; the 1962 Missal uses Vulgate numbering throughout, so it can contain no +; incipit for a book that exists only under a later convention. Latin forms +; follow the Vulgate's own naming for the same physical book. +; +; They were previously set to the internal id ("kings_1.full = kings_1") by +; applying the UNSOURCED rule mechanically -- but that rule says to fall back +; to the spelling THE DATA USES, and these books are never cited by the data +; at all, so the fallback yielded the id. `--sigla-tradition modern` then +; printed "kings_1 19:3-8", leaking an internal key that book.mli states is +; never shown to a reader. +; +; Note that in LATIN the modern tradition only really moves Kings and Esdras: +; Osee, Ionas, Ecclesiasticus and the Apocalypse keep their Vulgate names, so +; those four map to an identical rendering. That is correct, not a no-op bug +; -- modern numbering is a vernacular convention, and these files show it. +kings_1.full = Liber Regum I +; CONSTRUCTED -- same reasoning as kings_3.full above, one volume lower. +kings_1.abbr = 1 Reg +kings_2.full = Liber Regum II +; CONSTRUCTED. +kings_2.abbr = 2 Reg +nehemiah.full = Liber Nehemiae +; CONSTRUCTED. The Vulgate calls this book 2 Esdras; see esdras_2 above. +nehemiah.abbr = Neh +sirach.full = Liber Ecclesiastici +; CONSTRUCTED. Same book as ecclesiasticus above, under its modern name. +sirach.abbr = Sir +hosea.full = Osee Propheta +; CONSTRUCTED. Identical to osee above -- see the note at the top of this block. +hosea.abbr = Os +jonah.full = Ionas Propheta +; CONSTRUCTED. Identical to jonas above. +jonah.abbr = Ion +revelation.full = Apocalypsis +; CONSTRUCTED. Identical to apocalypse above. +revelation.abbr = Apoc diff --git a/man/colitur-config.5 b/man/colitur-config.5 index 8f21028..6b3a574 100644 --- a/man/colitur-config.5 +++ b/man/colitur-config.5 @@ -181,6 +181,69 @@ accepts it. Relevant only to which requires a format either from this setting or from .BR \-\-format ; no default. +.TP +.B sigla_style +Which citation STYLE to render book names and punctuation with: a language +code, or a path to a file carrying a +.B [sigla] +section. Defaults to the resolved +.IR lang , +so a booklet's citations follow its own language unless told otherwise. +See +.BR colitur (1)'s +.B SIGLA +section for the five style settings. +.TP +.B sigla_book +.BR full " or " abbr . +Overrides the selected style's own +.B book +setting. Default +.BR abbr , +giving +.B "Luc. 5:12\-14" +rather than +.BR "Evangelium secundum Lucam 5:12\-14" . +An unrecognised value is a hard error, the same discipline as an unknown +.BR \-\-lang . +.TP +.B sigla_tradition +A section name in +.IR lang/traditions.ini , +deciding which book a reference DENOTES rather than what it is called. +Default +.BR vulgate , +the identity \(em colitur never renumbers unless asked. With +.B modern +the Vulgate's own +.B "3 Kings 19:3\-8" +renders as +.B "1 Reg 19:3\-8" +in Latin, or +.B "1 Kgs 19:3\-8" +under +.BR "\-\-lang en" . +In Latin the modern tradition only really moves Kings and Esdras: Osee, +Ionas, Ecclesiasticus and the Apocalypse keep their Vulgate names either +way, because modern numbering is a vernacular convention. +Unlike +.IR sigla_book , +an unrecognised value is NOT fatal: it warns on stderr and falls back to +.BR vulgate , +because asking for a renumbering is optional the way asking for a language +is not. +.PP +Naming and renumbering are deliberately separate mechanisms. +.I sigla_style +selects what a book is CALLED, which varies by language; +.I sigla_tradition +selects which book a reference DENOTES, which does not \(em "modern +numbering" is the same decision in Latin, Polish and English. Conflating +them is how a citation ends up naming the wrong book. +.PP +None of these affect +.BR \-\-raw , +which emits every citation exactly as stored, byte for byte. .SH UNKNOWN KEYS AND SECTIONS A key inside .B [defaults] diff --git a/test/test_lang_coverage.ml b/test/test_lang_coverage.ml index b2d32a4..0271a59 100644 --- a/test/test_lang_coverage.ml +++ b/test/test_lang_coverage.ml @@ -119,6 +119,32 @@ let test_every_book_named () = in Alcotest.(check (list string)) "every book named, both forms" [] missing +(* A name must not BE the internal id. The check above compares the value + against the KEY ("kings_1.full"), so a row reading `kings_1.full = kings_1` + passes it -- the two strings differ. That is exactly what shipped: all seven + tradition targets carried their own id as their name, and + `--sigla-tradition modern` printed "kings_1 19:3-8", leaking a key that + book.mli states is never shown to a reader. + + The two checks are complementary and neither subsumes the other: that one + catches a MISSING row, this one catches a row present but filled with the + wrong thing. *) +let test_no_book_name_is_an_internal_id () = + let la = la () in + let leaked = + List.concat_map + (fun id -> + let n = Colitur_citation.Book.to_string id in + List.filter_map + (fun form -> + let v = L.bible la (n ^ "." ^ form) in + if v = n then Some (n ^ "." ^ form) else None) + [ "full"; "abbr" ]) + Colitur_citation.Book.all + in + Alcotest.(check (list string)) "no book name is its own internal id" [] + leaked + (* lang/en.ini is DELIBERATELY partial (see its own header note): it declares [meta] fallback = la, so a slug it does not carry itself should still resolve through the chain to la.ini's name rather than degrade to the bare @@ -161,4 +187,6 @@ let suite = Alcotest.test_case "vocabularies complete" `Quick test_vocabularies_are_complete; Alcotest.test_case "every rite has a Latin name" `Quick test_every_rite_has_a_latin_name; Alcotest.test_case "every book named, both forms" `Quick test_every_book_named; + Alcotest.test_case "no book name is an internal id" `Quick + test_no_book_name_is_an_internal_id; Alcotest.test_case "en.ini falls back to Latin" `Quick test_en_falls_back_to_latin ] ) -- cgit v1.3