package readings import ( "fmt" "time" "github.com/lukaszkasprzak/lectio/internal/bible" "github.com/lukaszkasprzak/lectio/internal/caldata" "github.com/lukaszkasprzak/lectio/internal/calendar" "github.com/lukaszkasprzak/lectio/internal/config" "github.com/lukaszkasprzak/lectio/internal/liturgy" "github.com/lukaszkasprzak/lectio/internal/naming" ) // offlineLoad resolves a day's readings entirely from the embedded calendar // engine and lectionary data -- no network. It returns the same source-agnostic // liturgy.Section / liturgy.DayInfo the CLI/TUI/web already render, so the daily // view is unchanged apart from where its data comes from. Citations are // lectio's English-canonical authored form; the render localises each one to // the chosen corpus's Psalter and the user's sigla dialect (see // render.GatherVersion, bible.OFRef). func offlineLoad(cfg config.Config, date string) ([]liturgy.Section, liturgy.DayInfo, error) { d, err := time.Parse("2006-01-02", date) if err != nil { return nil, liturgy.DayInfo{}, fmt.Errorf("bad date %q (want YYYY-MM-DD)", date) } sel := cfg.Selection() dir, _ := config.CalendarsDir() layers, _ := caldata.Stack(sel.Form, dir, cfg.Use) // Stack falls back to embedded data on error day := calendar.Compute(d.UTC(), sel, layers) rs := caldata.Readings(sel, layers, d.UTC(), day) tbl, _ := bible.LoadBookTable(config.UserBooksINI()) // nil on error -> citations shown as authored return sectionsFor(rs, sel.Form, cfg.UILanguage, cfg.SiglaLang(), tbl), dayInfo(cfg, day), nil } // citationForms renders a reading's authored (English) citation into its // display form (the reader's sigla dialect) and its English-canonical lookup // reference. When the book table is missing or cannot parse the citation, the // authored form is used verbatim for both. func citationForms(raw, siglaLang string, tbl *bible.BookTable) (display, ref string) { if tbl == nil { return raw, raw } canonical, ok := tbl.ParseRef("en", raw) if !ok { return raw, raw } return tbl.FormatRef(siglaLang, canonical), canonical } // ofPart maps a computed reading Part to the modern-lectionary section id and // its Polish heading label. The heading is always the Polish label: the render // (render.LocalizeHeading) rewrites it to English for an English UI, mirroring // how the niedziela sections were shaped. var ofPart = map[string]struct{ id, heading string }{ "first": {"pierwsze_czytanie", "1. czytanie"}, "psalm": {"psalm", "Psalm"}, "second": {"drugie_czytanie", "2. czytanie"}, "acclamation": {"aklamacja", "Aklamacja"}, "gospel": {"ewangelia", "Ewangelia"}, } // efPartHeading gives the traditional (1962) section's heading per UI language; // the EF has only an epistle/lesson and a gospel. Unlike the OF headings these // are not translated downstream, so they are set in the target language here. func efPartHeading(part, lang string) (id, heading string) { pl := lang == "pl" switch part { case "first": if pl { return "epistola", "Lekcja" } return "epistola", "Lesson" case "gospel": if pl { return "evangelium", "Ewangelia" } return "evangelium", "Gospel" } return "", "" } // sectionsFor turns computed readings into render-ready sections, tagging each // with the section id and heading its form expects and rendering its citation // into display (sigla dialect) and lookup (English-canonical) forms. func sectionsFor(rs []calendar.Reading, form, lang, siglaLang string, tbl *bible.BookTable) []liturgy.Section { var out []liturgy.Section for _, r := range rs { if r.Citation == "" { continue } var id, heading string if form == "old" { id, heading = efPartHeading(r.Part, lang) } else if p, ok := ofPart[r.Part]; ok { id, heading = p.id, p.heading } if id == "" { continue // an unknown part carries no section } display, ref := citationForms(r.Citation, siglaLang, tbl) out = append(out, liturgy.Section{ PartID: id, Heading: heading, Citation: display, Ref: ref, }) } return out } // dayInfo builds the header (celebration name, liturgical colour) for the // computed day. Season is left empty: the celebration name already carries the // temporal identity for temporal days, and the header is a nice-to-have. func dayInfo(cfg config.Config, day calendar.LiturgicalDay) liturgy.DayInfo { return liturgy.DayInfo{ Name: celebrationName(cfg, day.Observed), Colour: string(day.Colour), Rank: string(day.Observed.Rank), } } // celebrationName is the observed celebration's name in the UI language, // resolved by naming.CelebrationName (name. -> English -> Latin -> // humanized slug). An empty result (unnamed feria) omits the header line. func celebrationName(cfg config.Config, c calendar.Celebration) string { return naming.CelebrationName(cfg.UILanguage, c) }