fix(quick-261029-d9j): pick the mail language from Accept-Language, add mail.fallback_locale, Polish subjects
- preferred_locale and the request locale are normalised (comma lists, q-weights, region subtags, case) to pl or en by NormalizeMailLocale, so a raw Accept-Language such as en-GB,en;q=0.9 selects the -en templates - new golem15.user.mail.fallback_locale (default pl) is used when neither names a supported language; ResolveMailLocaleFor applies it, and Boot rejects any value other than pl or en - activate, reactivate and restore Polish templates get Polish subjects - no template, route or payload changes; MailTemplate and ResolveMailLocale keep their signatures
This commit is contained in:
117
classes/mail.go
117
classes/mail.go
@@ -2,27 +2,128 @@ package classes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.golem15.com/golem15/sm-user-plugin/models"
|
||||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||||
"git.golem15.com/golem15/summercms/modules/towel"
|
||||
)
|
||||
|
||||
// MailTemplate appends -en for English and leaves every other locale,
|
||||
// including empty and pl, on the unsuffixed Polish template name.
|
||||
const (
|
||||
// mailFallbackLocaleKey names the mail language used when neither the
|
||||
// user nor the request names one the plugin has mail for.
|
||||
mailFallbackLocaleKey = "golem15.user.mail.fallback_locale"
|
||||
// mailLocalePL is the unsuffixed (Polish) template set and the default
|
||||
// fallback.
|
||||
mailLocalePL = "pl"
|
||||
// mailLocaleEN is the -en template set.
|
||||
mailLocaleEN = "en"
|
||||
)
|
||||
|
||||
// supportedMailLocale reports whether the plugin has mail in lang.
|
||||
func supportedMailLocale(lang string) bool {
|
||||
return lang == mailLocalePL || lang == mailLocaleEN
|
||||
}
|
||||
|
||||
// NormalizeMailLocale reduces a locale value to the language the plugin has
|
||||
// mail for: pl, en or "". It accepts a plain code (en, PL), a region form
|
||||
// (en-GB, pl_PL) and an Accept-Language list with q-weights
|
||||
// (en-GB,en;q=0.9). Entries whose q does not parse, lies outside 0..1 or is
|
||||
// 0 are dropped, as are languages other than pl and en (the wildcard
|
||||
// included); of the rest the one with the highest q wins, ties going to the
|
||||
// earliest. The result never holds anything but pl, en or "".
|
||||
func NormalizeMailLocale(raw string) string {
|
||||
best, bestQ := "", -1.0
|
||||
for _, entry := range strings.Split(raw, ",") {
|
||||
parts := strings.Split(entry, ";")
|
||||
tag := strings.ToLower(strings.TrimSpace(parts[0]))
|
||||
if i := strings.IndexAny(tag, "-_"); i >= 0 {
|
||||
tag = tag[:i]
|
||||
}
|
||||
if !supportedMailLocale(tag) {
|
||||
continue
|
||||
}
|
||||
q, ok := 1.0, true
|
||||
for _, param := range parts[1:] {
|
||||
param = strings.TrimSpace(param)
|
||||
if len(param) < 2 || !strings.EqualFold(param[:2], "q=") {
|
||||
continue
|
||||
}
|
||||
v, err := strconv.ParseFloat(strings.TrimSpace(param[2:]), 64)
|
||||
if err != nil || v < 0 || v > 1 {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
q = v
|
||||
}
|
||||
if !ok || q == 0 {
|
||||
continue
|
||||
}
|
||||
if q > bestQ {
|
||||
best, bestQ = tag, q
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
// MailTemplate appends -en when locale normalises to English
|
||||
// (NormalizeMailLocale) and leaves every other value, empty included, on the
|
||||
// unsuffixed Polish template name. Callers that want the application's
|
||||
// configured fallback resolve the locale with ResolveMailLocaleFor first.
|
||||
func MailTemplate(base, locale string) string {
|
||||
if locale == "en" {
|
||||
if NormalizeMailLocale(locale) == mailLocaleEN {
|
||||
return base + "-en"
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
// ResolveMailLocale prefers the user's saved locale, then the request locale.
|
||||
// ResolveMailLocale prefers the user's saved locale, then the request locale,
|
||||
// each normalised by NormalizeMailLocale; a value naming neither pl nor en is
|
||||
// skipped. It returns pl, en or "" (neither names a supported language).
|
||||
func ResolveMailLocale(ctx context.Context, user *models.User) string {
|
||||
if user != nil && user.PreferredLocale != nil && *user.PreferredLocale != "" {
|
||||
return *user.PreferredLocale
|
||||
if user != nil && user.PreferredLocale != nil {
|
||||
if loc := NormalizeMailLocale(*user.PreferredLocale); loc != "" {
|
||||
return loc
|
||||
}
|
||||
}
|
||||
if loc, ok := towel.Locale(ctx); ok {
|
||||
return loc
|
||||
if ctx != nil {
|
||||
if raw, ok := towel.Locale(ctx); ok {
|
||||
return NormalizeMailLocale(raw)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// MailFallbackLocale returns golem15.user.mail.fallback_locale normalised to
|
||||
// pl or en: the mail language when neither the user nor the request names
|
||||
// one the plugin has mail for. The default is pl (the unsuffixed
|
||||
// templates); an English-only site sets en, for example through
|
||||
// SUMMER_GOLEM15__USER__MAIL__FALLBACK_LOCALE=en. A missing app, config or
|
||||
// blank value gives pl; any other value gives pl and an error naming the key.
|
||||
func MailFallbackLocale(app *backpack.App) (string, error) {
|
||||
if app == nil || app.Config == nil {
|
||||
return mailLocalePL, nil
|
||||
}
|
||||
raw := strings.TrimSpace(app.Config.String(mailFallbackLocaleKey))
|
||||
if raw == "" {
|
||||
return mailLocalePL, nil
|
||||
}
|
||||
if loc := NormalizeMailLocale(raw); loc != "" {
|
||||
return loc, nil
|
||||
}
|
||||
return mailLocalePL, fmt.Errorf("golem15.user: %s must be pl or en, got %q", mailFallbackLocaleKey, raw)
|
||||
}
|
||||
|
||||
// ResolveMailLocaleFor returns the language to mail user in, always pl or
|
||||
// en: ResolveMailLocale, then the application's MailFallbackLocale. It is the
|
||||
// helper other plugins use to mail in the user's language with the
|
||||
// application's fallback; an invalid fallback (rejected at boot) counts as pl.
|
||||
func ResolveMailLocaleFor(ctx context.Context, app *backpack.App, user *models.User) string {
|
||||
if loc := ResolveMailLocale(ctx, user); loc != "" {
|
||||
return loc
|
||||
}
|
||||
loc, _ := MailFallbackLocale(app)
|
||||
return loc
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user