package translate import ( "context" "log/slog" "net/http" "strings" ) func (s *Service) Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path segments := []string{} for segment := range strings.SplitSeq(path, "/") { if segment != "" { segments = append(segments, segment) } } // 1. Check if the first segment is a supported language. var currentLang string if len(segments) >= 1 { potentialLang := segments[0] if _, ok := s.translations[potentialLang]; ok { currentLang = potentialLang } } if currentLang != "" { // CASE A: Language is present in the URL. // Remove the language from the path so the router sees the clean path. newPath := "/" + strings.Join(segments[1:], "/") if newPath == "" { newPath = "/" } // Update the request URL path. r.URL.Path = newPath // Inject translator into context. ctx := context.WithValue(r.Context(), serviceKey, s) ctx = context.WithValue(ctx, langKey, currentLang) // Pass control to the next handler with the modified request. next.ServeHTTP(w, r.WithContext(ctx)) return } // CASE B: Language is NOT in the URL. // Detect best language from Accept-Language header. detectedLang := s.detectLanguage(r.Header.Get("Accept-Language")) // Redirect to the URL with the language prefix. targetURL := "/" + detectedLang + path if r.URL.RawQuery != "" { targetURL += "?" + r.URL.RawQuery } slog.Debug("redirecting for translated url", "old", path, "new", targetURL) http.Redirect(w, r, targetURL, http.StatusFound) }) } // detectLanguage parses the Accept-Language header and returns the best matching supported language. func (s *Service) detectLanguage(header string) string { if header == "" { return s.defaultLanguage } // Simple parser for Accept-Language: en-US,en;q=0.9,de;q=0.8 // We only care about the first match for simplicity here, or we could rank them. parts := strings.Split(header, ",") for _, part := range parts { tag := strings.Split(strings.TrimSpace(part), ";")[0] // Check for exact match (e.g., "en-US") if _, ok := s.translations[tag]; ok { return tag } // Check for base match (e.g., "en" from "en-US") base := strings.Split(tag, "-")[0] if _, ok := s.translations[base]; ok { return base } } return s.defaultLanguage }