diff --git a/pkg/schema/localizer/localizer.go b/pkg/schema/localizer/localizer.go index a818f6e922993330ea0c4bca396b5a51c49bec37..0d1ddabec5bc5be09fb0cbd0ff9c4e83272b4baa 100644 --- a/pkg/schema/localizer/localizer.go +++ b/pkg/schema/localizer/localizer.go @@ -12,25 +12,39 @@ import ( ) type Localizer struct { - Schema *schema.Schema - Locales map[string]*locales.Locale - LocaleID string + schema *schema.Schema + localesKV map[string]*locales.Locale + locales []*locales.Locale + localeID string + allowNoPublished bool + allowDisabled bool +} + +type Config struct { + Schema *schema.Schema + Locales []*locales.Locale + LocaleID string + AllowNoPublished bool + AllowDisabled bool } // NewLocalizer создает экземпляр локализатора. Требуется указать "загруженную" схему -func NewLocalizer(s *schema.Schema, locs []*locales.Locale, localeID string) *Localizer { - if localeID == "" { - localeID = locales.DefaultID +func NewLocalizer(cfg Config) *Localizer { + if cfg.LocaleID == "" { + cfg.LocaleID = locales.DefaultID } loc := &Localizer{ - Schema: s, - Locales: make(map[string]*locales.Locale, len(locs)), - LocaleID: localeID, + schema: cfg.Schema, + localesKV: make(map[string]*locales.Locale, len(cfg.Locales)), + locales: cfg.Locales, + localeID: cfg.LocaleID, + allowDisabled: cfg.AllowDisabled, + allowNoPublished: cfg.AllowNoPublished, } - for _, l := range locs { - loc.Locales[l.ID] = l + for _, l := range cfg.Locales { + loc.localesKV[l.ID] = l } return loc @@ -43,15 +57,15 @@ func NewLocalizer(s *schema.Schema, locs []*locales.Locale, localeID string) *Lo // При отсутствии каких-либо полей в переводе на `localeID` данные берутся сначала из fallback-локали, // если перевод отсутствует то из `data` func (l *Localizer) Localize(data map[string]interface{}, translations map[string]map[string]interface{}) (localized map[string]interface{}, err error) { - if l.LocaleID == locales.DefaultID { - return data, nil - } - target, fallback, err := l.getTargetAndFallBackLocales() if err != nil { return nil, err } + if target.IsDefault() { + return data, nil + } + // localize fallback -> target fallbackData := data var exist bool @@ -75,15 +89,15 @@ func (l *Localizer) Localize(data map[string]interface{}, translations map[strin // ExtractTranslation Получить "просеянные" данные для локали localeID: все поля, значения которых совпадают // с переводом на fallback-локаль или основными данными, удаляются из перевода func (l *Localizer) ExtractTranslation(data map[string]interface{}, translations map[string]map[string]interface{}) (translation map[string]interface{}, err error) { - if l.LocaleID == locales.DefaultID { - return data, nil - } - target, fallback, err := l.getTargetAndFallBackLocales() if err != nil { return nil, err } + if target.IsDefault() { + return data, nil + } + fallbackData := data var exist bool if !fallback.IsDefault() { @@ -100,13 +114,46 @@ func (l *Localizer) ExtractTranslation(data map[string]interface{}, translations return l.extractTranslation(translation, fallbackData) } +func (l *Localizer) locale(localeID string) (loc *locales.Locale, err error) { + if localeID == "" { + return nil, locales.ErrLocaleIDRequired + } + + var exist bool + if loc, exist = l.localesKV[localeID]; !exist { + return nil, locales.ErrNotFound + } + + if loc == nil { + return nil, locales.ErrNotFound + } + + if !l.allowDisabled && loc.Disabled { + return nil, errors.New("locale is disabled") + } + + if !l.allowNoPublished && localeID == l.localeID && loc.NoPublish { // can use non-publishing locale for fallback + return nil, errors.New("localizer not configured for non-publishing locales") + } + + return +} + +func (l *Localizer) Locale() (loc *locales.Locale, err error) { + return l.locale(l.localeID) +} + +func (l *Localizer) Locales() []*locales.Locale { + return l.locales +} + func (l *Localizer) getTargetAndFallBackLocales() (target, fallback *locales.Locale, err error) { - if target = l.Locales[l.LocaleID]; target == nil || target.Disabled { + if target, err = l.locale(l.localeID); err != nil { return nil, nil, errors.New("target locale not found or disabled") } - if fallback = l.Locales[target.Fallback]; fallback == nil || fallback.Disabled { - fallback = l.Locales[locales.DefaultID] + if fallback, err = l.locale(target.Fallback); err != nil { + fallback = l.localesKV[locales.DefaultID] } return } @@ -116,7 +163,7 @@ func (l *Localizer) localize(target, fallback map[string]interface{}) (map[strin return nil, nil } - single := l.Schema.GetFields(func(f *field.Field, p string) bool { + single := l.schema.GetFields(func(f *field.Field, p string) bool { return f.SingleLocale }) @@ -125,7 +172,7 @@ func (l *Localizer) localize(target, fallback map[string]interface{}) (map[strin cfg.Fields[sn.Path] = walk.FieldConfig{Fn: walk.KeepSrc} } - w := walk.NewWalker(l.Schema, cfg) + w := walk.NewWalker(l.schema, cfg) w.DefaultFn = localize res, _, err := w.DataWalk(context.Background(), target, fallback) @@ -145,7 +192,7 @@ func (l *Localizer) extractTranslation(target, fallback map[string]interface{}) return nil, nil } - single := l.Schema.GetFields(func(f *field.Field, p string) bool { + single := l.schema.GetFields(func(f *field.Field, p string) bool { return f.SingleLocale }) @@ -154,7 +201,7 @@ func (l *Localizer) extractTranslation(target, fallback map[string]interface{}) cfg.Fields[sn.Path] = walk.FieldConfig{Fn: walk.RemoveValue} } - w := walk.NewWalker(l.Schema, cfg) + w := walk.NewWalker(l.schema, cfg) w.DefaultFn = extractTranslation res, _, err := w.DataWalk(context.Background(), target, fallback) diff --git a/pkg/schema/localizer/localizer_test.go b/pkg/schema/localizer/localizer_test.go index 6f5fbdd687bb5d613f18fb039340a9489f5aaf9c..23e6f0fdad2440f3102f481ce410bb29a5acc190 100644 --- a/pkg/schema/localizer/localizer_test.go +++ b/pkg/schema/localizer/localizer_test.go @@ -355,7 +355,7 @@ func TestLocalizer_localize(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { l := &Localizer{ - Schema: s, + schema: s, } got, err := l.localize(tt.target, tt.fallback) if !tt.wantErr { @@ -598,7 +598,7 @@ func TestLocalizer_deLocalize(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { l := &Localizer{ - Schema: s, + schema: s, } got, err := l.extractTranslation(tt.target, tt.fallback) if !tt.wantErr {