mirror of https://gitlab.com/ngerakines/tavern.git
181 lines
4.4 KiB
Go
181 lines
4.4 KiB
Go
package web
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"strings"
|
|
|
|
"github.com/getsentry/sentry-go"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/go-playground/locales"
|
|
"github.com/go-playground/locales/currency"
|
|
ut "github.com/go-playground/universal-translator"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type Translator interface {
|
|
locales.Translator
|
|
T(key interface{}, params ...string) string
|
|
THTML(key interface{}, params ...template.HTML) template.HTML
|
|
C(key interface{}, num float64, digits uint64, param string) string
|
|
O(key interface{}, num float64, digits uint64, param string) string
|
|
R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) string
|
|
Currency() currency.Type
|
|
}
|
|
|
|
type translator struct {
|
|
locales.Translator
|
|
trans ut.Translator
|
|
logger *zap.Logger
|
|
enableSentry bool
|
|
}
|
|
|
|
var _ Translator = (*translator)(nil)
|
|
|
|
func (t *translator) T(key interface{}, params ...string) string {
|
|
keyValue := fmt.Sprintf("%s", key)
|
|
s, err := t.trans.T(keyValue, params...)
|
|
if err != nil {
|
|
t.logger.Warn("issue translating key",
|
|
zap.String("key", keyValue),
|
|
zap.String("locale", t.Locale()),
|
|
zap.Error(err))
|
|
if t.enableSentry {
|
|
hub := sentry.CurrentHub().Clone()
|
|
hub.Scope().SetExtras(map[string]interface{}{
|
|
"key": keyValue,
|
|
"locale": t.Locale(),
|
|
"param_count": len(params),
|
|
})
|
|
hub.CaptureException(err)
|
|
}
|
|
return keyValue
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (t *translator) THTML(key interface{}, params ...template.HTML) template.HTML {
|
|
var stringParams []string
|
|
for _, param := range params {
|
|
stringParams = append(stringParams, string(param))
|
|
}
|
|
s, err := t.trans.T(key, stringParams...)
|
|
if err != nil {
|
|
t.logger.Warn("issue translating key", zap.String("key", fmt.Sprintf("%v", key)), zap.Error(err))
|
|
return template.HTML(fmt.Sprintf("%s", key))
|
|
}
|
|
|
|
return template.HTML(s)
|
|
}
|
|
|
|
func (t *translator) C(key interface{}, num float64, digits uint64, param string) string {
|
|
|
|
s, err := t.trans.C(key, num, digits, param)
|
|
if err != nil {
|
|
t.logger.Warn("issue translating cardinal key", zap.String("key", fmt.Sprintf("%v", key)), zap.Error(err))
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (t *translator) O(key interface{}, num float64, digits uint64, param string) string {
|
|
|
|
s, err := t.trans.C(key, num, digits, param)
|
|
if err != nil {
|
|
t.logger.Warn("issue translating ordinal key", zap.String("key", fmt.Sprintf("%v", key)), zap.Error(err))
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (t *translator) R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) string {
|
|
|
|
s, err := t.trans.R(key, num1, digits1, num2, digits2, param1, param2)
|
|
if err != nil {
|
|
t.logger.Warn("issue translating range key", zap.String("key", fmt.Sprintf("%v", key)), zap.Error(err))
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (t *translator) Currency() currency.Type {
|
|
switch t.Locale() {
|
|
case "en":
|
|
return currency.USD
|
|
case "fr":
|
|
return currency.EUR
|
|
default:
|
|
return currency.USD
|
|
}
|
|
}
|
|
|
|
type i18nMiddleware struct {
|
|
utrans *ut.UniversalTranslator
|
|
logger *zap.Logger
|
|
domain string
|
|
enableSentry bool
|
|
}
|
|
|
|
func (h i18nMiddleware) findLocale(c *gin.Context) {
|
|
var langs []string
|
|
|
|
setCookie := false
|
|
if localeParam := c.Query("lang"); len(localeParam) > 0 {
|
|
setCookie = true
|
|
langs = append(langs, localeParam)
|
|
}
|
|
localeCookie, err := c.Cookie("lang")
|
|
if err == nil && len(localeCookie) > 0 {
|
|
langs = append(langs, localeCookie)
|
|
}
|
|
langs = append(langs, AcceptedLanguages(c)...)
|
|
langs = append(langs, "en")
|
|
|
|
t, found := h.utrans.FindTranslator(langs...)
|
|
if !found {
|
|
h.logger.Warn("no translation found for language", zap.Strings("langs", langs))
|
|
}
|
|
if localeCookie == "" || t.Locale() != localeCookie {
|
|
setCookie = true
|
|
}
|
|
if setCookie {
|
|
c.SetCookie("lang", t.Locale(), 0, "/", h.domain, true, true)
|
|
}
|
|
|
|
c.Set("locale", t.Locale())
|
|
c.Set("trans", &translator{
|
|
trans: t,
|
|
Translator: t.(locales.Translator),
|
|
logger: h.logger,
|
|
enableSentry: h.enableSentry,
|
|
})
|
|
c.Next()
|
|
}
|
|
|
|
func localeOrDefault(c *gin.Context) string {
|
|
ctxValue, ok := c.Get("locale")
|
|
if ok {
|
|
if locale, ok := ctxValue.(string); ok {
|
|
return locale
|
|
}
|
|
}
|
|
return "en"
|
|
}
|
|
|
|
func AcceptedLanguages(c *gin.Context) (languages []string) {
|
|
accepted := c.GetHeader("Accept-Language")
|
|
if accepted == "" {
|
|
return
|
|
}
|
|
options := strings.Split(accepted, ",")
|
|
l := len(options)
|
|
languages = make([]string, l)
|
|
|
|
for i := 0; i < l; i++ {
|
|
locale := strings.SplitN(options[i], ";", 2)
|
|
languages[i] = strings.Trim(locale[0], " ")
|
|
}
|
|
return
|
|
}
|