Add support for decoding content-language header from request

This commit is contained in:
Denis Arh
2021-09-22 11:26:14 +02:00
committed by Tomaž Jerman
parent 1e02164c01
commit 4276000add
7 changed files with 61 additions and 18 deletions
+1 -1
View File
@@ -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 {
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4 -4
View File
@@ -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