234 lines
6.9 KiB
Go
234 lines
6.9 KiB
Go
|
/*
|
||
|
Copyright 2020, 2021 Jan Dittberner
|
||
|
|
||
|
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License at
|
||
|
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package services
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
|
||
|
log "github.com/sirupsen/logrus"
|
||
|
|
||
|
"github.com/BurntSushi/toml"
|
||
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||
|
"golang.org/x/text/language"
|
||
|
)
|
||
|
|
||
|
func AddMessages(ctx context.Context) {
|
||
|
messages := make(map[string]*i18n.Message)
|
||
|
messages["unknown"] = &i18n.Message{
|
||
|
ID: "ErrorUnknown",
|
||
|
Other: "Unknown error",
|
||
|
}
|
||
|
messages["TitleRequestConsent"] = &i18n.Message{
|
||
|
ID: "TitleRequestConsent",
|
||
|
Other: "Application requests your consent",
|
||
|
}
|
||
|
messages["LabelSubmit"] = &i18n.Message{
|
||
|
ID: "LabelSubmit",
|
||
|
Other: "Submit",
|
||
|
}
|
||
|
messages["LabelConsent"] = &i18n.Message{
|
||
|
ID: "LabelConsent",
|
||
|
Other: "I hereby agree that the application may get the requested permissions.",
|
||
|
}
|
||
|
messages["IntroConsentRequested"] = &i18n.Message{
|
||
|
ID: "IntroConsentRequested",
|
||
|
Other: "The <strong>{{ .client }}</strong> application requested your consent for the following set of permissions:",
|
||
|
}
|
||
|
messages["IntroConsentMoreInformation"] = &i18n.Message{
|
||
|
ID: "IntroConsentMoreInformation",
|
||
|
Other: "You can find more information about <strong>{{ .client }}</strong> at <a href=\"{{ .clientLink }}\">its description page</a>.",
|
||
|
}
|
||
|
messages["ClaimsInformation"] = &i18n.Message{
|
||
|
ID: "ClaimsInformation",
|
||
|
Other: "In addition the application wants access to the following information:",
|
||
|
}
|
||
|
messages["WrongOrLockedUserOrInvalidPassword"] = &i18n.Message{
|
||
|
ID: "WrongOrLockedUserOrInvalidPassword",
|
||
|
Other: "You entered an invalid username or password or your account has been locked.",
|
||
|
}
|
||
|
messages["CertLoginIntroText"] = &i18n.Message{
|
||
|
ID: "CertLoginIntroText",
|
||
|
Other: "The application <strong>{{ .ClientName }}</strong> requests a login.",
|
||
|
}
|
||
|
messages["EmailChoiceText"] = &i18n.Message{
|
||
|
ID: "EmailChoiceText",
|
||
|
One: "You have presented a valid client certificate for the following email address:",
|
||
|
Other: "You have presented a valid client certificate for multiple email addresses. Please choose which one you want to present to the application:",
|
||
|
}
|
||
|
messages["LoginTitle"] = &i18n.Message{
|
||
|
ID: "LoginTitle",
|
||
|
Other: "Authenticate with a client certificate",
|
||
|
}
|
||
|
messages["CertLoginRequestText"] = &i18n.Message{
|
||
|
ID: "CertLoginRequestText",
|
||
|
Other: "Do you want to use the chosen identity from the certificate for authentication?",
|
||
|
}
|
||
|
messages["LabelAcceptCertLogin"] = &i18n.Message{
|
||
|
ID: "LabelAcceptCertLogin",
|
||
|
Description: "Label for a button to accept certificate login",
|
||
|
Other: "Yes, please use this identity",
|
||
|
}
|
||
|
messages["LabelRejectCertLogin"] = &i18n.Message{
|
||
|
ID: "LabelRejectCertLogin",
|
||
|
Description: "Label for a button to reject certificate login",
|
||
|
Other: "No, please send me back",
|
||
|
}
|
||
|
messages["LoginDeniedByUser"] = &i18n.Message{
|
||
|
ID: "LoginDeniedByUser",
|
||
|
Other: "Login has been denied by the user.",
|
||
|
}
|
||
|
messages["HintChooseAnIdentityForAuthentication"] = &i18n.Message{
|
||
|
ID: "HintChooseAnIdentityForAuthentication",
|
||
|
Other: "Choose an identity for authentication.",
|
||
|
}
|
||
|
GetMessageCatalog(ctx).AddMessages(messages)
|
||
|
}
|
||
|
|
||
|
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.Warnf("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 {
|
||
|
switch err.(type) {
|
||
|
case *i18n.MessageNotFoundErr:
|
||
|
m.logger.Warnf("message %s not found: %v", id, err)
|
||
|
if translation != "" {
|
||
|
return translation
|
||
|
}
|
||
|
break
|
||
|
default:
|
||
|
m.logger.Error(err)
|
||
|
}
|
||
|
return id
|
||
|
}
|
||
|
return translation
|
||
|
} else {
|
||
|
m.logger.Warnf("no translation found for %s", id)
|
||
|
return id
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *MessageCatalog) LookupMessagePlural(id string, templateData map[string]interface{}, localizer *i18n.Localizer, count int) string {
|
||
|
if message, ok := m.messages[id]; ok {
|
||
|
translation, err := localizer.Localize(&i18n.LocalizeConfig{
|
||
|
DefaultMessage: message,
|
||
|
TemplateData: templateData,
|
||
|
PluralCount: count,
|
||
|
})
|
||
|
if err != nil {
|
||
|
switch err.(type) {
|
||
|
case *i18n.MessageNotFoundErr:
|
||
|
m.logger.Warnf("message %s not found: %v", id, err)
|
||
|
if translation != "" {
|
||
|
return translation
|
||
|
}
|
||
|
break
|
||
|
default:
|
||
|
m.logger.Error(err)
|
||
|
}
|
||
|
return id
|
||
|
}
|
||
|
return translation
|
||
|
} else {
|
||
|
m.logger.Warnf("no translation found for %s", id)
|
||
|
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)
|
||
|
messages["ErrorTitle"] = &i18n.Message{
|
||
|
ID: "ErrorTitle",
|
||
|
Other: "An error has occurred",
|
||
|
}
|
||
|
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)
|
||
|
}
|