diff --git a/src/core/i18n/i18n.go b/src/core/i18n/i18n.go new file mode 100644 index 0000000..4d62f45 --- /dev/null +++ b/src/core/i18n/i18n.go @@ -0,0 +1,87 @@ +package i18n + +// T holds all translation keys. +type T map[string]string + +var translations = map[string]T{ + "en": { + "greeting_morning": "Good morning", + "greeting_afternoon": "Good afternoon", + "greeting_evening": "Good evening", + "workspace_ready": "Your workspace is ready", + "install_app": "Install app", + "settings": "Settings", + "logout": "Logout", + "language": "Language", + "timezone": "Timezone", + "save": "Save", + "available": "Available", + "coming_soon": "Coming Soon", + "users_groups": "Users, groups & settings", + "storage_sharing": "Storage & sharing", + "email_client": "Email client", + "schedule_events": "Schedule & events", + "documents_sheets": "Documents & sheets", + "people_directory": "People & directory", + "project_management": "Project management", + "team_messaging": "Team messaging", + "pwa_desktop_title": "Desktop Chrome/Edge", + "pwa_desktop_step1": "Click the ⊕ icon in the address bar", + "pwa_desktop_step2": "Click Install", + "pwa_ios_title": "iOS Safari", + "pwa_ios_step1": "Tap Share ⎋", + "pwa_ios_step2": "Tap Add to Home Screen", + "pwa_android_title": "Android Chrome", + "pwa_android_step1": "Tap ⋮ menu", + "pwa_android_step2": "Tap Install app", + "pwa_got_it": "Got it", + "install_workspace": "Install Next Workspace", + }, + "de": { + "greeting_morning": "Guten Morgen", + "greeting_afternoon": "Guten Tag", + "greeting_evening": "Guten Abend", + "workspace_ready": "Ihr Arbeitsbereich ist bereit", + "install_app": "App installieren", + "settings": "Einstellungen", + "logout": "Abmelden", + "language": "Sprache", + "timezone": "Zeitzone", + "save": "Speichern", + "available": "Verfügbar", + "coming_soon": "Demnächst", + "users_groups": "Benutzer, Gruppen & Einstellungen", + "storage_sharing": "Speicher & Freigabe", + "email_client": "E-Mail-Client", + "schedule_events": "Termine & Ereignisse", + "documents_sheets": "Dokumente & Tabellen", + "people_directory": "Personen & Verzeichnis", + "project_management": "Projektmanagement", + "team_messaging": "Team-Chat", + "pwa_desktop_title": "Desktop Chrome/Edge", + "pwa_desktop_step1": "Klicken Sie auf das ⊕-Symbol in der Adressleiste", + "pwa_desktop_step2": "Klicken Sie auf Installieren", + "pwa_ios_title": "iOS Safari", + "pwa_ios_step1": "Tippen Sie auf Teilen ⎋", + "pwa_ios_step2": "Tippen Sie auf Zum Home-Bildschirm", + "pwa_android_title": "Android Chrome", + "pwa_android_step1": "Tippen Sie auf ⋮ Menü", + "pwa_android_step2": "Tippen Sie auf App installieren", + "pwa_got_it": "Verstanden", + "install_workspace": "Next Workspace installieren", + }, +} + +// Get returns the translation map for a language code. +func Get(lang string) T { + t, ok := translations[lang] + if !ok { + t = translations["en"] + } + return t +} + +// Supported returns the list of supported language codes. +func Supported() []string { + return []string{"en", "de"} +} diff --git a/src/core/ui/handler.go b/src/core/ui/handler.go index 063bec8..8f447af 100644 --- a/src/core/ui/handler.go +++ b/src/core/ui/handler.go @@ -3,21 +3,27 @@ package ui import ( "net/http" "path/filepath" + "strings" "git.lohmar.co.uk/lexton-it/NextWks/core/auth" + "git.lohmar.co.uk/lexton-it/NextWks/core/i18n" ) -// Handler serves the workspace launcher UI. +// PageCtx holds page-level data passed to templates. +type PageCtx struct { + UserID string + Role string + Locale i18n.T +} + type Handler struct { appDir string } -// NewHandler creates a UI handler that serves the launcher and static assets. func NewHandler(appDir string) *Handler { return &Handler{appDir: appDir} } -// RegisterRoutes mounts the public UI routes on the given mux. func (h *Handler) RegisterRoutes(mux *http.ServeMux, authGate func(http.Handler) http.Handler) { staticDir := filepath.Join(h.appDir, "static") mux.Handle("GET /static/", http.StripPrefix("/static", http.FileServer(http.Dir(staticDir)))) @@ -30,18 +36,35 @@ func (h *Handler) launcherPage(w http.ResponseWriter, r *http.Request) { return } - userID, _ := auth.GetUserID(r) - role := getUserRole(r) - apps := DefaultApps(role) - - component := LauncherPage(userID, role, apps) + ctx := getContext(r) + apps := DefaultApps(ctx.Role) + component := LauncherPage(ctx, apps) component.Render(r.Context(), w) } -// getUserRole extracts the user's role from the request context or session. -func getUserRole(r *http.Request) string { - if role, ok := r.Context().Value(auth.ContextRole).(string); ok { - return role +func getContext(r *http.Request) PageCtx { + userID, _ := auth.GetUserID(r) + role, _ := r.Context().Value(auth.ContextRole).(string) + if role == "" { + role = "user" + } + + // Detect language: cookie > header > default + lang := "en" + if cookie, err := r.Cookie("lang"); err == nil { + lang = cookie.Value + } else if al := r.Header.Get("Accept-Language"); al != "" { + for _, l := range i18n.Supported() { + if strings.HasPrefix(al, l) { + lang = l + break + } + } + } + + return PageCtx{ + UserID: userID, + Role: role, + Locale: i18n.Get(lang), } - return "user" } diff --git a/src/core/ui/launcher.templ b/src/core/ui/launcher.templ index 9858682..85b7634 100644 --- a/src/core/ui/launcher.templ +++ b/src/core/ui/launcher.templ @@ -2,7 +2,7 @@ package ui import appver "git.lohmar.co.uk/lexton-it/NextWks/core/version" -templ LauncherPage(userName string, role string, apps []AppTile) { +templ LauncherPage(c PageCtx, apps []AppTile) {
@@ -18,9 +18,9 @@ templ LauncherPage(userName string, role string, apps []AppTile) {