package main import ( "fmt" "github.com/go-playground/locales/en" ut "github.com/go-playground/universal-translator" validator "github.com/go-playground/validator/v10" en_translations "github.com/go-playground/validator/v10/translations/en" "regexp" ) type Validator struct { validate *validator.Validate trans ut.Translator } func (v Validator) Struct(i interface{}) error { err := v.validate.Struct(i) return v.Translate(err) } type Translation struct { format string params func(fe validator.FieldError) []any } var translations = map[string]Translation{ "oneof": { "{0} must be one of [{1}], got '{2}'", func(fe validator.FieldError) []any { return []any{fe.Namespace(), fe.Param(), fe.Value()} }, }, "applicationName": { "{0} must not end with -[0-9]+$ to prevent conflicts with generated resource names", func(fe validator.FieldError) []any { return []any{fe.Namespace()} }, }, } type TranslatedFieldError struct { validator.FieldError msg string } func NewTranslatedFieldError(msg string, e validator.FieldError) TranslatedFieldError { return TranslatedFieldError{ FieldError: e, msg: msg, } } func (e TranslatedFieldError) Error() string { return e.msg } func applicationNameValidator(fl validator.FieldLevel) bool { // Get the field's value as string value := fl.Field().String() regexString := "-p[0-9]+$" regex, err := regexp.Compile(regexString) if err != nil { // programming error panic(err) } return !regex.MatchString(value) } func NewValidator() (*Validator, error) { validate := validator.New(validator.WithRequiredStructEnabled()) validate.RegisterValidation("applicationName", applicationNameValidator) language := en.New() translator := ut.New(language, language) trans, ok := translator.GetTranslator("en") if !ok { return nil, fmt.Errorf("Could not find translator to print error meesage") } en_translations.RegisterDefaultTranslations(validate, trans) for validation, translation := range translations { validate.RegisterTranslation(validation, trans, func(ut ut.Translator) error { return ut.Add(validation, translation.format, true) }, func(ut ut.Translator, fe validator.FieldError) string { params := translation.params(fe) stringParams := make([]string, len(params)) for i, param := range params { stringParams[i] = fmt.Sprintf("%s", param) } t, _ := ut.T(validation, stringParams...) return t }) } return &Validator{ validate: validate, trans: trans, }, nil } func (v Validator) ValidateStruct(s interface{}) error { err := v.validate.Struct(s) return v.Translate(err) } func (v Validator) Translate(err error) error { if err == nil { return err } errs, ok := err.(validator.ValidationErrors) if !ok { return err } var errorList validator.ValidationErrors = nil for _, e := range errs { var translation validator.FieldError = TranslatedFieldError{ FieldError: e, msg: e.Translate(v.trans), } errorList = append(errorList, translation) } return errorList }