summaryrefslogtreecommitdiff
path: root/internal/i18n
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-23 21:51:40 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-23 21:51:40 +0200
commit4c776396115c8120c3e1cb8fda993449fcdcd326 (patch)
tree0144fb325663dde063057a5ec568c59d4cbe79da /internal/i18n
parentbfa8df7b36ccbd44703e0705d51a2ed553a23164 (diff)
downloadlectio-4c776396115c8120c3e1cb8fda993449fcdcd326.tar.gz
lectio-4c776396115c8120c3e1cb8fda993449fcdcd326.zip
i18n: ui_language config (default en) for UI chrome across cli/tui/web; version labels localised
Diffstat (limited to 'internal/i18n')
-rw-r--r--internal/i18n/i18n.go114
-rw-r--r--internal/i18n/i18n_test.go145
2 files changed, 259 insertions, 0 deletions
diff --git a/internal/i18n/i18n.go b/internal/i18n/i18n.go
new file mode 100644
index 0000000..a5cb77b
--- /dev/null
+++ b/internal/i18n/i18n.go
@@ -0,0 +1,114 @@
+// Package i18n holds lectio's UI-chrome string tables (English and Polish)
+// for the ui_language config setting. It localises the chrome only --
+// version-column labels, the TUI keybar/messages, the CLI banner words, the
+// web control labels, and the modern-lectionary section-heading label words
+// -- never the scripture verse text or citation, which stay source-language.
+package i18n
+
+// UI is one language's complete set of chrome strings.
+type UI struct {
+ // Version names each bible version for column headers / prose, keyed by
+ // version code (pl, wuj, vul, grb, drb).
+ Version map[string]string
+
+ // PartLabel names each modern-lectionary section's heading label word,
+ // keyed by liturgy.Section.PartID (pierwsze_czytanie, psalm,
+ // drugie_czytanie, aklamacja, ewangelia). The pl entries are the exact
+ // prefixes niedziela.pl's scraped headings carry, used by
+ // render.LocalizeHeading to recognise and swap the label word while
+ // keeping the citation untouched.
+ PartLabel map[string]string
+
+ // TUI keybar and status messages.
+ FooterKeys, Loading, NoReadingsFor, ErrorPrefix, ErrorHint string
+
+ // CLI banner label words: the banner is "<word> — <date>".
+ BannerGospel, BannerReadings string
+
+ // Web control labels.
+ Lectionary, OptModern, OptTraditional string
+ Parts, OptGospel, OptAll string
+ Layout, OptHorizontal, OptColumns string
+ OptInterlinear, Theme, Mono string
+}
+
+// Get returns lang's chrome string set, falling back to English for
+// anything other than "pl".
+func Get(lang string) UI {
+ if lang == "pl" {
+ return plUI
+ }
+ return enUI
+}
+
+var enUI = UI{
+ Version: map[string]string{
+ "pl": "Polish (niedziela.pl)",
+ "wuj": "Wujek (Polish)",
+ "vul": "Vulgate (Latin)",
+ "grb": "Greek",
+ "drb": "Douay-Rheims (English)",
+ },
+ PartLabel: map[string]string{
+ "pierwsze_czytanie": "1st reading",
+ "psalm": "Psalm",
+ "drugie_czytanie": "2nd reading",
+ "aklamacja": "Acclamation",
+ "ewangelia": "Gospel",
+ },
+ FooterKeys: "tab/⇧tab version ←/→ day j/k scroll space/b page g/G top/bottom r refresh q quit",
+ Loading: "loading…",
+ NoReadingsFor: "no readings for ",
+ ErrorPrefix: "error: ",
+ ErrorHint: "change date (←/→) or refresh (r)",
+ BannerGospel: "Gospel",
+ BannerReadings: "Readings",
+ Lectionary: "lectionary",
+ OptModern: "modern",
+ OptTraditional: "traditional",
+ Parts: "parts",
+ OptGospel: "Gospel",
+ OptAll: "all parts",
+ Layout: "layout",
+ OptHorizontal: "horizontal",
+ OptColumns: "columns",
+ OptInterlinear: "interlinear",
+ Theme: "theme",
+ Mono: "mono",
+}
+
+var plUI = UI{
+ Version: map[string]string{
+ "pl": "Polski (niedziela.pl)",
+ "wuj": "Wujek (pol.)",
+ "vul": "Wulgata (lac.)",
+ "grb": "Grecki",
+ "drb": "Douay-Rheims (ang.)",
+ },
+ PartLabel: map[string]string{
+ "pierwsze_czytanie": "1. czytanie",
+ "psalm": "Psalm",
+ "drugie_czytanie": "2. czytanie",
+ "aklamacja": "Aklamacja",
+ "ewangelia": "Ewangelia",
+ },
+ FooterKeys: "tab/⇧tab wersja ←/→ dzień j/k przewiń spacja/b strona g/G góra/dół r odśwież q wyjście",
+ Loading: "ładowanie…",
+ NoReadingsFor: "brak czytań na ",
+ ErrorPrefix: "błąd: ",
+ ErrorHint: "zmień datę (←/→) lub odśwież (r)",
+ BannerGospel: "Ewangelia",
+ BannerReadings: "Czytania",
+ Lectionary: "lekcjonarz",
+ OptModern: "nowy",
+ OptTraditional: "tradycyjny",
+ Parts: "zakres",
+ OptGospel: "Ewangelia",
+ OptAll: "wszystkie części",
+ Layout: "układ",
+ OptHorizontal: "poziomo",
+ OptColumns: "kolumny",
+ OptInterlinear: "interlinearnie",
+ Theme: "motyw",
+ Mono: "mono",
+}
diff --git a/internal/i18n/i18n_test.go b/internal/i18n/i18n_test.go
new file mode 100644
index 0000000..8c6f137
--- /dev/null
+++ b/internal/i18n/i18n_test.go
@@ -0,0 +1,145 @@
+package i18n
+
+import "testing"
+
+func TestGetTheme(t *testing.T) {
+ if got := Get("pl").Theme; got != "motyw" {
+ t.Errorf(`Get("pl").Theme = %q, want "motyw"`, got)
+ }
+ if got := Get("en").Theme; got != "theme" {
+ t.Errorf(`Get("en").Theme = %q, want "theme"`, got)
+ }
+}
+
+func TestGetUnknownFallsBackToEnglish(t *testing.T) {
+ en := Get("en")
+ if got := Get("xx"); got.Theme != en.Theme || got.BannerGospel != en.BannerGospel {
+ t.Errorf(`Get("xx") = %+v, want the English set %+v`, got, en)
+ }
+ if got := Get(""); got.Theme != en.Theme || got.BannerGospel != en.BannerGospel {
+ t.Errorf(`Get("") = %+v, want the English set %+v`, got, en)
+ }
+}
+
+func TestVersionLabels(t *testing.T) {
+ cases := []struct {
+ lang, code, want string
+ }{
+ {"en", "pl", "Polish (niedziela.pl)"},
+ {"pl", "pl", "Polski (niedziela.pl)"},
+ {"en", "wuj", "Wujek (Polish)"},
+ {"pl", "wuj", "Wujek (pol.)"},
+ {"en", "vul", "Vulgate (Latin)"},
+ {"pl", "vul", "Wulgata (lac.)"},
+ {"en", "grb", "Greek"},
+ {"pl", "grb", "Grecki"},
+ {"en", "drb", "Douay-Rheims (English)"},
+ {"pl", "drb", "Douay-Rheims (ang.)"},
+ }
+ for _, c := range cases {
+ if got := Get(c.lang).Version[c.code]; got != c.want {
+ t.Errorf("Get(%q).Version[%q] = %q, want %q", c.lang, c.code, got, c.want)
+ }
+ }
+}
+
+func TestTUIStrings(t *testing.T) {
+ en := Get("en")
+ pl := Get("pl")
+
+ if en.FooterKeys != "tab/⇧tab version ←/→ day j/k scroll space/b page g/G top/bottom r refresh q quit" {
+ t.Errorf("en.FooterKeys = %q", en.FooterKeys)
+ }
+ if pl.FooterKeys != "tab/⇧tab wersja ←/→ dzień j/k przewiń spacja/b strona g/G góra/dół r odśwież q wyjście" {
+ t.Errorf("pl.FooterKeys = %q", pl.FooterKeys)
+ }
+ if en.Loading != "loading…" || pl.Loading != "ładowanie…" {
+ t.Errorf("Loading en=%q pl=%q", en.Loading, pl.Loading)
+ }
+ if en.NoReadingsFor != "no readings for " || pl.NoReadingsFor != "brak czytań na " {
+ t.Errorf("NoReadingsFor en=%q pl=%q", en.NoReadingsFor, pl.NoReadingsFor)
+ }
+ if en.ErrorPrefix != "error: " || pl.ErrorPrefix != "błąd: " {
+ t.Errorf("ErrorPrefix en=%q pl=%q", en.ErrorPrefix, pl.ErrorPrefix)
+ }
+ if en.ErrorHint != "change date (←/→) or refresh (r)" || pl.ErrorHint != "zmień datę (←/→) lub odśwież (r)" {
+ t.Errorf("ErrorHint en=%q pl=%q", en.ErrorHint, pl.ErrorHint)
+ }
+}
+
+func TestBannerWords(t *testing.T) {
+ en := Get("en")
+ pl := Get("pl")
+ if en.BannerGospel != "Gospel" || pl.BannerGospel != "Ewangelia" {
+ t.Errorf("BannerGospel en=%q pl=%q", en.BannerGospel, pl.BannerGospel)
+ }
+ if en.BannerReadings != "Readings" || pl.BannerReadings != "Czytania" {
+ t.Errorf("BannerReadings en=%q pl=%q", en.BannerReadings, pl.BannerReadings)
+ }
+}
+
+func TestWebControlLabels(t *testing.T) {
+ en := Get("en")
+ pl := Get("pl")
+
+ cases := []struct {
+ name, en, pl string
+ }{
+ {"Lectionary", en.Lectionary, pl.Lectionary},
+ {"OptModern", en.OptModern, pl.OptModern},
+ {"OptTraditional", en.OptTraditional, pl.OptTraditional},
+ {"Parts", en.Parts, pl.Parts},
+ {"OptGospel", en.OptGospel, pl.OptGospel},
+ {"OptAll", en.OptAll, pl.OptAll},
+ {"Layout", en.Layout, pl.Layout},
+ {"OptHorizontal", en.OptHorizontal, pl.OptHorizontal},
+ {"OptColumns", en.OptColumns, pl.OptColumns},
+ {"OptInterlinear", en.OptInterlinear, pl.OptInterlinear},
+ {"Theme", en.Theme, pl.Theme},
+ {"Mono", en.Mono, pl.Mono},
+ }
+ want := map[string][2]string{
+ "Lectionary": {"lectionary", "lekcjonarz"},
+ "OptModern": {"modern", "nowy"},
+ "OptTraditional": {"traditional", "tradycyjny"},
+ "Parts": {"parts", "zakres"},
+ "OptGospel": {"Gospel", "Ewangelia"},
+ "OptAll": {"all parts", "wszystkie części"},
+ "Layout": {"layout", "układ"},
+ "OptHorizontal": {"horizontal", "poziomo"},
+ "OptColumns": {"columns", "kolumny"},
+ "OptInterlinear": {"interlinear", "interlinearnie"},
+ "Theme": {"theme", "motyw"},
+ "Mono": {"mono", "mono"},
+ }
+ for _, c := range cases {
+ w := want[c.name]
+ if c.en != w[0] {
+ t.Errorf("en.%s = %q, want %q", c.name, c.en, w[0])
+ }
+ if c.pl != w[1] {
+ t.Errorf("pl.%s = %q, want %q", c.name, c.pl, w[1])
+ }
+ }
+}
+
+func TestModernPartLabels(t *testing.T) {
+ en := Get("en")
+ pl := Get("pl")
+
+ want := map[string][2]string{
+ "pierwsze_czytanie": {"1. czytanie", "1st reading"},
+ "psalm": {"Psalm", "Psalm"},
+ "drugie_czytanie": {"2. czytanie", "2nd reading"},
+ "aklamacja": {"Aklamacja", "Acclamation"},
+ "ewangelia": {"Ewangelia", "Gospel"},
+ }
+ for partID, w := range want {
+ if got := pl.PartLabel[partID]; got != w[0] {
+ t.Errorf("pl.PartLabel[%q] = %q, want %q", partID, got, w[0])
+ }
+ if got := en.PartLabel[partID]; got != w[1] {
+ t.Errorf("en.PartLabel[%q] = %q, want %q", partID, got, w[1])
+ }
+ }
+}