3
0

Support for select field option text translations

This commit is contained in:
Denis Arh
2021-12-02 13:18:05 +01:00
parent 57ebacdbb8
commit de11e7d20a
7 changed files with 211 additions and 24 deletions
+17 -9
View File
@@ -145,15 +145,8 @@ func (svc module) Find(ctx context.Context, filter types.ModuleFilter) (set type
return err
}
// i18n
tag := locale.GetAcceptLanguageFromContext(ctx)
set.Walk(func(m *types.Module) error {
m.DecodeTranslations(svc.locale.Locale().ResourceTranslations(tag, m.ResourceTranslation()))
m.Fields.Walk(func(mf *types.ModuleField) error {
mf.DecodeTranslations(svc.locale.Locale().ResourceTranslations(tag, mf.ResourceTranslation()))
return nil
})
svc.proc(ctx, m)
return nil
})
return nil
@@ -220,6 +213,20 @@ func (svc module) FindByAny(ctx context.Context, namespaceID uint64, identifier
return m, nil
}
func (svc module) proc(ctx context.Context, m *types.Module) {
if svc.locale == nil || svc.locale.Locale() == nil {
return
}
tag := locale.GetAcceptLanguageFromContext(ctx)
m.DecodeTranslations(svc.locale.Locale().ResourceTranslations(tag, m.ResourceTranslation()))
m.Fields.Walk(func(mf *types.ModuleField) error {
mf.DecodeTranslations(svc.locale.Locale().ResourceTranslations(tag, mf.ResourceTranslation()))
return nil
})
}
func (svc module) Create(ctx context.Context, new *types.Module) (*types.Module, error) {
var (
ns *types.Namespace
@@ -444,9 +451,10 @@ func (svc module) lookup(ctx context.Context, namespaceID uint64, lookup func(*m
}
return loadModuleFields(ctx, svc.store, m)
}()
svc.proc(ctx, m)
return m, svc.recordAction(ctx, aProps, ModuleActionLookup, err)
}
+10 -1
View File
@@ -14,8 +14,9 @@ package types
import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/locale"
"strconv"
"github.com/cortezaproject/corteza-server/pkg/locale"
)
type (
@@ -71,6 +72,12 @@ var (
Path: "expression.validator.{{validatorID}}.error",
CustomHandler: "validatorError",
}
LocaleKeyModuleFieldOptionsOptionTexts = LocaleKey{
Name: "optionsOptionTexts",
Resource: ModuleFieldResourceTranslationType,
Path: "meta.options.{{value}}.text",
CustomHandler: "optionsOptionTexts",
}
LocaleKeyModuleName = LocaleKey{
Name: "name",
Resource: ModuleResourceTranslationType,
@@ -154,6 +161,7 @@ func (r *ModuleField) DecodeTranslations(tt locale.ResourceTranslationIndex) {
r.decodeTranslationsHintView(tt)
r.decodeTranslationsHintEdit(tt)
r.decodeTranslationsValidatorError(tt)
r.decodeTranslationsOptionsOptionTexts(tt)
}
func (r *ModuleField) EncodeTranslations() (out locale.ResourceTranslationSet) {
@@ -171,6 +179,7 @@ func (r *ModuleField) EncodeTranslations() (out locale.ResourceTranslationSet) {
out = append(out, r.encodeTranslationsHintView()...)
out = append(out, r.encodeTranslationsHintEdit()...)
out = append(out, r.encodeTranslationsValidatorError()...)
out = append(out, r.encodeTranslationsOptionsOptionTexts()...)
return out
}
+91 -6
View File
@@ -103,6 +103,58 @@ func (f *ModuleField) decodeTranslationsHintEdit(tt locale.ResourceTranslationIn
}
}
// Decodes translations and modifies options
//
// Why "options-option-texts"? Because we're translating option txts under options key-value
func (f *ModuleField) decodeTranslationsOptionsOptionTexts(tt locale.ResourceTranslationIndex) {
var (
tr *locale.ResourceTranslation
)
optsUnknown, has := f.Options["options"]
if !has {
return
}
optsSlice, is := optsUnknown.([]interface{})
if !is {
return
}
for i, optUnknown := range optsSlice {
outOpt := map[string]string{}
// what is this we're dealing with? slice of strings (values) or map (value+text)
switch opt := optUnknown.(type) {
case string:
// cast string (value) into map (value+text)
// and use value as text (as a fallback in case
// of missing translation)
outOpt["value"] = opt
outOpt["text"] = opt
case map[string]interface{}:
outOpt["value"], is = opt["value"].(string)
if !is {
continue
}
outOpt["text"], _ = opt["text"].(string)
}
// find the translation for that value
// and update the option (effectively overwriting
// the original text value (in case of map option)
trKey := strings.NewReplacer("{{value}}", outOpt["value"]).Replace(LocaleKeyModuleFieldOptionsOptionTexts.Path)
if tr = tt.FindByKey(trKey); tr != nil {
outOpt["text"] = tr.Msg
}
// Update slice item with translated option
optsSlice[i] = outOpt
}
}
func (m *ModuleField) encodeTranslationsValidatorError() (out locale.ResourceTranslationSet) {
out = make(locale.ResourceTranslationSet, 0, 3)
@@ -179,12 +231,45 @@ func (f *ModuleField) encodeTranslationsHintEdit() (out locale.ResourceTranslati
return out
}
func (m ModuleField) Clone() *ModuleField {
return &m
// extracts option texts and converts (encodes) them to translations
func (f *ModuleField) encodeTranslationsOptionsOptionTexts() (out locale.ResourceTranslationSet) {
out = make(locale.ResourceTranslationSet, 0, 3)
optsUnknown, has := f.Options["options"]
if !has {
return
}
optsSlice, is := optsUnknown.([]interface{})
if !is {
return
}
for _, optUnknown := range optsSlice {
// we only care about maps (items with value & text)
switch opt := optUnknown.(type) {
case map[string]interface{}:
value, _ := opt["value"].(string)
text, _ := opt["text"].(string)
out = append(out, &locale.ResourceTranslation{
Resource: f.ResourceTranslation(),
Key: strings.NewReplacer("{{value}}", value).
Replace(LocaleKeyModuleFieldOptionsOptionTexts.Path),
Msg: text,
})
}
}
return
}
func (m ModuleField) setOptionKey(v interface{}, kk ...string) {
opt := m.Options
func (f ModuleField) Clone() *ModuleField {
return &f
}
func (f ModuleField) setOptionKey(v interface{}, kk ...string) {
opt := f.Options
for _, k := range kk[0 : len(kk)-1] {
_, ok := opt[k]
@@ -206,8 +291,8 @@ func (m ModuleField) setOptionKey(v interface{}, kk ...string) {
opt[k] = v
}
func (m ModuleField) getOptionKey(kk ...string) interface{} {
opt := m.Options
func (f ModuleField) getOptionKey(kk ...string) interface{} {
opt := f.Options
for _, k := range kk[0 : len(kk)-1] {
_, ok := opt[k]
+2
View File
@@ -19,6 +19,8 @@ const (
moduleFieldOptionIsUnique = "isUnique"
moduleFieldOptionIsUniqueMultiValue = "isUniqueMultiValue"
moduleFieldOptionOptions = "options"
moduleFieldNumberOptionPrecision = "precision"
moduleFieldNumberOptionPrecisionMin uint = 0
moduleFieldNumberOptionPrecisionMax uint = 6
+78
View File
@@ -0,0 +1,78 @@
package types
import (
"testing"
"github.com/cortezaproject/corteza-server/pkg/locale"
"github.com/stretchr/testify/require"
)
func TestModuleField_decodeTranslationsOptionsOptionTexts(t *testing.T) {
rti := locale.ResourceTranslationIndex{
"meta.options.val1.text": &locale.ResourceTranslation{Msg: "TEXT-1"},
}
tests := []struct {
name string
opts ModuleFieldOptions
out interface{}
}{
// TODO: Add test cases.
{"all-nil", nil, nil},
{"options-nil",
ModuleFieldOptions{
"options": nil,
},
nil,
},
{"options-foo",
ModuleFieldOptions{
"options": "foo",
},
nil,
},
{"options-foo-number",
ModuleFieldOptions{
"options": []interface{}{1},
},
nil,
},
{"options-valid-slice",
ModuleFieldOptions{
"options": []interface{}{
"val1",
"val2",
},
},
[]interface{}{
map[string]string{"value": "val1", "text": "TEXT-1"},
map[string]string{"value": "val2", "text": "val2"},
},
},
{"options-valid-map",
ModuleFieldOptions{
"options": []interface{}{
map[string]interface{}{"value": "val1", "text": "Text1"},
map[string]interface{}{"value": "val2", "text": "Text2"},
},
},
[]interface{}{
map[string]string{"value": "val1", "text": "TEXT-1"},
map[string]string{"value": "val2", "text": "Text2"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var (
req = require.New(t)
f = &ModuleField{Options: tt.opts}
)
f.decodeTranslationsOptionsOptionTexts(rti)
if tt.out != nil {
req.Equal(tt.out, f.Options["options"])
}
})
}
}
+8 -8
View File
@@ -57,17 +57,17 @@ func (m Module) LabelResourceID() uint64 {
}
// SetLabel adds new label to label map
func (m *ModuleField) SetLabel(key string, value string) {
if m.Labels == nil {
m.Labels = make(map[string]string)
func (f *ModuleField) SetLabel(key string, value string) {
if f.Labels == nil {
f.Labels = make(map[string]string)
}
m.Labels[key] = value
f.Labels[key] = value
}
// GetLabels adds new label to label map
func (m ModuleField) GetLabels() map[string]string {
return m.Labels
func (f ModuleField) GetLabels() map[string]string {
return f.Labels
}
// GetLabels adds new label to label map
@@ -76,8 +76,8 @@ func (ModuleField) LabelResourceKind() string {
}
// GetLabels adds new label to label map
func (m ModuleField) LabelResourceID() uint64 {
return m.ID
func (f ModuleField) LabelResourceID() uint64 {
return f.ID
}
// SetLabel adds new label to label map
+5
View File
@@ -22,3 +22,8 @@ locale:
- { name: hintView, path: meta.hint.view, custom: true, customHandler: hintView }
- { name: hintEdit, path: meta.hint.edit, custom: true, customHandler: hintEdit }
- { name: validatorError, path: "expression.validator.{{validatorID}}.error", custom: true, customHandler: validatorError }
- { name: optionsOptionTexts,
path: "meta.options.{{value}}.text",
custom: true,
customHandler: optionsOptionTexts
}