Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(fiberi18n): use sync.Map and utils.CopyString to fix concurrent problem #718

Merged
merged 1 commit into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions fiberi18n/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package fiberi18n

import (
"os"
"sync"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/utils"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
"gopkg.in/yaml.v2"
Expand Down Expand Up @@ -52,7 +54,7 @@ type Config struct {

ctx *fiber.Ctx
bundle *i18n.Bundle
localizerMap map[string]*i18n.Localizer
localizerMap *sync.Map
}

type Loader interface {
Expand All @@ -77,18 +79,16 @@ var ConfigDefault = &Config{

func defaultLangHandler(c *fiber.Ctx, defaultLang string) string {
var lang string
lang = c.Query("lang")
lang = utils.CopyString(c.Query("lang"))
if lang != "" {
return lang
}

lang = c.Get("Accept-Language")
lang = utils.CopyString(c.Get("Accept-Language"))
if lang != "" {
return lang
}

return defaultLang

}

func configDefault(config ...*Config) *Config {
Expand Down Expand Up @@ -135,5 +135,6 @@ func configDefault(config ...*Config) *Config {
if cfg.LangHandler == nil {
cfg.LangHandler = ConfigDefault.LangHandler
}

return cfg
}
69 changes: 34 additions & 35 deletions fiberi18n/i18n.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fiberi18n
import (
"fmt"
"path"
"sync"

"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
Expand All @@ -13,59 +14,56 @@ var appCfg *Config
// New creates a new middleware handler
func New(config ...*Config) fiber.Handler {
appCfg = configDefault(config...)
appCfg.bundle = initBundle()
loadMessages()
appCfg.localizerMap = initLocalizerMap()
// init bundle
bundle := i18n.NewBundle(appCfg.DefaultLanguage)
bundle.RegisterUnmarshalFunc(appCfg.FormatBundleFile, appCfg.UnmarshalFunc)
appCfg.bundle = bundle

appCfg.loadMessages().initLocalizerMap()

return func(c *fiber.Ctx) error {
if appCfg.Next != nil && appCfg.Next(c) {
return c.Next()
}

appCfg.ctx = c

return c.Next()
}
}

func initBundle() *i18n.Bundle {
bundle := i18n.NewBundle(appCfg.DefaultLanguage)
bundle.RegisterUnmarshalFunc(appCfg.FormatBundleFile, appCfg.UnmarshalFunc)

return bundle
}

func loadMessage(filepath string) {
buf, err := appCfg.Loader.LoadMessage(filepath)
func (c *Config) loadMessage(filepath string) {
buf, err := c.Loader.LoadMessage(filepath)
if err != nil {
panic(err)
}
if _, err := appCfg.bundle.ParseMessageFileBytes(buf, filepath); err != nil {
if _, err := c.bundle.ParseMessageFileBytes(buf, filepath); err != nil {
panic(err)
}
}

func loadMessages() {
for _, lang := range appCfg.AcceptLanguages {
bundleFile := fmt.Sprintf("%s.%s", lang.String(), appCfg.FormatBundleFile)
filepath := path.Join(appCfg.RootPath, bundleFile)

loadMessage(filepath)
func (c *Config) loadMessages() *Config {
for _, lang := range c.AcceptLanguages {
bundleFilePath := fmt.Sprintf("%s.%s", lang.String(), c.FormatBundleFile)
filepath := path.Join(c.RootPath, bundleFilePath)
c.loadMessage(filepath)
}
return c
}

func initLocalizerMap() map[string]*i18n.Localizer {
localizerMap := map[string]*i18n.Localizer{}
func (c *Config) initLocalizerMap() {
localizerMap := &sync.Map{}

for _, lang := range appCfg.AcceptLanguages {
for _, lang := range c.AcceptLanguages {
s := lang.String()
localizerMap[s] = i18n.NewLocalizer(appCfg.bundle, s)
localizerMap.Store(s, i18n.NewLocalizer(c.bundle, s))
}

lang := appCfg.DefaultLanguage.String()
if _, ok := localizerMap[lang]; !ok {
localizerMap[lang] = i18n.NewLocalizer(appCfg.bundle, lang)
lang := c.DefaultLanguage.String()
if _, ok := localizerMap.Load(lang); !ok {
localizerMap.Store(lang, i18n.NewLocalizer(c.bundle, lang))
}

return localizerMap
c.localizerMap = localizerMap
}

/*
Expand Down Expand Up @@ -100,24 +98,25 @@ GetMessage get the i18n message
})
*/
func GetMessage(params interface{}) (string, error) {
var localizeConfig *i18n.LocalizeConfig

lang := appCfg.LangHandler(appCfg.ctx, appCfg.DefaultLanguage.String())
localizer, hasValue := appCfg.localizerMap[lang]
if !hasValue {
localizer = appCfg.localizerMap[appCfg.DefaultLanguage.String()]

localizer, _ := appCfg.localizerMap.Load(lang)
if localizer == nil {
defaultLang := appCfg.DefaultLanguage.String()
localizer, _ = appCfg.localizerMap.Load(defaultLang)
}

var localizeConfig *i18n.LocalizeConfig
switch paramValue := params.(type) {
case string:
localizeConfig = &i18n.LocalizeConfig{MessageID: paramValue}
case *i18n.LocalizeConfig:
localizeConfig = paramValue
}

message, err := localizer.Localize(localizeConfig)
message, err := localizer.(*i18n.Localizer).Localize(localizeConfig)
if err != nil {
return "", fmt.Errorf("i18n.Localize error: %v", err.Error())
return "", fmt.Errorf("i18n.Localize error: %v", err)
}
return message, nil
}
Loading