1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
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, rank) 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.<lang> -> 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)
}
|