mirror of
https://github.com/rjNemo/rentease.git
synced 2026-06-06 02:36:49 +00:00
129 lines
3 KiB
Go
129 lines
3 KiB
Go
package i18n
|
|
|
|
import (
|
|
"context"
|
|
"embed"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
|
|
gi18n "github.com/nicksnyder/go-i18n/v2/i18n"
|
|
"golang.org/x/text/language"
|
|
)
|
|
|
|
//go:embed locales/*.json
|
|
var localeFS embed.FS
|
|
|
|
const (
|
|
CookieName = "lang"
|
|
)
|
|
|
|
type ctxKey string
|
|
|
|
const (
|
|
ctxKeyLang ctxKey = "lang"
|
|
ctxKeyLocalizer ctxKey = "localizer"
|
|
)
|
|
|
|
// Bundle initializes the go-i18n bundle and loads embedded locale files.
|
|
func Bundle() (*gi18n.Bundle, error) {
|
|
b := gi18n.NewBundle(language.English)
|
|
b.RegisterUnmarshalFunc("json", json.Unmarshal)
|
|
|
|
// Load embedded message files
|
|
for _, name := range []string{"locales/en.json", "locales/fr.json"} {
|
|
data, err := localeFS.ReadFile(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := b.ParseMessageFileBytes(data, name); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
// LanguageFromRequest resolves the preferred language from cookie or Accept-Language.
|
|
func LanguageFromRequest(r *http.Request) string {
|
|
// 1. Cookie preference
|
|
if c, err := r.Cookie(CookieName); err == nil {
|
|
if c.Value == "fr" {
|
|
return "fr"
|
|
}
|
|
return "en"
|
|
}
|
|
|
|
// 2. Accept-Language header
|
|
al := r.Header.Get("Accept-Language")
|
|
if strings.HasPrefix(strings.ToLower(al), "fr") {
|
|
return "fr"
|
|
}
|
|
|
|
return "en"
|
|
}
|
|
|
|
// LanguageMiddleware injects `lang` and a gi18n.Localizer in the request context.
|
|
func LanguageMiddleware(bundle *gi18n.Bundle) func(http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
lang := LanguageFromRequest(r)
|
|
loc := gi18n.NewLocalizer(bundle, lang)
|
|
|
|
ctx := context.WithValue(r.Context(), ctxKeyLang, lang)
|
|
ctx = context.WithValue(ctx, ctxKeyLocalizer, loc)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
})
|
|
}
|
|
}
|
|
|
|
// FromContext returns (lang, localizer) if present.
|
|
func FromContext(ctx context.Context) (string, *gi18n.Localizer) {
|
|
var lang string
|
|
if v := ctx.Value(ctxKeyLang); v != nil {
|
|
if s, ok := v.(string); ok {
|
|
lang = s
|
|
}
|
|
}
|
|
var loc *gi18n.Localizer
|
|
if v := ctx.Value(ctxKeyLocalizer); v != nil {
|
|
if l, ok := v.(*gi18n.Localizer); ok {
|
|
loc = l
|
|
}
|
|
}
|
|
if lang == "" {
|
|
lang = "en"
|
|
}
|
|
if loc == nil {
|
|
loc = gi18n.NewLocalizer(gi18n.NewBundle(language.English), lang)
|
|
}
|
|
return lang, loc
|
|
}
|
|
|
|
// LangFromContext returns the resolved language code from context.
|
|
func LangFromContext(ctx context.Context) string {
|
|
lang, _ := FromContext(ctx)
|
|
return lang
|
|
}
|
|
|
|
// Localize returns the localized message for a given message ID.
|
|
func Localize(ctx context.Context, id string) string {
|
|
_, loc := FromContext(ctx)
|
|
msg, err := loc.Localize(&gi18n.LocalizeConfig{MessageID: id})
|
|
if err != nil {
|
|
return id
|
|
}
|
|
return msg
|
|
}
|
|
|
|
// TranslatorFromRequest returns a simple `func(id string) string` based on request context.
|
|
func TranslatorFromRequest(r *http.Request) func(string) string {
|
|
_, loc := FromContext(r.Context())
|
|
return func(id string) string {
|
|
msg, err := loc.Localize(&gi18n.LocalizeConfig{MessageID: id})
|
|
if err != nil {
|
|
// Fallback to message id
|
|
return id
|
|
}
|
|
return msg
|
|
}
|
|
}
|