aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-20 10:39:47 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-20 10:39:47 +0200
commit1935f40c156563c134f87093bf6ec8da1cf4fc46 (patch)
tree28e26f391c2b7c034e680b3e46d8dadb000ef7e3
parentcd46163fa347bbc49bf8008d70d408f62feabda5 (diff)
downloadcolitur-1935f40c156563c134f87093bf6ec8da1cf4fc46.tar.gz
colitur-1935f40c156563c134f87093bf6ec8da1cf4fc46.zip
fix(naming): the rite prints its display name, not the internal id ef
The booklet's title page and running header read "Ordo 2027 . ef" -- an internal identifier reaching a reader, the same defect class the sanctoral/temporal slugs were already fixed for. lang/la.ini and lang/en.ini each gain a [rite] section mapping the rite id to a reader-facing name, sourced from the Missal's own title page (docs/research/LT.txt:6 "MISSALE ROMANUM", :15 "EDITIO TYPICA") plus the 1962 dating this project uses throughout (rules-register.md, CLAUDE.md) -- both cited in the ini comments, marked PATTERN since the phrase combines two title-page elements rather than quoting one verbatim heading. Lang.t gains a rite table/lookup (same total, miss-echoes-the-key contract as every other lookup here); View.of_days exposes rite_name alongside the existing rite field, which stays the stable key exactly as slug is kept beside name. Every shipped template that printed {{rite}} now prints {{rite_name}} instead. test_lang_coverage.ml gains a coverage assertion over every rite id the engine can emit -- one today -- so a second rite module (OF) landing without a matching [rite] entry fails loudly instead of printing its own bare id. Goldens regenerated through the Test_view.view_of path test_render_golden.ml itself uses (English with a Latin fallback), not the CLI (whose --lang default is plain Latin and would have pinned output the tests never produce). test/cli.t's own JSON prefix assertion updated to match: rite_name is a real new key in that generic view dump, sitting right after rite.
-rw-r--r--lang/en.ini10
-rw-r--r--lang/la.ini30
-rw-r--r--lib/naming/lang.ml7
-rw-r--r--lib/naming/lang.mli6
-rw-r--r--lib/render/view.ml6
-rw-r--r--templates/ef/grid.html2
-rw-r--r--templates/ef/grid.ms2
-rw-r--r--templates/ef/ordo.adoc2
-rw-r--r--templates/ef/ordo.html2
-rw-r--r--templates/ef/ordo.md2
-rw-r--r--templates/ef/ordo.ms2
-rw-r--r--templates/ef/ordo.tex4
-rw-r--r--templates/ef/ordo.typ4
-rw-r--r--test/cli.t4
-rw-r--r--test/golden/grid-2027.html2
-rw-r--r--test/golden/grid-2027.ms2
-rw-r--r--test/golden/ordo-2027.adoc2
-rw-r--r--test/golden/ordo-2027.html2
-rw-r--r--test/golden/ordo-2027.md2
-rw-r--r--test/golden/ordo-2027.ms2
-rw-r--r--test/golden/ordo-2027.tex4
-rw-r--r--test/golden/ordo-2027.typ4
-rw-r--r--test/test_lang_coverage.ml13
-rw-r--r--test/test_view.ml9
24 files changed, 100 insertions, 25 deletions
diff --git a/lang/en.ini b/lang/en.ini
index 25084d8..bfd7689 100644
--- a/lang/en.ini
+++ b/lang/en.ini
@@ -80,6 +80,16 @@ gospel = Gospel
commemoration = Commemoration
week = Week
+[rite]
+; See lang/la.ini's own [rite] section for the full sourcing (the Missal's
+; own title page, docs/research/LT.txt:6/8/15, plus the 1962 dating this
+; project uses throughout). This is that same phrase in English, not a
+; second, independent translation decision: "Missale Romanum" -> "Roman
+; Missal" and "editio typica" -> "typical edition" are the standard English
+; renderings used for exactly this kind of front-matter (both terms of art
+; in English-language liturgical publishing, not colitur's own coinage).
+ef = Roman Missal, 1962 typical edition
+
[celebration]
; --- Temporal (394 slugs, 2020-2045 measured; three added Task 5,
; 2026-08-19, following ef-nativity-octave-day-5/6/7's own pattern -- see
diff --git a/lang/la.ini b/lang/la.ini
index e82650d..9cbb8fe 100644
--- a/lang/la.ini
+++ b/lang/la.ini
@@ -156,6 +156,36 @@ gospel = Evangelium
commemoration = Commemoratio
week = Hebdomada
+[rite]
+; Every rite id the engine can emit needs a reader-facing display name here
+; (test_lang_coverage.ml's own "every rite id has a Latin name" case) --
+; the same discipline [celebration]/[season]/[rank]/[colour] already
+; enforce, extended to the one new axis Defect 1 found uncovered: `rite` is
+; a stable internal key (Temporal_ef.id = "ef"), never meant for a reader,
+; exactly like a slug -- but unlike a slug there was no [rite] section at
+; all before this one, so nothing degraded gracefully; the booklet printed
+; the bare id "ef" outright.
+;
+; ef -- PATTERN, not a single verbatim heading: constructed from two
+; distinct elements of the Missal's OWN title page, docs/research/LT.txt:6
+; ("MISSALE ROMANUM") and LT.txt:15 ("EDITIO TYPICA"), the standard formula
+; a Missale Romanum typical edition has carried on its own cover since
+; Trent (LT.txt:8, "SS. CONCILII TRIDENTINI RESTITUTUM" -- present
+; unabridged in the full title but dropped here for a running header's
+; sake, the same "shorter than the source" trade-off already accepted in
+; [term]'s "contents = Index" above). The year is not a quote: 1960 is the
+; motu proprio's own promulgation date (LT.txt:102-103, "Rubricarum
+; instructum diei 23 iulii anno 1960"), but the RESULTING typical edition
+; this project treats as its source of truth is dated 1962 throughout
+; docs/research/rules-register.md ("the actual 1962 typical edition") and
+; this project's own CLAUDE.md ("the 1962 Missale Romanum") -- both
+; disclosed here rather than mechanically cited, since neither is an
+; LT.txt line. No single phrase in either scan says "1962" against this
+; book's own cover (the year is a publication fact, not a title-page
+; word), so appending it is a deliberate, disclosed choice for a reader
+; who needs to know WHICH typical edition this is, not an invented title.
+ef = Missale Romanum, editio typica 1962
+
[celebration]
; ---------------------------------------------------------------------
; TEMPORAL slugs only (^ef-). The SANCTORAL half (a fixed saint's day, not
diff --git a/lib/naming/lang.ml b/lib/naming/lang.ml
index 147a49b..92c8a9a 100644
--- a/lib/naming/lang.ml
+++ b/lib/naming/lang.ml
@@ -14,6 +14,7 @@ type t = {
rank : table;
colour : table;
term : table;
+ rite : table;
chain : t option; (** consulted when this table misses *)
}
@@ -32,6 +33,7 @@ let season t k = get t (fun x -> x.season) k
let rank t k = get t (fun x -> x.rank) k
let colour t k = get t (fun x -> x.colour) k
let term t k = get t (fun x -> x.term) k
+let rite t k = get t (fun x -> x.rite) k
let weekday_key = [| "sunday"; "monday"; "tuesday"; "wednesday"; "thursday"; "friday"; "saturday" |]
@@ -58,7 +60,7 @@ let fallback_code t = t.fallback_code
let raw =
{ code = "raw"; fallback_code = None; celebration = empty_table; weekday = empty_table;
month = empty_table; season = empty_table; rank = empty_table; colour = empty_table;
- term = empty_table; chain = None }
+ term = empty_table; rite = empty_table; chain = None }
let with_fallback t base = { t with chain = Some base }
@@ -98,6 +100,7 @@ let of_string text =
rank = find "rank";
colour = find "colour";
term = find "term";
+ rite = find "rite";
chain = None })
let keys t =
@@ -105,5 +108,5 @@ let keys t =
List.concat
[ qualify "celebration" t.celebration; qualify "weekday" t.weekday;
qualify "month" t.month; qualify "season" t.season; qualify "rank" t.rank;
- qualify "colour" t.colour; qualify "term" t.term ]
+ qualify "colour" t.colour; qualify "term" t.term; qualify "rite" t.rite ]
|> List.sort compare
diff --git a/lib/naming/lang.mli b/lib/naming/lang.mli
index 36931d4..4d7f82f 100644
--- a/lib/naming/lang.mli
+++ b/lib/naming/lang.mli
@@ -49,6 +49,12 @@ val rank : t -> string -> string
val colour : t -> string -> string
val term : t -> string -> string
+(** A rite id's reader-facing display name (e.g. ["ef"] ->
+ ["Missale Romanum, editio typica 1962"]). A miss returns the id itself,
+ same TOTAL-lookup contract as every other function here -- the same
+ degradation an unnamed slug already gets, not a special case. *)
+val rite : t -> string -> string
+
(** [weekday t n], 0 = Sunday. Out-of-range [n] returns [string_of_int n]. *)
val weekday : t -> int -> string
diff --git a/lib/render/view.ml b/lib/render/view.ml
index bbb8c7d..7bd5416 100644
--- a/lib/render/view.ml
+++ b/lib/render/view.ml
@@ -191,6 +191,12 @@ let of_days ~lang ~vocab ~rite ~year days =
in
T.Obj
[ ("rite", str rite);
+ (* The reader-facing display name (lang/*.ini's own [rite] section,
+ Defect 1) -- [rite] itself stays the stable internal key, exactly
+ as [slug] is kept beside [name] on a day/commemoration. Every
+ template that used to print the bare id ("ef") now prints this
+ field instead. *)
+ ("rite_name", str (Lang.rite lang rite));
("year", str (string_of_int year));
("term", term_value lang);
("weekday_headings", weekday_headings lang);
diff --git a/templates/ef/grid.html b/templates/ef/grid.html
index e313f9d..b2d4a96 100644
--- a/templates/ef/grid.html
+++ b/templates/ef/grid.html
@@ -35,7 +35,7 @@
.violet{background:#eae} .rose{background:#fde} .black{background:#ddd}
@media print{@page{size:landscape}body{margin:0}}
</style></head><body>
-<h1>Calendarium {{year}} &middot; {{rite}}</h1>
+<h1>Calendarium {{year}} &middot; {{rite_name}}</h1>
{{#months}}
<table>
<caption>{{name}}</caption>
diff --git a/templates/ef/grid.ms b/templates/ef/grid.ms
index 727143b..7ed483a 100644
--- a/templates/ef/grid.ms
+++ b/templates/ef/grid.ms
@@ -96,7 +96,7 @@
.nr LL 10.9i
.ll \n[LL]u
.TL
-Calendarium {{year}} \(bu {{rite}}
+Calendarium {{year}} \(bu {{rite_name}}
.ll \n[LL]u
.na
{{#months}}
diff --git a/templates/ef/ordo.adoc b/templates/ef/ordo.adoc
index 3146f2e..3c7e69b 100644
--- a/templates/ef/ordo.adoc
+++ b/templates/ef/ordo.adoc
@@ -12,7 +12,7 @@
// (Ordo, Epistle, Gospel, Commemoration) comes from the view's term
// vocabulary rather than being written into this file, so a translated
// booklet needs no template edit.
-= {{term.ordo}} {{year}} · {{rite}}
+= {{term.ordo}} {{year}} · {{rite_name}}
:toc:
{{#months}}
diff --git a/templates/ef/ordo.html b/templates/ef/ordo.html
index 3353000..c80a2a4 100644
--- a/templates/ef/ordo.html
+++ b/templates/ef/ordo.html
@@ -20,7 +20,7 @@
.rose{border-left:4px solid #e7a} .black{border-left:4px solid #000}
@media print{body{margin:0}}
</style></head><body>
-<h1>{{term.ordo}} {{year}} &middot; {{rite}}</h1>
+<h1>{{term.ordo}} {{year}} &middot; {{rite_name}}</h1>
{{#months}}<h2>{{name}}</h2>
{{#days}}<div class="day {{colour}}">
<span class="dom">{{dom}}</span>{{name}}
diff --git a/templates/ef/ordo.md b/templates/ef/ordo.md
index 805bf67..fde5bb2 100644
--- a/templates/ef/ordo.md
+++ b/templates/ef/ordo.md
@@ -11,7 +11,7 @@
(Ordo, Epistle, Gospel, Commemoration) comes from the view's term
vocabulary rather than being written into this file, so a translated
booklet needs no template edit. -->
-# {{term.ordo}} {{year}} · {{rite}}
+# {{term.ordo}} {{year}} · {{rite_name}}
{{#months}}
## {{name}}
diff --git a/templates/ef/ordo.ms b/templates/ef/ordo.ms
index 88dcb6c..d578455 100644
--- a/templates/ef/ordo.ms
+++ b/templates/ef/ordo.ms
@@ -10,7 +10,7 @@
.\" vocabulary rather than being written into this file, so a translated
.\" booklet needs no template edit.
.TL
-{{term.ordo}} {{year}} \(bu {{rite}}
+{{term.ordo}} {{year}} \(bu {{rite_name}}
{{#months}}
.SH
{{name}}
diff --git a/templates/ef/ordo.tex b/templates/ef/ordo.tex
index 6a298df..4b5bed9 100644
--- a/templates/ef/ordo.tex
+++ b/templates/ef/ordo.tex
@@ -61,7 +61,7 @@
% alone cannot outline a filled rule.
\newcommand{\swatch}[1]{\tikz[baseline=-0.6ex]{\fill[draw=black,line width=0.4pt,fill=#1] (0,0) rectangle (2.4ex,2.4ex);}}
\pagestyle{fancy}\fancyhf{}
-\fancyhead[C]{\small {{term.ordo}} {{year}} \textperiodcentered\ {{rite}}}
+\fancyhead[C]{\small {{term.ordo}} {{year}} \textperiodcentered\ {{rite_name}}}
\fancyfoot[C]{\small\thepage}
\renewcommand{\headrulewidth}{0.4pt}
\setlength{\parindent}{0pt}
@@ -69,7 +69,7 @@
\begin{center}
\Large\bfseries {{term.ordo}} {{year}}\\[2pt]
-\normalsize\mdseries {{rite}}
+\normalsize\mdseries {{rite_name}}
\end{center}
\vspace{4mm}
{\bfseries {{term.contents}}}\par\vspace{2mm}
diff --git a/templates/ef/ordo.typ b/templates/ef/ordo.typ
index 33bc802..162a485 100644
--- a/templates/ef/ordo.typ
+++ b/templates/ef/ordo.typ
@@ -61,7 +61,7 @@
paper: "a5",
margin: (inside: 14mm, outside: 10mm, top: 12mm, bottom: 12mm),
numbering: "1",
- header: align(center)[#text(size: 8pt)[{{term.ordo}} {{year}} #sym.dot.c {{rite}}]],
+ header: align(center)[#text(size: 8pt)[{{term.ordo}} {{year}} #sym.dot.c {{rite_name}}]],
)
#set text(size: 9pt)
#set par(justify: false)
@@ -82,7 +82,7 @@
#align(center)[
#text(size: 18pt, weight: "bold")[{{term.ordo}} {{year}}] \
- {{rite}}
+ {{rite_name}}
]
#v(4mm)
#text(weight: "bold")[{{term.contents}}]
diff --git a/test/cli.t b/test/cli.t
index ef1243e..9ea7beb 100644
--- a/test/cli.t
+++ b/test/cli.t
@@ -427,8 +427,8 @@ CSV emits a header and one row per day:
JSON is one object, ICS one VCALENDAR:
- $ colitur emit --format json --from 2027 --to 2027 | cut -c1-20
- {"rite":"ef","year":
+ $ colitur emit --format json --from 2027 --to 2027 | cut -c1-75
+ {"rite":"ef","rite_name":"Missale Romanum, editio typica 1962","year":"2027
$ colitur emit --format ics --from 2027 --to 2027 | head -1 | cat -A | head -1
BEGIN:VCALENDAR^M$
diff --git a/test/golden/grid-2027.html b/test/golden/grid-2027.html
index 2a85eca..f69b8f3 100644
--- a/test/golden/grid-2027.html
+++ b/test/golden/grid-2027.html
@@ -35,7 +35,7 @@
.violet{background:#eae} .rose{background:#fde} .black{background:#ddd}
@media print{@page{size:landscape}body{margin:0}}
</style></head><body>
-<h1>Calendarium 2027 &middot; ef</h1>
+<h1>Calendarium 2027 &middot; Roman Missal, 1962 typical edition</h1>
<table>
<caption>January</caption>
diff --git a/test/golden/grid-2027.ms b/test/golden/grid-2027.ms
index 98e9594..b7539ed 100644
--- a/test/golden/grid-2027.ms
+++ b/test/golden/grid-2027.ms
@@ -96,7 +96,7 @@
.nr LL 10.9i
.ll \n[LL]u
.TL
-Calendarium 2027 \(bu ef
+Calendarium 2027 \(bu Roman Missal, 1962 typical edition
.ll \n[LL]u
.na
diff --git a/test/golden/ordo-2027.adoc b/test/golden/ordo-2027.adoc
index f3d33cb..7eae94c 100644
--- a/test/golden/ordo-2027.adoc
+++ b/test/golden/ordo-2027.adoc
@@ -12,7 +12,7 @@
// (Ordo, Epistle, Gospel, Commemoration) comes from the view's term
// vocabulary rather than being written into this file, so a translated
// booklet needs no template edit.
-= Ordo 2027 · ef
+= Ordo 2027 · Roman Missal, 1962 typical edition
:toc:
diff --git a/test/golden/ordo-2027.html b/test/golden/ordo-2027.html
index 96ec943..ce7aa26 100644
--- a/test/golden/ordo-2027.html
+++ b/test/golden/ordo-2027.html
@@ -20,7 +20,7 @@
.rose{border-left:4px solid #e7a} .black{border-left:4px solid #000}
@media print{body{margin:0}}
</style></head><body>
-<h1>Ordo 2027 &middot; ef</h1>
+<h1>Ordo 2027 &middot; Roman Missal, 1962 typical edition</h1>
<h2>January</h2>
<div class="day white">
<span class="dom">1</span>The Octave Day of the Nativity
diff --git a/test/golden/ordo-2027.md b/test/golden/ordo-2027.md
index 4742dc2..ac3419f 100644
--- a/test/golden/ordo-2027.md
+++ b/test/golden/ordo-2027.md
@@ -11,7 +11,7 @@
(Ordo, Epistle, Gospel, Commemoration) comes from the view's term
vocabulary rather than being written into this file, so a translated
booklet needs no template edit. -->
-# Ordo 2027 · ef
+# Ordo 2027 · Roman Missal, 1962 typical edition
## January
diff --git a/test/golden/ordo-2027.ms b/test/golden/ordo-2027.ms
index 9e869dd..4594e43 100644
--- a/test/golden/ordo-2027.ms
+++ b/test/golden/ordo-2027.ms
@@ -10,7 +10,7 @@
.\" vocabulary rather than being written into this file, so a translated
.\" booklet needs no template edit.
.TL
-Ordo 2027 \(bu ef
+Ordo 2027 \(bu Roman Missal, 1962 typical edition
.SH
January
diff --git a/test/golden/ordo-2027.tex b/test/golden/ordo-2027.tex
index c447f99..21b711e 100644
--- a/test/golden/ordo-2027.tex
+++ b/test/golden/ordo-2027.tex
@@ -61,7 +61,7 @@
% alone cannot outline a filled rule.
\newcommand{\swatch}[1]{\tikz[baseline=-0.6ex]{\fill[draw=black,line width=0.4pt,fill=#1] (0,0) rectangle (2.4ex,2.4ex);}}
\pagestyle{fancy}\fancyhf{}
-\fancyhead[C]{\small Ordo 2027 \textperiodcentered\ ef}
+\fancyhead[C]{\small Ordo 2027 \textperiodcentered\ Roman Missal, 1962 typical edition}
\fancyfoot[C]{\small\thepage}
\renewcommand{\headrulewidth}{0.4pt}
\setlength{\parindent}{0pt}
@@ -69,7 +69,7 @@
\begin{center}
\Large\bfseries Ordo 2027\\[2pt]
-\normalsize\mdseries ef
+\normalsize\mdseries Roman Missal, 1962 typical edition
\end{center}
\vspace{4mm}
{\bfseries Contents}\par\vspace{2mm}
diff --git a/test/golden/ordo-2027.typ b/test/golden/ordo-2027.typ
index 8abad25..2f3cf92 100644
--- a/test/golden/ordo-2027.typ
+++ b/test/golden/ordo-2027.typ
@@ -61,7 +61,7 @@
paper: "a5",
margin: (inside: 14mm, outside: 10mm, top: 12mm, bottom: 12mm),
numbering: "1",
- header: align(center)[#text(size: 8pt)[Ordo 2027 #sym.dot.c ef]],
+ header: align(center)[#text(size: 8pt)[Ordo 2027 #sym.dot.c Roman Missal, 1962 typical edition]],
)
#set text(size: 9pt)
#set par(justify: false)
@@ -82,7 +82,7 @@
#align(center)[
#text(size: 18pt, weight: "bold")[Ordo 2027] \
- ef
+ Roman Missal, 1962 typical edition
]
#v(4mm)
#text(weight: "bold")[Contents]
diff --git a/test/test_lang_coverage.ml b/test/test_lang_coverage.ml
index 34758b2..9ed633c 100644
--- a/test/test_lang_coverage.ml
+++ b/test/test_lang_coverage.ml
@@ -80,6 +80,18 @@ let test_vocabularies_are_complete () =
if L.month t n = string_of_int n then Alcotest.failf "no Latin month for %d" n
done
+(* Defect 1: the booklet used to print the bare rite id ("ef") because no
+ [rite] section existed in either language file. Walks every rite id the
+ engine can actually emit -- one today (Rite_ef.Temporal_ef.id, the same
+ id `Rite_ef.context` stamps onto every Rite.t.id -- see rite_ef.ml) -- so
+ this is cheap now and fails loudly the moment a second rite module (OF)
+ is wired in without a matching [rite] entry in la.ini. *)
+let test_every_rite_has_a_latin_name () =
+ let t = la () in
+ List.iter
+ (fun id -> if L.rite t id = id then Alcotest.failf "no Latin display name for rite %S" id)
+ [ Rite_ef.Temporal_ef.id ]
+
(* 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
@@ -120,4 +132,5 @@ let suite =
( "Lang/coverage",
[ 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 "en.ini falls back to Latin" `Quick test_en_falls_back_to_latin ] )
diff --git a/test/test_view.ml b/test/test_view.ml
index b49b090..e8de6a0 100644
--- a/test/test_view.ml
+++ b/test/test_view.ml
@@ -66,6 +66,11 @@ let as_bool = function T.Bool b -> b | _ -> Alcotest.fail "expected a bool"
let test_year_shape () =
let v = view_of 2027 in
Alcotest.(check string) "rite" "ef" (as_str (get [ "rite" ] v));
+ (* Defect 1: [rite] stays the stable key ("ef"); [rite_name] is the
+ reader-facing display name a template prints instead. en.ini's own
+ [rite] section names it directly. *)
+ Alcotest.(check string) "rite_name" "Roman Missal, 1962 typical edition"
+ (as_str (get [ "rite_name" ] v));
Alcotest.(check string) "year" "2027" (as_str (get [ "year" ] v));
Alcotest.(check int) "twelve months" 12 (List.length (as_list (get [ "months" ] v)));
Alcotest.(check int) "365 days" 365 (List.length (as_list (get [ "days" ] v)))
@@ -193,7 +198,9 @@ let test_vocabularies_localise () =
let d1 = List.hd (as_list (get [ "days" ] v)) in
Alcotest.(check string) "weekday" "Feria VI" (as_str (get [ "weekday" ] d1));
Alcotest.(check string) "rank" "I classis" (as_str (get [ "rank_name" ] d1));
- Alcotest.(check string) "colour" "albus" (as_str (get [ "colour_name" ] d1))
+ Alcotest.(check string) "colour" "albus" (as_str (get [ "colour_name" ] d1));
+ Alcotest.(check string) "rite_name" "Missale Romanum, editio typica 1962"
+ (as_str (get [ "rite_name" ] v))
let suite =
( "View",