diff --git a/app/boot_levels.go b/app/boot_levels.go
index 71ae84073..46d6678d3 100644
--- a/app/boot_levels.go
+++ b/app/boot_levels.go
@@ -101,7 +101,7 @@ func (app *CortezaApp) Setup() (err error) {
localeLog = app.Log
}
- if languages, err = locale.New(localeLog, strings.Split(app.Opt.Locale.Path, ":")...); err != nil {
+ if languages, err = locale.New(localeLog, app.Opt.Locale); err != nil {
return err
} else {
locale.SetGlobal(languages)
diff --git a/auth/assets/templates/inc_header.html.tpl b/auth/assets/templates/inc_header.html.tpl
index 7f9f4320e..b53ca413c 100644
--- a/auth/assets/templates/inc_header.html.tpl
+++ b/auth/assets/templates/inc_header.html.tpl
@@ -1,5 +1,5 @@
-
+
@@ -12,9 +12,7 @@
-
- Corteza
-
+ Corteza
{{ if .user }}
diff --git a/auth/handlers/handler.go b/auth/handlers/handler.go
index af4b1ab09..def920680 100644
--- a/auth/handlers/handler.go
+++ b/auth/handlers/handler.go
@@ -232,8 +232,12 @@ func (h *AuthHandlers) handle(fn handlerFn) http.HandlerFunc {
var (
// translator template function
+ //
+ // we're adding these functions on every request on all templates
+ // 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 loc.Current(req.Context()).String() },
"tr": func(key string, pp ...interface{}) template.HTML {
ss := make([]string, len(pp))
for i := range pp {
diff --git a/pkg/locale/http.go b/pkg/locale/http.go
index a477de100..783276464 100644
--- a/pkg/locale/http.go
+++ b/pkg/locale/http.go
@@ -3,6 +3,7 @@ package locale
import (
"net/http"
+ "go.uber.org/zap"
"golang.org/x/text/language"
)
@@ -11,34 +12,56 @@ const AcceptLanguageHeader = "Accept-Language"
func DetectLanguage(ll *Languages) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // parse & ignore errors
var (
- preferred = ll.Default()
- supported = ll.Tags()
-
- accepted, _, err = language.ParseAcceptLanguage(r.Header.Get(AcceptLanguageHeader))
+ rawLanguageTag string
)
- if err == nil {
- // ignoring index & confidence
- preferred, _, _ = language.NewMatcher(supported).Match(accepted...)
+ // try to detect the language from the request's query string:
+ if ll.Options().QueryStringParam != "" {
+ rawLanguageTag = r.URL.Query().Get(ll.Options().QueryStringParam)
+ }
- var match bool
- for _, s := range supported {
- if s == preferred {
- match = true
- break
+ // try to detect the language from the request's headers:
+ if len(rawLanguageTag) == 0 {
+ rawLanguageTag = r.Header.Get(AcceptLanguageHeader)
+ }
+
+ if len(rawLanguageTag) > 0 {
+ // parse & ignore errors
+ var (
+ preferred = ll.Default()
+ supported = ll.Tags()
+
+ accepted, _, err = language.ParseAcceptLanguage(rawLanguageTag)
+ )
+
+ if err == nil {
+ // ignoring index & confidence
+ preferred, _, _ = language.NewMatcher(supported).Match(accepted...)
+
+ var match bool
+ for _, s := range supported {
+ if s == preferred {
+ match = true
+ break
+ }
+ }
+
+ if !match {
+ base, _ := preferred.Base()
+ preferred = language.MustParse(base.String())
}
}
- if !match {
- base, _ := preferred.Base()
- preferred = language.MustParse(base.String())
- }
- }
+ // new request with new context
+ r = r.WithContext(SetLanguageToContext(r.Context(), preferred))
- // new request with new context
- r = r.WithContext(SetLanguageToContext(r.Context(), preferred))
+ ll.log.Debug(
+ "language detected",
+ zap.String("preferred", preferred.String()),
+ zap.String("raw", rawLanguageTag),
+ )
+ }
next.ServeHTTP(w, r)
})
diff --git a/pkg/locale/locale.go b/pkg/locale/locale.go
index 50f9c90eb..c24fccd50 100644
--- a/pkg/locale/locale.go
+++ b/pkg/locale/locale.go
@@ -8,6 +8,7 @@ import (
"strings"
"sync"
+ "github.com/cortezaproject/corteza-server/pkg/options"
"go.uber.org/zap"
"golang.org/x/text/language"
)
@@ -45,6 +46,8 @@ type (
log *zap.Logger
+ opt options.LocaleOpt
+
// sources
src []string
ll map[language.Tag]*Language
@@ -56,8 +59,12 @@ type (
ErrorMetaKey struct{}
)
-func New(log *zap.Logger, src ...string) (*Languages, error) {
- ll := &Languages{src: src, log: log.Named("locale")}
+func New(log *zap.Logger, opt options.LocaleOpt) (*Languages, error) {
+ ll := &Languages{
+ opt: opt,
+ src: strings.Split(opt.Path, ":"),
+ log: log.Named("locale"),
+ }
return ll, ll.Reload()
}
@@ -65,6 +72,10 @@ func (set *Languages) Default() language.Tag {
return set.def
}
+func (set *Languages) Options() options.LocaleOpt {
+ return set.opt
+}
+
func (set *Languages) Tags() (tt []language.Tag) {
tt = make([]language.Tag, 0, len(set.ll))
for t := range set.ll {
@@ -144,6 +155,10 @@ func (set *Languages) GetNS(ctx context.Context, ns string) func(key string, rr
}
}
+func (set *Languages) Current(ctx context.Context) language.Tag {
+ return GetLanguageFromContext(ctx)
+}
+
func (set *Languages) Get(ctx context.Context, ns, key string, rr ...string) string {
return set.get(GetLanguageFromContext(ctx), ns, key, rr...)
}
diff --git a/pkg/options/locale.gen.go b/pkg/options/locale.gen.go
index dd3b9862b..60053855a 100644
--- a/pkg/options/locale.gen.go
+++ b/pkg/options/locale.gen.go
@@ -10,14 +10,17 @@ package options
type (
LocaleOpt struct {
- Path string `env:"LOCALE_PATH"`
- Log bool `env:"LOCALE_LOG"`
+ Path string `env:"LOCALE_PATH"`
+ QueryStringParam string `env:"LOCALE_QUERY_STRING_PARAM"`
+ Log bool `env:"LOCALE_LOG"`
}
)
// Locale initializes and returns a LocaleOpt with default values
func Locale() (o *LocaleOpt) {
- o = &LocaleOpt{}
+ o = &LocaleOpt{
+ QueryStringParam: "lng",
+ }
fill(o)
diff --git a/pkg/options/locale.yaml b/pkg/options/locale.yaml
index 97af87c03..2084de167 100644
--- a/pkg/options/locale.yaml
+++ b/pkg/options/locale.yaml
@@ -2,6 +2,13 @@ props:
- name: path
description: Path to locale config and translation files, separated by colon
+ - name: queryStringParam
+ description: |
+ Name of the query string parameter used to pass the language tag (it overrides Accept-Language header).
+ Set it to empty string to disable detection from the query string.
+ This parameter is ignored if only one language is enabled
+ default: "lng"
+
- name: log
type: bool
description: Log locale related events and actions