ad i18n and renderer and move headers

This commit is contained in:
Livio Amstutz
2020-03-23 13:26:05 +01:00
parent 781e8b215e
commit 5843bac5f9
7 changed files with 213 additions and 15 deletions

View File

@@ -7,6 +7,7 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/caos/zitadel/internal/api"
"github.com/caos/zitadel/internal/api/auth"
)
@@ -22,7 +23,7 @@ func AuthorizationInterceptor(verifier auth.TokenVerifier, authConfig *auth.Conf
return nil, status.Error(codes.Unauthenticated, "auth header missing")
}
orgID := GetHeader(ctx, ZitadelOrgID)
orgID := GetHeader(ctx, api.ZitadelOrgID)
ctx, err := auth.CheckUserAuthorization(ctx, req, authToken, orgID, verifier, authConfig, authOpt)
if err != nil {

View File

@@ -4,12 +4,8 @@ import (
"context"
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
)
const (
Authorization = "authorization"
ZitadelOrgID = "x-zitadel-orgid"
"github.com/caos/zitadel/internal/api"
)
func GetHeader(ctx context.Context, headername string) string {
@@ -17,5 +13,5 @@ func GetHeader(ctx context.Context, headername string) string {
}
func GetAuthorizationHeader(ctx context.Context) string {
return GetHeader(ctx, Authorization)
return GetHeader(ctx, api.Authorization)
}

8
internal/api/header.go Normal file
View File

@@ -0,0 +1,8 @@
package api
const (
Authorization = "authorization"
AcceptLanguage = "Accept-Language"
ZitadelOrgID = "x-zitadel-orgid"
)

108
internal/api/html/i18n.go Normal file
View File

@@ -0,0 +1,108 @@
package html
import (
"encoding/json"
"io/ioutil"
"net/http"
"path"
"github.com/BurntSushi/toml"
"github.com/caos/logging"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
"gopkg.in/yaml.v2"
"github.com/caos/zitadel/internal/api"
http_util "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/errors"
)
type Translator struct {
bundle *i18n.Bundle
cookieName string
cookieHandler *http_util.CookieHandler
}
type TranslatorConfig struct {
Path string
DefaultLanguage language.Tag
CookieName string
}
func NewTranslator(config TranslatorConfig) (*Translator, error) {
t := new(Translator)
var err error
t.bundle, err = newBundle(config.Path, config.DefaultLanguage)
if err != nil {
return nil, err
}
t.cookieHandler = http_util.NewCookieHandler()
t.cookieName = config.CookieName
return t, nil
}
func newBundle(i18nDir string, defaultLanguage language.Tag) (*i18n.Bundle, error) {
bundle := i18n.NewBundle(defaultLanguage)
bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal)
bundle.RegisterUnmarshalFunc("yml", yaml.Unmarshal)
bundle.RegisterUnmarshalFunc("json", json.Unmarshal)
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
files, err := ioutil.ReadDir(i18nDir)
if err != nil {
return nil, errors.ThrowNotFound(err, "HTML-MnXRie", "path not found")
}
for _, file := range files {
bundle.MustLoadMessageFile(path.Join(i18nDir, file.Name()))
}
return bundle, nil
}
func (t *Translator) LocalizeFromRequest(r *http.Request, id string, args map[string]interface{}) string {
s, err := t.localizerFromRequest(r).Localize(&i18n.LocalizeConfig{
MessageID: id,
TemplateData: args,
})
if err != nil {
logging.Log("HTML-MsF5sx").WithError(err).Warnf("missing translation")
return id
}
return s
}
func (t *Translator) Localize(id string, args map[string]interface{}) string {
s, _ := t.localizer().Localize(&i18n.LocalizeConfig{
MessageID: id,
TemplateData: args,
})
return s
}
func (t *Translator) Lang(r *http.Request) language.Tag {
matcher := language.NewMatcher(t.bundle.LanguageTags())
tag, _ := language.MatchStrings(matcher, t.langsFromRequest(r)...)
return tag
}
func (t *Translator) SetLangCookie(w http.ResponseWriter, lang language.Tag) {
t.cookieHandler.SetCookie(w, t.cookieName, lang.String())
}
func (t *Translator) localizerFromRequest(r *http.Request) *i18n.Localizer {
return t.localizer(t.langsFromRequest(r)...)
}
func (t *Translator) localizer(langs ...string) *i18n.Localizer {
return i18n.NewLocalizer(t.bundle, langs...)
}
func (t *Translator) langsFromRequest(r *http.Request) []string {
langs := make([]string, 0)
if r != nil {
lang, err := t.cookieHandler.GetCookieValue(r, t.cookieName)
if err == nil {
langs = append(langs, lang)
}
langs = append(langs, r.Header.Get(api.AcceptLanguage))
}
return langs
}

View File

@@ -0,0 +1,82 @@
package html
import (
"net/http"
"path"
"text/template"
"github.com/caos/logging"
"golang.org/x/text/language"
)
const (
TranslateFn = "t"
)
type Renderer struct {
Templates map[string]*template.Template
i18n *Translator
}
func NewRenderer(templatesDir string, tmplMapping map[string]string, funcs map[string]interface{}, translatorConfig TranslatorConfig) (*Renderer, error) {
var err error
r := new(Renderer)
r.i18n, err = NewTranslator(translatorConfig)
if err != nil {
return nil, err
}
r.loadTemplates(templatesDir, tmplMapping, funcs)
return r, nil
}
func (r *Renderer) RenderTemplate(w http.ResponseWriter, req *http.Request, tmpl *template.Template, data interface{}, reqFuncs map[string]interface{}) {
reqFuncs = r.registerTranslateFn(req, reqFuncs)
if err := tmpl.Funcs(reqFuncs).Execute(w, data); err != nil {
logging.Log("HTML-lF8F6w").WithError(err).WithField("template", tmpl.Name).Error("error rendering template")
}
}
func (r *Renderer) Localize(id string, args map[string]interface{}) string {
return r.i18n.Localize(id, args)
}
func (r *Renderer) LocalizeFromRequest(req *http.Request, id string, args map[string]interface{}) string {
return r.i18n.LocalizeFromRequest(req, id, args)
}
func (r *Renderer) Lang(req *http.Request) language.Tag {
return r.i18n.Lang(req)
}
func (r *Renderer) loadTemplates(templatesDir string, tmplMapping map[string]string, funcs map[string]interface{}) {
funcs = r.registerTranslateFn(nil, funcs)
funcs[TranslateFn] = func(id string, args ...interface{}) string {
return id
}
tmpls := template.Must(template.New("").Funcs(funcs).ParseGlob(path.Join(templatesDir, "*.html")))
r.Templates = make(map[string]*template.Template, len(tmplMapping))
for name, file := range tmplMapping {
r.Templates[name] = tmpls.Lookup(file)
}
}
func (r *Renderer) registerTranslateFn(req *http.Request, funcs map[string]interface{}) map[string]interface{} {
if funcs == nil {
funcs = make(map[string]interface{})
}
funcs[TranslateFn] = func(id string, args ...interface{}) string {
m := map[string]interface{}{}
var key string
for i, arg := range args {
if i%2 == 0 {
key = arg.(string)
continue
}
m[key] = arg
}
if r == nil {
return r.Localize(id, m)
}
return r.LocalizeFromRequest(req, id, m)
}
return funcs
}