The translate functions are separated semantically

This commit is contained in:
Roman Perekhod
2024-08-02 14:59:37 +02:00
committed by jkoberg
parent fc85837d82
commit 70fc7ecb92
3 changed files with 151 additions and 55 deletions

View File

@@ -53,45 +53,79 @@ func (t Translator) Translate(str, locale string) string {
return t.Locale(locale).Get(str)
}
type field func() (string, []any)
func TranslateLocation(t Translator, locale string) func(string, ...any) string {
return t.Locale(locale).Get
}
func TranslateField(fieldName string, fn ...any) field {
type field func() string
type structs func() (string, []any)
type each func() (string, []any)
func TranslateField(fieldName string) field {
return func() string {
return fieldName
}
}
func TranslateStruct(fieldName string, fn ...any) structs {
return func() (string, []any) {
return fieldName, fn
}
}
func TranslateLocation(t Translator, locale string) func(string, ...any) string {
return t.Locale(locale).Get
func TranslateEach(fieldName string, fn ...any) each {
return func() (string, []any) {
return fieldName, fn
}
}
// TranslateEntity function provides the generic way to translate the necessary fields in composite entities.
// The function takes the entity, translation function and fields to translate
// that are described by the TranslateField function. The function supports nested structs and slices of structs.
//
// type InnreStruct struct {
// Description string
// DisplayName *string
// }
// type InnreStruct struct {
// Description string
// DisplayName *string
// }
//
// type TopLevelStruct struct {
// Description string
// DisplayName *string
// SubStruct *InnreStruct
// }
//
// TranslateEntity(tt.args.structPtr, translateFunc(),
// TranslateField("Description"),
// TranslateField("DisplayName"),
// TranslateField("SubStruct",
// TranslateField("Description"),
// TranslateField("DisplayName")))
// type TopLevelStruct struct {
// Description string
// DisplayName *string
// SubStruct *InnreStruct
// StructList []*InnreStruct
// }
// s:= &TopLevelStruct{
// Description: "description",
// DisplayName: toStrPointer("displayName"),
// SubStruct: &InnreStruct{
// Description: "inner",
// DisplayName: toStrPointer("innerDisplayName"),
// },
// StructList: []*InnreStruct{
// {
// Description: "inner 1",
// DisplayName: toStrPointer("innerDisplayName 1"),
// },
// {
// Description: "inner 2",
// DisplayName: toStrPointer("innerDisplayName 2"),
// },
// },
// }
// TranslateEntity(s, translateFunc(),
// TranslateField("Description"),
// TranslateField("DisplayName"),
// TranslateStruct("SubStruct",
// TranslateField("Description"),
// TranslateField("DisplayName")),
// TranslateEach("StructList",
// TranslateField("Description"),
// TranslateField("DisplayName")))
func TranslateEntity(entity any, tr func(string, ...any) string, fields ...any) error {
value := reflect.ValueOf(entity)
// Indirect through pointers and interfaces
if value.Kind() == reflect.Ptr || value.Kind() == reflect.Interface {
if value.IsNil() {
// treat a nil struct pointer as valid
return nil
}
value = value.Elem()
@@ -118,18 +152,9 @@ func TranslateEntity(entity any, tr func(string, ...any) string, fields ...any)
}
func translateInner(value reflect.Value, tr func(string, ...any) string, fields ...any) {
for _, fl := range fields {
if _, ok := fl.(field); ok {
translateField(value, tr, fl.(field))
}
}
}
func translateField(value reflect.Value, tr func(string, ...any) string, fl field) {
if !value.IsValid() {
return
}
fieldName, fields := fl()
// Indirect through pointers and interfaces
if value.Kind() == reflect.Ptr || value.Kind() == reflect.Interface {
if value.IsNil() {
@@ -137,6 +162,24 @@ func translateField(value reflect.Value, tr func(string, ...any) string, fl fiel
}
value = value.Elem()
}
if !value.IsValid() {
return
}
for _, fl := range fields {
switch fl.(type) {
case field:
translateStringField(value, tr, fl.(field))
case each:
translateEach(value, tr, fl.(each))
case structs:
translateStruct(value, tr, fl.(structs))
}
}
}
func translateEach(value reflect.Value, tr func(string, ...any) string, fl each) {
fieldName, fields := fl()
// exported field
innerValue := value.FieldByName(fieldName)
if !innerValue.IsValid() {
return
@@ -147,20 +190,23 @@ func translateField(value reflect.Value, tr func(string, ...any) string, fl fiel
translateInner(innerValue.Index(i), tr, fields...)
}
}
}
func translateStruct(value reflect.Value, tr func(string, ...any) string, fl structs) {
fieldName, fields := fl()
// exported field
innerValue := value.FieldByName(fieldName)
if !innerValue.IsValid() {
return
}
if isStruct(innerValue) {
translateInner(innerValue, tr, fields...)
return
}
translateStringField(value, tr, fieldName)
}
func translateStringField(value reflect.Value, tr func(string, ...any) string, fieldName string) {
if value.Kind() == reflect.Ptr {
if value.IsNil() {
return
}
value = value.Elem()
}
func translateStringField(value reflect.Value, tr func(string, ...any) string, fl field) {
fieldName := fl()
// exported field
f := value.FieldByName(fieldName)
if f.IsValid() {

View File

@@ -20,7 +20,8 @@ func TestTranslateStruct(t *testing.T) {
}
type WrapperStruct struct {
StructList []*InnreStruct
Description string
StructList []*InnreStruct
}
toStrPointer := func(str string) *string {
@@ -65,6 +66,45 @@ func TestTranslateStruct(t *testing.T) {
},
},
},
{
name: "top level slice of struct",
args: args{
structPtr: []*TopLevelStruct{
{
Description: "inner 1",
DisplayName: toStrPointer("innerDisplayName 1"),
SubStruct: &InnreStruct{
Description: "inner",
DisplayName: toStrPointer("innerDisplayName"),
},
},
{
Description: "inner 2",
DisplayName: toStrPointer("innerDisplayName 2"),
},
},
request: []any{
TranslateField("Description"),
TranslateField("DisplayName"),
TranslateStruct("SubStruct",
TranslateField("Description"),
TranslateField("DisplayName"))},
},
expected: []*TopLevelStruct{
{
Description: "new Inner 1",
DisplayName: toStrPointer("new InnerDisplayName 1"),
SubStruct: &InnreStruct{
Description: "new Inner",
DisplayName: toStrPointer("new InnerDisplayName"),
},
},
{
Description: "new Inner 2",
DisplayName: toStrPointer("new InnerDisplayName 2"),
},
},
},
{
name: "wrapped struct full",
args: args{
@@ -81,7 +121,7 @@ func TestTranslateStruct(t *testing.T) {
},
},
request: []any{
TranslateField("StructList",
TranslateEach("StructList",
TranslateField("Description"),
TranslateField("DisplayName"))},
},
@@ -105,7 +145,7 @@ func TestTranslateStruct(t *testing.T) {
request: []any{
TranslateField("Description"),
TranslateField("DisplayName"),
TranslateField("NotExistingSubStructName",
TranslateStruct("NotExistingSubStructName",
TranslateField("Description"),
TranslateField("DisplayName")),
},
@@ -119,7 +159,7 @@ func TestTranslateStruct(t *testing.T) {
request: []any{
TranslateField("Description"),
TranslateField("DisplayName"),
TranslateField("SubStruct",
TranslateStruct("SubStruct",
TranslateField("Description"),
TranslateField("DisplayName"))},
},
@@ -134,7 +174,7 @@ func TestTranslateStruct(t *testing.T) {
},
request: []any{
TranslateField("NotExistingFieldName"),
TranslateField("SubStruct",
TranslateStruct("SubStruct",
TranslateField("NotExistingFieldName"))},
},
expected: &TopLevelStruct{
@@ -151,7 +191,7 @@ func TestTranslateStruct(t *testing.T) {
},
request: []any{TranslateField("Description"),
TranslateField("DisplayName"),
TranslateField("SubStruct",
TranslateStruct("SubStruct",
TranslateField("Description"),
TranslateField("DisplayName"))},
},
@@ -169,7 +209,7 @@ func TestTranslateStruct(t *testing.T) {
},
request: []any{TranslateField("Description"),
TranslateField("DisplayName"),
TranslateField("SubStruct",
TranslateStruct("SubStruct",
TranslateField("Description"),
TranslateField("DisplayName"))},
},
@@ -192,7 +232,7 @@ func TestTranslateStruct(t *testing.T) {
request: []any{
TranslateField("Description"),
TranslateField("DisplayName"),
TranslateField("SubStruct",
TranslateStruct("SubStruct",
TranslateField("Description"),
TranslateField("DisplayName"))},
},
@@ -213,6 +253,14 @@ func TestTranslateStruct(t *testing.T) {
},
expected: nil,
},
{
name: "empty slice",
args: args{
structPtr: []string{},
request: []any{TranslateField("Description")},
},
expected: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -26,7 +26,7 @@ import (
"github.com/go-chi/render"
libregraph "github.com/owncloud/libre-graph-api-go"
"github.com/owncloud/ocis/v2/ocis-pkg/l10n"
l10n2 "github.com/owncloud/ocis/v2/services/graph/pkg/l10n"
l10n_pkg "github.com/owncloud/ocis/v2/services/graph/pkg/l10n"
"github.com/owncloud/ocis/v2/ocis-pkg/conversions"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
@@ -637,10 +637,11 @@ func (api DriveItemPermissionsApi) ListPermissions(w http.ResponseWriter, r *htt
loc := r.Header.Get(l10n.HeaderAcceptLanguage)
w.Header().Add("Content-Language", loc)
if loc != "" && loc != "en" {
trf := l10n2.NewTranslateLocation(loc, "en")
err := l10n.TranslateEntity(permissions.LibreGraphPermissionsRolesAllowedValues, trf,
l10n.TranslateField("Description"),
l10n.TranslateField("DisplayName"),
trf := l10n_pkg.NewTranslateLocation(loc, "en")
err := l10n.TranslateEntity(permissions, trf,
l10n.TranslateEach("LibreGraphPermissionsRolesAllowedValues",
l10n.TranslateField("Description"),
l10n.TranslateField("DisplayName")),
)
if err != nil {
api.logger.Error().Err(err).Msg("tranlation error")
@@ -671,10 +672,11 @@ func (api DriveItemPermissionsApi) ListSpaceRootPermissions(w http.ResponseWrite
loc := r.Header.Get(l10n.HeaderAcceptLanguage)
w.Header().Add("Content-Language", loc)
if loc != "" && loc != "en" {
trf := l10n2.NewTranslateLocation(loc, "en")
err := l10n.TranslateEntity(permissions.LibreGraphPermissionsRolesAllowedValues, trf,
l10n.TranslateField("Description"),
l10n.TranslateField("DisplayName"),
trf := l10n_pkg.NewTranslateLocation(loc, "en")
err := l10n.TranslateEntity(permissions, trf,
l10n.TranslateEach("LibreGraphPermissionsRolesAllowedValues",
l10n.TranslateField("Description"),
l10n.TranslateField("DisplayName")),
)
if err != nil {
api.logger.Error().Err(err).Msg("tranlation error")