2022-09-27 17:59:05 +08:00
|
|
|
package validator
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2022-12-19 17:07:34 +08:00
|
|
|
"fmt"
|
2022-09-27 17:59:05 +08:00
|
|
|
"reflect"
|
2022-11-11 11:24:36 +08:00
|
|
|
"strings"
|
2022-09-27 17:59:05 +08:00
|
|
|
|
2022-10-24 16:51:05 +08:00
|
|
|
"github.com/answerdev/answer/internal/base/reason"
|
|
|
|
"github.com/answerdev/answer/internal/base/translator"
|
2022-09-27 17:59:05 +08:00
|
|
|
"github.com/go-playground/locales"
|
2022-12-19 17:07:34 +08:00
|
|
|
german "github.com/go-playground/locales/de"
|
2022-09-27 17:59:05 +08:00
|
|
|
english "github.com/go-playground/locales/en"
|
2022-12-19 17:07:34 +08:00
|
|
|
spanish "github.com/go-playground/locales/es"
|
|
|
|
french "github.com/go-playground/locales/fr"
|
|
|
|
italian "github.com/go-playground/locales/it"
|
|
|
|
japanese "github.com/go-playground/locales/ja"
|
|
|
|
korean "github.com/go-playground/locales/ko"
|
|
|
|
portuguese "github.com/go-playground/locales/pt"
|
|
|
|
russian "github.com/go-playground/locales/ru"
|
|
|
|
vietnamese "github.com/go-playground/locales/vi"
|
|
|
|
chinese "github.com/go-playground/locales/zh"
|
|
|
|
chineseTraditional "github.com/go-playground/locales/zh_Hant_TW"
|
2022-11-01 15:25:44 +08:00
|
|
|
ut "github.com/go-playground/universal-translator"
|
2022-09-27 17:59:05 +08:00
|
|
|
"github.com/go-playground/validator/v10"
|
|
|
|
"github.com/go-playground/validator/v10/translations/en"
|
2022-12-19 17:07:34 +08:00
|
|
|
"github.com/go-playground/validator/v10/translations/es"
|
|
|
|
"github.com/go-playground/validator/v10/translations/fr"
|
|
|
|
"github.com/go-playground/validator/v10/translations/it"
|
|
|
|
"github.com/go-playground/validator/v10/translations/ja"
|
|
|
|
"github.com/go-playground/validator/v10/translations/pt"
|
|
|
|
"github.com/go-playground/validator/v10/translations/ru"
|
|
|
|
"github.com/go-playground/validator/v10/translations/vi"
|
2022-09-27 17:59:05 +08:00
|
|
|
"github.com/go-playground/validator/v10/translations/zh"
|
2022-12-19 17:07:34 +08:00
|
|
|
"github.com/go-playground/validator/v10/translations/zh_tw"
|
2022-10-14 17:01:06 +08:00
|
|
|
myErrors "github.com/segmentfault/pacman/errors"
|
2022-09-27 17:59:05 +08:00
|
|
|
"github.com/segmentfault/pacman/i18n"
|
2022-10-20 16:38:56 +08:00
|
|
|
"github.com/segmentfault/pacman/log"
|
2022-09-27 17:59:05 +08:00
|
|
|
)
|
|
|
|
|
2022-12-19 17:07:34 +08:00
|
|
|
type TranslatorLocal struct {
|
|
|
|
La i18n.Language
|
|
|
|
Lo locales.Translator
|
|
|
|
RegisterFunc func(v *validator.Validate, trans ut.Translator) (err error)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
allLanguageTranslators = []*TranslatorLocal{
|
|
|
|
{La: i18n.LanguageChinese, Lo: chinese.New(), RegisterFunc: zh.RegisterDefaultTranslations},
|
|
|
|
{La: i18n.LanguageChineseTraditional, Lo: chineseTraditional.New(), RegisterFunc: zh_tw.RegisterDefaultTranslations},
|
|
|
|
{La: i18n.LanguageEnglish, Lo: english.New(), RegisterFunc: en.RegisterDefaultTranslations},
|
|
|
|
{La: i18n.LanguageGerman, Lo: german.New(), RegisterFunc: nil},
|
|
|
|
{La: i18n.LanguageSpanish, Lo: spanish.New(), RegisterFunc: es.RegisterDefaultTranslations},
|
|
|
|
{La: i18n.LanguageFrench, Lo: french.New(), RegisterFunc: fr.RegisterDefaultTranslations},
|
|
|
|
{La: i18n.LanguageItalian, Lo: italian.New(), RegisterFunc: it.RegisterDefaultTranslations},
|
|
|
|
{La: i18n.LanguageJapanese, Lo: japanese.New(), RegisterFunc: ja.RegisterDefaultTranslations},
|
|
|
|
{La: i18n.LanguageKorean, Lo: korean.New(), RegisterFunc: nil},
|
|
|
|
{La: i18n.LanguagePortuguese, Lo: portuguese.New(), RegisterFunc: pt.RegisterDefaultTranslations},
|
|
|
|
{La: i18n.LanguageRussian, Lo: russian.New(), RegisterFunc: ru.RegisterDefaultTranslations},
|
|
|
|
{La: i18n.LanguageVietnamese, Lo: vietnamese.New(), RegisterFunc: vi.RegisterDefaultTranslations},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2022-09-27 17:59:05 +08:00
|
|
|
// MyValidator my validator
|
|
|
|
type MyValidator struct {
|
|
|
|
Validate *validator.Validate
|
|
|
|
Tran ut.Translator
|
|
|
|
Lang i18n.Language
|
|
|
|
}
|
|
|
|
|
2022-11-17 17:57:13 +08:00
|
|
|
// FormErrorField indicates the current form error content. which field is error and error message.
|
|
|
|
type FormErrorField struct {
|
|
|
|
ErrorField string `json:"error_field"`
|
|
|
|
ErrorMsg string `json:"error_msg"`
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
|
2022-11-01 15:25:44 +08:00
|
|
|
// GlobalValidatorMapping is a mapping from validator to translator used
|
2022-12-19 18:15:17 +08:00
|
|
|
var GlobalValidatorMapping = make(map[i18n.Language]*MyValidator, 0)
|
2022-09-27 17:59:05 +08:00
|
|
|
|
|
|
|
func init() {
|
2022-12-19 17:07:34 +08:00
|
|
|
for _, t := range allLanguageTranslators {
|
|
|
|
tran, val := getTran(t.Lo), createDefaultValidator(t.La)
|
|
|
|
if t.RegisterFunc != nil {
|
|
|
|
if err := t.RegisterFunc(val, tran); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
2022-12-19 18:15:17 +08:00
|
|
|
GlobalValidatorMapping[t.La] = &MyValidator{Validate: val, Tran: tran, Lang: t.La}
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-19 17:07:34 +08:00
|
|
|
func getTran(lo locales.Translator) ut.Translator {
|
|
|
|
tran, ok := ut.New(lo, lo).GetTranslator(lo.Locale())
|
2022-09-27 17:59:05 +08:00
|
|
|
if !ok {
|
2022-12-19 17:07:34 +08:00
|
|
|
panic(fmt.Sprintf("not found translator %s", lo.Locale()))
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
return tran
|
|
|
|
}
|
|
|
|
|
|
|
|
func createDefaultValidator(la i18n.Language) *validator.Validate {
|
|
|
|
validate := validator.New()
|
|
|
|
validate.RegisterTagNameFunc(func(fld reflect.StructField) (res string) {
|
|
|
|
defer func() {
|
|
|
|
if len(res) > 0 {
|
|
|
|
res = translator.GlobalTrans.Tr(la, res)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
if jsonTag := fld.Tag.Get("json"); len(jsonTag) > 0 {
|
|
|
|
if jsonTag == "-" {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return jsonTag
|
|
|
|
}
|
|
|
|
if formTag := fld.Tag.Get("form"); len(formTag) > 0 {
|
|
|
|
return formTag
|
|
|
|
}
|
|
|
|
return fld.Name
|
|
|
|
})
|
|
|
|
return validate
|
|
|
|
}
|
|
|
|
|
2022-12-19 18:15:17 +08:00
|
|
|
func GetValidatorByLang(lang i18n.Language) *MyValidator {
|
|
|
|
if GlobalValidatorMapping[lang] != nil {
|
|
|
|
return GlobalValidatorMapping[lang]
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
2022-12-19 18:15:17 +08:00
|
|
|
return GlobalValidatorMapping[i18n.DefaultLanguage]
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check /
|
2022-11-17 17:57:13 +08:00
|
|
|
func (m *MyValidator) Check(value interface{}) (errFields []*FormErrorField, err error) {
|
2022-09-27 17:59:05 +08:00
|
|
|
err = m.Validate.Struct(value)
|
|
|
|
if err != nil {
|
|
|
|
var valErrors validator.ValidationErrors
|
|
|
|
if !errors.As(err, &valErrors) {
|
2022-10-20 16:38:56 +08:00
|
|
|
log.Error(err)
|
2022-09-27 17:59:05 +08:00
|
|
|
return nil, errors.New("validate check exception")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, fieldError := range valErrors {
|
2022-11-17 17:57:13 +08:00
|
|
|
errField := &FormErrorField{
|
|
|
|
ErrorField: fieldError.Field(),
|
|
|
|
ErrorMsg: fieldError.Translate(m.Tran),
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
2022-11-11 11:24:36 +08:00
|
|
|
|
|
|
|
// get original tag name from value for set err field key.
|
|
|
|
structNamespace := fieldError.StructNamespace()
|
|
|
|
_, fieldName, found := strings.Cut(structNamespace, ".")
|
|
|
|
if found {
|
|
|
|
originalTag := getObjectTagByFieldName(value, fieldName)
|
|
|
|
if len(originalTag) > 0 {
|
2022-11-17 17:57:13 +08:00
|
|
|
errField.ErrorField = originalTag
|
2022-11-11 11:24:36 +08:00
|
|
|
}
|
|
|
|
}
|
2022-11-17 17:57:13 +08:00
|
|
|
errFields = append(errFields, errField)
|
|
|
|
}
|
|
|
|
if len(errFields) > 0 {
|
|
|
|
errMsg := ""
|
|
|
|
if len(errFields) == 1 {
|
|
|
|
errMsg = errFields[0].ErrorMsg
|
|
|
|
}
|
|
|
|
return errFields, myErrors.BadRequest(reason.RequestFormatError).WithMsg(errMsg)
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := value.(Checker); ok {
|
2022-11-17 17:57:13 +08:00
|
|
|
errFields, err = v.Check()
|
2022-12-13 12:11:29 +08:00
|
|
|
if err == nil {
|
|
|
|
return nil, nil
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
2022-12-13 12:11:29 +08:00
|
|
|
for _, errField := range errFields {
|
|
|
|
errField.ErrorMsg = translator.GlobalTrans.Tr(m.Lang, errField.ErrorMsg)
|
|
|
|
}
|
|
|
|
return errFields, err
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checker .
|
|
|
|
type Checker interface {
|
2022-11-17 17:57:13 +08:00
|
|
|
Check() (errField []*FormErrorField, err error)
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
2022-11-11 11:24:36 +08:00
|
|
|
|
|
|
|
func getObjectTagByFieldName(obj interface{}, fieldName string) (tag string) {
|
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
objT := reflect.TypeOf(obj)
|
|
|
|
objT = objT.Elem()
|
|
|
|
|
|
|
|
structField, exists := objT.FieldByName(fieldName)
|
|
|
|
if !exists {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
tag = structField.Tag.Get("json")
|
|
|
|
if len(tag) == 0 {
|
|
|
|
return structField.Tag.Get("form")
|
|
|
|
}
|
|
|
|
return tag
|
|
|
|
}
|