Files
summercms/modules/cabana/lang.go
Jakub Zych f67e8915b8 feat(quick-261016-0fm): admin language follows the stored preference
- principalFrom sets PreferredLocale from metadata admin_locale; personal
  API token principals clear it, so token clients keep the request locale
- the guarded admin group runs locale.from-principal after the backend
  guard, overriding the LocaleResolver or Accept-Language locale
- GET /lang honours a supported ?locale= and reports meta.locales
  (cabana.LangEnvelope, cabana.LangMeta)
- Docker test for the override, refusals and /lang; docs and OpenAPI
2026-10-16 10:58:29 +02:00

35 lines
1.2 KiB
Go

package cabana
import (
"net/http"
"slices"
)
// adminLangPrefix is the only namespace the public bundle serves (D-20).
const adminLangPrefix = "backend::lang."
// langBundle serves GET {prefix}/api/v1/lang: every backend::lang key as CLDR
// forms for the request locale, with the fallback locale's keys underneath.
// A locale query parameter naming a supported admin locale (one of
// Translator.Locales(adminLangPrefix)) replaces the request locale; any other
// value is ignored. meta.locales lists the supported admin locales. It is
// public (the login screen needs it) and never includes another namespace.
func (s *service) langBundle(w http.ResponseWriter, r *http.Request) {
tr := s.translator()
locales := tr.Locales(adminLangPrefix)
if locales == nil {
locales = []string{}
}
locale := schemaLocale(r.Context(), tr)
if requested := r.URL.Query().Get("locale"); requested != "" && slices.Contains(locales, requested) {
locale = requested
}
data := map[string]map[string]string{}
if tr != nil {
locale = tr.Resolved(locale, adminLangPrefix)
data = tr.Bundle(locale, adminLangPrefix)
}
w.Header().Set("Cache-Control", "no-cache")
WriteData(w, http.StatusOK, data, map[string]any{"locale": locale, "locales": locales})
}