package language import ( "errors" "regexp" "slices" "strings" ) var ( ErrInvalidLanguage = errors.New("invalid language tag") ErrUnsupportedLanguage = errors.New("unsupported language") ErrNoDefaultLanguage = errors.New("the site has no default language") ) const ( Unassigned = "" MaxTagLength = 35 ) var supported = []string{"en", "en-GB", "en-US", "fr", "fr-CA", "pt", "pt-BR", "es", "de", "ar"} var tagPattern = regexp.MustCompile(`^([A-Za-z]{2,3})(?:-([A-Za-z]{2}))?$`) func Supported() []string { return slices.Clone(supported) } func Normalize(tag string) (string, error) { parts := tagPattern.FindStringSubmatch(strings.TrimSpace(tag)) if parts == nil { return "", ErrInvalidLanguage } normalized := strings.ToLower(parts[1]) if parts[2] != "" { normalized += "-" + strings.ToUpper(parts[2]) } if !slices.Contains(supported, normalized) { return "", ErrUnsupportedLanguage } return normalized, nil } type Suggestion struct { Tag string Found bool Substituted bool } func Suggest(locale *string) Suggestion { if locale == nil { return Suggestion{} } tag, err := Normalize(*locale) if err == nil { return Suggestion{Tag: tag, Found: true} } if !errors.Is(err, ErrUnsupportedLanguage) { return Suggestion{} } base, _, _ := strings.Cut(strings.TrimSpace(*locale), "-") if fallback, err := Normalize(base); err == nil { return Suggestion{Tag: fallback, Found: true, Substituted: true} } return Suggestion{} } type SiteLanguage struct { SiteID uint32 Tag string IsDefault bool Enabled bool Confirmed bool }