diff --git a/deploy.sh b/deploy.sh index c79648f..bf9deb9 100755 --- a/deploy.sh +++ b/deploy.sh @@ -185,6 +185,11 @@ else echo "[4/5] Swapping binary..." cp "$BINARY_NAME" "$TARGET_DIR/$BINARY_NAME" + # Copy language files + if [ -d "$SCRIPT_DIR/lng" ]; then + cp -r "$SCRIPT_DIR/lng" "$TARGET_DIR/" + fi + # Ensure AUTHELIA_SECRET is in .env AUTHELIA_SECRET=$(grep -oP 'session_secret: \K.*' "$TARGET_DIR/config/authelia/configuration.yml" 2>/dev/null || echo "") if [ -n "$AUTHELIA_SECRET" ]; then diff --git a/main.go b/main.go index 9446feb..9e91770 100644 --- a/main.go +++ b/main.go @@ -190,13 +190,12 @@ func healthHandler(w http.ResponseWriter, r *http.Request) { } func launcherHandler(cfg *Config, apps []AppEntry) http.HandlerFunc { - tmpl := template.Must(template.New("launcher").Parse(launcherHTML)) - return func(w http.ResponseWriter, r *http.Request) { user := r.Header.Get("Remote-User") groupsHeader := r.Header.Get("Remote-Groups") userGroups := strings.Split(groupsHeader, ",") settings, _ := loadSettings(os.Getenv("CONFIG_DIR")) + lang := getUserLang(r) var allowedApps []AppEntry for _, app := range apps { @@ -213,6 +212,7 @@ func launcherHandler(cfg *Config, apps []AppEntry) http.HandlerFunc { Apps []AppEntry CompanyLogo string Subtitle string + Lang string }{ AppName: cfg.App.Name, Description: cfg.App.Description, @@ -221,9 +221,10 @@ func launcherHandler(cfg *Config, apps []AppEntry) http.HandlerFunc { Apps: allowedApps, CompanyLogo: settings.Company.Logo, Subtitle: settings.Company.Subtitle, + Lang: lang, } w.Header().Set("Content-Type", "text/html; charset=utf-8") - tmpl.Execute(w, data) + launcherTmpl.Execute(w, data) } } @@ -280,12 +281,6 @@ func adminHandler(w http.ResponseWriter, r *http.Request) { return } - funcMap := template.FuncMap{ - "t": func(key string) string { - return T(settings.Company.Language, key) - }, - } - tmpl := template.Must(template.New("admin").Funcs(funcMap).Parse(adminHTML)) w.Header().Set("Content-Type", "text/html; charset=utf-8") autheliaUp := false @@ -294,11 +289,20 @@ func adminHandler(w http.ResponseWriter, r *http.Request) { resp.Body.Close() } - tmpl.Execute(w, map[string]interface{}{ + userLang := os.Getenv("LANG") + if userLang == "" { + userLang = "en" + } + if settings != nil && settings.Company.Language != "" { + userLang = settings.Company.Language + } + + adminTmpl.Execute(w, map[string]interface{}{ "Settings": settings, "Domain": domain, "AdminEmail": adminEmail, "AutheliaUp": autheliaUp, + "Lang": userLang, }) case http.MethodPost: @@ -465,59 +469,53 @@ func publicSettingsHandler(w http.ResponseWriter, r *http.Request) { // --- Translation system --- -type Translations map[string]string +var translations = make(map[string]map[string]string) +var launcherTmpl *template.Template +var adminTmpl *template.Template -var translationCache map[string]Translations - -func loadTranslations(lang string) Translations { - if translationCache != nil { - if cached, ok := translationCache[lang]; ok { - return cached - } - } - basePath := filepath.Join("/opt/nextworkspace", "lng", lang) - result := Translations{} +func loadTranslations(lngDir string) { + langs := []string{"en", "de"} files := []string{"launcher.yaml", "admin.yaml"} - for _, f := range files { - data, err := os.ReadFile(filepath.Join(basePath, f)) - if err != nil { - continue + + for _, lang := range langs { + if translations[lang] == nil { + translations[lang] = make(map[string]string) } - var m map[string]string - yaml.Unmarshal(data, &m) - for k, v := range m { - result[k] = v + for _, file := range files { + path := filepath.Join(lngDir, lang, file) + data, err := os.ReadFile(path) + if err != nil { + continue + } + var keys map[string]string + if err := yaml.Unmarshal(data, &keys); err != nil { + continue + } + for k, v := range keys { + translations[lang][k] = v + } } } - if translationCache == nil { - translationCache = make(map[string]Translations) - } - translationCache[lang] = result - return result } -func T(lang, key string, args ...string) string { - t := loadTranslations(lang) - text, ok := t[key] - if !ok { - en := loadTranslations("en") - text = en[key] - if text == "" { - return key - } +func t(lang, key string, args ...string) string { + text := translations[lang][key] + if text == "" { + text = translations["en"][key] + } + if text == "" { + return key } for i := 0; i < len(args); i += 2 { - if i+1 < len(args) { - text = strings.ReplaceAll(text, "{"+args[i]+"}", args[i+1]) - } + text = strings.ReplaceAll(text, "{"+args[i]+"}", args[i+1]) } return text } func getUserLang(r *http.Request) string { - headerLang := r.Header.Get("Remote-Lang") - if headerLang == "de" || headerLang == "en" { - return headerLang + lang := r.Header.Get("Accept-Language") + if strings.HasPrefix(lang, "de") { + return "de" } settings, _ := loadSettings(os.Getenv("CONFIG_DIR")) if settings != nil && settings.Company.Language == "de" { @@ -620,9 +618,9 @@ const launcherHTML = ` {{if .Subtitle}}

{{.Subtitle}}

{{end}}
- Welcome, {{.User}} - Settings - Logout + {{t .Lang "welcome" "user" .User}} + {{t .Lang "settings"}} + {{t .Lang "logout"}}
{{range .Apps}} @@ -897,15 +895,15 @@ const adminHTML = `