aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 20:19:00 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 20:19:00 +0200
commit42636312160892d1bc6a7f021ce27354f7cbb80d (patch)
tree88d75c31ed996dfd459268b8d75aeb24cd646ac6
parent643d01c388eece9f3a00d2db16cb421f5483b229 (diff)
downloadlectio-42636312160892d1bc6a7f021ce27354f7cbb80d.tar.gz
lectio-42636312160892d1bc6a7f021ce27354f7cbb80d.zip
feat(config): reading_lang/reading_version + ReadingCorpus resolver
Task 3 of national-bibles. Adds reading_lang (any language code) and reading_version (explicit corpus code) config fields, setField cases, renderConfigINI lines + self-documenting header, and Config.ReadingCorpus(): reading_version > reading_lang match > ui_language match > "" (Latin fallback applied by the caller). Also adds an `autoselect` corpus-metadata flag (default true), surfaced during this task: the built-in wuj declares lang=pl, so lang-auto would have re-selected the incomplete Wujek for Polish users and defeated the wuj-drop. wuj.ini now sets autoselect=false, so it is reachable only via an explicit reading_version or --ref; the resolver's language auto-match skips non-autoselectable corpora. A corrected Wujek drop-in (autoselect defaults true) auto-serves Polish with zero config.
-rw-r--r--internal/bible/corpora/wuj.ini1
-rw-r--r--internal/bible/corpusmeta.go9
-rw-r--r--internal/bible/registry.go2
-rw-r--r--internal/config/config.go33
-rw-r--r--internal/config/config_test.go20
5 files changed, 63 insertions, 2 deletions
diff --git a/internal/bible/corpora/wuj.ini b/internal/bible/corpora/wuj.ini
index 8364923..22eeca3 100644
--- a/internal/bible/corpora/wuj.ini
+++ b/internal/bible/corpora/wuj.ini
@@ -2,3 +2,4 @@
lang = pl
name = Wujek (Polish)
psalm_system = vulgate
+autoselect = false
diff --git a/internal/bible/corpusmeta.go b/internal/bible/corpusmeta.go
index 04b9dcb..60d90a9 100644
--- a/internal/bible/corpusmeta.go
+++ b/internal/bible/corpusmeta.go
@@ -3,15 +3,20 @@ package bible
import "github.com/lukaszkasprzak/lectio/internal/ini"
// CorpusMeta is the sidecar metadata for a bible corpus (<code>.ini).
+// Autoselect (default true) governs whether the corpus may be picked by the
+// reading resolver's language auto-match; a corpus with Autoselect=false stays
+// reachable only via an explicit reading_version or --ref (e.g. the built-in
+// wuj, which is incomplete). See docs national-bibles design.
type CorpusMeta struct {
Code, Lang, Name, PsalmSystem, Sigla string
+ Autoselect bool
}
// parseCorpusMeta reads a section-less sidecar. ini.Parse returns
// []ini.Section{Name, Pairs}; keys before any [section] land in the section
// whose Name == "" (verified against internal/ini).
func parseCorpusMeta(code string, data []byte) CorpusMeta {
- m := CorpusMeta{Code: code}
+ m := CorpusMeta{Code: code, Autoselect: true} // absent autoselect => true
secs, err := ini.Parse(data)
if err != nil {
return m
@@ -30,6 +35,8 @@ func parseCorpusMeta(code string, data []byte) CorpusMeta {
m.PsalmSystem = p.Val
case "sigla":
m.Sigla = p.Val
+ case "autoselect":
+ m.Autoselect = p.Val != "false"
}
}
}
diff --git a/internal/bible/registry.go b/internal/bible/registry.go
index 2ab721b..28571ea 100644
--- a/internal/bible/registry.go
+++ b/internal/bible/registry.go
@@ -52,7 +52,7 @@ func buildRegistry() {
if code, ok := strings.CutSuffix(name, ".tsv"); ok {
regUserTSV[code] = filepath.Join(userDir, name)
if _, have := regMeta[code]; !have {
- regMeta[code] = CorpusMeta{Code: code, PsalmSystem: "vulgate"}
+ regMeta[code] = CorpusMeta{Code: code, PsalmSystem: "vulgate", Autoselect: true}
}
}
}
diff --git a/internal/config/config.go b/internal/config/config.go
index 55abd72..91fd8c3 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -18,6 +18,7 @@ import (
"github.com/pelletier/go-toml/v2"
+ "github.com/lukaszkasprzak/lectio/internal/bible"
"github.com/lukaszkasprzak/lectio/internal/calendar"
"github.com/lukaszkasprzak/lectio/internal/ini"
)
@@ -38,6 +39,8 @@ const configHeader = `# lectio configuration (INI). Full-line comments only (# o
# all true | false (all readings, or just the gospel)
# offline true | false (never fetch; cache/sigla only)
# ui_language en | pl (interface chrome; readings stay source-language)
+# reading_lang language for offline reading text (e.g. en, pl); blank follows ui_language
+# reading_version force a specific bible corpus code (e.g. drb, wuj); blank = auto-resolve
# sigla_style auto | polish | english | latin (citation dialect; auto follows ui_language)
# web_theme a theme name (see docs/THEMES.md)
# web_port integer; 0 = try 1099, then any free port
@@ -156,6 +159,8 @@ type Config struct {
All bool `toml:"all"`
Offline bool `toml:"offline"`
UILanguage string `toml:"ui_language"`
+ ReadingLang string `toml:"reading_lang"`
+ ReadingVersion string `toml:"reading_version"`
SiglaStyle string `toml:"sigla_style"`
WebTheme string `toml:"web_theme"`
WebPort int `toml:"web_port"`
@@ -204,6 +209,28 @@ func (c Config) SiglaLang() string {
}
}
+// ReadingCorpus resolves which bible corpus renders offline reading text:
+// an explicit reading_version, else the first corpus matching reading_lang,
+// else the first matching ui_language, else "" (caller uses the Latin vul).
+func (c Config) ReadingCorpus() string {
+ if c.ReadingVersion != "" {
+ if _, ok := bible.Meta(c.ReadingVersion); ok {
+ return c.ReadingVersion
+ }
+ }
+ for _, lang := range []string{c.ReadingLang, NormalizeUILanguage(c.UILanguage)} {
+ if lang == "" {
+ continue
+ }
+ for _, m := range bible.CorporaForLang(lang) {
+ if m.Autoselect { // skip explicit-only corpora (e.g. the incomplete wuj)
+ return m.Code
+ }
+ }
+ }
+ return ""
+}
+
// Selection maps the config onto the calendar engine's Selection: the form
// (from Lectionary: "new" -> OF, "traditional" -> EF) plus the national
// placement options, each falling back to the Universal Roman default.
@@ -507,6 +534,10 @@ func applyScalar(cfg *Config, key, val string) {
cfg.Offline = val == "true"
case "ui_language":
cfg.UILanguage = val
+ case "reading_lang":
+ cfg.ReadingLang = val
+ case "reading_version":
+ cfg.ReadingVersion = val
case "sigla_style":
cfg.SiglaStyle = val
case "web_theme":
@@ -589,6 +620,8 @@ func renderConfigINI(cfg Config) []byte {
fmt.Fprintf(&b, "all = %t\n", cfg.All)
fmt.Fprintf(&b, "offline = %t\n", cfg.Offline)
fmt.Fprintf(&b, "ui_language = %s\n", cfg.UILanguage)
+ fmt.Fprintf(&b, "reading_lang = %s\n", cfg.ReadingLang)
+ fmt.Fprintf(&b, "reading_version = %s\n", cfg.ReadingVersion)
fmt.Fprintf(&b, "sigla_style = %s\n", cfg.SiglaStyle)
fmt.Fprintf(&b, "web_theme = %s\n", cfg.WebTheme)
fmt.Fprintf(&b, "web_port = %d\n", cfg.WebPort)
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index e3af475..3830cb6 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -301,3 +301,23 @@ func TestPartShown(t *testing.T) {
t.Error("ewangelia not mentioned should still be shown")
}
}
+
+func TestReadingCorpusResolution(t *testing.T) {
+ // drb is an embedded corpus with lang=en.
+ c := Config{ReadingVersion: "drb"}
+ if got := c.ReadingCorpus(); got != "drb" {
+ t.Fatalf("explicit reading_version: got %q", got)
+ }
+ c = Config{ReadingLang: "en"}
+ if got := c.ReadingCorpus(); got != "drb" {
+ t.Fatalf("reading_lang match: got %q", got)
+ }
+ c = Config{UILanguage: "en"}
+ if got := c.ReadingCorpus(); got != "drb" {
+ t.Fatalf("ui_language fallback: got %q", got)
+ }
+ c = Config{UILanguage: "pl"} // no pl corpus embedded
+ if got := c.ReadingCorpus(); got != "" {
+ t.Fatalf("no match should be empty: got %q", got)
+ }
+}