diff options
| -rw-r--r-- | Makefile | 5 | ||||
| -rw-r--r-- | README.md | 26 | ||||
| -rw-r--r-- | man/colitur-config.5 | 308 | ||||
| -rw-r--r-- | man/colitur-templates.5 | 562 |
4 files changed, 698 insertions, 203 deletions
@@ -120,13 +120,14 @@ install: build ## install binary, calendar data, language tables, templates, sch @mkdir -p $(MAN5DIR) install -m 644 man/colitur-overlay.5 $(MAN5DIR)/colitur-overlay.5 install -m 644 man/colitur-templates.5 $(MAN5DIR)/colitur-templates.5 + install -m 644 man/colitur-config.5 $(MAN5DIR)/colitur-config.5 @echo "installed $(BINDIR)/$(COLITUR), data in $(PREFIX)/share/colitur/{ef,lang,templates,schema}, man pages in $(MANDIR) and $(MAN5DIR)" @command -v $(COLITUR) >/dev/null 2>&1 || \ echo "note: $(BINDIR) is not on PATH -- add it, or run $(BINDIR)/$(COLITUR) directly" uninstall: ## remove everything install put into PREFIX -$(DUNE) dune uninstall --prefix $(PREFIX) - rm -f $(MANDIR)/colitur.1 $(MAN5DIR)/colitur-overlay.5 $(MAN5DIR)/colitur-templates.5 + rm -f $(MANDIR)/colitur.1 $(MAN5DIR)/colitur-overlay.5 $(MAN5DIR)/colitur-templates.5 $(MAN5DIR)/colitur-config.5 @echo "removed $(COLITUR) from $(PREFIX)" reinstall: uninstall install ## uninstall then install (the installed copy is a snapshot, not a link) @@ -135,11 +136,13 @@ man: ## preview the man pages man -l man/colitur.1 man -l man/colitur-overlay.5 man -l man/colitur-templates.5 + man -l man/colitur-config.5 doc: ## lint the man pages (groff warnings; silence means clean) groff -man -Tutf8 -ww -z man/colitur.1 groff -man -Tutf8 -ww -z man/colitur-overlay.5 groff -man -Tutf8 -ww -z man/colitur-templates.5 + groff -man -Tutf8 -ww -z man/colitur-config.5 fmt: ## format the OCaml sources $(DUNE) dune build @fmt --auto-promote @@ -41,6 +41,32 @@ See `colitur-templates(5)` for the template format (syntax, escaping, the full field reference) and `colitur(1)` for `emit`, `table`/`render` and `publish` in full. +## Language + +Every command's output is names, not slugs, by default (`--raw` restores +the bare-slug form). Start a new translation from the shipped Latin table, +edit a couple of entries, check it, and render with it -- every line below +is real, not transcribed: + +```sh +$ dune exec colitur -- lang --dump la > my-lang.ini +$ sed -i \ + -e 's/^ef-circumcision = .*/ef-circumcision = My Own Circumcision Text/' \ + -e 's/^ef-epiphany = .*/ef-epiphany = My Own Epiphany Text/' \ + my-lang.ini +$ dune exec colitur -- lang --check my-lang.ini +my-lang.ini: 725 of 725 celebrations named, 0 missing, 0 unknown +$ dune exec colitur -- day 2026 --lang ./my-lang.ini | head -1 +2026-01-01 thursday christmastide - ef-circumcision class-1 white My Own Circumcision Text +``` + +`--check` reports what a language file is still missing, and rejects any +entry naming a slug that does not exist (a typo, otherwise silently dead). +`colitur lang --list` shows what this build can find; `colitur config +--show` shows the effective `--lang` (and every other setting) plus where +it came from. See `colitur(1)`'s `NAMING` section and `colitur-config(5)` +for both in full. + ## License AGPL-3.0-or-later. See `LICENSE`. diff --git a/man/colitur-config.5 b/man/colitur-config.5 new file mode 100644 index 0000000..8f21028 --- /dev/null +++ b/man/colitur-config.5 @@ -0,0 +1,308 @@ +.TH COLITUR\-CONFIG 5 "2026" "colitur" "File Formats" +.SH NAME +colitur\-config \- default settings file for colitur(1) +.SH SYNOPSIS +.I ~/.config/colitur/config.ini +.SH DESCRIPTION +A +.B colitur +config file supplies DEFAULTS for four settings \(em +.IR lang ", " overlay ", " template " and " format +\(em so a command line does not have to repeat +.B \-\-lang +.IR en , +.B \-\-overlay +.I ~/calendars/diocese.sexp +or +.B \-\-template +.I booklet.tex +on every invocation. +.PP +A config file is entirely +.BR OPTIONAL . +With none present, every command behaves exactly as it does without this +feature, except that display names still resolve through the built\-in +default language +.RI ( la ). +.SH LOCATION +.RS +.nf +$XDG_CONFIG_HOME/colitur/config.ini if $XDG_CONFIG_HOME is set and non\-blank +$HOME/.config/colitur/config.ini otherwise +.fi +.RE +.PP +If neither variable is usable (no +.BR HOME ), +there is no config path at all, and colitur behaves exactly as if no config +file existed \(em this is not an error. +.SH PRECEDENCE +For every one of the four settings, in order: +.RS +.nf +1. the command\-line flag (\-\-lang, \-\-overlay, \-\-template, \-\-format) +2. the config file's own value +3. the built\-in default +.fi +.RE +.PP +.BR "colitur config \-\-show" , +described below, reports EXACTLY this resolution for the flags given on +that same command line \(em it is not a separate calculation that could +disagree with what a real command actually does; both go through the same +resolver. +.PP +.IR lang ", " template " and " format +are each a single scalar value: config supplies one only when the +corresponding flag is +.B entirely absent +from the command line, never merged or overridden field\-by\-field with a +partial flag value. +.I overlay +is a +.B list +(it is repeatable on the command line, and overlays apply in order, last +writer wins) and follows the identical rule at the list level: config's +.I overlay +lines are used only when +.B zero +.B \-\-overlay +flags were given; a single +.B \-\-overlay +on the command line replaces the config list entirely rather than adding to +it. This keeps the direction of precedence uniform across all four settings +\(em flag beats config beats default \(em rather than inventing a merge +order nothing else in this file documents. +.SH FORMAT +An INI file, read by the same section reader +.BR colitur\-overlay (5)'s +flat INI form and colitur's own language files use: section names in +.RB \(lq [ name ] \(rq , +.RB \(lq key " = " value \(rq +lines, +.B ; +and +.B # +comments, blank lines ignored. No continuations, no quoting. +.PP +Every setting lives in a section named +.BR defaults . +A section named anything else is +.B unrecognised +\(em including a plain typo such as +.B [deafults] +\(em and is warned about, not silently dropped (see +.B UNKNOWN KEYS AND SECTIONS +below). +.PP +If +.B [defaults] +appears more than once, every occurrence is merged, not only the first: a +hand\-edited config file will naturally grow a second +.B [defaults] +block as a user appends a line later, and only reading the first would +silently ignore it. Within one key, or across two blocks, the +.B LAST +occurrence wins \(em the natural reading of "the bottom line is the one +that took effect" for a file edited by hand over time. A repeated +.B overlay +key is the one exception: it +.B accumulates +(one entry per line), because a user legitimately has more than one local +overlay. +.SH SETTINGS +.TP +.B lang +A language CODE (e.g. +.BR la ", " en ) +or a file path, exactly as +.B \-\-lang +accepts it. Default +.BR la . +See +.BR colitur (1)'s +own +.B NAMING +section for how a CODE resolves to a file, and what an unknown language +does (a hard error, on every command, never a silent fallback). +.TP +.B overlay +A local calendar file, applied on top of the shipped universal one, exactly +as +.B \-\-overlay +accepts it. Repeatable \(em one +.B overlay +.B = +line per file, applied in the order they appear in the config file. No +default (an empty list). +.TP +.B template +A template file path, exactly as +.B \-\-template +accepts it. Relevant only to +.BR table " and " render , +which require a template either from this setting or from +.BR \-\-template ; +no default. +.PP +.B Neither +.I overlay +.B nor +.I template +.B is shell\-expanded. +A value is read as a literal string and handed straight to the filesystem +\(em there is no shell here to turn a leading +.B ~ +into +.IR $HOME . +.RS +.nf + +$ printf '[defaults]\enoverlay = ~/calendars/diocese.sexp\en' > config.ini +$ XDG_CONFIG_HOME=. colitur day 2026 +colitur: failed to load ~/calendars/diocese.sexp: ~/calendars/diocese.sexp: No such file or directory +.fi +.RE +.PP +Write the full path instead +.RI ( $HOME /calendars/diocese.sexp, +which the SHELL expands when the config file is CREATED, e.g. via +.BR "echo \(dqoverlay = $HOME/calendars/diocese.sexp\(dq >> config.ini" , +not a bare +.BR ~ / ... " written literally into the file)." +.TP +.B format +One of +.BR csv ", " json ", " sexp ", " xml " or " ics , +exactly as +.B \-\-format +accepts it. Relevant only to +.BR emit , +which requires a format either from this setting or from +.BR \-\-format ; +no default. +.SH UNKNOWN KEYS AND SECTIONS +A key inside +.B [defaults] +that is none of the four settings above, and a section that is not named +.BR defaults , +are each reported \(em on standard error, prefixed with the config file's +own path \(em and then +.BR ignored . +Neither is fatal on its own. This is deliberate: a config file written for +a newer +.B colitur +must still work on an older one that does not yet recognise every setting, +but silently discarding a line the user wrote is exactly how a setting they +believe is active quietly does nothing. The two are reported separately, +worded differently, so a misspelled +.B SECTION +(the whole block silently doing nothing) reads differently from a +misspelled +.B KEY +inside an otherwise\-recognised one: +.RS +.nf + +colitur: ~/.config/colitur/config.ini: unknown setting "langauge" (ignored) +colitur: ~/.config/colitur/config.ini: unknown section [deafults] (ignored) +.fi +.RE +.PP +A config file that +.B EXISTS +but fails to +.B parse +at all \(em an unterminated +.RB \(lq [ \(rq , +a +.B key = value +line before any section header \(em is a different case and is +.BR FATAL : +exit +.BR 2 , +naming the file and the reason, on +.B every +command, not only ones that read +.IR lang / overlay / template / format . +The config file is loaded once, unconditionally, before any command +dispatches \(em a config the user wrote and colitur cannot honour at all is +not something to silently carry on past, even for a command +( +.B \-\-help +included) that would not otherwise have consulted it. +.SH SHOWING THE RESOLVED CONFIGURATION +.BI "colitur config " \-\-show +prints the config file's own path and whether it exists, then every one of +the four settings with its resolved value and where that value came from: +.BR flag ", " config " or " default . +There is deliberately no separate "provenance" function elsewhere in the +program \(em the same resolver that decides what +.B colitur day +actually uses is what +.B \-\-show +reports, so the two cannot disagree. +.PP +Flags given on the SAME +.B config \-\-show +command line preview exactly what they would resolve to on any other +command \(em +.B config +is the one command that does NOT refuse +.BR \-\-lang / \-\-overlay / \-\-template / \-\-format +as having no effect on it, because previewing their resolution +.I is +its effect. +.I overlay +has no single scalar value, so it is listed one line per effective entry +instead of one line total. +.SH WORKED EXAMPLE +Reproducible against the shipped example overlay \(em every literal below is +a real run, not a transcription. The full, absolute path is written into the +file (see the shell\-expansion note above \(em a bare +.B ~ +would fail to load): +.RS +.nf +$ mkdir \-p ~/.config/colitur +$ cat > ~/.config/colitur/config.ini <<EOF +[defaults] +lang = en +overlay = $HOME/.local/share/colitur/examples/diocesan\-example.sexp +EOF +$ colitur config \-\-show +config file: /home/user/.config/colitur/config.ini (exists) +lang en (config) +template (none) (default) +format (none) (default) +overlay /home/user/.local/share/colitur/examples/diocesan\-example.sexp (config) +$ colitur day 2026 | head \-1 +2026\-01\-01 thursday christmastide \- ef\-circumcision class\-1 white The Octave Day of the Nativity +.fi +.RE +.PP +.B day +above used +.I en +and the diocesan example overlay with no flags at all, because neither was +given on the command line; a real run with +.B \-\-lang la +or +.B \-\-overlay +would override each independently, per\-invocation, without editing the +file. +.SH SEE ALSO +.BR colitur (1) +for +.BR \-\-lang / \-\-raw , +.BR "colitur lang" , +and the +.B NAMING +section in full. +.PP +.BR colitur\-overlay (5) +for the overlay file format +.I overlay +names. +.SH LICENSE +AGPL\-3.0\-or\-later. diff --git a/man/colitur-templates.5 b/man/colitur-templates.5 index 2f42d68..06ad583 100644 --- a/man/colitur-templates.5 +++ b/man/colitur-templates.5 @@ -107,9 +107,6 @@ like an .BR colitur\-overlay (5) file, and is never allowed to crash the program that reads it. .SH SCOPE AND LOOKUP -.B This section documents a real hazard, not a theoretical one \(em it has -.B produced wrong output during this program's own development. -.PP Scope is a stack. Rendering starts with the whole view (the year) as the one entry on the stack; each .B {{#section}} @@ -131,79 +128,46 @@ could never reach .BR {{year}} , which lives only on the outermost object. .PP -.B The hazard: a DOTTED PATH that resolves only PART WAY inward falls back -.B WHOLESALE to an outer scope of the SAME NAME. -A bare key inside the immediately enclosing section is not at risk this way -\(em it either resolves right there or fails outright (see -.B num -below); it is specifically a dotted path, one step of which is missing from -the inner object, that abandons the whole path and restarts the lookup one -level out. Falling back outward means a name that exists at -.I both -levels never fails and never warns \(em it just silently resolves to the -.I outer -one, because the inner object's own absence of that key is indistinguishable -from "look further out" and "this key does not apply here". Two collisions -are known to exist in the shipped view model: -.TP +.B A hazard this section used to document here no longer exists, and is +.B recorded as removed rather than silently dropped from this page. +Before the view model's naming rework, .B name -Both a +was an object keyed by language (e.g. +.BR la " and " en ), +carried on both a .B month and a -.B day -carry a -.I name -field (each an object keyed by language, e.g. -.BR la " and " en ). -Written naively, when -.B {{#days}} -is nested inside -.BR {{#months}} , -a bare +.BR day . +A dotted path that resolved only part way inward \(em a bare .B {{name.la}} -does -.I not -resolve to the day's own Latin name. It resolves to the -.I enclosing month's -Latin name, because the day's own -.I name -object either has no -.B la -key (an unnamed day) or the dotted path fails partway and the WHOLE path -falls back to the outer scope, which does have one. This is not a corner -case: on an ordinary month, most days carry no Latin name at all (only named -sanctoral days do), so the naive form renders the +on a day with no Latin name of its own \(em fell back WHOLESALE to the +enclosing +.BR month 's +own +.B name.la +instead of failing, because the lookup rule above cannot distinguish "this +key does not apply here" from "look further out": on an ordinary month, most +days carried no Latin name at all, so the naive form silently printed the .I month's -name on nearly every day \(em in a per\-month booklet, dozens of wrong -lines; in a month grid (whose -.B {{#weeks}} -is itself only ever reachable through -.BR {{#months}} , -since -.I weeks -is a field of -.IR month , -never a top\-level list) EVERY cell reads the month's own name. -.PP -.B The collision needs a month actually on the scope stack to fire. -Iterating the TOP\-LEVEL, flat -.B days -list directly ( -.B {{#days}}...{{/days}} -at the outermost level, never passing through +own name on nearly every day. +.B name +is now a PLAIN STRING, always fully resolved before the template ever sees +it (see +.B VIEW MODEL +below) \(em there is no dotted path left for a partial match to climb out +of, so this specific hazard is unrepresentable, not merely avoided by +convention. A bare +.B {{name}} +inside +.B {{#days}} +always reads that day's own name, nested under .B {{#months}} -first) puts no month object anywhere on the stack, so the naive form does -.I not -silently substitute the wrong answer there \(em it silently resolves to -nothing, exactly as any other absent key would, because there is no -outer scope left to climb to. This is precisely what makes the hazard easy -to miss: an author who tries the naive form against the flat list first -sees the unnamed days come out empty, reasonably concludes the form is -safe, and then hits the real collision the moment the identical fields are -read from inside -.BR {{#months}} , -which is what every shipped template that produces a grid or a per\-month -booklet actually does. +or not, full stop. +.PP +.B One collision of the same underlying shape still exists in the shipped +.B view model, because it involves two PLAIN (non\-dotted) fields that +.B happen to share a name, which the fallback rule above cannot help with +.B either: .TP .B num Both a @@ -216,46 +180,77 @@ field. Inside .BR {{#weeks}} , a bare .B {{num}} -is the week's own number, correctly \(em but only because nothing between -the week and the day currently redefines it. A template that reaches +is the week's own ordinal (1, 2, 3, ...) within its month, correctly \(em +but only because nothing between the week and wherever +.B {{num}} +is actually written currently redefines it. A +.B day +object has no .I num -from any scope where the immediately enclosing section does not itself -carry it will silently climb to whichever ancestor does, and that may not be -the one the author meant. -.PP -.B The safe idiom. -Push the object you actually want onto the scope stack yourself, with a -.B {{#name}} -section, before reading its fields \(em then a bare field inside that -section can only resolve against the object you just pushed, or fail -outright and fall through to an inverted fallback you write explicitly: +field of its own at all, so a bare +.B {{num}} +written inside +.B {{#days}} +climbs straight past the day to the enclosing +.BR week 's +own +.IR num , +not the month's, even where a template author reaching for "the month +number" from deep inside a day cell might expect otherwise. Verified live, +not merely reasoned about (a template iterating +.BR {{#months}}{{#weeks}}{{#days}} , +printing the enclosing week's own +.I num +beside the month's own +.IR month_num , +both carried on every day and week object for exactly this reason \(em see +.B VIEW MODEL +below): .RS .nf -{{#name}}{{la}}{{^la}}{{slug}}{{/la}}{{/name}} +.B colitur table \-\-year 2027 \-\-template num\-hazard.txt +week 1 of month 1 + day\-scope num: 1 +week 2 of month 1 + day\-scope num: 2 .fi .RE -This opens the day's own -.I name -object (never the month's \(em a -.B {{#section}} -always resolves its OWN path from the point it appears, which for -.B {{#name}} -written inside -.B {{#days}} -is the day's -.IR name , -shadowing the month's identically\-named field exactly as intended), reads -.B la -from it, and falls back to the day's own -.B slug -only when -.B la -is genuinely absent from -.I that -object \(em never when it is merely absent from an ancestor. Every shipped -template that prints a day's name uses exactly this idiom; none uses the -bare dotted form. +The "day\-scope num" line is reading a bare +.B {{num}} +from inside +.BR {{#days}} . +It changes from week to week, proving it is the enclosing +.BR week 's +own ordinal, not the constant +.B 1 +a reader expecting "month number" might assume. +.PP +.B The safe idiom, when a template genuinely needs a number from an +.B ancestor further out than the immediately enclosing section: +use the field the view already carries FOR that purpose rather than a bare, +climbing +.BR {{num}} . +A +.B week +object already carries +.B month_num +(and +.BR month_name ) +precisely because the engine has no +.RB \(lq "../" \(rq +parent\-path syntax to reach the enclosing month any other way \(em see +.B week +under +.B VIEW MODEL +below. There is no equivalent shortcut for reaching a +.B week +object's own +.I num +from inside +.BR {{#days}} , +because no shipped template needs one; a template that does should carry it +down explicitly the same way, rather than relying on the climb. .SH HOST\-LANGUAGE COMMENTS .B The engine has no awareness of the target language's own comment syntax. A @@ -438,6 +433,12 @@ for the rite's own scope and limitations (readings cover only the Epistle and Gospel; the votive Office of the Blessed Virgin Mary on Saturday does not yet select among its five seasonal Masses). .SH VIEW MODEL +.B Every field below is verified against +.I lib/render/view.ml +.B and +.IR schema/day\-v1.json , +.B not transcribed from memory or from an earlier version of this page. +.PP The value a template renders against is built once per .B colitur table / @@ -447,6 +448,7 @@ The value a template renders against is built once per invocation, from the same resolved calendar .B colitur emit uses, and is shaped for two artefacts from one model: a flat booklet (the +top\-level .B days list, one entry per day of the requested year) and a month grid (the .B months @@ -457,6 +459,28 @@ cells so every row has exactly seven). This is the same shape published at .IR schema/day\-v1.json , described here in prose; the JSON Schema is the machine\-checked contract and this page is its worked explanation. +.PP +Every localised field below (marked +.RI \(lq "in the active language" \(rq ) +resolves through whichever language +.B \-\-lang +selected, default +.IR la ; +under +.BR \-\-raw , +each equals its own unlocalised counterpart (a +.B name +equals its +.BR slug , +a +.B rank_name +equals its +.BR rank , +and so on) rather than being blank \(em see +.BR colitur (1)'s +own +.B NAMING +section. .SS Top level .TP .B rite @@ -466,6 +490,32 @@ The rite identifier, currently always the string .B year The civil year requested, as a four\-digit string. .TP +.B term +A fixed, closed vocabulary of strings in the active language, so a +translated booklet needs no template edit for its own boilerplate words. +Object keys: +.BR ordo ", " contents ", " epistle ", " lesson ", " gospel ", " +.BR commemoration " and " week . +Referenced as +.BR {{term.ordo}} , +.BR {{term.epistle}} , +and so on. +.TP +.B weekday_headings +A list of exactly seven objects, Sunday first, each +.RB { name ", " last }, +for a localised grid header row \(em +.B name +is the weekday's own name in the active language, +.B last +is true on the seventh (Saturday) entry, the same +.RB \(lq "unless this is the last one" \(rq +flag the +.B day +object's own +.B last +field below provides for a week's row of cells. +.TP .B months A list of twelve .B month @@ -483,14 +533,9 @@ template iterates over directly, without going through The month number, 1 through 12, as a string. .TP .B name -An object keyed by language (currently -.B la -and -.BR en ), -each value the month's own name in that language (e.g. -.RB \(lq Ianuarius \(rq -/ -.RB \(lq January \(rq ). +The month's own resolved display name in the active language (e.g. +.RB \(lq Ianuarius \(rq ), +a plain string. .TP .B days This month's own @@ -512,7 +557,39 @@ The week's ordinal within its month (1, 2, 3, ...), as a string. This is .I not a liturgical week number \(em see .B day \(-> week -below for that. +below for that, and +.B SCOPE AND LOOKUP +above for the +.B num +collision this field and the enclosing month's own +.B num +share. +.TP +.B month_num +.TQ +.B month_name +The enclosing month's own +.B num +and +.BR name , +carried onto every week because the engine has no +.RB \(lq "../" \(rq +parent\-path syntax \(em a nested +.B {{month_num}} +or +.B {{name}} +read from inside +.B {{#weeks}} +would otherwise be unable to reach the month at all (a bare +.B {{name}} +here would in fact resolve to the WEEK's enclosing DAY's name once one is +pushed, not the month's, since +.I week +itself carries no +.B name +field of its own \(em carrying +.B month_name +explicitly is what avoids relying on that climb). .TP .B days Exactly seven @@ -556,8 +633,13 @@ seven entries); a template checks this, not emptiness, to decide whether to render a cell's contents. .TP .B season -The liturgical season's own string name (e.g. -.BR paschaltide ", " lent ). +The liturgical season's own unlocalised string key (e.g. +.BR paschaltide ", " lent ), +stable across every language \(em unaffected by +.BR \-\-lang / \-\-raw . +.TP +.B season_name +The season's own resolved display name, in the active language. .TP .B week The liturgical week number within the season, as a string, or the empty @@ -578,30 +660,60 @@ key of its own at all, only so there is nothing for it to shadow). .TP .B slug -The observed celebration's stable identifier (e.g. -.BR ef\-easter\-sunday ). +The observed celebration's stable, unlocalised identifier (e.g. +.BR ef\-easter\-sunday ), +unaffected by +.BR \-\-lang / \-\-raw \(em +the machine key a template or a downstream script keys off, distinct from +.B name +below. .TP .B name -An object keyed by language, the observed celebration's own name in each -language colitur's data supplies one for. Frequently has no -.B la -or -.B en -key at all (most temporal days, most sanctoral entries in the shipped data) -\(em see -.B SCOPE AND LOOKUP -above for the resulting month\-name collision and its safe idiom. +The observed celebration's resolved display name, in the active language +\(em a PLAIN STRING, not an object keyed by language. Under +.B \-\-raw +this equals +.B slug +exactly, which is what makes +.B \-\-raw +output byte\-stable; a real language's own table names essentially every +slug the engine can produce (verified: 725 of 725 over the window colitur's +own coverage test measures), so in ordinary operation +.B name +differs from +.B slug +on nearly every day, not only the ones with a proper name in the historical +sense. +.TP +.B weekday +The day's own weekday name in the active language (e.g. +.RB \(lq "Feria V" \(rq , +.RB \(lq Sabbatum \(rq ), matching +.BR dow 's +numbering (0 = Sunday). .TP .B rank -The observed celebration's class, as the kernel's own string (e.g. -.BR class\-1 ). -There is deliberately no separate, localized rank label: the kernel carries -no per\-language rank names to draw one from. +The observed celebration's class, as the kernel's own unlocalised string +key (e.g. +.BR class\-1 ), +unaffected by +.BR \-\-lang / \-\-raw . +.TP +.B rank_name +The rank's own resolved display name, in the active language (e.g. +.RB \(lq "I classis" \(rq ). .TP .B colour -The observed celebration's liturgical colour, lowercase (one of +The observed celebration's liturgical colour, lowercase, unlocalised (one of .BR white ", " red ", " green ", " violet ", " rose ", " black , -or the empty string). +or the empty string), unaffected by +.BR \-\-lang / \-\-raw . +.TP +.B colour_name +The colour's own resolved display name, in the active language (e.g. +.RB \(lq albus \(rq +for +.BR white ). .TP .BR is_white ", " is_red ", " is_green ", " is_violet ", " is_rose ", " is_black Six booleans, exactly one true (matching @@ -614,16 +726,17 @@ is no expression evaluation, so this is the only way a template branches on colour at all. .TP .B subject -Whose feast this is, lowercase (one of +Whose feast this is, lowercase, unlocalised (one of .BR lord ", " bvm ", " saint ", " temporal ). .TP .B comms A list of commemoration objects admitted on this day, each carrying -.BR slug , -.B name -(an object keyed by language, same shape as the day's own -.BR name ), -and +.BR slug " (unlocalised), " name +(the SAME resolved\-string shape as the day's own +.BR name , +through the identical language table \(em a commemoration's slug is drawn +from the same sanctoral/temporal pool as the observed day's, not a second +vocabulary), and .B privileged (boolean: true for a privileged commemoration under RG 109, which survives even where an ordinary one would be capped out). Empty list on a day with no @@ -634,14 +747,18 @@ cell. A list of at most one object, present when a feast impeded elsewhere was transferred onto THIS day (RG 96\(en98); carries the transferred celebration's own -.BR slug . +.B slug +only \(em +.I not +a resolved +.BR name . Empty list when nothing transferred in. .TP .B transferred_out A list of objects, one per celebration that would have fallen on this day but was displaced and moved to a later date; each carries .B slug -and +(only, as above) and .B to (the ISO\-8601 date it was moved to). Empty on the ordinary day. .TP @@ -649,6 +766,9 @@ and The Epistle/Lesson reading citation (e.g. .RB \(lq "Heb 1:1\-12" \(rq ), never scripture text \(em a reference only. Empty string when none resolved. +Unaffected by +.BR \-\-lang / \-\-raw : +a citation is a reference, not a display name. .TP .B gospel The Gospel reading citation, same shape as @@ -666,16 +786,15 @@ an eighth, empty column and .B pdflatex would reject the file outright. .SH A WORKED MINIMAL TEMPLATE +.B Every line below is pasted from a real run, not written by hand. A plain\-text booklet, days nested inside months \(em the shape every -shipped template actually uses, and the shape the -.B SCOPE AND LOOKUP -hazard needs to fire \(em using the safe name idiom from that section: +shipped template actually uses: .RS .nf {{rite}} {{year}} {{#months}}{{#days}} -{{iso}} {{#name}}{{la}}{{^la}}{{slug}}{{/la}}{{/name}} {{colour}}{{#comms}} +{{slug}}{{/comms}} +{{iso}} {{weekday}} {{name}} {{colour_name}}{{#comms}} +{{name}}{{/comms}} {{/days}}{{/months}} .fi .RE @@ -693,89 +812,119 @@ one): .B colitur table \-\-year 2026 \-\-template minimal.txt | head \-7 ef 2026 -2026\-01\-01 ef\-circumcision white +2026\-01\-01 Feria V In Octava Nativitatis Domini albus -2026\-01\-02 ef\-christmas\-1\-friday white +2026\-01\-02 Feria VI Feria VI ante Epiphaniam albus -2026\-01\-03 Officium sanctae Mariae in sabbato white +2026\-01\-03 Sabbatum Officium sanctae Mariae in sabbato albus .fi .RE .PP -The second data line shows the fallback firing: 2 January carries no Latin +Every line names a real celebration in Latin \(em there is no unnamed\-day +fallback to demonstrate here the way an earlier version of this page showed +one: the shipped +.I la +table names every slug the engine can produce (725 of 725 over the window +its own coverage test measures), so .B name -in the shipped data, so -.B {{^la}} -supplies -.B {{slug}} -instead. The third shows the non\-fallback case: 3 January -.I does -carry a Latin name (the votive Office of the Blessed Virgin Mary on -Saturday), and the idiom prints it correctly. -.PP -Now swap only the interpolation \(em the safe idiom above becomes the -naive, unsafe -.B {{name.la}} , -nesting left exactly as it was: +is never the bare +.B slug +in ordinary operation. +.B \-\-raw +is what produces the bare\-slug form instead, on demand, not a gap in the +data: .RS .nf -{{rite}} {{year}} -{{#months}}{{#days}} -{{iso}} {{name.la}} {{colour}} -{{/days}}{{/months}} +.B colitur table \-\-year 2026 \-\-template minimal.txt \-\-raw | head \-3 +ef 2026 + +2026\-01\-01 4 ef\-circumcision white .fi .RE .PP -Rendered against the identical year, same extension and flavour: +.RB \(lq 4 \(rq +above is +.BR weekday 's +own raw form: with no language table at all, +.I weekday +falls back to the numeral +.BR dow +itself carries (Thursday, 1 January 2026), not a slug\-like string \(em +there is no bare English or Latin word for a weekday to echo back the way a +missing celebration name echoes its own +.BR slug . +.PP +The +.B num +collision from +.B SCOPE AND LOOKUP +above, reproduced here in full \(em a grid\-shaped template, a week nested +inside a month, a day nested inside the week: .RS .nf -.B colitur table \-\-year 2026 \-\-template minimal\-unsafe.txt | head \-7 -ef 2026 - -2026\-01\-01 Ianuarius white - -2026\-01\-02 Ianuarius white +{{#months}}{{#weeks}}week {{num}} of month {{month_num}} +{{#days}} day\-scope num: {{num}} +{{/days}}{{/weeks}}{{/months}} +.fi +.RE +.RS +.nf -2026\-01\-03 Officium sanctae Mariae in sabbato white +.B colitur table \-\-year 2027 \-\-template num\-hazard.txt | head \-9 +week 1 of month 1 + day\-scope num: 1 + day\-scope num: 1 + day\-scope num: 1 + day\-scope num: 1 + day\-scope num: 1 + day\-scope num: 1 + day\-scope num: 1 +week 2 of month 1 .fi .RE .PP -The collision fires for real on the first two lines: with a month on the -scope stack, the day's own missing -.B la -key falls back all the way out to the ENCLOSING MONTH's own -.RB \(lq Ianuarius \(rq -rather than failing. The third line is untouched, because 3 January's own -.I name -object genuinely has a -.B la -key, so the dotted path resolves without ever needing to fall back. -.PP -.B This is specific to nesting, and that is the point. -The identical naive -.B {{name.la}} -written against the TOP\-LEVEL, flat -.B days -list (no -.B {{#months}} -wrapping it) does -.I not -print -.RB \(lq Ianuarius \(rq -anywhere \(em there is no month on the stack to fall back to, so it prints -nothing on an unnamed day instead, silently but not wrongly. See +Every +.RB \(lq "day\-scope num" \(rq +line inside week 1 reads +.BR 1 , +and every one inside week 2 (not shown above, but the next seven lines of +the same run) reads +.BR 2 : +a bare +.B {{num}} +written inside +.B {{#days}} +is reading the enclosing +.BR week 's +own ordinal, silently, because +.I day +itself carries no +.I num +field to shadow it with. Nothing here is wrong \(em the lookup rule is +doing exactly what .B SCOPE AND LOOKUP -above for why testing the naive form only against a flat list is exactly -how this hazard goes unnoticed until a template is later nested under -.BR {{#months}} . +documents \(em but a template author who expected "the month number" from +that bare +.B {{num}} +would be reading the wrong field with no warning at all. Compare +.B {{month_num}} +in the same template, which stays +.B 1 +throughout January regardless of which week or day it is read from, +because it is carried explicitly rather than reached by climbing. .SH SEE ALSO .BR colitur (1) for .BR table ", " render " and " publish , -and for the five +for the five .B emit -formats that share this same view model. +formats that share this same view model, and for +.BR \-\-lang / \-\-raw +and the +.B NAMING +section that controls every localised field this page documents. .PP .BR colitur\-overlay (5) for the local\-calendar file format that supplies the celebrations a @@ -784,5 +933,14 @@ template's and .B slug fields can carry. +.PP +.BR colitur\-config (5) +for setting a default +.B \-\-template +so +.B table +/ +.B render +need not repeat it on every invocation. .SH LICENSE AGPL\-3.0\-or\-later. |
