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/.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) } } }