(** A language table: strings to strings, nothing more. Knows nothing about calendars, dates or rites, and never touches the filesystem -- callers hand it text. That is what lets every command use it without the kernel learning about presentation. Every lookup is TOTAL. A key with no entry returns THE KEY ITSELF, never the empty string: a partial translation must be usable from its first line, and an untranslated day must still print something a reader can act on. This is also why the pre-naming output (bare slugs) is exactly what an empty table produces -- the degraded case is the old behaviour, not a blank page. *) type t (** Parse INI text. Never raises. [Error] on a malformed file or a missing [\[meta\] lang]. Two duplicate policies, both LAST-WINS: - A section name repeated in the file (e.g. two [\[celebration\]] blocks) has ALL of its blocks merged, not only the first -- a 595-entry hand-edited language file WILL grow duplicate section headers as contributors append entries over time, and dropping a later block would silently lose real translations. - Where the same key appears more than once -- within one block or across two of them -- the value from further down the file wins. Both read the same order a reader would: later in the file overrides earlier. This is the OPPOSITE direction from {!Colitur_kernel.Overlay_ini.get} ([List.assoc_opt], first match) over the very same [section.fields] shape -- the two modules resolve a duplicate key in opposite directions, so do not assume one's behaviour from the other's. *) val of_string : string -> (t, string) result val code : t -> string val fallback_code : t -> string option (** [with_fallback t base] resolves through [t] first, then [base], then the key. *) val with_fallback : t -> t -> t (** The identity table: every lookup returns its key. This is what [--raw] uses, so raw output is one table passed around rather than a special case threaded through every call site. *) val raw : t val celebration : t -> string -> string val season : t -> string -> string 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 (** A Bible book's name, from the [\[bible\]] section. The key is [.full] or [.abbr] (e.g. ["luke.abbr"]). A miss returns the key itself, the same TOTAL-lookup contract as every other function here -- callers turn that into a real fallback via {!Colitur_citation.Book.default_spelling}, not this module. *) val bible : t -> string -> string (** [weekday t n], 0 = Sunday. Out-of-range [n] returns [string_of_int n]. *) val weekday : t -> int -> string (** [month t n], 1 = January. Out-of-range [n] returns [string_of_int n]. *) val month : t -> int -> string (** [month_abbr t n], 1 = January, e.g. ["Ian"]/["Jan"]. Same shape as {!month}: out-of-range [n] returns [string_of_int n], and a miss falls back through the chain to [string_of_int n] as well, never to [month]'s own full name -- an abbreviation is its own vocabulary, not a truncation of another lookup. *) val month_abbr : t -> int -> string (** Every (section-qualified key, value) pair, sorted. Used by [lang --dump] and [lang --check]. Keys are qualified as e.g. ["celebration.ef-epiphany"]. Includes [\[bible\]] (["bible.luke.abbr"]): book names are translatable text, so a file missing them is genuinely incomplete and [lang --check] should say so. Deliberately EXCLUDES [\[sigla\]]: those are five citation- style settings with working defaults ({!Colitur_citation.Render.default_style}), not names a translator owes -- listing them here would make [--check] demand five settings from every language file. *) val keys : t -> (string * string) list (** The [\[sigla\]] section's raw fields, for {!Colitur_citation.Render.style_of_fields} to read. Values come back TRIMMED (whitespace at both ends) but still QUOTED if the file quoted them -- unquoting is [Render]'s job, not this module's, the same reason {!Colitur_kernel.Overlay_ini} (which this parses through) never unquotes either. An empty (or absent) [\[sigla\]] section returns [[]]; unlike every lookup above, there is no per-key TOTAL contract to keep here -- this is a settings bag, not a translation table, and [Render] already supplies its own defaults for whatever is missing. *) val sigla_fields : t -> (string * string) list