package locale import ( "context" "net/http" "strings" ) // Default is the fallback language when no supported language is detected. const Default = "en" // Supported is the set of language codes the application currently handles. // Keys are ISO 639-1 two-letter codes (lower-case). var Supported = map[string]bool{ "en": true, "ru": true, "es": true, "de": true, "fr": true, "it": true, "pt": true, "zh": true, "ja": true, "ko": true, "ar": true, "hi": true, } type contextKey struct{} // Parse returns the best-matching supported language from an Accept-Language // header value. It iterates through the comma-separated list in preference // order and returns the first entry whose primary subtag is in Supported. // Returns Default when the header is empty or no match is found. func Parse(acceptLang string) string { if acceptLang == "" { return Default } for part := range strings.SplitSeq(acceptLang, ",") { // Strip quality value (e.g. ";q=0.9"). tag := strings.TrimSpace(strings.SplitN(part, ";", 2)[0]) // Use only the primary subtag (e.g. "ru" from "ru-RU"). lang := strings.ToLower(strings.SplitN(tag, "-", 2)[0]) if Supported[lang] { return lang } } return Default } // WithLang returns a copy of ctx carrying the given language code. func WithLang(ctx context.Context, lang string) context.Context { return context.WithValue(ctx, contextKey{}, lang) } // FromContext returns the language stored in ctx. // Returns Default when no language has been set. func FromContext(ctx context.Context) string { if lang, ok := ctx.Value(contextKey{}).(string); ok && lang != "" { return lang } return Default } // FromRequest extracts the preferred language from the request's // Accept-Language header. func FromRequest(r *http.Request) string { return Parse(r.Header.Get("Accept-Language")) }