diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-23 22:08:56 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-23 22:08:56 +0200 |
| commit | 390f8cb146c69d8d3a0e6e0d76ea3546f30a7611 (patch) | |
| tree | 99510b1fe2d80bfc1ab26473a53def3318bed256 /internal/i18n | |
| parent | 4c776396115c8120c3e1cb8fda993449fcdcd326 (diff) | |
| download | lectio-390f8cb146c69d8d3a0e6e0d76ea3546f30a7611.tar.gz lectio-390f8cb146c69d8d3a0e6e0d76ea3546f30a7611.zip | |
i18n: fix consistency-review gaps in ui_language (en/pl chrome)
Closes six findings from review of the ui_language feature so English
mode carries no leftover Polish UI text:
- Web: interlinear "no verses" note and the empty-day error fragment now
route through i18n instead of being unconditionally Polish.
- render.GatherVersion/GatherVerses/resolveRef's four lookup/citation-
failure blocks are now lang-aware (resolveRef gained a lang param).
- i18n.ErrorPrefix/NoReadingsFor lost a stray extra trailing space,
restoring the pre-i18n single-space TUI concatenation.
- render.LocalizeHeading now matches a heading's actual leading label
text against all known modern pl labels, rather than trusting the
label keyed by sec.PartID -- fixes split-feast days where PartID is
"drugie_czytanie" but the heading still reads "1. czytanie ...".
- cli bannerFor is lang-specific again (pl "na", en "for"), restoring
pl's exact pre-i18n wording instead of "--" for both languages.
- index.html's <html lang> now follows cfg.UILanguage instead of being
hardcoded "pl".
gofmt/vet clean, go test ./... green, all three binaries build, no new
go.mod deps. Consistency grep across cli/tui/web/render's live source
turns up only two non-displayed identifiers (a PartID slug list and a
pre-existing incipit-stripping constant on the pl scripture text itself).
Diffstat (limited to 'internal/i18n')
| -rw-r--r-- | internal/i18n/i18n.go | 117 | ||||
| -rw-r--r-- | internal/i18n/i18n_test.go | 72 |
2 files changed, 145 insertions, 44 deletions
diff --git a/internal/i18n/i18n.go b/internal/i18n/i18n.go index a5cb77b..4d034a5 100644 --- a/internal/i18n/i18n.go +++ b/internal/i18n/i18n.go @@ -19,17 +19,38 @@ type UI struct { // keeping the citation untouched. PartLabel map[string]string - // TUI keybar and status messages. + // TUI keybar and status messages. NoReadingsFor and ErrorPrefix each + // carry their own single trailing space (the TUI concatenates a date or + // error string directly onto them, no separator added at the call site). FooterKeys, Loading, NoReadingsFor, ErrorPrefix, ErrorHint string - // CLI banner label words: the banner is "<word> — <date>". - BannerGospel, BannerReadings string + // CLI banner label words and connective: the banner is + // "<word> <BannerConnective> <date>" (e.g. "Gospel for 2026-07-22" / + // "Ewangelia na 2026-07-22"). + BannerGospel, BannerReadings, BannerConnective string // Web control labels. Lectionary, OptModern, OptTraditional string Parts, OptGospel, OptAll string Layout, OptHorizontal, OptColumns string OptInterlinear, Theme, Mono string + + // NoInterlinearVerses is the web interlinear pane's note when no + // requested version could be interleaved for a section. + NoInterlinearVerses string + + // NoReadingsDay is the web error fragment shown when readings.Load + // returns no sections at all for the requested date. + NoReadingsDay string + + // NoVersion/NoVersionPartial/NoReference/NoReferenceErr are + // render.GatherVersion/GatherVerses' lookup/citation-failure blocks, + // rendered verbatim by cli/tui/web. NoVersion takes the version code; + // NoVersionPartial takes the version code and the comma-joined missing + // references; NoReferenceErr takes the underlying error (pl keeps %w so + // it still wraps, en uses %v -- the verb only controls + // errors.Unwrap-ability, both format identically via Error()). + NoVersion, NoVersionPartial, NoReference, NoReferenceErr string } // Get returns lang's chrome string set, falling back to English for @@ -56,25 +77,32 @@ var enUI = UI{ "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", + 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", + BannerConnective: "for", + 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", + NoInterlinearVerses: "(no verses to align)", + NoReadingsDay: "no readings for this day", + NoVersion: "(not in %s)", + NoVersionPartial: "(not in %s: %s)", + NoReference: "(no reference)", + NoReferenceErr: "(no reference: %v)", } var plUI = UI{ @@ -92,23 +120,30 @@ var plUI = UI{ "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", + 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", + BannerConnective: "na", + 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", + NoInterlinearVerses: "(brak wersetów do zestawienia interlinearnego)", + NoReadingsDay: "brak czytań na ten dzień", + NoVersion: "(brak w „%s”)", + NoVersionPartial: "(brak w „%s”: %s)", + NoReference: "(brak odwołania)", + NoReferenceErr: "(brak odwołania: %w)", } diff --git a/internal/i18n/i18n_test.go b/internal/i18n/i18n_test.go index 8c6f137..59771a8 100644 --- a/internal/i18n/i18n_test.go +++ b/internal/i18n/i18n_test.go @@ -1,6 +1,9 @@ package i18n -import "testing" +import ( + "strings" + "testing" +) func TestGetTheme(t *testing.T) { if got := Get("pl").Theme; got != "motyw" { @@ -56,12 +59,21 @@ func TestTUIStrings(t *testing.T) { 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 " { + 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: " { + if en.ErrorPrefix != "error: " || pl.ErrorPrefix != "błąd: " { t.Errorf("ErrorPrefix en=%q pl=%q", en.ErrorPrefix, pl.ErrorPrefix) } + // Both carry exactly one trailing space: the TUI concatenates a date or + // error string directly onto them with no separator of its own (see + // internal/tui bodyLines: ui.NoReadingsFor+m.date, ui.ErrorPrefix+err). + if strings.HasSuffix(en.NoReadingsFor, " ") || strings.HasSuffix(pl.NoReadingsFor, " ") { + t.Errorf("NoReadingsFor has 2+ trailing spaces: en=%q pl=%q", en.NoReadingsFor, pl.NoReadingsFor) + } + if strings.HasSuffix(en.ErrorPrefix, " ") || strings.HasSuffix(pl.ErrorPrefix, " ") { + t.Errorf("ErrorPrefix has 2+ trailing spaces: 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) } @@ -76,6 +88,60 @@ func TestBannerWords(t *testing.T) { if en.BannerReadings != "Readings" || pl.BannerReadings != "Czytania" { t.Errorf("BannerReadings en=%q pl=%q", en.BannerReadings, pl.BannerReadings) } + if en.BannerConnective != "for" || pl.BannerConnective != "na" { + t.Errorf("BannerConnective en=%q pl=%q", en.BannerConnective, pl.BannerConnective) + } +} + +// TestWebFragmentMessages covers the two web-only messages (interlinear +// "no verses" note, "no readings at all for this day" error fragment) that +// were unconditionally Polish before this fix. +func TestWebFragmentMessages(t *testing.T) { + en := Get("en") + pl := Get("pl") + if en.NoInterlinearVerses != "(no verses to align)" { + t.Errorf("en.NoInterlinearVerses = %q", en.NoInterlinearVerses) + } + if pl.NoInterlinearVerses != "(brak wersetów do zestawienia interlinearnego)" { + t.Errorf("pl.NoInterlinearVerses = %q", pl.NoInterlinearVerses) + } + if en.NoReadingsDay != "no readings for this day" { + t.Errorf("en.NoReadingsDay = %q", en.NoReadingsDay) + } + if pl.NoReadingsDay != "brak czytań na ten dzień" { + t.Errorf("pl.NoReadingsDay = %q", pl.NoReadingsDay) + } +} + +// TestLookupFailureMessages covers render.GatherVersion/GatherVerses/ +// resolveRef's four lookup/citation-failure templates. +func TestLookupFailureMessages(t *testing.T) { + en := Get("en") + pl := Get("pl") + + cases := []struct { + name, en, pl string + }{ + {"NoVersion", en.NoVersion, pl.NoVersion}, + {"NoVersionPartial", en.NoVersionPartial, pl.NoVersionPartial}, + {"NoReference", en.NoReference, pl.NoReference}, + {"NoReferenceErr", en.NoReferenceErr, pl.NoReferenceErr}, + } + want := map[string][2]string{ + "NoVersion": {"(not in %s)", "(brak w „%s”)"}, + "NoVersionPartial": {"(not in %s: %s)", "(brak w „%s”: %s)"}, + "NoReference": {"(no reference)", "(brak odwołania)"}, + "NoReferenceErr": {"(no reference: %v)", "(brak odwołania: %w)"}, + } + 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 TestWebControlLabels(t *testing.T) { |
