Refactor i18n, add templating for resource app
This commit is contained in:
parent
e4f17ca315
commit
161ea7fe0c
21 changed files with 432 additions and 152 deletions
102
common/services/i18n.go
Normal file
102
common/services/i18n.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
type contextKey int
|
||||
|
||||
const (
|
||||
ctxI18nBundle contextKey = iota
|
||||
ctxI18nCatalog
|
||||
)
|
||||
|
||||
type MessageCatalog struct {
|
||||
messages map[string]*i18n.Message
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func (m *MessageCatalog) AddMessages(messages map[string]*i18n.Message) {
|
||||
for key, value := range messages {
|
||||
m.messages[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MessageCatalog) LookupErrorMessage(tag string, field string, value interface{}, localizer *i18n.Localizer) string {
|
||||
var message *i18n.Message
|
||||
message, ok := m.messages[fmt.Sprintf("%s-%s", field, tag)]
|
||||
if !ok {
|
||||
m.logger.Infof("no specific error message %s-%s", field, tag)
|
||||
message, ok = m.messages[tag]
|
||||
if !ok {
|
||||
m.logger.Infof("no specific error message %s", tag)
|
||||
message, ok = m.messages["unknown"]
|
||||
if !ok {
|
||||
m.logger.Error("no default translation found")
|
||||
return tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
translation, err := localizer.Localize(&i18n.LocalizeConfig{
|
||||
DefaultMessage: message,
|
||||
TemplateData: map[string]interface{}{
|
||||
"Value": value,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
m.logger.Error(err)
|
||||
return tag
|
||||
}
|
||||
return translation
|
||||
}
|
||||
|
||||
func (m *MessageCatalog) LookupMessage(id string, templateData map[string]interface{}, localizer *i18n.Localizer) string {
|
||||
if message, ok := m.messages[id]; ok {
|
||||
translation, err := localizer.Localize(&i18n.LocalizeConfig{
|
||||
DefaultMessage: message,
|
||||
TemplateData: templateData,
|
||||
})
|
||||
if err != nil {
|
||||
m.logger.Error(err)
|
||||
return id
|
||||
}
|
||||
return translation
|
||||
} else {
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
func InitI18n(ctx context.Context, logger *log.Logger, languages []string) context.Context {
|
||||
bundle := i18n.NewBundle(language.English)
|
||||
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
|
||||
for _, lang := range languages {
|
||||
_, err := bundle.LoadMessageFile(fmt.Sprintf("active.%s.toml", lang))
|
||||
if err != nil {
|
||||
logger.Warnln("message bundle de.toml not found")
|
||||
}
|
||||
}
|
||||
catalog := initMessageCatalog(logger)
|
||||
ctx = context.WithValue(ctx, ctxI18nBundle, bundle)
|
||||
ctx = context.WithValue(ctx, ctxI18nCatalog, catalog)
|
||||
return ctx
|
||||
}
|
||||
|
||||
func initMessageCatalog(logger *log.Logger) *MessageCatalog {
|
||||
messages := make(map[string]*i18n.Message)
|
||||
return &MessageCatalog{messages: messages, logger: logger}
|
||||
}
|
||||
|
||||
func GetI18nBundle(ctx context.Context) *i18n.Bundle {
|
||||
return ctx.Value(ctxI18nBundle).(*i18n.Bundle)
|
||||
}
|
||||
|
||||
func GetMessageCatalog(ctx context.Context) *MessageCatalog {
|
||||
return ctx.Value(ctxI18nCatalog).(*MessageCatalog)
|
||||
}
|
|
@ -2,11 +2,22 @@ package services
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type oidcContextKey int
|
||||
|
||||
const (
|
||||
ctxOidcConfig oidcContextKey = iota
|
||||
ctxOAuth2Config
|
||||
ctxOidcJwks
|
||||
)
|
||||
|
||||
type OpenIDConfiguration struct {
|
||||
|
@ -16,11 +27,19 @@ type OpenIDConfiguration struct {
|
|||
EndSessionEndpoint string `json:"end_session_endpoint"`
|
||||
}
|
||||
|
||||
func DiscoverOIDC(logger *log.Logger, oidcServer string, apiClient *http.Client) (o *OpenIDConfiguration, err error) {
|
||||
type OidcParams struct {
|
||||
OidcServer string
|
||||
OidcClientId string
|
||||
OidcClientSecret string
|
||||
APIClient *http.Client
|
||||
}
|
||||
|
||||
func DiscoverOIDC(ctx context.Context, logger *log.Logger, params *OidcParams) (context.Context, error) {
|
||||
var discoveryUrl *url.URL
|
||||
|
||||
if discoveryUrl, err = url.Parse(oidcServer); err != nil {
|
||||
logger.Fatalf("could not parse oidc.server parameter value %s: %s", oidcServer, err)
|
||||
discoveryUrl, err := url.Parse(params.OidcServer)
|
||||
if err != nil {
|
||||
logger.Fatalf("could not parse oidc.server parameter value %s: %s", params.OidcServer, err)
|
||||
} else {
|
||||
discoveryUrl.Path = "/.well-known/openid-configuration"
|
||||
}
|
||||
|
@ -29,23 +48,53 @@ func DiscoverOIDC(logger *log.Logger, oidcServer string, apiClient *http.Client)
|
|||
var req *http.Request
|
||||
req, err = http.NewRequest(http.MethodGet, discoveryUrl.String(), bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
req.Header = map[string][]string{
|
||||
"Accept": {"application/json"},
|
||||
}
|
||||
|
||||
resp, err := apiClient.Do(req)
|
||||
resp, err := params.APIClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
o = &OpenIDConfiguration{}
|
||||
err = dec.Decode(o)
|
||||
discoveryResponse := &OpenIDConfiguration{}
|
||||
err = dec.Decode(discoveryResponse)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
ctx = context.WithValue(ctx, ctxOidcConfig, discoveryResponse)
|
||||
|
||||
return
|
||||
oauth2Config := &oauth2.Config{
|
||||
ClientID: params.OidcClientId,
|
||||
ClientSecret: params.OidcClientSecret,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: discoveryResponse.AuthorizationEndpoint,
|
||||
TokenURL: discoveryResponse.TokenEndpoint,
|
||||
},
|
||||
Scopes: []string{"openid", "offline"},
|
||||
}
|
||||
ctx = context.WithValue(ctx, ctxOAuth2Config, oauth2Config)
|
||||
|
||||
keySet, err := jwk.FetchHTTP(discoveryResponse.JwksUri, jwk.WithHTTPClient(params.APIClient))
|
||||
if err != nil {
|
||||
log.Fatalf("could not fetch JWKs: %s", err)
|
||||
}
|
||||
ctx = context.WithValue(ctx, ctxOidcJwks, keySet)
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func GetOidcConfig(ctx context.Context) *OpenIDConfiguration {
|
||||
return ctx.Value(ctxOidcConfig).(*OpenIDConfiguration)
|
||||
}
|
||||
|
||||
func GetOAuth2Config(ctx context.Context) *oauth2.Config {
|
||||
return ctx.Value(ctxOAuth2Config).(*oauth2.Config)
|
||||
}
|
||||
|
||||
func GetJwkSet(ctx context.Context) *jwk.Set {
|
||||
return ctx.Value(ctxOidcJwks).(*jwk.Set)
|
||||
}
|
||||
|
|
Reference in a new issue