aboutsummaryrefslogtreecommitdiff
path: root/internal/i18n/gen_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-28 19:03:15 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-28 19:03:15 +0200
commit9100973576f87adcadaf6c8b331df7e729aeb117 (patch)
treeb67377705bb50c30004c52abc423573d1b335ca8 /internal/i18n/gen_test.go
parent771ec96f07499d6efbcb587f94e30ffcbc4e0e58 (diff)
downloadlectio-9100973576f87adcadaf6c8b331df7e729aeb117.tar.gz
lectio-9100973576f87adcadaf6c8b331df7e729aeb117.zip
i18n: make all UI chrome user-translatable via ui/<code>.ini
The interface chrome (version labels, TUI keybar/messages, CLI banner words, web control labels, section-heading words) was English/Polish hardcoded in Go. It is now data: the English and Polish tables ship as embedded lang/en.ini and lang/pl.ini, and any language is user-overridable at <config>/ui/<code>.ini, resolved per key with an English fallback. With ui_language = <code> plus a names/<code>.ini, the WHOLE interface -- chrome, day names, saint names -- is translatable without a rebuild. - i18n.Get loads the embedded English baseline, overlays the embedded/user language file, caches per lang; field <-> INI-key mapping is by reflection (lang.go) so the tables and files stay in sync automatically. Edge-space values (e.g. "error: ") are double-quoted so the INI trim keeps them. The Go tables move to golden_test.go; TestGoldenMatchesEmbedded asserts the embedded files still parse back to them, and TestGenerateLangFiles regenerates them (LECTIO_GEN=1). - config.UIDir(); wire i18n.SetUserDir in the CLI, TUI and web entry points. - README/config: document ui/<code>.ini alongside names/<code>.ini.
Diffstat (limited to 'internal/i18n/gen_test.go')
-rw-r--r--internal/i18n/gen_test.go69
1 files changed, 69 insertions, 0 deletions
diff --git a/internal/i18n/gen_test.go b/internal/i18n/gen_test.go
new file mode 100644
index 0000000..82dfd83
--- /dev/null
+++ b/internal/i18n/gen_test.go
@@ -0,0 +1,69 @@
+package i18n
+
+import (
+ "os"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+)
+
+// quote wraps a value in double quotes when it has meaningful leading/trailing
+// whitespace, so the INI reader's trim does not eat it.
+func quote(s string) string {
+ if s != strings.TrimSpace(s) {
+ return `"` + s + `"`
+ }
+ return s
+}
+
+// marshalUI renders a UI table as an INI file, the inverse of applyUI. It is the
+// generator for the embedded lang/<code>.ini files; the mapping is by reflection
+// so adding a UI field needs no generator change.
+func marshalUI(u UI) []byte {
+ var b strings.Builder
+ v := reflect.ValueOf(u)
+ t := v.Type()
+ for i := 0; i < t.NumField(); i++ {
+ key := iniKey(t.Field(i).Name)
+ fv := v.Field(i)
+ switch fv.Kind() {
+ case reflect.String:
+ b.WriteString(key + " = " + quote(fv.String()) + "\n")
+ case reflect.Map:
+ ks := make([]string, 0, fv.Len())
+ for _, k := range fv.MapKeys() {
+ ks = append(ks, k.String())
+ }
+ sort.Strings(ks)
+ for _, mk := range ks {
+ b.WriteString(key + "." + mk + " = " + quote(fv.MapIndex(reflect.ValueOf(mk)).String()) + "\n")
+ }
+ case reflect.Array:
+ parts := make([]string, fv.Len())
+ for j := 0; j < fv.Len(); j++ {
+ parts[j] = fv.Index(j).String()
+ }
+ b.WriteString(key + " = " + strings.Join(parts, ",") + "\n")
+ }
+ }
+ return []byte(b.String())
+}
+
+// TestGenerateLangFiles (re)writes the embedded English and Polish chrome files
+// from the Go golden tables. It is a generator, not an assertion: run it with
+// LECTIO_GEN=1 after changing enUI/plUI to refresh lang/en.ini and lang/pl.ini.
+func TestGenerateLangFiles(t *testing.T) {
+ if os.Getenv("LECTIO_GEN") == "" {
+ t.Skip("set LECTIO_GEN=1 to regenerate lang/*.ini from the Go tables")
+ }
+ if err := os.MkdirAll("lang", 0o755); err != nil {
+ t.Fatal(err)
+ }
+ for code, u := range map[string]UI{"en": enUI, "pl": plUI} {
+ if err := os.WriteFile(filepath.Join("lang", code+".ini"), marshalUI(u), 0o644); err != nil {
+ t.Fatalf("write %s: %v", code, err)
+ }
+ }
+}