From 0f94c388880972f8e665ed01a6b38190bcc61499 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 20 Aug 2026 09:28:08 +0200 Subject: feat(cli): --lang and --raw, colitur lang, colitur config Names are Latin by default now; --raw restores the old byte-exact output, verified against a pre-branch binary built from a clean HEAD checkout, not merely asserted. day and readings gain a trailing display-name field (day's own name suffix, readings' own " | name" suffix) rather than a substitution in the slug's existing position: a name contains spaces and would otherwise break the space-separated columns, the same reasoning that already keeps readings a separate command. The field is present only when it differs from the slug already shown, which is a direct consequence of Lang.raw being the identity table (no lookup ever misses), not a special case threaded through day_line/readings_line -- under --raw, or any language with no entry for a given day, the trailing field is simply absent, byte for byte what this project printed before naming existed. An unknown language is an error naming what is available, never a silent fallback to Latin: a booklet quietly printed in the wrong language is worse than one that refuses to print. A declared fallback still chains, so a partial translation shows its fallback language rather than slugs. lang_dir mirrors data_dir's probe order exactly -- installed prefix, then build tree -- because an installed binary that finds calendar data but no language files is the failure this would otherwise ship. Config (lang/overlay/template/format) is loaded once, unconditionally, before dispatch: a config file that exists but fails to parse is fatal on every command, not only the ones that consult it, because a config the user wrote and colitur cannot honour is not something to silently carry on past. An unknown key or unknown section is reported and ignored, worded differently so a misspelled section reads differently from a misspelled key inside a recognised one. overlay's config default applies only when zero --overlay flags were given, never merged with a partial CLI list, so precedence stays flag > config > default in one direction throughout. colitur lang --list/--dump/--check and colitur config --show are what make "anyone can write a language file" true rather than merely permitted, the same pattern the overlay system already established with new-overlay and check. --check reports what is missing AND rejects an unknown slug (exit 1), because a key matching nothing is silently dead and its author would otherwise never learn why their own name does not appear. --dump emits sections in a fixed order (Lang.keys' own global sort keeps every section's keys contiguous), so two dumps of the same table are byte-identical. config --show resolves through the same Config.resolve every other command uses -- deliberately no separate provenance function, so the two cannot disagree -- and previews exactly what flags given on the same command line would resolve to anywhere else. One authorized change outside this task's own file list: emit_ics.ml's SUMMARY line was reading the unlocalised rank/colour pair ("class-1, white") beside an already-localised name, where the JSON schema right next to it correctly carries rank_name/colour_name ("II classis"/"albus"). Found while reviewing the published site; fixed at the source. --- man/colitur.1 | 322 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 298 insertions(+), 24 deletions(-) (limited to 'man/colitur.1') diff --git a/man/colitur.1 b/man/colitur.1 index 005cc0a..b9ce1d1 100644 --- a/man/colitur.1 +++ b/man/colitur.1 @@ -9,8 +9,9 @@ colitur \- deterministic liturgical calendar and lectionary engine (Roman rite, .B colitur .BR day | readings .I YEAR -.BI \-\-overlay " FILE" -.RI [ ... ] +.RB [ \-\-overlay " FILE" " ...]" +.RB [ \-\-lang " CODE\(brFILE" ] +.RB [ \-\-raw ] .br .B colitur .B emit @@ -19,6 +20,8 @@ colitur \- deterministic liturgical calendar and lectionary engine (Roman rite, .BI \-\-to " YEAR" .RB [ \-\-overlay " FILE" " ...]" .RB [ \-\-dtstamp " STAMP" ] +.RB [ \-\-lang " CODE\(brFILE" ] +.RB [ \-\-raw ] .br .B colitur .BR table | render @@ -26,6 +29,8 @@ colitur \- deterministic liturgical calendar and lectionary engine (Roman rite, .BI \-\-template " FILE" .RB [ \-\-flavour " FLAVOUR" ] .RB [ \-\-overlay " FILE" " ...]" +.RB [ \-\-lang " CODE\(brFILE" ] +.RB [ \-\-raw ] .br .B colitur .B publish @@ -35,6 +40,16 @@ colitur \- deterministic liturgical calendar and lectionary engine (Roman rite, .RB [ \-\-overlay " FILE" " ...]" .RB [ \-\-prune ] .RB [ \-\-dtstamp " STAMP" ] +.RB [ \-\-lang " CODE\(brFILE" ] +.RB [ \-\-raw ] +.br +.B colitur +.B lang +.RB { \-\-list | \-\-dump " CODE" | \-\-check " FILE" } +.br +.B colitur +.B config +.B \-\-show .br .B colitur .BR \-h | \-\-help @@ -145,6 +160,41 @@ validate a calendar against the rubrics, and cannot: see .B OVERLAYS below. .TP +.B lang \-\-list +List the language files this build can find, and each one's own declared +.BR fallback , +if any. +.TP +.BI lang " " \-\-dump " CODE" +Print the named language's full key set, in INI form, to standard output +\(em a starting point for a new translation, or a way to diff two versions +of one. See +.B NAMING +below. +.TP +.BI lang " " \-\-check " FILE" +Report what a language file is +.B MISSING +(a real slug the engine can produce with no entry for it) and, separately, +any entry naming a slug that does not exist at all \(em a typo, which would +otherwise be silently dead. Exits +.B 1 +if anything is unknown, so it composes into a Makefile or a pre\-commit +hook, the same way +.B check +does for an overlay. See +.B NAMING +below. +.TP +.B config \-\-show +Print every effective setting \(em +.IR lang ", " overlay ", " template ", " format +\(em its resolved value, and where it came from: +.BR flag ", " config " or " default . +See +.BR colitur\-config (5) +for the config file's location and precedence in full. +.TP .BI \-\-overlay " FILE" Apply a user calendar on top of the shipped one. Repeatable and ordered; .BR day ", " readings ", " emit ", " table ", " render " and " publish @@ -222,6 +272,31 @@ own extension. See .B RENDERING below. .TP +.BI \-\-lang " CODE\(brFILE" +.RB ( day ", " readings ", " emit ", " table ", " render " and " publish " only)" +Resolve display names through this language instead of the default. +.I CODE +(e.g. +.BR la ", " en ) +is looked up as a file in the installed language directory; +a value containing +.B / +or ending +.B .ini +is read as a literal path instead. Default +.BR la , +overridable by a config file. An unknown language is a hard error naming +what is available, never a silent fallback to Latin. See +.B NAMING +below. +.TP +.B \-\-raw +.RB ( day ", " readings ", " emit ", " table ", " render " and " publish " only)" +Restore the pre\-naming output: every display name equals its bare machine +slug. See +.B NAMING +below. +.TP .BR \-h ", " \-\-help Print a usage summary to standard output and exit 0. .TP @@ -231,7 +306,7 @@ Print the version and exit 0. .SS day .RS .nf -date weekday season week slug rank colour [+commemoration ...] +date weekday season week slug rank colour [+commemoration ...] [name] .fi .RE .PP @@ -239,19 +314,40 @@ Space\-separated, with one .BI + slug suffix per admitted commemoration. A .B \- -in the week column means the day carries no week number. +in the week column means the day carries no week number. The resolved +display +.I name +(see +.B NAMING +below) is appended LAST, after any commemorations, rather than substituted +for +.I slug +above: a name may itself contain spaces, and inserting it earlier in the row +would break every fixed\-position field that follows it \(em the same +mechanical reason +.B readings +is a separate command rather than extra columns on +.B day +(see below). It is present only when it differs from +.I slug +already shown; under +.BR \-\-raw , +or a language with no entry for that particular day, the trailing field is +simply absent \(em not merely empty \(em which is what makes +.B \-\-raw +byte\-identical to this program's pre\-naming output. .RS .nf -2026\-04\-05 sunday paschaltide 1 ef\-easter\-sunday class\-1 white -2026\-11\-02 monday time\-after\-pentecost 23 commemoration\-of\-all\-souls class\-1 black -2057\-03\-26 monday lent 3 annunciation\-of\-the\-blessed\-virgin\-mary class\-1 white +ef\-lent\-3\-monday +2026\-04\-05 sunday paschaltide 1 ef\-easter\-sunday class\-1 white Dominica Resurrectionis +2026\-11\-02 monday time\-after\-pentecost 23 commemoration\-of\-all\-souls class\-1 black In Commemoratione Omnium Fidelium Defunctorum +2057\-03\-26 monday lent 3 annunciation\-of\-the\-blessed\-virgin\-mary class\-1 white +ef\-lent\-3\-monday In Annuntiatione B. Mariae Virg. .fi .RE .SS readings .RS .nf -date slug | Epistle | Gospel +date slug | Epistle | Gospel [| name] .fi .RE .PP @@ -265,12 +361,20 @@ rather than extra columns on .BR day : appended there, no field number could recover where the Epistle ended. A .B \- -in either citation field means none was resolved. +in either citation field means none was resolved. The resolved display +.I name +is appended as a fourth, +.RB \(lq " | " \(rq \-delimited +field on the same "present only when it differs from +.IR slug "" \(cq +terms as +.BR day 's +own trailing field, above. .RS .nf -2026\-12\-25 ef\-nativity | Heb 1:1\-12 | John 1:1\-14 -2038\-03\-06 sts\-felicitas\-perpetua | Ecclus 51:1\-8, 12 | Matt 13:44\-52 +2026\-12\-25 ef\-nativity | Heb 1:1\-12 | John 1:1\-14 | In Nativitate Domini +2038\-03\-06 sts\-felicitas\-perpetua | Ecclus 51:1\-8, 12 | Matt 13:44\-52 | Ss. Perpetuae et Felicitatis Mm. .fi .RE .PP @@ -279,12 +383,17 @@ Both reports are one line per day and ordered by date, so they compose with .BR awk (1) and .BR join (1) -in the ordinary way. +in the ordinary way. Pass +.B \-\-raw +to restore the pre\-naming byte\-exact output of either report \(em no +trailing field at all \(em for a script written against it before this +feature existed. .SH EMIT .BI "colitur emit " \-\-format " FMT " \-\-from " YEAR " \-\-to " YEAR" renders the same resolved day \(em season, week, slug, rank, colour, -subject, Latin and English names, citations, commemorations \(em through one -of five emitters, for every day in the inclusive civil\-year range +subject, the resolved display name in the active language, citations, +commemorations \(em through one of five emitters, for every day in the +inclusive civil\-year range .IR FROM .. TO . Every emitter consumes one shared view of the data, so all five describe exactly the same fields. @@ -300,10 +409,20 @@ behave as expected. .nf .B colitur emit \-\-format csv \-\-from 2026 \-\-to 2026 | head \-2 -date,rite,season,week,slug,rank,colour,subject,name_la,name_en,first,gospel,comms -2026\-01\-01,ef,christmastide,,ef\-circumcision,class\-1,white,temporal,,,Titus 2:11\-15,Luke 2:21, +date,rite,season,season_name,week,slug,name,weekday,rank,rank_name,colour,colour_name,subject,first,gospel,comms +2026\-01\-01,ef,christmastide,Tempus Nativitatis,,ef\-circumcision,In Octava Nativitatis Domini,Feria V,class\-1,I classis,white,albus,temporal,Titus 2:11\-15,Luke 2:21, .fi .RE +.PP +.IR name ", " season_name ", " rank_name " and " colour_name +are the resolved display strings in the active language (see +.B NAMING +below); +.IR slug ", " season ", " rank " and " colour +stay the kernel's own unlocalised keys, unaffected by +.BR \-\-lang / \-\-raw , +so a script can key off the stable machine value while a human reads the +localised one beside it. .TP .B json One JSON object per requested year, concatenated. Shape pinned by @@ -437,8 +556,9 @@ out of its flavour's escaping. .PP The template renders against the same schema .B emit -uses (season, week, slug, rank, colour, subject, names, citations, -commemorations), reshaped for two artefacts from one model: a flat booklet +uses (season, week, slug, rank, colour, subject, the resolved display name, +citations, commemorations), reshaped for two artefacts from one model: a +flat booklet (the .B days list, one entry per day of the year) and a month grid (the @@ -455,10 +575,24 @@ for the full syntax, the escaping table per flavour, the complete view\-model field reference, and \(em before writing a template of any complexity \(em its .B SCOPE AND LOOKUP -section: an inner key silently loses to an outer key of the same name (a bare -.B {{name.la}} -inside a day resolves to the enclosing MONTH's name, not the day's own), -which has produced wrong output in this project's own templates. +section: a +.B month +and a +.B week +both carry a +.I num +field, and a bare +.B {{num}} +read from inside +.B {{#days}} +silently climbs to the enclosing week's own value, not the month's. A +similar hazard around +.B name +existed before the view model's naming rework and is now unrepresentable +\(em +.I name +is a plain resolved string, with no dotted path left for a partial match to +fall back through. .SS Flavours .BI \-\-flavour controls how interpolated @@ -718,6 +852,131 @@ calendar. None of them can vouch for a file you supply. A directive naming a slug that does not exist prints a warning to standard error and the run continues, so a typo in a local calendar is visible rather than silent; a file that fails to load is fatal. +.SH NAMING +.TP +.BI \-\-lang " CODE\(brFILE" +Resolve every display name \(em a day's +.IR name , +its localised +.IR weekday / rank_name / colour_name / season_name , +each month's own name, and the fixed +.I term +vocabulary a template routes through \(em through this language instead of +the default. Accepted on +.BR day ", " readings ", " emit ", " table ", " render " and " publish ; +refused elsewhere, the same discipline +.B \-\-overlay +gets. +.I CODE +(e.g. +.BR la ", " en ) +is looked up as +.IR / CODE .ini ; +a value containing +.B / +or ending +.B .ini +is read as a literal file path instead. Default +.BR la , +overridable by +.I lang +in the config file (see +.BR colitur\-config (5)), +itself overridden by +.BR \-\-lang . +.PP +.B An unknown language is a hard error naming what is available, never a +.B silent fallback to Latin: +a booklet quietly printed in the wrong language is worse than one that +refuses to print. +.RS +.nf + +.B colitur day 2027 \-\-lang xx +colitur: no language "xx" (looked in .../share/colitur/lang); try: colitur lang \-\-list +.fi +.RE +.PP +A language file may declare +.B fallback " = " CODE +in its +.I [meta] +section (both shipped files show the shape; +.I lang/en.ini +declares +.BR "fallback = la" ). +A slug the active language does not itself name still resolves through the +fallback chain, so a partial translation is usable from its first line +rather than only once it is complete. +.TP +.B \-\-raw +Restore every command's pre\-naming output: a day's +.I name +(and every other localised field the +.B emit +schema carries) equals the bare machine +.IR slug , +exactly as if no language had ever been resolved. This is not a special +case threaded through the naming code \(em the identity table under +.RB ( raw ) +is an ordinary language table like any other, under which every lookup +echoes its key back unchanged, so +.B \-\-raw +and a real language file share the same code path throughout. +.B day +and +.B readings +under +.B \-\-raw +are byte\-identical to this program's own pre\-naming output; every existing +script built against that output therefore needs one flag, not a rewrite. +.TP +.B lang \-\-list +.TQ +.BI lang " " \-\-dump " CODE" +.TQ +.BI lang " " \-\-check " FILE" +What make "anyone can write a language file" true rather than merely +permitted \(em the same idea the overlay system already established with +.B new\-overlay +and +.BR check . +.RS +.nf + +.B colitur lang \-\-dump la > my\-lang.ini +.B $EDITOR my\-lang.ini +.B colitur lang \-\-check my\-lang.ini +.B colitur day 2026 \-\-lang ./my\-lang.ini +.fi +.RE +.PP +.B \-\-check +reports what is +.B MISSING +(a real slug the engine can produce, with no entry for it in the file) and, +separately, any entry naming a slug that does not exist at all \(em a typo, +which would otherwise be silently dead, its author never learning why their +own name never appears. Exits +.B 1 +if anything is unknown, so it composes into a Makefile or a pre\-commit +hook. The reference slug set is the same one +.BR "colitur lang \-\-check" 's +own coverage test walks: the observed office AND every commemoration and +transfer the engine can produce, over a fixed multi\-year window \(em not +merely a hand\-picked sample. +.TP +.B config \-\-show +Print every effective setting \(em +.IR lang ", " overlay ", " template ", " format +\(em its resolved value, and where it came from: +.BR flag ", " config " or " default , +via the same resolver every other command uses (there is deliberately no +separate "provenance" function, so the two cannot disagree). Also prints +the config file's own path and whether it exists. See +.BR colitur\-config (5) +for the file's location, its precedence in full, and every setting it +recognises. .SH ENVIRONMENT .TP .B COLITUR_DATA_DIR @@ -790,6 +1049,17 @@ Days carrying at least one commemoration: .fi .RE .PP +Names are Latin by default; render in English instead, or fall back to the +pre\-naming, slugs\-only output a script written before this feature existed +still expects: +.RS +.nf + +.B colitur day 2026 \-\-lang en | head \-1 +.B colitur day 2026 \-\-raw | head \-1 +.fi +.RE +.PP A local calendar on top of the universal one: .RS .nf @@ -858,8 +1128,12 @@ shapes and worked examples. for the template format used by .BR table ", " render " and " publish \(em the four syntax forms, the six flavours and their escaping, the full -view\-model field reference, and the scope\-shadowing hazard a template author -will hit. +view\-model field reference, and the one remaining scope hazard a template +author can still hit. +.PP +.BR colitur\-config (5) +for the config file's location, the flag > config > default precedence, and +every setting it recognises. .PP .BR lectio (1) .SH LICENSE -- cgit v1.3