aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-20 17:33:26 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-20 17:33:26 +0200
commit46ffa91fd4fcc2249cd097b3b3a639d37a7d592e (patch)
tree8aa5ec165685a21c190bee99a3ed58f648d6fdf8
parent152bba4c94060ea2ac7430dfdf826450ee4f2bd8 (diff)
downloadcolitur-46ffa91fd4fcc2249cd097b3b3a639d37a7d592e.tar.gz
colitur-46ffa91fd4fcc2249cd097b3b3a639d37a7d592e.zip
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.
-rw-r--r--bin/main.ml7
-rw-r--r--lang/la.ini51
-rw-r--r--man/colitur-config.563
-rw-r--r--test/test_lang_coverage.ml28
4 files changed, 132 insertions, 17 deletions
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 ] )