package server import ( "encoding/json" "fmt" "net/http" "strings" ) // translations holds the loaded translation maps keyed by language code. type translations struct { langs map[string]map[string]any // e.g. {"en": {...}, "de": {...}} } func loadTranslations(enJSON, deJSON []byte) (*translations, error) { t := &translations{langs: make(map[string]map[string]any)} var en map[string]any if err := json.Unmarshal(enJSON, &en); err != nil { return nil, fmt.Errorf("parse en.json: %w", err) } t.langs["en"] = en var de map[string]any if err := json.Unmarshal(deJSON, &de); err != nil { return nil, fmt.Errorf("parse de.json: %w", err) } t.langs["de"] = de return t, nil } // get resolves a dotted key (e.g. "setup.rooms.title") from the translation map. func (t *translations) get(lang, key string) string { m, ok := t.langs[lang] if !ok { m = t.langs["en"] } if m == nil { return key } return resolve(m, key) } func resolve(m map[string]any, key string) string { parts := strings.Split(key, ".") var current any = m for _, p := range parts { cm, ok := current.(map[string]any) if !ok { return key } current, ok = cm[p] if !ok { return key } } s, ok := current.(string) if !ok { return key } return s } // supportedLangs are the available languages. var supportedLangs = []string{"en", "de"} // detectLanguage determines the active language from query param, cookie, or Accept-Language header. func detectLanguage(r *http.Request) string { // 1. Query parameter if lang := r.URL.Query().Get("lang"); isSupported(lang) { return lang } // 2. Cookie if c, err := r.Cookie("heatguard_lang"); err == nil && isSupported(c.Value) { return c.Value } // 3. Accept-Language header accept := r.Header.Get("Accept-Language") for _, part := range strings.Split(accept, ",") { lang := strings.TrimSpace(strings.SplitN(part, ";", 2)[0]) lang = strings.SplitN(lang, "-", 2)[0] if isSupported(lang) { return lang } } return "en" } func isSupported(lang string) bool { for _, l := range supportedLangs { if l == lang { return true } } return false }