Add support for decoding content-language header from request
This commit is contained in:
@@ -239,7 +239,7 @@ func (h *AuthHandlers) handle(fn handlerFn) http.HandlerFunc {
|
||||
// because we need request's context to detect the language from!
|
||||
ttf = func(t *template.Template) *template.Template {
|
||||
return t.Funcs(map[string]interface{}{
|
||||
"language": func() string { return locale.GetLanguageFromContext(req.Context()).String() },
|
||||
"language": func() string { return locale.GetAcceptLanguageFromContext(req.Context()).String() },
|
||||
"tr": func(key string, pp ...interface{}) template.HTML {
|
||||
ss := make([]string, len(pp))
|
||||
for i := range pp {
|
||||
|
||||
@@ -131,7 +131,7 @@ func (svc module) Find(ctx context.Context, filter types.ModuleFilter) (set type
|
||||
}
|
||||
|
||||
// i18n
|
||||
tag := locale.GetLanguageFromContext(ctx)
|
||||
tag := locale.GetAcceptLanguageFromContext(ctx)
|
||||
set.Walk(func(m *types.Module) error {
|
||||
m.DecodeTranslations(svc.locale.Locale().ResourceTranslations(tag, m.ResourceTranslation()))
|
||||
|
||||
@@ -353,7 +353,7 @@ func (svc module) updater(ctx context.Context, namespaceID, moduleID uint64, act
|
||||
tt = append(tt, f.EncodeTranslations()...)
|
||||
}
|
||||
|
||||
tt.SetLanguage(locale.GetLanguageFromContext(ctx))
|
||||
tt.SetLanguage(locale.GetAcceptLanguageFromContext(ctx))
|
||||
err = svc.locale.Upsert(ctx, tt)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -110,7 +110,7 @@ func (svc namespace) Find(ctx context.Context, filter types.NamespaceFilter) (se
|
||||
}
|
||||
|
||||
// i18n
|
||||
tag := locale.GetLanguageFromContext(ctx)
|
||||
tag := locale.GetAcceptLanguageFromContext(ctx)
|
||||
set.Walk(func(n *types.Namespace) error {
|
||||
n.DecodeTranslations(svc.locale.Locale().ResourceTranslations(tag, n.ResourceTranslation()))
|
||||
return nil
|
||||
|
||||
@@ -144,7 +144,7 @@ func (svc page) search(ctx context.Context, filter types.PageFilter) (set types.
|
||||
}
|
||||
|
||||
// i18n
|
||||
tag := locale.GetLanguageFromContext(ctx)
|
||||
tag := locale.GetAcceptLanguageFromContext(ctx)
|
||||
set.Walk(func(p *types.Page) error {
|
||||
p.DecodeTranslations(svc.locale.Locale().ResourceTranslations(tag, p.ResourceTranslation()))
|
||||
return nil
|
||||
@@ -362,7 +362,7 @@ func (svc page) updater(ctx context.Context, namespaceID, pageID uint64, action
|
||||
|
||||
// i18n
|
||||
tt := p.EncodeTranslations()
|
||||
tt.SetLanguage(locale.GetLanguageFromContext(ctx))
|
||||
tt.SetLanguage(locale.GetAcceptLanguageFromContext(ctx))
|
||||
err = svc.locale.Upsert(ctx, tt)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -403,7 +403,7 @@ func (svc page) lookup(ctx context.Context, namespaceID uint64, lookup func(*pag
|
||||
return err
|
||||
}
|
||||
|
||||
p.DecodeTranslations(svc.locale.Locale().ResourceTranslations(locale.GetLanguageFromContext(ctx), p.ResourceTranslation()))
|
||||
p.DecodeTranslations(svc.locale.Locale().ResourceTranslations(locale.GetAcceptLanguageFromContext(ctx), p.ResourceTranslation()))
|
||||
|
||||
aProps.setPage(p)
|
||||
|
||||
|
||||
+18
-3
@@ -6,15 +6,30 @@ import (
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func SetLanguageToContext(ctx context.Context, code language.Tag) context.Context {
|
||||
func SetAcceptLanguageToContext(ctx context.Context, code language.Tag) context.Context {
|
||||
return context.WithValue(ctx, language.Tag{}, code)
|
||||
}
|
||||
|
||||
// GetLanguageFromContext always returns language, either valid or default
|
||||
func GetLanguageFromContext(ctx context.Context) language.Tag {
|
||||
// GetAcceptLanguageFromContext always returns language, either valid or default
|
||||
func GetAcceptLanguageFromContext(ctx context.Context) language.Tag {
|
||||
if tag, ok := ctx.Value(language.Tag{}).(language.Tag); ok {
|
||||
return tag
|
||||
} else {
|
||||
return defaultLanguage
|
||||
}
|
||||
}
|
||||
|
||||
type contentLanguageCtx struct{}
|
||||
|
||||
func SetContentLanguageToContext(ctx context.Context, code language.Tag) context.Context {
|
||||
return context.WithValue(ctx, contentLanguageCtx{}, code)
|
||||
}
|
||||
|
||||
// GetContentLanguageFromContext always returns language, either valid or default
|
||||
func GetContentLanguageFromContext(ctx context.Context) language.Tag {
|
||||
if tag, ok := ctx.Value(contentLanguageCtx{}).(language.Tag); ok {
|
||||
return tag
|
||||
} else {
|
||||
return language.Und
|
||||
}
|
||||
}
|
||||
|
||||
+32
-4
@@ -8,20 +8,48 @@ import (
|
||||
)
|
||||
|
||||
const AcceptLanguageHeader = "Accept-Language"
|
||||
const ContentLanguageHeader = "Content-Language"
|
||||
|
||||
func DetectLanguage(ll *service) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
next.ServeHTTP(w, upgradeRequest(ll, r))
|
||||
var ctx = r.Context()
|
||||
|
||||
// resolve accept-language header
|
||||
ctx = SetAcceptLanguageToContext(ctx, resolveAcceptLanguageHeaders(ll, r))
|
||||
|
||||
// resolve content-language header
|
||||
ctx = SetContentLanguageToContext(ctx, resolveContentLanguageHeaders(r.Header, ll.Default().Tag))
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func upgradeRequest(ll *service, r *http.Request) *http.Request {
|
||||
return r.WithContext(SetLanguageToContext(r.Context(), detectLanguage(ll, r)))
|
||||
// reads Content-Language headers from the request and returns
|
||||
// parsed value as language.Tag
|
||||
//
|
||||
// There are 4 valid scenarios for :
|
||||
// - lang == 'skip': returns und & services will (likely) ignore all translatable content
|
||||
// - invalid language: (same as skip)
|
||||
// - valid language: returns valid language; services will treat translatable content from the payload as translations
|
||||
// - no header: returns default language; (same as valid language)
|
||||
func resolveContentLanguageHeaders(h http.Header, def language.Tag) language.Tag {
|
||||
var cLang = h.Get(ContentLanguageHeader)
|
||||
|
||||
if cLang == "skip" || cLang == "" {
|
||||
// more than 1 header or value equal to skip
|
||||
return language.Und
|
||||
}
|
||||
|
||||
if tag, err := language.Parse(cLang); err != nil {
|
||||
return language.Und
|
||||
} else {
|
||||
return tag
|
||||
}
|
||||
}
|
||||
|
||||
func detectLanguage(ll *service, r *http.Request) (tag language.Tag) {
|
||||
func resolveAcceptLanguageHeaders(ll *service, r *http.Request) (tag language.Tag) {
|
||||
if ll.opt.DevelopmentMode {
|
||||
if err := ll.ReloadStatic(); err != nil {
|
||||
// when in development mode, refresh languages for every request
|
||||
|
||||
@@ -302,7 +302,7 @@ func (svc *service) LocalizedList(ctx context.Context) []*Language {
|
||||
var (
|
||||
l *Language
|
||||
ll = make([]*Language, 0, len(svc.set))
|
||||
reqLang = GetLanguageFromContext(ctx)
|
||||
reqLang = GetAcceptLanguageFromContext(ctx)
|
||||
)
|
||||
|
||||
for _, tag := range svc.tags {
|
||||
@@ -365,7 +365,7 @@ func (svc *service) EncodeExternal(w io.Writer, app string, ll ...language.Tag)
|
||||
// Language is picked from the context
|
||||
func (svc *service) NS(ctx context.Context, ns string) func(key string, rr ...string) string {
|
||||
var (
|
||||
tag = GetLanguageFromContext(ctx)
|
||||
tag = GetAcceptLanguageFromContext(ctx)
|
||||
)
|
||||
|
||||
return func(key string, rr ...string) string {
|
||||
@@ -377,7 +377,7 @@ func (svc *service) NS(ctx context.Context, ns string) func(key string, rr ...st
|
||||
//
|
||||
// Language is picked from the context
|
||||
func (svc *service) T(ctx context.Context, ns, key string, rr ...string) string {
|
||||
return svc.t(GetLanguageFromContext(ctx), ns, key, rr...)
|
||||
return svc.t(GetAcceptLanguageFromContext(ctx), ns, key, rr...)
|
||||
}
|
||||
|
||||
// T returns translated key from namespaces using list of replacement pairs
|
||||
@@ -392,7 +392,7 @@ func (svc *service) TFor(tag language.Tag, ns, key string, rr ...string) string
|
||||
// Language is picked from the context
|
||||
func (svc *service) TResource(ctx context.Context, ns, key string, rr ...string) string {
|
||||
|
||||
return svc.tResource(GetLanguageFromContext(ctx), ns, key, rr...)
|
||||
return svc.tResource(GetAcceptLanguageFromContext(ctx), ns, key, rr...)
|
||||
}
|
||||
|
||||
// TResourceFor returns translated key for resource using list of replacement pairs
|
||||
|
||||
Reference in New Issue
Block a user