diff --git a/gin/b/b.go b/gin/b/b.go
new file mode 100644
index 0000000..d56b65d
--- /dev/null
+++ b/gin/b/b.go
@@ -0,0 +1,62 @@
+package b
+
+import (
+ "gin-valid/gin/binding"
+ "gin-valid/go-playground/validator/v10"
+ "mime"
+ "net/http"
+)
+
+type ValidError struct {
+ ErrString string
+}
+
+func (e *ValidError) Error() string {
+ return e.ErrString
+}
+
+func ShouldBind(req *http.Request, obj interface{}) error {
+ content, err := contentType(req)
+ if err != nil {
+ return err
+ }
+ b := binding.Default(req.Method, content)
+ err = ShouldBindWith(req, obj, b)
+ errs, ok := err.(validator.ValidationErrors)
+ if !ok {
+ // 非validator.ValidationErrors类型错误直接返回
+ return err
+ }
+ return errs.Translate(binding.ValidTrans)
+}
+
+func ShouldBindWith(req *http.Request, obj interface{}, b binding.Binding) error {
+ return b.Bind(req, obj)
+}
+func ShouldBindJSON(req *http.Request, obj interface{}) error {
+ return ShouldBindWith(req, obj, binding.JSON)
+}
+func ShouldBindHeader(req *http.Request, obj interface{}) error {
+ return ShouldBindWith(req, obj, binding.Header)
+}
+func ShouldBindQuery(req *http.Request, obj interface{}) error {
+ return ShouldBindWith(req, obj, binding.Query)
+}
+
+func contentType(r *http.Request) (string, error) {
+ ct := r.Header.Get("Content-Type")
+ if ct == "" {
+ ct = "application/octet-stream"
+ }
+ ct, _, err := mime.ParseMediaType(ct)
+ return ct, err
+}
+
+func filterFlags(content string) string {
+ for i, char := range content {
+ if char == ' ' || char == ';' {
+ return content[:i]
+ }
+ }
+ return content
+}
diff --git a/gin/binding/binding.go b/gin/binding/binding.go
new file mode 100644
index 0000000..5756284
--- /dev/null
+++ b/gin/binding/binding.go
@@ -0,0 +1,116 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+// +build !nomsgpack
+
+package binding
+
+import "net/http"
+
+// Content-Type MIME of the most common data formats.
+const (
+ MIMEJSON = "application/json"
+ MIMEHTML = "text/html"
+ MIMEXML = "application/xml"
+ MIMEXML2 = "text/xml"
+ MIMEPlain = "text/plain"
+ MIMEPOSTForm = "application/x-www-form-urlencoded"
+ MIMEMultipartPOSTForm = "multipart/form-data"
+ MIMEPROTOBUF = "application/x-protobuf"
+ MIMEMSGPACK = "application/x-msgpack"
+ MIMEMSGPACK2 = "application/msgpack"
+ MIMEYAML = "application/x-yaml"
+)
+
+// Binding describes the interface which needs to be implemented for binding the
+// data present in the request such as JSON request body, query parameters or
+// the form POST.
+type Binding interface {
+ Name() string
+ Bind(*http.Request, interface{}) error
+}
+
+// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
+// but it reads the body from supplied bytes instead of req.Body.
+type BindingBody interface {
+ Binding
+ BindBody([]byte, interface{}) error
+}
+
+// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
+// but it read the Params.
+type BindingUri interface {
+ Name() string
+ BindUri(map[string][]string, interface{}) error
+}
+
+// StructValidator is the minimal interface which needs to be implemented in
+// order for it to be used as the validator engine for ensuring the correctness
+// of the request. Gin provides a default implementation for this using
+// https://github.com/go-playground/validator/tree/v8.18.2.
+type StructValidator interface {
+ // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
+ // If the received type is not a struct, any validation should be skipped and nil must be returned.
+ // If the received type is a struct or pointer to a struct, the validation should be performed.
+ // If the struct is not valid or the validation itself fails, a descriptive error should be returned.
+ // Otherwise nil must be returned.
+ ValidateStruct(interface{}) error
+
+ // Engine returns the underlying validator engine which powers the
+ // StructValidator implementation.
+ Engine() interface{}
+}
+
+// Validator is the default validator which implements the StructValidator
+// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
+// under the hood.
+var Validator StructValidator = &defaultValidator{}
+
+// These implement the Binding interface and can be used to bind the data
+// present in the request to struct instances.
+var (
+ JSON = jsonBinding{}
+ XML = xmlBinding{}
+ Form = formBinding{}
+ Query = queryBinding{}
+ FormPost = formPostBinding{}
+ FormMultipart = formMultipartBinding{}
+ ProtoBuf = protobufBinding{}
+ MsgPack = msgpackBinding{}
+ YAML = yamlBinding{}
+ Uri = uriBinding{}
+ Header = headerBinding{}
+)
+
+// Default returns the appropriate Binding instance based on the HTTP method
+// and the content type.
+func Default(method, contentType string) Binding {
+ if method == http.MethodGet {
+ return Form
+ }
+
+ switch contentType {
+ case MIMEJSON:
+ return JSON
+ case MIMEXML, MIMEXML2:
+ return XML
+ case MIMEPROTOBUF:
+ return ProtoBuf
+ case MIMEMSGPACK, MIMEMSGPACK2:
+ return MsgPack
+ case MIMEYAML:
+ return YAML
+ case MIMEMultipartPOSTForm:
+ return FormMultipart
+ default: // case MIMEPOSTForm:
+ return Form
+ }
+}
+
+func validate(obj interface{}) error {
+ if Validator == nil {
+ return nil
+ }
+ return Validator.ValidateStruct(obj)
+}
diff --git a/gin/binding/binding_nomsgpack.go b/gin/binding/binding_nomsgpack.go
new file mode 100644
index 0000000..fd227b1
--- /dev/null
+++ b/gin/binding/binding_nomsgpack.go
@@ -0,0 +1,111 @@
+// Copyright 2020 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+// +build nomsgpack
+
+package binding
+
+import "net/http"
+
+// Content-Type MIME of the most common data formats.
+const (
+ MIMEJSON = "application/json"
+ MIMEHTML = "text/html"
+ MIMEXML = "application/xml"
+ MIMEXML2 = "text/xml"
+ MIMEPlain = "text/plain"
+ MIMEPOSTForm = "application/x-www-form-urlencoded"
+ MIMEMultipartPOSTForm = "multipart/form-data"
+ MIMEPROTOBUF = "application/x-protobuf"
+ MIMEYAML = "application/x-yaml"
+)
+
+// Binding describes the interface which needs to be implemented for binding the
+// data present in the request such as JSON request body, query parameters or
+// the form POST.
+type Binding interface {
+ Name() string
+ Bind(*http.Request, interface{}) error
+}
+
+// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
+// but it reads the body from supplied bytes instead of req.Body.
+type BindingBody interface {
+ Binding
+ BindBody([]byte, interface{}) error
+}
+
+// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
+// but it read the Params.
+type BindingUri interface {
+ Name() string
+ BindUri(map[string][]string, interface{}) error
+}
+
+// StructValidator is the minimal interface which needs to be implemented in
+// order for it to be used as the validator engine for ensuring the correctness
+// of the request. Gin provides a default implementation for this using
+// https://github.com/go-playground/validator/tree/v8.18.2.
+type StructValidator interface {
+ // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
+ // If the received type is not a struct, any validation should be skipped and nil must be returned.
+ // If the received type is a struct or pointer to a struct, the validation should be performed.
+ // If the struct is not valid or the validation itself fails, a descriptive error should be returned.
+ // Otherwise nil must be returned.
+ ValidateStruct(interface{}) error
+
+ // Engine returns the underlying validator engine which powers the
+ // StructValidator implementation.
+ Engine() interface{}
+}
+
+// Validator is the default validator which implements the StructValidator
+// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
+// under the hood.
+var Validator StructValidator = &defaultValidator{}
+
+// These implement the Binding interface and can be used to bind the data
+// present in the request to struct instances.
+var (
+ JSON = jsonBinding{}
+ XML = xmlBinding{}
+ Form = formBinding{}
+ Query = queryBinding{}
+ FormPost = formPostBinding{}
+ FormMultipart = formMultipartBinding{}
+ ProtoBuf = protobufBinding{}
+ YAML = yamlBinding{}
+ Uri = uriBinding{}
+ Header = headerBinding{}
+)
+
+// Default returns the appropriate Binding instance based on the HTTP method
+// and the content type.
+func Default(method, contentType string) Binding {
+ if method == "GET" {
+ return Form
+ }
+
+ switch contentType {
+ case MIMEJSON:
+ return JSON
+ case MIMEXML, MIMEXML2:
+ return XML
+ case MIMEPROTOBUF:
+ return ProtoBuf
+ case MIMEYAML:
+ return YAML
+ case MIMEMultipartPOSTForm:
+ return FormMultipart
+ default: // case MIMEPOSTForm:
+ return Form
+ }
+}
+
+func validate(obj interface{}) error {
+ if Validator == nil {
+ return nil
+ }
+ return Validator.ValidateStruct(obj)
+}
diff --git a/gin/binding/default_validator.go b/gin/binding/default_validator.go
new file mode 100644
index 0000000..684e029
--- /dev/null
+++ b/gin/binding/default_validator.go
@@ -0,0 +1,74 @@
+// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "gin-valid/go-playground/validator/v10"
+ ut "github.com/go-playground/universal-translator"
+ zhTrans "go-playground/validator/v10/translations/zh"
+ "go-playground/locales/zh"
+ "reflect"
+ "strings"
+ "sync"
+
+ "go-playground/validator/v10"
+)
+
+type defaultValidator struct {
+ once sync.Once
+ validate *validator.Validate
+}
+
+var _ StructValidator = &defaultValidator{} //这是啥情况?yang
+
+// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
+func (v *defaultValidator) ValidateStruct(obj interface{}) error {
+ value := reflect.ValueOf(obj)
+ valueType := value.Kind()
+ if valueType == reflect.Ptr {
+ valueType = value.Elem().Kind()
+ }
+ if valueType == reflect.Struct {
+ v.lazyinit()
+ if err := v.validate.Struct(obj); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Engine returns the underlying validator engine which powers the default
+// Validator instance. This is useful if you want to register custom validations
+// or struct level validations. See validator GoDoc for more info -
+// https://godoc.org/gopkg.in/go-playground/validator.v8
+func (v *defaultValidator) Engine() interface{} {
+ v.lazyinit()
+ return v.validate
+}
+
+var ValidTrans ut.Translator
+
+func (v *defaultValidator) lazyinit() {
+ v.once.Do(func() {
+ v.validate = validator.New()
+ zh := zh.New()
+ uni := ut.New(zh, zh)
+
+ // this is usually know or extracted from http 'Accept-Language' header
+ // also see uni.FindTranslator(...)
+ ValidTrans, _ = uni.GetTranslator("zh")
+
+ zhTrans.RegisterDefaultTranslations(v.validate, ValidTrans) // 为gin的校验 注册翻译
+ v.validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("description"), ",", 2)[0]
+ //if name == "-" {
+ // return ""
+ //}
+ return name
+ })
+ // 设置 tag 的名字
+ v.validate.SetTagName("binding")
+ })
+}
diff --git a/gin/binding/form.go b/gin/binding/form.go
new file mode 100644
index 0000000..b93c34c
--- /dev/null
+++ b/gin/binding/form.go
@@ -0,0 +1,63 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "net/http"
+)
+
+const defaultMemory = 32 << 20
+
+type formBinding struct{}
+type formPostBinding struct{}
+type formMultipartBinding struct{}
+
+func (formBinding) Name() string {
+ return "form"
+}
+
+func (formBinding) Bind(req *http.Request, obj interface{}) error {
+ if err := req.ParseForm(); err != nil {
+ return err
+ }
+ if err := req.ParseMultipartForm(defaultMemory); err != nil {
+ if err != http.ErrNotMultipart {
+ return err
+ }
+ }
+ if err := mapForm(obj, req.Form); err != nil {
+ return err
+ }
+ return validate(obj)
+}
+
+func (formPostBinding) Name() string {
+ return "form-urlencoded"
+}
+
+func (formPostBinding) Bind(req *http.Request, obj interface{}) error {
+ if err := req.ParseForm(); err != nil {
+ return err
+ }
+ if err := mapForm(obj, req.PostForm); err != nil {
+ return err
+ }
+ return validate(obj)
+}
+
+func (formMultipartBinding) Name() string {
+ return "multipart/form-data"
+}
+
+func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
+ if err := req.ParseMultipartForm(defaultMemory); err != nil {
+ return err
+ }
+ if err := mappingByPtr(obj, (*multipartRequest)(req), "form"); err != nil {
+ return err
+ }
+
+ return validate(obj)
+}
diff --git a/gin/binding/form_mapping.go b/gin/binding/form_mapping.go
new file mode 100644
index 0000000..a2d3b3d
--- /dev/null
+++ b/gin/binding/form_mapping.go
@@ -0,0 +1,392 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/gin/internal/bytesconv"
+ "gin-valid/gin/internal/json"
+)
+
+var errUnknownType = errors.New("unknown type")
+
+func mapUri(ptr interface{}, m map[string][]string) error {
+ return mapFormByTag(ptr, m, "uri")
+}
+
+func mapForm(ptr interface{}, form map[string][]string) error {
+ return mapFormByTag(ptr, form, "form")
+}
+
+var emptyField = reflect.StructField{}
+
+func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
+ // Check if ptr is a map
+ ptrVal := reflect.ValueOf(ptr)
+ var pointed interface{}
+ if ptrVal.Kind() == reflect.Ptr {
+ ptrVal = ptrVal.Elem()
+ pointed = ptrVal.Interface()
+ }
+ if ptrVal.Kind() == reflect.Map &&
+ ptrVal.Type().Key().Kind() == reflect.String {
+ if pointed != nil {
+ ptr = pointed
+ }
+ return setFormMap(ptr, form)
+ }
+
+ return mappingByPtr(ptr, formSource(form), tag)
+}
+
+// setter tries to set value on a walking by fields of a struct
+type setter interface {
+ TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error)
+}
+
+type formSource map[string][]string
+
+var _ setter = formSource(nil)
+
+// TrySet tries to set a value by request's form source (like map[string][]string)
+func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
+ return setByForm(value, field, form, tagValue, opt)
+}
+
+func mappingByPtr(ptr interface{}, setter setter, tag string) error {
+ _, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag)
+ return err
+}
+
+func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
+ if field.Tag.Get(tag) == "-" { // just ignoring this field
+ return false, nil
+ }
+
+ var vKind = value.Kind()
+
+ if vKind == reflect.Ptr {
+ var isNew bool
+ vPtr := value
+ if value.IsNil() {
+ isNew = true
+ vPtr = reflect.New(value.Type().Elem())
+ }
+ isSetted, err := mapping(vPtr.Elem(), field, setter, tag)
+ if err != nil {
+ return false, err
+ }
+ if isNew && isSetted {
+ value.Set(vPtr)
+ }
+ return isSetted, nil
+ }
+
+ if vKind != reflect.Struct || !field.Anonymous {
+ ok, err := tryToSetValue(value, field, setter, tag)
+ if err != nil {
+ return false, err
+ }
+ if ok {
+ return true, nil
+ }
+ }
+
+ if vKind == reflect.Struct {
+ tValue := value.Type()
+
+ var isSetted bool
+ for i := 0; i < value.NumField(); i++ {
+ sf := tValue.Field(i)
+ if sf.PkgPath != "" && !sf.Anonymous { // unexported
+ continue
+ }
+ ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag)
+ if err != nil {
+ return false, err
+ }
+ isSetted = isSetted || ok
+ }
+ return isSetted, nil
+ }
+ return false, nil
+}
+
+type setOptions struct {
+ isDefaultExists bool
+ defaultValue string
+}
+
+func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
+ var tagValue string
+ var setOpt setOptions
+
+ tagValue = field.Tag.Get(tag)
+ tagValue, opts := head(tagValue, ",")
+
+ if tagValue == "" { // default value is FieldName
+ tagValue = field.Name
+ }
+ if tagValue == "" { // when field is "emptyField" variable
+ return false, nil
+ }
+
+ var opt string
+ for len(opts) > 0 {
+ opt, opts = head(opts, ",")
+
+ if k, v := head(opt, "="); k == "default" {
+ setOpt.isDefaultExists = true
+ setOpt.defaultValue = v
+ }
+ }
+
+ return setter.TrySet(value, field, tagValue, setOpt)
+}
+
+func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSetted bool, err error) {
+ vs, ok := form[tagValue]
+ if !ok && !opt.isDefaultExists {
+ return false, nil
+ }
+
+ switch value.Kind() {
+ case reflect.Slice:
+ if !ok {
+ vs = []string{opt.defaultValue}
+ }
+ return true, setSlice(vs, value, field)
+ case reflect.Array:
+ if !ok {
+ vs = []string{opt.defaultValue}
+ }
+ if len(vs) != value.Len() {
+ return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
+ }
+ return true, setArray(vs, value, field)
+ default:
+ var val string
+ if !ok {
+ val = opt.defaultValue
+ }
+
+ if len(vs) > 0 {
+ val = vs[0]
+ }
+ return true, setWithProperType(val, value, field)
+ }
+}
+
+func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
+ switch value.Kind() {
+ case reflect.Int:
+ return setIntField(val, 0, value)
+ case reflect.Int8:
+ return setIntField(val, 8, value)
+ case reflect.Int16:
+ return setIntField(val, 16, value)
+ case reflect.Int32:
+ return setIntField(val, 32, value)
+ case reflect.Int64:
+ switch value.Interface().(type) {
+ case time.Duration:
+ return setTimeDuration(val, value, field)
+ }
+ return setIntField(val, 64, value)
+ case reflect.Uint:
+ return setUintField(val, 0, value)
+ case reflect.Uint8:
+ return setUintField(val, 8, value)
+ case reflect.Uint16:
+ return setUintField(val, 16, value)
+ case reflect.Uint32:
+ return setUintField(val, 32, value)
+ case reflect.Uint64:
+ return setUintField(val, 64, value)
+ case reflect.Bool:
+ return setBoolField(val, value)
+ case reflect.Float32:
+ return setFloatField(val, 32, value)
+ case reflect.Float64:
+ return setFloatField(val, 64, value)
+ case reflect.String:
+ value.SetString(val)
+ case reflect.Struct:
+ switch value.Interface().(type) {
+ case time.Time:
+ return setTimeField(val, field, value)
+ }
+ return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
+ case reflect.Map:
+ return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
+ default:
+ return errUnknownType
+ }
+ return nil
+}
+
+func setIntField(val string, bitSize int, field reflect.Value) error {
+ if val == "" {
+ val = "0"
+ }
+ intVal, err := strconv.ParseInt(val, 10, bitSize)
+ if err == nil {
+ field.SetInt(intVal)
+ }
+ return err
+}
+
+func setUintField(val string, bitSize int, field reflect.Value) error {
+ if val == "" {
+ val = "0"
+ }
+ uintVal, err := strconv.ParseUint(val, 10, bitSize)
+ if err == nil {
+ field.SetUint(uintVal)
+ }
+ return err
+}
+
+func setBoolField(val string, field reflect.Value) error {
+ if val == "" {
+ val = "false"
+ }
+ boolVal, err := strconv.ParseBool(val)
+ if err == nil {
+ field.SetBool(boolVal)
+ }
+ return err
+}
+
+func setFloatField(val string, bitSize int, field reflect.Value) error {
+ if val == "" {
+ val = "0.0"
+ }
+ floatVal, err := strconv.ParseFloat(val, bitSize)
+ if err == nil {
+ field.SetFloat(floatVal)
+ }
+ return err
+}
+
+func setTimeField(val string, structField reflect.StructField, value reflect.Value) error {
+ timeFormat := structField.Tag.Get("time_format")
+ if timeFormat == "" {
+ timeFormat = time.RFC3339
+ }
+
+ switch tf := strings.ToLower(timeFormat); tf {
+ case "unix", "unixnano":
+ tv, err := strconv.ParseInt(val, 10, 64)
+ if err != nil {
+ return err
+ }
+
+ d := time.Duration(1)
+ if tf == "unixnano" {
+ d = time.Second
+ }
+
+ t := time.Unix(tv/int64(d), tv%int64(d))
+ value.Set(reflect.ValueOf(t))
+ return nil
+
+ }
+
+ if val == "" {
+ value.Set(reflect.ValueOf(time.Time{}))
+ return nil
+ }
+
+ l := time.Local
+ if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
+ l = time.UTC
+ }
+
+ if locTag := structField.Tag.Get("time_location"); locTag != "" {
+ loc, err := time.LoadLocation(locTag)
+ if err != nil {
+ return err
+ }
+ l = loc
+ }
+
+ t, err := time.ParseInLocation(timeFormat, val, l)
+ if err != nil {
+ return err
+ }
+
+ value.Set(reflect.ValueOf(t))
+ return nil
+}
+
+func setArray(vals []string, value reflect.Value, field reflect.StructField) error {
+ for i, s := range vals {
+ err := setWithProperType(s, value.Index(i), field)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func setSlice(vals []string, value reflect.Value, field reflect.StructField) error {
+ slice := reflect.MakeSlice(value.Type(), len(vals), len(vals))
+ err := setArray(vals, slice, field)
+ if err != nil {
+ return err
+ }
+ value.Set(slice)
+ return nil
+}
+
+func setTimeDuration(val string, value reflect.Value, field reflect.StructField) error {
+ d, err := time.ParseDuration(val)
+ if err != nil {
+ return err
+ }
+ value.Set(reflect.ValueOf(d))
+ return nil
+}
+
+func head(str, sep string) (head string, tail string) {
+ idx := strings.Index(str, sep)
+ if idx < 0 {
+ return str, ""
+ }
+ return str[:idx], str[idx+len(sep):]
+}
+
+func setFormMap(ptr interface{}, form map[string][]string) error {
+ el := reflect.TypeOf(ptr).Elem()
+
+ if el.Kind() == reflect.Slice {
+ ptrMap, ok := ptr.(map[string][]string)
+ if !ok {
+ return errors.New("cannot convert to map slices of strings")
+ }
+ for k, v := range form {
+ ptrMap[k] = v
+ }
+
+ return nil
+ }
+
+ ptrMap, ok := ptr.(map[string]string)
+ if !ok {
+ return errors.New("cannot convert to map of strings")
+ }
+ for k, v := range form {
+ ptrMap[k] = v[len(v)-1] // pick last
+ }
+
+ return nil
+}
diff --git a/gin/binding/form_mapping_benchmark_test.go b/gin/binding/form_mapping_benchmark_test.go
new file mode 100644
index 0000000..9572ea0
--- /dev/null
+++ b/gin/binding/form_mapping_benchmark_test.go
@@ -0,0 +1,67 @@
+// Copyright 2019 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+var form = map[string][]string{
+ "name": {"mike"},
+ "friends": {"anna", "nicole"},
+ "id_number": {"12345678"},
+ "id_date": {"2018-01-20"},
+}
+
+type structFull struct {
+ Name string `form:"name"`
+ Age int `form:"age,default=25"`
+ Friends []string `form:"friends"`
+ ID *struct {
+ Number string `form:"id_number"`
+ DateOfIssue time.Time `form:"id_date" time_format:"2006-01-02" time_utc:"true"`
+ }
+ Nationality *string `form:"nationality"`
+}
+
+func BenchmarkMapFormFull(b *testing.B) {
+ var s structFull
+ for i := 0; i < b.N; i++ {
+ err := mapForm(&s, form)
+ if err != nil {
+ b.Fatalf("Error on a form mapping")
+ }
+ }
+ b.StopTimer()
+
+ t := b
+ assert.Equal(t, "mike", s.Name)
+ assert.Equal(t, 25, s.Age)
+ assert.Equal(t, []string{"anna", "nicole"}, s.Friends)
+ assert.Equal(t, "12345678", s.ID.Number)
+ assert.Equal(t, time.Date(2018, 1, 20, 0, 0, 0, 0, time.UTC), s.ID.DateOfIssue)
+ assert.Nil(t, s.Nationality)
+}
+
+type structName struct {
+ Name string `form:"name"`
+}
+
+func BenchmarkMapFormName(b *testing.B) {
+ var s structName
+ for i := 0; i < b.N; i++ {
+ err := mapForm(&s, form)
+ if err != nil {
+ b.Fatalf("Error on a form mapping")
+ }
+ }
+ b.StopTimer()
+
+ t := b
+ assert.Equal(t, "mike", s.Name)
+}
diff --git a/gin/binding/form_mapping_test.go b/gin/binding/form_mapping_test.go
new file mode 100644
index 0000000..2675d46
--- /dev/null
+++ b/gin/binding/form_mapping_test.go
@@ -0,0 +1,281 @@
+// Copyright 2019 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMappingBaseTypes(t *testing.T) {
+ intPtr := func(i int) *int {
+ return &i
+ }
+ for _, tt := range []struct {
+ name string
+ value interface{}
+ form string
+ expect interface{}
+ }{
+ {"base type", struct{ F int }{}, "9", int(9)},
+ {"base type", struct{ F int8 }{}, "9", int8(9)},
+ {"base type", struct{ F int16 }{}, "9", int16(9)},
+ {"base type", struct{ F int32 }{}, "9", int32(9)},
+ {"base type", struct{ F int64 }{}, "9", int64(9)},
+ {"base type", struct{ F uint }{}, "9", uint(9)},
+ {"base type", struct{ F uint8 }{}, "9", uint8(9)},
+ {"base type", struct{ F uint16 }{}, "9", uint16(9)},
+ {"base type", struct{ F uint32 }{}, "9", uint32(9)},
+ {"base type", struct{ F uint64 }{}, "9", uint64(9)},
+ {"base type", struct{ F bool }{}, "True", true},
+ {"base type", struct{ F float32 }{}, "9.1", float32(9.1)},
+ {"base type", struct{ F float64 }{}, "9.1", float64(9.1)},
+ {"base type", struct{ F string }{}, "test", string("test")},
+ {"base type", struct{ F *int }{}, "9", intPtr(9)},
+
+ // zero values
+ {"zero value", struct{ F int }{}, "", int(0)},
+ {"zero value", struct{ F uint }{}, "", uint(0)},
+ {"zero value", struct{ F bool }{}, "", false},
+ {"zero value", struct{ F float32 }{}, "", float32(0)},
+ } {
+ tp := reflect.TypeOf(tt.value)
+ testName := tt.name + ":" + tp.Field(0).Type.String()
+
+ val := reflect.New(reflect.TypeOf(tt.value))
+ val.Elem().Set(reflect.ValueOf(tt.value))
+
+ field := val.Elem().Type().Field(0)
+
+ _, err := mapping(val, emptyField, formSource{field.Name: {tt.form}}, "form")
+ assert.NoError(t, err, testName)
+
+ actual := val.Elem().Field(0).Interface()
+ assert.Equal(t, tt.expect, actual, testName)
+ }
+}
+
+func TestMappingDefault(t *testing.T) {
+ var s struct {
+ Int int `form:",default=9"`
+ Slice []int `form:",default=9"`
+ Array [1]int `form:",default=9"`
+ }
+ err := mappingByPtr(&s, formSource{}, "form")
+ assert.NoError(t, err)
+
+ assert.Equal(t, 9, s.Int)
+ assert.Equal(t, []int{9}, s.Slice)
+ assert.Equal(t, [1]int{9}, s.Array)
+}
+
+func TestMappingSkipField(t *testing.T) {
+ var s struct {
+ A int
+ }
+ err := mappingByPtr(&s, formSource{}, "form")
+ assert.NoError(t, err)
+
+ assert.Equal(t, 0, s.A)
+}
+
+func TestMappingIgnoreField(t *testing.T) {
+ var s struct {
+ A int `form:"A"`
+ B int `form:"-"`
+ }
+ err := mappingByPtr(&s, formSource{"A": {"9"}, "B": {"9"}}, "form")
+ assert.NoError(t, err)
+
+ assert.Equal(t, 9, s.A)
+ assert.Equal(t, 0, s.B)
+}
+
+func TestMappingUnexportedField(t *testing.T) {
+ var s struct {
+ A int `form:"a"`
+ b int `form:"b"`
+ }
+ err := mappingByPtr(&s, formSource{"a": {"9"}, "b": {"9"}}, "form")
+ assert.NoError(t, err)
+
+ assert.Equal(t, 9, s.A)
+ assert.Equal(t, 0, s.b)
+}
+
+func TestMappingPrivateField(t *testing.T) {
+ var s struct {
+ f int `form:"field"`
+ }
+ err := mappingByPtr(&s, formSource{"field": {"6"}}, "form")
+ assert.NoError(t, err)
+ assert.Equal(t, int(0), s.f)
+}
+
+func TestMappingUnknownFieldType(t *testing.T) {
+ var s struct {
+ U uintptr
+ }
+
+ err := mappingByPtr(&s, formSource{"U": {"unknown"}}, "form")
+ assert.Error(t, err)
+ assert.Equal(t, errUnknownType, err)
+}
+
+func TestMappingURI(t *testing.T) {
+ var s struct {
+ F int `uri:"field"`
+ }
+ err := mapUri(&s, map[string][]string{"field": {"6"}})
+ assert.NoError(t, err)
+ assert.Equal(t, int(6), s.F)
+}
+
+func TestMappingForm(t *testing.T) {
+ var s struct {
+ F int `form:"field"`
+ }
+ err := mapForm(&s, map[string][]string{"field": {"6"}})
+ assert.NoError(t, err)
+ assert.Equal(t, int(6), s.F)
+}
+
+func TestMappingTime(t *testing.T) {
+ var s struct {
+ Time time.Time
+ LocalTime time.Time `time_format:"2006-01-02"`
+ ZeroValue time.Time
+ CSTTime time.Time `time_format:"2006-01-02" time_location:"Asia/Shanghai"`
+ UTCTime time.Time `time_format:"2006-01-02" time_utc:"1"`
+ }
+
+ var err error
+ time.Local, err = time.LoadLocation("Europe/Berlin")
+ assert.NoError(t, err)
+
+ err = mapForm(&s, map[string][]string{
+ "Time": {"2019-01-20T16:02:58Z"},
+ "LocalTime": {"2019-01-20"},
+ "ZeroValue": {},
+ "CSTTime": {"2019-01-20"},
+ "UTCTime": {"2019-01-20"},
+ })
+ assert.NoError(t, err)
+
+ assert.Equal(t, "2019-01-20 16:02:58 +0000 UTC", s.Time.String())
+ assert.Equal(t, "2019-01-20 00:00:00 +0100 CET", s.LocalTime.String())
+ assert.Equal(t, "2019-01-19 23:00:00 +0000 UTC", s.LocalTime.UTC().String())
+ assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", s.ZeroValue.String())
+ assert.Equal(t, "2019-01-20 00:00:00 +0800 CST", s.CSTTime.String())
+ assert.Equal(t, "2019-01-19 16:00:00 +0000 UTC", s.CSTTime.UTC().String())
+ assert.Equal(t, "2019-01-20 00:00:00 +0000 UTC", s.UTCTime.String())
+
+ // wrong location
+ var wrongLoc struct {
+ Time time.Time `time_location:"wrong"`
+ }
+ err = mapForm(&wrongLoc, map[string][]string{"Time": {"2019-01-20T16:02:58Z"}})
+ assert.Error(t, err)
+
+ // wrong time value
+ var wrongTime struct {
+ Time time.Time
+ }
+ err = mapForm(&wrongTime, map[string][]string{"Time": {"wrong"}})
+ assert.Error(t, err)
+}
+
+func TestMappingTimeDuration(t *testing.T) {
+ var s struct {
+ D time.Duration
+ }
+
+ // ok
+ err := mappingByPtr(&s, formSource{"D": {"5s"}}, "form")
+ assert.NoError(t, err)
+ assert.Equal(t, 5*time.Second, s.D)
+
+ // error
+ err = mappingByPtr(&s, formSource{"D": {"wrong"}}, "form")
+ assert.Error(t, err)
+}
+
+func TestMappingSlice(t *testing.T) {
+ var s struct {
+ Slice []int `form:"slice,default=9"`
+ }
+
+ // default value
+ err := mappingByPtr(&s, formSource{}, "form")
+ assert.NoError(t, err)
+ assert.Equal(t, []int{9}, s.Slice)
+
+ // ok
+ err = mappingByPtr(&s, formSource{"slice": {"3", "4"}}, "form")
+ assert.NoError(t, err)
+ assert.Equal(t, []int{3, 4}, s.Slice)
+
+ // error
+ err = mappingByPtr(&s, formSource{"slice": {"wrong"}}, "form")
+ assert.Error(t, err)
+}
+
+func TestMappingArray(t *testing.T) {
+ var s struct {
+ Array [2]int `form:"array,default=9"`
+ }
+
+ // wrong default
+ err := mappingByPtr(&s, formSource{}, "form")
+ assert.Error(t, err)
+
+ // ok
+ err = mappingByPtr(&s, formSource{"array": {"3", "4"}}, "form")
+ assert.NoError(t, err)
+ assert.Equal(t, [2]int{3, 4}, s.Array)
+
+ // error - not enough vals
+ err = mappingByPtr(&s, formSource{"array": {"3"}}, "form")
+ assert.Error(t, err)
+
+ // error - wrong value
+ err = mappingByPtr(&s, formSource{"array": {"wrong"}}, "form")
+ assert.Error(t, err)
+}
+
+func TestMappingStructField(t *testing.T) {
+ var s struct {
+ J struct {
+ I int
+ }
+ }
+
+ err := mappingByPtr(&s, formSource{"J": {`{"I": 9}`}}, "form")
+ assert.NoError(t, err)
+ assert.Equal(t, 9, s.J.I)
+}
+
+func TestMappingMapField(t *testing.T) {
+ var s struct {
+ M map[string]int
+ }
+
+ err := mappingByPtr(&s, formSource{"M": {`{"one": 1}`}}, "form")
+ assert.NoError(t, err)
+ assert.Equal(t, map[string]int{"one": 1}, s.M)
+}
+
+func TestMappingIgnoredCircularRef(t *testing.T) {
+ type S struct {
+ S *S `form:"-"`
+ }
+ var s S
+
+ err := mappingByPtr(&s, formSource{}, "form")
+ assert.NoError(t, err)
+}
diff --git a/gin/binding/header.go b/gin/binding/header.go
new file mode 100644
index 0000000..179ce4e
--- /dev/null
+++ b/gin/binding/header.go
@@ -0,0 +1,34 @@
+package binding
+
+import (
+ "net/http"
+ "net/textproto"
+ "reflect"
+)
+
+type headerBinding struct{}
+
+func (headerBinding) Name() string {
+ return "header"
+}
+
+func (headerBinding) Bind(req *http.Request, obj interface{}) error {
+
+ if err := mapHeader(obj, req.Header); err != nil {
+ return err
+ }
+
+ return validate(obj)
+}
+
+func mapHeader(ptr interface{}, h map[string][]string) error {
+ return mappingByPtr(ptr, headerSource(h), "header")
+}
+
+type headerSource map[string][]string
+
+var _ setter = headerSource(nil)
+
+func (hs headerSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
+ return setByForm(value, field, hs, textproto.CanonicalMIMEHeaderKey(tagValue), opt)
+}
diff --git a/gin/binding/json.go b/gin/binding/json.go
new file mode 100644
index 0000000..26d390f
--- /dev/null
+++ b/gin/binding/json.go
@@ -0,0 +1,56 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "net/http"
+
+ "gin-valid/gin/internal/json"
+)
+
+// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
+// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
+// interface{} as a Number instead of as a float64.
+var EnableDecoderUseNumber = false
+
+// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method
+// on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to
+// return an error when the destination is a struct and the input contains object
+// keys which do not match any non-ignored, exported fields in the destination.
+var EnableDecoderDisallowUnknownFields = false
+
+type jsonBinding struct{}
+
+func (jsonBinding) Name() string {
+ return "json"
+}
+
+func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
+ if req == nil || req.Body == nil {
+ return fmt.Errorf("invalid request")
+ }
+ return decodeJSON(req.Body, obj)
+}
+
+func (jsonBinding) BindBody(body []byte, obj interface{}) error {
+ return decodeJSON(bytes.NewReader(body), obj)
+}
+
+func decodeJSON(r io.Reader, obj interface{}) error {
+ decoder := json.NewDecoder(r)
+ if EnableDecoderUseNumber {
+ decoder.UseNumber()
+ }
+ if EnableDecoderDisallowUnknownFields {
+ decoder.DisallowUnknownFields()
+ }
+ if err := decoder.Decode(obj); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/gin/binding/json_test.go b/gin/binding/json_test.go
new file mode 100644
index 0000000..0f61a20
--- /dev/null
+++ b/gin/binding/json_test.go
@@ -0,0 +1,30 @@
+/ Copyright 2019 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestJSONBindingBindBody(t *testing.T) {
+ var s struct {
+ Foo string `json:"foo"`
+ }
+ err := jsonBinding{}.BindBody([]byte(`{"foo": "FOO"}`), &s)
+ require.NoError(t, err)
+ assert.Equal(t, "FOO", s.Foo)
+}
+
+func TestJSONBindingBindBodyMap(t *testing.T) {
+ s := make(map[string]string)
+ err := jsonBinding{}.BindBody([]byte(`{"foo": "FOO","hello":"world"}`), &s)
+ require.NoError(t, err)
+ assert.Len(t, s, 2)
+ assert.Equal(t, "FOO", s["foo"])
+ assert.Equal(t, "world", s["hello"])
+}
diff --git a/gin/binding/msgpack.go b/gin/binding/msgpack.go
new file mode 100644
index 0000000..a5bc2ad
--- /dev/null
+++ b/gin/binding/msgpack.go
@@ -0,0 +1,37 @@
+// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+// +build !nomsgpack
+
+package binding
+
+import (
+ "bytes"
+ "io"
+ "net/http"
+
+ "github.com/ugorji/go/codec"
+)
+
+type msgpackBinding struct{}
+
+func (msgpackBinding) Name() string {
+ return "msgpack"
+}
+
+func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {
+ return decodeMsgPack(req.Body, obj)
+}
+
+func (msgpackBinding) BindBody(body []byte, obj interface{}) error {
+ return decodeMsgPack(bytes.NewReader(body), obj)
+}
+
+func decodeMsgPack(r io.Reader, obj interface{}) error {
+ cdc := new(codec.MsgpackHandle)
+ if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/gin/binding/msgpack_test.go b/gin/binding/msgpack_test.go
new file mode 100644
index 0000000..296d3eb
--- /dev/null
+++ b/gin/binding/msgpack_test.go
@@ -0,0 +1,34 @@
+// Copyright 2019 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+// +build !nomsgpack
+
+package binding
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "github.com/ugorji/go/codec"
+)
+
+func TestMsgpackBindingBindBody(t *testing.T) {
+ type teststruct struct {
+ Foo string `msgpack:"foo"`
+ }
+ var s teststruct
+ err := msgpackBinding{}.BindBody(msgpackBody(t, teststruct{"FOO"}), &s)
+ require.NoError(t, err)
+ assert.Equal(t, "FOO", s.Foo)
+}
+
+func msgpackBody(t *testing.T, obj interface{}) []byte {
+ var bs bytes.Buffer
+ h := &codec.MsgpackHandle{}
+ err := codec.NewEncoder(&bs, h).Encode(obj)
+ require.NoError(t, err)
+ return bs.Bytes()
+}
diff --git a/gin/binding/multipart_form_mapping.go b/gin/binding/multipart_form_mapping.go
new file mode 100644
index 0000000..f85a1aa
--- /dev/null
+++ b/gin/binding/multipart_form_mapping.go
@@ -0,0 +1,66 @@
+// Copyright 2019 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "errors"
+ "mime/multipart"
+ "net/http"
+ "reflect"
+)
+
+type multipartRequest http.Request
+
+var _ setter = (*multipartRequest)(nil)
+
+// TrySet tries to set a value by the multipart request with the binding a form file
+func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error) {
+ if files := r.MultipartForm.File[key]; len(files) != 0 {
+ return setByMultipartFormFile(value, field, files)
+ }
+
+ return setByForm(value, field, r.MultipartForm.Value, key, opt)
+}
+
+func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
+ switch value.Kind() {
+ case reflect.Ptr:
+ switch value.Interface().(type) {
+ case *multipart.FileHeader:
+ value.Set(reflect.ValueOf(files[0]))
+ return true, nil
+ }
+ case reflect.Struct:
+ switch value.Interface().(type) {
+ case multipart.FileHeader:
+ value.Set(reflect.ValueOf(*files[0]))
+ return true, nil
+ }
+ case reflect.Slice:
+ slice := reflect.MakeSlice(value.Type(), len(files), len(files))
+ isSetted, err = setArrayOfMultipartFormFiles(slice, field, files)
+ if err != nil || !isSetted {
+ return isSetted, err
+ }
+ value.Set(slice)
+ return true, nil
+ case reflect.Array:
+ return setArrayOfMultipartFormFiles(value, field, files)
+ }
+ return false, errors.New("unsupported field type for multipart.FileHeader")
+}
+
+func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
+ if value.Len() != len(files) {
+ return false, errors.New("unsupported len of array for []*multipart.FileHeader")
+ }
+ for i := range files {
+ setted, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1])
+ if err != nil || !setted {
+ return setted, err
+ }
+ }
+ return true, nil
+}
diff --git a/gin/binding/multipart_form_mapping_test.go b/gin/binding/multipart_form_mapping_test.go
new file mode 100644
index 0000000..4c75d1f
--- /dev/null
+++ b/gin/binding/multipart_form_mapping_test.go
@@ -0,0 +1,138 @@
+// Copyright 2019 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "bytes"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFormMultipartBindingBindOneFile(t *testing.T) {
+ var s struct {
+ FileValue multipart.FileHeader `form:"file"`
+ FilePtr *multipart.FileHeader `form:"file"`
+ SliceValues []multipart.FileHeader `form:"file"`
+ SlicePtrs []*multipart.FileHeader `form:"file"`
+ ArrayValues [1]multipart.FileHeader `form:"file"`
+ ArrayPtrs [1]*multipart.FileHeader `form:"file"`
+ }
+ file := testFile{"file", "file1", []byte("hello")}
+
+ req := createRequestMultipartFiles(t, file)
+ err := FormMultipart.Bind(req, &s)
+ assert.NoError(t, err)
+
+ assertMultipartFileHeader(t, &s.FileValue, file)
+ assertMultipartFileHeader(t, s.FilePtr, file)
+ assert.Len(t, s.SliceValues, 1)
+ assertMultipartFileHeader(t, &s.SliceValues[0], file)
+ assert.Len(t, s.SlicePtrs, 1)
+ assertMultipartFileHeader(t, s.SlicePtrs[0], file)
+ assertMultipartFileHeader(t, &s.ArrayValues[0], file)
+ assertMultipartFileHeader(t, s.ArrayPtrs[0], file)
+}
+
+func TestFormMultipartBindingBindTwoFiles(t *testing.T) {
+ var s struct {
+ SliceValues []multipart.FileHeader `form:"file"`
+ SlicePtrs []*multipart.FileHeader `form:"file"`
+ ArrayValues [2]multipart.FileHeader `form:"file"`
+ ArrayPtrs [2]*multipart.FileHeader `form:"file"`
+ }
+ files := []testFile{
+ {"file", "file1", []byte("hello")},
+ {"file", "file2", []byte("world")},
+ }
+
+ req := createRequestMultipartFiles(t, files...)
+ err := FormMultipart.Bind(req, &s)
+ assert.NoError(t, err)
+
+ assert.Len(t, s.SliceValues, len(files))
+ assert.Len(t, s.SlicePtrs, len(files))
+ assert.Len(t, s.ArrayValues, len(files))
+ assert.Len(t, s.ArrayPtrs, len(files))
+
+ for i, file := range files {
+ assertMultipartFileHeader(t, &s.SliceValues[i], file)
+ assertMultipartFileHeader(t, s.SlicePtrs[i], file)
+ assertMultipartFileHeader(t, &s.ArrayValues[i], file)
+ assertMultipartFileHeader(t, s.ArrayPtrs[i], file)
+ }
+}
+
+func TestFormMultipartBindingBindError(t *testing.T) {
+ files := []testFile{
+ {"file", "file1", []byte("hello")},
+ {"file", "file2", []byte("world")},
+ }
+
+ for _, tt := range []struct {
+ name string
+ s interface{}
+ }{
+ {"wrong type", &struct {
+ Files int `form:"file"`
+ }{}},
+ {"wrong array size", &struct {
+ Files [1]*multipart.FileHeader `form:"file"`
+ }{}},
+ {"wrong slice type", &struct {
+ Files []int `form:"file"`
+ }{}},
+ } {
+ req := createRequestMultipartFiles(t, files...)
+ err := FormMultipart.Bind(req, tt.s)
+ assert.Error(t, err)
+ }
+}
+
+type testFile struct {
+ Fieldname string
+ Filename string
+ Content []byte
+}
+
+func createRequestMultipartFiles(t *testing.T, files ...testFile) *http.Request {
+ var body bytes.Buffer
+
+ mw := multipart.NewWriter(&body)
+ for _, file := range files {
+ fw, err := mw.CreateFormFile(file.Fieldname, file.Filename)
+ assert.NoError(t, err)
+
+ n, err := fw.Write(file.Content)
+ assert.NoError(t, err)
+ assert.Equal(t, len(file.Content), n)
+ }
+ err := mw.Close()
+ assert.NoError(t, err)
+
+ req, err := http.NewRequest("POST", "/", &body)
+ assert.NoError(t, err)
+
+ req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+mw.Boundary())
+ return req
+}
+
+func assertMultipartFileHeader(t *testing.T, fh *multipart.FileHeader, file testFile) {
+ assert.Equal(t, file.Filename, fh.Filename)
+ // assert.Equal(t, int64(len(file.Content)), fh.Size) // fh.Size does not exist on go1.8
+
+ fl, err := fh.Open()
+ assert.NoError(t, err)
+
+ body, err := ioutil.ReadAll(fl)
+ assert.NoError(t, err)
+ assert.Equal(t, string(file.Content), string(body))
+
+ err = fl.Close()
+ assert.NoError(t, err)
+}
diff --git a/gin/binding/protobuf.go b/gin/binding/protobuf.go
new file mode 100644
index 0000000..f9ece92
--- /dev/null
+++ b/gin/binding/protobuf.go
@@ -0,0 +1,36 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "io/ioutil"
+ "net/http"
+
+ "github.com/golang/protobuf/proto"
+)
+
+type protobufBinding struct{}
+
+func (protobufBinding) Name() string {
+ return "protobuf"
+}
+
+func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
+ buf, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ return err
+ }
+ return b.BindBody(buf, obj)
+}
+
+func (protobufBinding) BindBody(body []byte, obj interface{}) error {
+ if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
+ return err
+ }
+ // Here it's same to return validate(obj), but util now we can't add
+ // `binding:""` to the struct which automatically generate by gen-proto
+ return nil
+ // return validate(obj)
+}
diff --git a/gin/binding/query.go b/gin/binding/query.go
new file mode 100644
index 0000000..219743f
--- /dev/null
+++ b/gin/binding/query.go
@@ -0,0 +1,21 @@
+// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import "net/http"
+
+type queryBinding struct{}
+
+func (queryBinding) Name() string {
+ return "query"
+}
+
+func (queryBinding) Bind(req *http.Request, obj interface{}) error {
+ values := req.URL.Query()
+ if err := mapForm(obj, values); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/gin/binding/uri.go b/gin/binding/uri.go
new file mode 100644
index 0000000..f91ec38
--- /dev/null
+++ b/gin/binding/uri.go
@@ -0,0 +1,18 @@
+// Copyright 2018 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+type uriBinding struct{}
+
+func (uriBinding) Name() string {
+ return "uri"
+}
+
+func (uriBinding) BindUri(m map[string][]string, obj interface{}) error {
+ if err := mapUri(obj, m); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/gin/binding/validate_test.go b/gin/binding/validate_test.go
new file mode 100644
index 0000000..18bc8ec
--- /dev/null
+++ b/gin/binding/validate_test.go
@@ -0,0 +1,228 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "bytes"
+ "testing"
+ "time"
+
+ "go-playground/validator/v10"
+ "github.com/stretchr/testify/assert"
+)
+
+type testInterface interface {
+ String() string
+}
+
+type substructNoValidation struct {
+ IString string
+ IInt int
+}
+
+type mapNoValidationSub map[string]substructNoValidation
+
+type structNoValidationValues struct {
+ substructNoValidation
+
+ Boolean bool
+
+ Uinteger uint
+ Integer int
+ Integer8 int8
+ Integer16 int16
+ Integer32 int32
+ Integer64 int64
+ Uinteger8 uint8
+ Uinteger16 uint16
+ Uinteger32 uint32
+ Uinteger64 uint64
+
+ Float32 float32
+ Float64 float64
+
+ String string
+
+ Date time.Time
+
+ Struct substructNoValidation
+ InlinedStruct struct {
+ String []string
+ Integer int
+ }
+
+ IntSlice []int
+ IntPointerSlice []*int
+ StructPointerSlice []*substructNoValidation
+ StructSlice []substructNoValidation
+ InterfaceSlice []testInterface
+
+ UniversalInterface interface{}
+ CustomInterface testInterface
+
+ FloatMap map[string]float32
+ StructMap mapNoValidationSub
+}
+
+func createNoValidationValues() structNoValidationValues {
+ integer := 1
+ s := structNoValidationValues{
+ Boolean: true,
+ Uinteger: 1 << 29,
+ Integer: -10000,
+ Integer8: 120,
+ Integer16: -20000,
+ Integer32: 1 << 29,
+ Integer64: 1 << 61,
+ Uinteger8: 250,
+ Uinteger16: 50000,
+ Uinteger32: 1 << 31,
+ Uinteger64: 1 << 62,
+ Float32: 123.456,
+ Float64: 123.456789,
+ String: "text",
+ Date: time.Time{},
+ CustomInterface: &bytes.Buffer{},
+ Struct: substructNoValidation{},
+ IntSlice: []int{-3, -2, 1, 0, 1, 2, 3},
+ IntPointerSlice: []*int{&integer},
+ StructSlice: []substructNoValidation{},
+ UniversalInterface: 1.2,
+ FloatMap: map[string]float32{
+ "foo": 1.23,
+ "bar": 232.323,
+ },
+ StructMap: mapNoValidationSub{
+ "foo": substructNoValidation{},
+ "bar": substructNoValidation{},
+ },
+ // StructPointerSlice []noValidationSub
+ // InterfaceSlice []testInterface
+ }
+ s.InlinedStruct.Integer = 1000
+ s.InlinedStruct.String = []string{"first", "second"}
+ s.IString = "substring"
+ s.IInt = 987654
+ return s
+}
+
+func TestValidateNoValidationValues(t *testing.T) {
+ origin := createNoValidationValues()
+ test := createNoValidationValues()
+ empty := structNoValidationValues{}
+
+ assert.Nil(t, validate(test))
+ assert.Nil(t, validate(&test))
+ assert.Nil(t, validate(empty))
+ assert.Nil(t, validate(&empty))
+
+ assert.Equal(t, origin, test)
+}
+
+type structNoValidationPointer struct {
+ substructNoValidation
+
+ Boolean bool
+
+ Uinteger *uint
+ Integer *int
+ Integer8 *int8
+ Integer16 *int16
+ Integer32 *int32
+ Integer64 *int64
+ Uinteger8 *uint8
+ Uinteger16 *uint16
+ Uinteger32 *uint32
+ Uinteger64 *uint64
+
+ Float32 *float32
+ Float64 *float64
+
+ String *string
+
+ Date *time.Time
+
+ Struct *substructNoValidation
+
+ IntSlice *[]int
+ IntPointerSlice *[]*int
+ StructPointerSlice *[]*substructNoValidation
+ StructSlice *[]substructNoValidation
+ InterfaceSlice *[]testInterface
+
+ FloatMap *map[string]float32
+ StructMap *mapNoValidationSub
+}
+
+func TestValidateNoValidationPointers(t *testing.T) {
+ //origin := createNoValidation_values()
+ //test := createNoValidation_values()
+ empty := structNoValidationPointer{}
+
+ //assert.Nil(t, validate(test))
+ //assert.Nil(t, validate(&test))
+ assert.Nil(t, validate(empty))
+ assert.Nil(t, validate(&empty))
+
+ //assert.Equal(t, origin, test)
+}
+
+type Object map[string]interface{}
+
+func TestValidatePrimitives(t *testing.T) {
+ obj := Object{"foo": "bar", "bar": 1}
+ assert.NoError(t, validate(obj))
+ assert.NoError(t, validate(&obj))
+ assert.Equal(t, Object{"foo": "bar", "bar": 1}, obj)
+
+ obj2 := []Object{{"foo": "bar", "bar": 1}, {"foo": "bar", "bar": 1}}
+ assert.NoError(t, validate(obj2))
+ assert.NoError(t, validate(&obj2))
+
+ nu := 10
+ assert.NoError(t, validate(nu))
+ assert.NoError(t, validate(&nu))
+ assert.Equal(t, 10, nu)
+
+ str := "value"
+ assert.NoError(t, validate(str))
+ assert.NoError(t, validate(&str))
+ assert.Equal(t, "value", str)
+}
+
+// structCustomValidation is a helper struct we use to check that
+// custom validation can be registered on it.
+// The `notone` binding directive is for custom validation and registered later.
+type structCustomValidation struct {
+ Integer int `binding:"notone"`
+}
+
+func notOne(f1 validator.FieldLevel) bool {
+ if val, ok := f1.Field().Interface().(int); ok {
+ return val != 1
+ }
+ return false
+}
+
+func TestValidatorEngine(t *testing.T) {
+ // This validates that the function `notOne` matches
+ // the expected function signature by `defaultValidator`
+ // and by extension the validator library.
+ engine, ok := Validator.Engine().(*validator.Validate)
+ assert.True(t, ok)
+
+ err := engine.RegisterValidation("notone", notOne)
+ // Check that we can register custom validation without error
+ assert.Nil(t, err)
+
+ // Create an instance which will fail validation
+ withOne := structCustomValidation{Integer: 1}
+ errs := validate(withOne)
+
+ // Check that we got back non-nil errs
+ assert.NotNil(t, errs)
+ // Check that the error matches expectation
+ assert.Error(t, errs, "", "", "notone")
+}
diff --git a/gin/binding/xml.go b/gin/binding/xml.go
new file mode 100644
index 0000000..4e90114
--- /dev/null
+++ b/gin/binding/xml.go
@@ -0,0 +1,33 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "bytes"
+ "encoding/xml"
+ "io"
+ "net/http"
+)
+
+type xmlBinding struct{}
+
+func (xmlBinding) Name() string {
+ return "xml"
+}
+
+func (xmlBinding) Bind(req *http.Request, obj interface{}) error {
+ return decodeXML(req.Body, obj)
+}
+
+func (xmlBinding) BindBody(body []byte, obj interface{}) error {
+ return decodeXML(bytes.NewReader(body), obj)
+}
+func decodeXML(r io.Reader, obj interface{}) error {
+ decoder := xml.NewDecoder(r)
+ if err := decoder.Decode(obj); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/gin/binding/xml_test.go b/gin/binding/xml_test.go
new file mode 100644
index 0000000..f9546c1
--- /dev/null
+++ b/gin/binding/xml_test.go
@@ -0,0 +1,25 @@
+// Copyright 2019 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestXMLBindingBindBody(t *testing.T) {
+ var s struct {
+ Foo string `xml:"foo"`
+ }
+ xmlBody := `
+
+ FOO
+`
+ err := xmlBinding{}.BindBody([]byte(xmlBody), &s)
+ require.NoError(t, err)
+ assert.Equal(t, "FOO", s.Foo)
+}
diff --git a/gin/binding/yaml.go b/gin/binding/yaml.go
new file mode 100644
index 0000000..a2d36d6
--- /dev/null
+++ b/gin/binding/yaml.go
@@ -0,0 +1,35 @@
+// Copyright 2018 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "bytes"
+ "io"
+ "net/http"
+
+ "gopkg.in/yaml.v2"
+)
+
+type yamlBinding struct{}
+
+func (yamlBinding) Name() string {
+ return "yaml"
+}
+
+func (yamlBinding) Bind(req *http.Request, obj interface{}) error {
+ return decodeYAML(req.Body, obj)
+}
+
+func (yamlBinding) BindBody(body []byte, obj interface{}) error {
+ return decodeYAML(bytes.NewReader(body), obj)
+}
+
+func decodeYAML(r io.Reader, obj interface{}) error {
+ decoder := yaml.NewDecoder(r)
+ if err := decoder.Decode(obj); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/gin/binding/yaml_test.go b/gin/binding/yaml_test.go
new file mode 100644
index 0000000..e66338b
--- /dev/null
+++ b/gin/binding/yaml_test.go
@@ -0,0 +1,21 @@
+// Copyright 2019 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestYAMLBindingBindBody(t *testing.T) {
+ var s struct {
+ Foo string `yaml:"foo"`
+ }
+ err := yamlBinding{}.BindBody([]byte("foo: FOO"), &s)
+ require.NoError(t, err)
+ assert.Equal(t, "FOO", s.Foo)
+}
diff --git a/gin/internal/bytesconv/bytesconv.go b/gin/internal/bytesconv/bytesconv.go
new file mode 100644
index 0000000..7b80e33
--- /dev/null
+++ b/gin/internal/bytesconv/bytesconv.go
@@ -0,0 +1,23 @@
+// Copyright 2020 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package bytesconv
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+// StringToBytes converts string to byte slice without a memory allocation.
+func StringToBytes(s string) (b []byte) {
+ sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
+ bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+ bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
+ return b
+}
+
+// BytesToString converts byte slice to string without a memory allocation.
+func BytesToString(b []byte) string {
+ return *(*string)(unsafe.Pointer(&b))
+}
diff --git a/gin/internal/bytesconv/bytesconv_test.go b/gin/internal/bytesconv/bytesconv_test.go
new file mode 100644
index 0000000..eeaad5e
--- /dev/null
+++ b/gin/internal/bytesconv/bytesconv_test.go
@@ -0,0 +1,99 @@
+// Copyright 2020 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package bytesconv
+
+import (
+ "bytes"
+ "math/rand"
+ "strings"
+ "testing"
+ "time"
+)
+
+var testString = "Albert Einstein: Logic will get you from A to B. Imagination will take you everywhere."
+var testBytes = []byte(testString)
+
+func rawBytesToStr(b []byte) string {
+ return string(b)
+}
+
+func rawStrToBytes(s string) []byte {
+ return []byte(s)
+}
+
+// go test -v
+
+func TestBytesToString(t *testing.T) {
+ data := make([]byte, 1024)
+ for i := 0; i < 100; i++ {
+ rand.Read(data)
+ if rawBytesToStr(data) != BytesToString(data) {
+ t.Fatal("don't match")
+ }
+ }
+}
+
+const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+const (
+ letterIdxBits = 6 // 6 bits to represent a letter index
+ letterIdxMask = 1<= 0; {
+ if remain == 0 {
+ cache, remain = src.Int63(), letterIdxMax
+ }
+ if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
+ sb.WriteByte(letterBytes[idx])
+ i--
+ }
+ cache >>= letterIdxBits
+ remain--
+ }
+
+ return sb.String()
+}
+
+func TestStringToBytes(t *testing.T) {
+ for i := 0; i < 100; i++ {
+ s := RandStringBytesMaskImprSrcSB(64)
+ if !bytes.Equal(rawStrToBytes(s), StringToBytes(s)) {
+ t.Fatal("don't match")
+ }
+ }
+}
+
+// go test -v -run=none -bench=^BenchmarkBytesConv -benchmem=true
+
+func BenchmarkBytesConvBytesToStrRaw(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ rawBytesToStr(testBytes)
+ }
+}
+
+func BenchmarkBytesConvBytesToStr(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ BytesToString(testBytes)
+ }
+}
+
+func BenchmarkBytesConvStrToBytesRaw(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ rawStrToBytes(testString)
+ }
+}
+
+func BenchmarkBytesConvStrToBytes(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ StringToBytes(testString)
+ }
+}
diff --git a/gin/internal/json/json.go b/gin/internal/json/json.go
new file mode 100644
index 0000000..480e8bf
--- /dev/null
+++ b/gin/internal/json/json.go
@@ -0,0 +1,22 @@
+// Copyright 2017 Bo-Yi Wu. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+// +build !jsoniter
+
+package json
+
+import "encoding/json"
+
+var (
+ // Marshal is exported by gin/json package.
+ Marshal = json.Marshal
+ // Unmarshal is exported by gin/json package.
+ Unmarshal = json.Unmarshal
+ // MarshalIndent is exported by gin/json package.
+ MarshalIndent = json.MarshalIndent
+ // NewDecoder is exported by gin/json package.
+ NewDecoder = json.NewDecoder
+ // NewEncoder is exported by gin/json package.
+ NewEncoder = json.NewEncoder
+)
diff --git a/gin/internal/json/jsoniter.go b/gin/internal/json/jsoniter.go
new file mode 100644
index 0000000..649a3cd
--- /dev/null
+++ b/gin/internal/json/jsoniter.go
@@ -0,0 +1,23 @@
+// Copyright 2017 Bo-Yi Wu. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+// +build jsoniter
+
+package json
+
+import jsoniter "github.com/json-iterator/go"
+
+var (
+ json = jsoniter.ConfigCompatibleWithStandardLibrary
+ // Marshal is exported by gin/json package.
+ Marshal = json.Marshal
+ // Unmarshal is exported by gin/json package.
+ Unmarshal = json.Unmarshal
+ // MarshalIndent is exported by gin/json package.
+ MarshalIndent = json.MarshalIndent
+ // NewDecoder is exported by gin/json package.
+ NewDecoder = json.NewDecoder
+ // NewEncoder is exported by gin/json package.
+ NewEncoder = json.NewEncoder
+)
diff --git a/go-playground/locales/.gitignore b/go-playground/locales/.gitignore
new file mode 100644
index 0000000..daf913b
--- /dev/null
+++ b/go-playground/locales/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/go-playground/locales/.travis.yml b/go-playground/locales/.travis.yml
new file mode 100644
index 0000000..d50237a
--- /dev/null
+++ b/go-playground/locales/.travis.yml
@@ -0,0 +1,26 @@
+language: go
+go:
+ - 1.13.1
+ - tip
+matrix:
+ allow_failures:
+ - go: tip
+
+notifications:
+ email:
+ recipients: dean.karn@gmail.com
+ on_success: change
+ on_failure: always
+
+before_install:
+ - go install github.com/mattn/goveralls
+
+# Only clone the most recent commit.
+git:
+ depth: 1
+
+script:
+ - go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
+
+after_success: |
+ goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
\ No newline at end of file
diff --git a/go-playground/locales/LICENSE b/go-playground/locales/LICENSE
new file mode 100644
index 0000000..75854ac
--- /dev/null
+++ b/go-playground/locales/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Go Playground
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/go-playground/locales/README.md b/go-playground/locales/README.md
new file mode 100644
index 0000000..db4c4d0
--- /dev/null
+++ b/go-playground/locales/README.md
@@ -0,0 +1,172 @@
+## locales
+![Project status](https://img.shields.io/badge/version-0.13.0-green.svg)
+[![Build Status](https://travis-ci.org/go-playground/locales.svg?branch=master)](https://travis-ci.org/go-playground/locales)
+[![Go Report Card](https://goreportcard.com/badge/go-playground/locales)](https://goreportcard.com/report/go-playground/locales)
+[![GoDoc](https://godoc.org/go-playground/locales?status.svg)](https://godoc.org/go-playground/locales)
+![License](https://img.shields.io/dub/l/vibe-d.svg)
+[![Gitter](https://badges.gitter.im/go-playground/locales.svg)](https://gitter.im/go-playground/locales?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+
+Locales is a set of locales generated from the [Unicode CLDR Project](http://cldr.unicode.org/) which can be used independently or within
+an i18n package; these were built for use with, but not exclusive to, [Universal Translator](https://github.com/go-playground/universal-translator).
+
+Features
+--------
+- [x] Rules generated from the latest [CLDR](http://cldr.unicode.org/index/downloads) data, v31.0.1
+- [x] Contains Cardinal, Ordinal and Range Plural Rules
+- [x] Contains Month, Weekday and Timezone translations built in
+- [x] Contains Date & Time formatting functions
+- [x] Contains Number, Currency, Accounting and Percent formatting functions
+- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere )
+
+Full Tests
+--------------------
+I could sure use your help adding tests for every locale, it is a huge undertaking and I just don't have the free time to do it all at the moment;
+any help would be **greatly appreciated!!!!** please see [issue](https://go-playground/locales/issues/1) for details.
+
+Installation
+-----------
+
+Use go get
+
+```shell
+go get go-playground/locales
+```
+
+NOTES
+--------
+You'll notice most return types are []byte, this is because most of the time the results will be concatenated with a larger body
+of text and can avoid some allocations if already appending to a byte array, otherwise just cast as string.
+
+Usage
+-------
+```go
+package main
+
+import (
+ "fmt"
+ "time"
+
+ "go-playground/locales/currency"
+ "go-playground/locales/en_CA"
+)
+
+func main() {
+
+ loc, _ := time.LoadLocation("America/Toronto")
+ datetime := time.Date(2016, 02, 03, 9, 0, 1, 0, loc)
+
+ l := en_CA.New()
+
+ // Dates
+ fmt.Println(l.FmtDateFull(datetime))
+ fmt.Println(l.FmtDateLong(datetime))
+ fmt.Println(l.FmtDateMedium(datetime))
+ fmt.Println(l.FmtDateShort(datetime))
+
+ // Times
+ fmt.Println(l.FmtTimeFull(datetime))
+ fmt.Println(l.FmtTimeLong(datetime))
+ fmt.Println(l.FmtTimeMedium(datetime))
+ fmt.Println(l.FmtTimeShort(datetime))
+
+ // Months Wide
+ fmt.Println(l.MonthWide(time.January))
+ fmt.Println(l.MonthWide(time.February))
+ fmt.Println(l.MonthWide(time.March))
+ // ...
+
+ // Months Abbreviated
+ fmt.Println(l.MonthAbbreviated(time.January))
+ fmt.Println(l.MonthAbbreviated(time.February))
+ fmt.Println(l.MonthAbbreviated(time.March))
+ // ...
+
+ // Months Narrow
+ fmt.Println(l.MonthNarrow(time.January))
+ fmt.Println(l.MonthNarrow(time.February))
+ fmt.Println(l.MonthNarrow(time.March))
+ // ...
+
+ // Weekdays Wide
+ fmt.Println(l.WeekdayWide(time.Sunday))
+ fmt.Println(l.WeekdayWide(time.Monday))
+ fmt.Println(l.WeekdayWide(time.Tuesday))
+ // ...
+
+ // Weekdays Abbreviated
+ fmt.Println(l.WeekdayAbbreviated(time.Sunday))
+ fmt.Println(l.WeekdayAbbreviated(time.Monday))
+ fmt.Println(l.WeekdayAbbreviated(time.Tuesday))
+ // ...
+
+ // Weekdays Short
+ fmt.Println(l.WeekdayShort(time.Sunday))
+ fmt.Println(l.WeekdayShort(time.Monday))
+ fmt.Println(l.WeekdayShort(time.Tuesday))
+ // ...
+
+ // Weekdays Narrow
+ fmt.Println(l.WeekdayNarrow(time.Sunday))
+ fmt.Println(l.WeekdayNarrow(time.Monday))
+ fmt.Println(l.WeekdayNarrow(time.Tuesday))
+ // ...
+
+ var f64 float64
+
+ f64 = -10356.4523
+
+ // Number
+ fmt.Println(l.FmtNumber(f64, 2))
+
+ // Currency
+ fmt.Println(l.FmtCurrency(f64, 2, currency.CAD))
+ fmt.Println(l.FmtCurrency(f64, 2, currency.USD))
+
+ // Accounting
+ fmt.Println(l.FmtAccounting(f64, 2, currency.CAD))
+ fmt.Println(l.FmtAccounting(f64, 2, currency.USD))
+
+ f64 = 78.12
+
+ // Percent
+ fmt.Println(l.FmtPercent(f64, 0))
+
+ // Plural Rules for locale, so you know what rules you must cover
+ fmt.Println(l.PluralsCardinal())
+ fmt.Println(l.PluralsOrdinal())
+
+ // Cardinal Plural Rules
+ fmt.Println(l.CardinalPluralRule(1, 0))
+ fmt.Println(l.CardinalPluralRule(1.0, 0))
+ fmt.Println(l.CardinalPluralRule(1.0, 1))
+ fmt.Println(l.CardinalPluralRule(3, 0))
+
+ // Ordinal Plural Rules
+ fmt.Println(l.OrdinalPluralRule(21, 0)) // 21st
+ fmt.Println(l.OrdinalPluralRule(22, 0)) // 22nd
+ fmt.Println(l.OrdinalPluralRule(33, 0)) // 33rd
+ fmt.Println(l.OrdinalPluralRule(34, 0)) // 34th
+
+ // Range Plural Rules
+ fmt.Println(l.RangePluralRule(1, 0, 1, 0)) // 1-1
+ fmt.Println(l.RangePluralRule(1, 0, 2, 0)) // 1-2
+ fmt.Println(l.RangePluralRule(5, 0, 8, 0)) // 5-8
+}
+```
+
+NOTES:
+-------
+These rules were generated from the [Unicode CLDR Project](http://cldr.unicode.org/), if you encounter any issues
+I strongly encourage contributing to the CLDR project to get the locale information corrected and the next time
+these locales are regenerated the fix will come with.
+
+I do however realize that time constraints are often important and so there are two options:
+
+1. Create your own locale, copy, paste and modify, and ensure it complies with the `Translator` interface.
+2. Add an exception in the locale generation code directly and once regenerated, fix will be in place.
+
+Please to not make fixes inside the locale files, they WILL get overwritten when the locales are regenerated.
+
+License
+------
+Distributed under MIT License, please see license file in code for more details.
diff --git a/go-playground/locales/currency/currency.go b/go-playground/locales/currency/currency.go
new file mode 100644
index 0000000..cdaba59
--- /dev/null
+++ b/go-playground/locales/currency/currency.go
@@ -0,0 +1,308 @@
+package currency
+
+// Type is the currency type associated with the locales currency enum
+type Type int
+
+// locale currencies
+const (
+ ADP Type = iota
+ AED
+ AFA
+ AFN
+ ALK
+ ALL
+ AMD
+ ANG
+ AOA
+ AOK
+ AON
+ AOR
+ ARA
+ ARL
+ ARM
+ ARP
+ ARS
+ ATS
+ AUD
+ AWG
+ AZM
+ AZN
+ BAD
+ BAM
+ BAN
+ BBD
+ BDT
+ BEC
+ BEF
+ BEL
+ BGL
+ BGM
+ BGN
+ BGO
+ BHD
+ BIF
+ BMD
+ BND
+ BOB
+ BOL
+ BOP
+ BOV
+ BRB
+ BRC
+ BRE
+ BRL
+ BRN
+ BRR
+ BRZ
+ BSD
+ BTN
+ BUK
+ BWP
+ BYB
+ BYN
+ BYR
+ BZD
+ CAD
+ CDF
+ CHE
+ CHF
+ CHW
+ CLE
+ CLF
+ CLP
+ CNH
+ CNX
+ CNY
+ COP
+ COU
+ CRC
+ CSD
+ CSK
+ CUC
+ CUP
+ CVE
+ CYP
+ CZK
+ DDM
+ DEM
+ DJF
+ DKK
+ DOP
+ DZD
+ ECS
+ ECV
+ EEK
+ EGP
+ ERN
+ ESA
+ ESB
+ ESP
+ ETB
+ EUR
+ FIM
+ FJD
+ FKP
+ FRF
+ GBP
+ GEK
+ GEL
+ GHC
+ GHS
+ GIP
+ GMD
+ GNF
+ GNS
+ GQE
+ GRD
+ GTQ
+ GWE
+ GWP
+ GYD
+ HKD
+ HNL
+ HRD
+ HRK
+ HTG
+ HUF
+ IDR
+ IEP
+ ILP
+ ILR
+ ILS
+ INR
+ IQD
+ IRR
+ ISJ
+ ISK
+ ITL
+ JMD
+ JOD
+ JPY
+ KES
+ KGS
+ KHR
+ KMF
+ KPW
+ KRH
+ KRO
+ KRW
+ KWD
+ KYD
+ KZT
+ LAK
+ LBP
+ LKR
+ LRD
+ LSL
+ LTL
+ LTT
+ LUC
+ LUF
+ LUL
+ LVL
+ LVR
+ LYD
+ MAD
+ MAF
+ MCF
+ MDC
+ MDL
+ MGA
+ MGF
+ MKD
+ MKN
+ MLF
+ MMK
+ MNT
+ MOP
+ MRO
+ MTL
+ MTP
+ MUR
+ MVP
+ MVR
+ MWK
+ MXN
+ MXP
+ MXV
+ MYR
+ MZE
+ MZM
+ MZN
+ NAD
+ NGN
+ NIC
+ NIO
+ NLG
+ NOK
+ NPR
+ NZD
+ OMR
+ PAB
+ PEI
+ PEN
+ PES
+ PGK
+ PHP
+ PKR
+ PLN
+ PLZ
+ PTE
+ PYG
+ QAR
+ RHD
+ ROL
+ RON
+ RSD
+ RUB
+ RUR
+ RWF
+ SAR
+ SBD
+ SCR
+ SDD
+ SDG
+ SDP
+ SEK
+ SGD
+ SHP
+ SIT
+ SKK
+ SLL
+ SOS
+ SRD
+ SRG
+ SSP
+ STD
+ STN
+ SUR
+ SVC
+ SYP
+ SZL
+ THB
+ TJR
+ TJS
+ TMM
+ TMT
+ TND
+ TOP
+ TPE
+ TRL
+ TRY
+ TTD
+ TWD
+ TZS
+ UAH
+ UAK
+ UGS
+ UGX
+ USD
+ USN
+ USS
+ UYI
+ UYP
+ UYU
+ UZS
+ VEB
+ VEF
+ VND
+ VNN
+ VUV
+ WST
+ XAF
+ XAG
+ XAU
+ XBA
+ XBB
+ XBC
+ XBD
+ XCD
+ XDR
+ XEU
+ XFO
+ XFU
+ XOF
+ XPD
+ XPF
+ XPT
+ XRE
+ XSU
+ XTS
+ XUA
+ XXX
+ YDD
+ YER
+ YUD
+ YUM
+ YUN
+ YUR
+ ZAL
+ ZAR
+ ZMK
+ ZMW
+ ZRN
+ ZRZ
+ ZWD
+ ZWL
+ ZWR
+)
diff --git a/go-playground/locales/go.sum b/go-playground/locales/go.sum
new file mode 100644
index 0000000..63c9200
--- /dev/null
+++ b/go-playground/locales/go.sum
@@ -0,0 +1,3 @@
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/go-playground/locales/logo.png b/go-playground/locales/logo.png
new file mode 100644
index 0000000..3038276
Binary files /dev/null and b/go-playground/locales/logo.png differ
diff --git a/go-playground/locales/rules.go b/go-playground/locales/rules.go
new file mode 100644
index 0000000..26f1c08
--- /dev/null
+++ b/go-playground/locales/rules.go
@@ -0,0 +1,293 @@
+package locales
+
+import (
+ "strconv"
+ "time"
+
+ "go-playground/locales/currency"
+)
+
+// // ErrBadNumberValue is returned when the number passed for
+// // plural rule determination cannot be parsed
+// type ErrBadNumberValue struct {
+// NumberValue string
+// InnerError error
+// }
+
+// // Error returns ErrBadNumberValue error string
+// func (e *ErrBadNumberValue) Error() string {
+// return fmt.Sprintf("Invalid Number Value '%s' %s", e.NumberValue, e.InnerError)
+// }
+
+// var _ error = new(ErrBadNumberValue)
+
+// PluralRule denotes the type of plural rules
+type PluralRule int
+
+// PluralRule's
+const (
+ PluralRuleUnknown PluralRule = iota
+ PluralRuleZero // zero
+ PluralRuleOne // one - singular
+ PluralRuleTwo // two - dual
+ PluralRuleFew // few - paucal
+ PluralRuleMany // many - also used for fractions if they have a separate class
+ PluralRuleOther // other - required—general plural form—also used if the language only has a single form
+)
+
+const (
+ pluralsString = "UnknownZeroOneTwoFewManyOther"
+)
+
+// Translator encapsulates an instance of a locale
+// NOTE: some values are returned as a []byte just in case the caller
+// wishes to add more and can help avoid allocations; otherwise just cast as string
+type Translator interface {
+
+ // The following Functions are for overriding, debugging or developing
+ // with a Translator Locale
+
+ // Locale returns the string value of the translator
+ Locale() string
+
+ // returns an array of cardinal plural rules associated
+ // with this translator
+ PluralsCardinal() []PluralRule
+
+ // returns an array of ordinal plural rules associated
+ // with this translator
+ PluralsOrdinal() []PluralRule
+
+ // returns an array of range plural rules associated
+ // with this translator
+ PluralsRange() []PluralRule
+
+ // returns the cardinal PluralRule given 'num' and digits/precision of 'v' for locale
+ CardinalPluralRule(num float64, v uint64) PluralRule
+
+ // returns the ordinal PluralRule given 'num' and digits/precision of 'v' for locale
+ OrdinalPluralRule(num float64, v uint64) PluralRule
+
+ // returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for locale
+ RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) PluralRule
+
+ // returns the locales abbreviated month given the 'month' provided
+ MonthAbbreviated(month time.Month) string
+
+ // returns the locales abbreviated months
+ MonthsAbbreviated() []string
+
+ // returns the locales narrow month given the 'month' provided
+ MonthNarrow(month time.Month) string
+
+ // returns the locales narrow months
+ MonthsNarrow() []string
+
+ // returns the locales wide month given the 'month' provided
+ MonthWide(month time.Month) string
+
+ // returns the locales wide months
+ MonthsWide() []string
+
+ // returns the locales abbreviated weekday given the 'weekday' provided
+ WeekdayAbbreviated(weekday time.Weekday) string
+
+ // returns the locales abbreviated weekdays
+ WeekdaysAbbreviated() []string
+
+ // returns the locales narrow weekday given the 'weekday' provided
+ WeekdayNarrow(weekday time.Weekday) string
+
+ // WeekdaysNarrowreturns the locales narrow weekdays
+ WeekdaysNarrow() []string
+
+ // returns the locales short weekday given the 'weekday' provided
+ WeekdayShort(weekday time.Weekday) string
+
+ // returns the locales short weekdays
+ WeekdaysShort() []string
+
+ // returns the locales wide weekday given the 'weekday' provided
+ WeekdayWide(weekday time.Weekday) string
+
+ // returns the locales wide weekdays
+ WeekdaysWide() []string
+
+ // The following Functions are common Formatting functionsfor the Translator's Locale
+
+ // returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v'
+ FmtNumber(num float64, v uint64) string
+
+ // returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v'
+ // NOTE: 'num' passed into FmtPercent is assumed to be in percent already
+ FmtPercent(num float64, v uint64) string
+
+ // returns the currency representation of 'num' with digits/precision of 'v' for locale
+ FmtCurrency(num float64, v uint64, currency currency.Type) string
+
+ // returns the currency representation of 'num' with digits/precision of 'v' for locale
+ // in accounting notation.
+ FmtAccounting(num float64, v uint64, currency currency.Type) string
+
+ // returns the short date representation of 't' for locale
+ FmtDateShort(t time.Time) string
+
+ // returns the medium date representation of 't' for locale
+ FmtDateMedium(t time.Time) string
+
+ // returns the long date representation of 't' for locale
+ FmtDateLong(t time.Time) string
+
+ // returns the full date representation of 't' for locale
+ FmtDateFull(t time.Time) string
+
+ // returns the short time representation of 't' for locale
+ FmtTimeShort(t time.Time) string
+
+ // returns the medium time representation of 't' for locale
+ FmtTimeMedium(t time.Time) string
+
+ // returns the long time representation of 't' for locale
+ FmtTimeLong(t time.Time) string
+
+ // returns the full time representation of 't' for locale
+ FmtTimeFull(t time.Time) string
+}
+
+// String returns the string value of PluralRule
+func (p PluralRule) String() string {
+
+ switch p {
+ case PluralRuleZero:
+ return pluralsString[7:11]
+ case PluralRuleOne:
+ return pluralsString[11:14]
+ case PluralRuleTwo:
+ return pluralsString[14:17]
+ case PluralRuleFew:
+ return pluralsString[17:20]
+ case PluralRuleMany:
+ return pluralsString[20:24]
+ case PluralRuleOther:
+ return pluralsString[24:]
+ default:
+ return pluralsString[:7]
+ }
+}
+
+//
+// Precision Notes:
+//
+// must specify a precision >= 0, and here is why https://play.golang.org/p/LyL90U0Vyh
+//
+// v := float64(3.141)
+// i := float64(int64(v))
+//
+// fmt.Println(v - i)
+//
+// or
+//
+// s := strconv.FormatFloat(v-i, 'f', -1, 64)
+// fmt.Println(s)
+//
+// these will not print what you'd expect: 0.14100000000000001
+// and so this library requires a precision to be specified, or
+// inaccurate plural rules could be applied.
+//
+//
+//
+// n - absolute value of the source number (integer and decimals).
+// i - integer digits of n.
+// v - number of visible fraction digits in n, with trailing zeros.
+// w - number of visible fraction digits in n, without trailing zeros.
+// f - visible fractional digits in n, with trailing zeros.
+// t - visible fractional digits in n, without trailing zeros.
+//
+//
+// Func(num float64, v uint64) // v = digits/precision and prevents -1 as a special case as this can lead to very unexpected behaviour, see precision note's above.
+//
+// n := math.Abs(num)
+// i := int64(n)
+// v := v
+//
+//
+// w := strconv.FormatFloat(num-float64(i), 'f', int(v), 64) // then parse backwards on string until no more zero's....
+// f := strconv.FormatFloat(n, 'f', int(v), 64) // then turn everything after decimal into an int64
+// t := strconv.FormatFloat(n, 'f', int(v), 64) // then parse backwards on string until no more zero's....
+//
+//
+//
+// General Inclusion Rules
+// - v will always be available inherently
+// - all require n
+// - w requires i
+//
+
+// W returns the number of visible fraction digits in N, without trailing zeros.
+func W(n float64, v uint64) (w int64) {
+
+ s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
+
+ // with either be '0' or '0.xxxx', so if 1 then w will be zero
+ // otherwise need to parse
+ if len(s) != 1 {
+
+ s = s[2:]
+ end := len(s) + 1
+
+ for i := end; i >= 0; i-- {
+ if s[i] != '0' {
+ end = i + 1
+ break
+ }
+ }
+
+ w = int64(len(s[:end]))
+ }
+
+ return
+}
+
+// F returns the visible fractional digits in N, with trailing zeros.
+func F(n float64, v uint64) (f int64) {
+
+ s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
+
+ // with either be '0' or '0.xxxx', so if 1 then f will be zero
+ // otherwise need to parse
+ if len(s) != 1 {
+
+ // ignoring error, because it can't fail as we generated
+ // the string internally from a real number
+ f, _ = strconv.ParseInt(s[2:], 10, 64)
+ }
+
+ return
+}
+
+// T returns the visible fractional digits in N, without trailing zeros.
+func T(n float64, v uint64) (t int64) {
+
+ s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
+
+ // with either be '0' or '0.xxxx', so if 1 then t will be zero
+ // otherwise need to parse
+ if len(s) != 1 {
+
+ s = s[2:]
+ end := len(s) + 1
+
+ for i := end; i >= 0; i-- {
+ if s[i] != '0' {
+ end = i + 1
+ break
+ }
+ }
+
+ // ignoring error, because it can't fail as we generated
+ // the string internally from a real number
+ t, _ = strconv.ParseInt(s[:end], 10, 64)
+ }
+
+ return
+}
diff --git a/go-playground/locales/zh/zh.go b/go-playground/locales/zh/zh.go
new file mode 100644
index 0000000..726938a
--- /dev/null
+++ b/go-playground/locales/zh/zh.go
@@ -0,0 +1,619 @@
+package zh
+
+import (
+ "math"
+ "strconv"
+ "time"
+
+ "go-playground/locales"
+ "go-playground/locales/currency"
+)
+
+type zh struct {
+ locale string
+ pluralsCardinal []locales.PluralRule
+ pluralsOrdinal []locales.PluralRule
+ pluralsRange []locales.PluralRule
+ decimal string
+ group string
+ minus string
+ percent string
+ perMille string
+ timeSeparator string
+ inifinity string
+ currencies []string // idx = enum of currency code
+ monthsAbbreviated []string
+ monthsNarrow []string
+ monthsWide []string
+ daysAbbreviated []string
+ daysNarrow []string
+ daysShort []string
+ daysWide []string
+ periodsAbbreviated []string
+ periodsNarrow []string
+ periodsShort []string
+ periodsWide []string
+ erasAbbreviated []string
+ erasNarrow []string
+ erasWide []string
+ timezones map[string]string
+}
+
+// New returns a new instance of translator for the 'zh' locale
+func New() locales.Translator {
+ return &zh{
+ locale: "zh",
+ pluralsCardinal: []locales.PluralRule{6},
+ pluralsOrdinal: []locales.PluralRule{6},
+ pluralsRange: []locales.PluralRule{6},
+ decimal: ".",
+ group: ",",
+ minus: "-",
+ percent: "%",
+ perMille: "‰",
+ timeSeparator: ":",
+ inifinity: "∞",
+ currencies: []string{"ADP", "AED", "AFA", "AFN", "ALK", "ALL", "AMD", "ANG", "AOA", "AOK", "AON", "AOR", "ARA", "ARL", "ARM", "ARP", "ARS", "ATS", "AU$", "AWG", "AZM", "AZN", "BAD", "BAM", "BAN", "BBD", "BDT", "BEC", "BEF", "BEL", "BGL", "BGM", "BGN", "BGO", "BHD", "BIF", "BMD", "BND", "BOB", "BOL", "BOP", "BOV", "BRB", "BRC", "BRE", "R$", "BRN", "BRR", "BRZ", "BSD", "BTN", "BUK", "BWP", "BYB", "BYN", "BYR", "BZD", "CA$", "CDF", "CHE", "CHF", "CHW", "CLE", "CLF", "CLP", "CNH", "CNX", "¥", "COP", "COU", "CRC", "CSD", "CSK", "CUC", "CUP", "CVE", "CYP", "CZK", "DDM", "DEM", "DJF", "DKK", "DOP", "DZD", "ECS", "ECV", "EEK", "EGP", "ERN", "ESA", "ESB", "ESP", "ETB", "€", "FIM", "FJD", "FKP", "FRF", "£", "GEK", "GEL", "GHC", "GHS", "GIP", "GMD", "GNF", "GNS", "GQE", "GRD", "GTQ", "GWE", "GWP", "GYD", "HK$", "HNL", "HRD", "HRK", "HTG", "HUF", "IDR", "IEP", "ILP", "ILS", "₪", "₹", "IQD", "IRR", "ISJ", "ISK", "ITL", "JMD", "JOD", "JP¥", "KES", "KGS", "KHR", "KMF", "KPW", "KRH", "KRO", "₩", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LTL", "LTT", "LUC", "LUF", "LUL", "LVL", "LVR", "LYD", "MAD", "MAF", "MCF", "MDC", "MDL", "MGA", "MGF", "MKD", "MKN", "MLF", "MMK", "MNT", "MOP", "MRO", "MTL", "MTP", "MUR", "MVP", "MVR", "MWK", "MX$", "MXP", "MXV", "MYR", "MZE", "MZM", "MZN", "NAD", "NGN", "NIC", "NIO", "NLG", "NOK", "NPR", "NZ$", "OMR", "PAB", "PEI", "PEN", "PES", "PGK", "PHP", "PKR", "PLN", "PLZ", "PTE", "PYG", "QAR", "RHD", "ROL", "RON", "RSD", "RUB", "RUR", "RWF", "SAR", "SBD", "SCR", "SDD", "SDG", "SDP", "SEK", "SGD", "SHP", "SIT", "SKK", "SLL", "SOS", "SRD", "SRG", "SSP", "STD", "STN", "SUR", "SVC", "SYP", "SZL", "THB", "TJR", "TJS", "TMM", "TMT", "TND", "TOP", "TPE", "TRL", "TRY", "TTD", "NT$", "TZS", "UAH", "UAK", "UGS", "UGX", "US$", "USN", "USS", "UYI", "UYP", "UYU", "UZS", "VEB", "VEF", "₫", "VNN", "VUV", "WST", "FCFA", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "EC$", "XDR", "XEU", "XFO", "XFU", "CFA", "XPD", "CFPF", "XPT", "XRE", "XSU", "XTS", "XUA", "XXX", "YDD", "YER", "YUD", "YUM", "YUN", "YUR", "ZAL", "ZAR", "ZMK", "ZMW", "ZRN", "ZRZ", "ZWD", "ZWL", "ZWR"},
+ monthsAbbreviated: []string{"", "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"},
+ monthsNarrow: []string{"", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"},
+ monthsWide: []string{"", "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"},
+ daysAbbreviated: []string{"周日", "周一", "周二", "周三", "周四", "周五", "周六"},
+ daysNarrow: []string{"日", "一", "二", "三", "四", "五", "六"},
+ daysShort: []string{"周日", "周一", "周二", "周三", "周四", "周五", "周六"},
+ daysWide: []string{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"},
+ periodsAbbreviated: []string{"上午", "下午"},
+ periodsNarrow: []string{"上午", "下午"},
+ periodsWide: []string{"上午", "下午"},
+ erasAbbreviated: []string{"公元前", "公元"},
+ erasNarrow: []string{"公元前", "公元"},
+ erasWide: []string{"公元前", "公元"},
+ timezones: map[string]string{"UYST": "乌拉圭夏令时间", "HNPMX": "墨西哥太平洋标准时间", "MDT": "北美山区夏令时间", "WESZ": "西欧夏令时间", "AKST": "阿拉斯加标准时间", "ACWST": "澳大利亚中西部标准时间", "HENOMX": "墨西哥西北部夏令时间", "WIT": "印度尼西亚东部时间", "HEPMX": "墨西哥太平洋夏令时间", "JST": "日本标准时间", "SRT": "苏里南时间", "CLST": "智利夏令时间", "UYT": "乌拉圭标准时间", "AWDT": "澳大利亚西部夏令时间", "MST": "北美山区标准时间", "WAST": "西部非洲夏令时间", "NZST": "新西兰标准时间", "EAT": "东部非洲时间", "HECU": "古巴夏令时间", "BT": "不丹时间", "EDT": "北美东部夏令时间", "WARST": "阿根廷西部夏令时间", "HNPM": "圣皮埃尔和密克隆群岛标准时间", "HNCU": "古巴标准时间", "PDT": "北美太平洋夏令时间", "LHDT": "豪勋爵岛夏令时间", "CLT": "智利标准时间", "PST": "北美太平洋标准时间", "JDT": "日本夏令时间", "OEZ": "东欧标准时间", "TMT": "土库曼斯坦标准时间", "CST": "北美中部标准时间", "AWST": "澳大利亚西部标准时间", "AEST": "澳大利亚东部标准时间", "AKDT": "阿拉斯加夏令时间", "HKT": "香港标准时间", "LHST": "豪勋爵岛标准时间", "HNNOMX": "墨西哥西北部标准时间", "BOT": "玻利维亚标准时间", "HNOG": "格陵兰岛西部标准时间", "EST": "北美东部标准时间", "MESZ": "中欧夏令时间", "WITA": "印度尼西亚中部时间", "CAT": "中部非洲时间", "COST": "哥伦比亚夏令时间", "CHADT": "查坦夏令时间", "AST": "大西洋标准时间", "MYT": "马来西亚时间", "OESZ": "东欧夏令时间", "COT": "哥伦比亚标准时间", "SAST": "南非标准时间", "HEOG": "格陵兰岛西部夏令时间", "ACWDT": "澳大利亚中西部夏令时间", "MEZ": "中欧标准时间", "GYT": "圭亚那时间", "ADT": "大西洋夏令时间", "HEEG": "格陵兰岛东部夏令时间", "WART": "阿根廷西部标准时间", "VET": "委内瑞拉时间", "GMT": "格林尼治标准时间", "∅∅∅": "巴西利亚夏令时间", "SGT": "新加坡标准时间", "ACDT": "澳大利亚中部夏令时间", "HAT": "纽芬兰夏令时间", "HADT": "夏威夷-阿留申夏令时间", "CHAST": "查坦标准时间", "CDT": "北美中部夏令时间", "AEDT": "澳大利亚东部夏令时间", "WEZ": "西欧标准时间", "NZDT": "新西兰夏令时间", "ECT": "厄瓜多尔标准时间", "GFT": "法属圭亚那标准时间", "HKST": "香港夏令时间", "IST": "印度时间", "HNT": "纽芬兰标准时间", "ART": "阿根廷标准时间", "ChST": "查莫罗时间", "WAT": "西部非洲标准时间", "HNEG": "格陵兰岛东部标准时间", "HEPM": "圣皮埃尔和密克隆群岛夏令时间", "TMST": "土库曼斯坦夏令时间", "HAST": "夏威夷-阿留申标准时间", "WIB": "印度尼西亚西部时间", "ACST": "澳大利亚中部标准时间", "ARST": "阿根廷夏令时间"},
+ }
+}
+
+// Locale returns the current translators string locale
+func (zh *zh) Locale() string {
+ return zh.locale
+}
+
+// PluralsCardinal returns the list of cardinal plural rules associated with 'zh'
+func (zh *zh) PluralsCardinal() []locales.PluralRule {
+ return zh.pluralsCardinal
+}
+
+// PluralsOrdinal returns the list of ordinal plural rules associated with 'zh'
+func (zh *zh) PluralsOrdinal() []locales.PluralRule {
+ return zh.pluralsOrdinal
+}
+
+// PluralsRange returns the list of range plural rules associated with 'zh'
+func (zh *zh) PluralsRange() []locales.PluralRule {
+ return zh.pluralsRange
+}
+
+// CardinalPluralRule returns the cardinal PluralRule given 'num' and digits/precision of 'v' for 'zh'
+func (zh *zh) CardinalPluralRule(num float64, v uint64) locales.PluralRule {
+ return locales.PluralRuleOther
+}
+
+// OrdinalPluralRule returns the ordinal PluralRule given 'num' and digits/precision of 'v' for 'zh'
+func (zh *zh) OrdinalPluralRule(num float64, v uint64) locales.PluralRule {
+ return locales.PluralRuleOther
+}
+
+// RangePluralRule returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for 'zh'
+func (zh *zh) RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) locales.PluralRule {
+ return locales.PluralRuleOther
+}
+
+// MonthAbbreviated returns the locales abbreviated month given the 'month' provided
+func (zh *zh) MonthAbbreviated(month time.Month) string {
+ return zh.monthsAbbreviated[month]
+}
+
+// MonthsAbbreviated returns the locales abbreviated months
+func (zh *zh) MonthsAbbreviated() []string {
+ return zh.monthsAbbreviated[1:]
+}
+
+// MonthNarrow returns the locales narrow month given the 'month' provided
+func (zh *zh) MonthNarrow(month time.Month) string {
+ return zh.monthsNarrow[month]
+}
+
+// MonthsNarrow returns the locales narrow months
+func (zh *zh) MonthsNarrow() []string {
+ return zh.monthsNarrow[1:]
+}
+
+// MonthWide returns the locales wide month given the 'month' provided
+func (zh *zh) MonthWide(month time.Month) string {
+ return zh.monthsWide[month]
+}
+
+// MonthsWide returns the locales wide months
+func (zh *zh) MonthsWide() []string {
+ return zh.monthsWide[1:]
+}
+
+// WeekdayAbbreviated returns the locales abbreviated weekday given the 'weekday' provided
+func (zh *zh) WeekdayAbbreviated(weekday time.Weekday) string {
+ return zh.daysAbbreviated[weekday]
+}
+
+// WeekdaysAbbreviated returns the locales abbreviated weekdays
+func (zh *zh) WeekdaysAbbreviated() []string {
+ return zh.daysAbbreviated
+}
+
+// WeekdayNarrow returns the locales narrow weekday given the 'weekday' provided
+func (zh *zh) WeekdayNarrow(weekday time.Weekday) string {
+ return zh.daysNarrow[weekday]
+}
+
+// WeekdaysNarrow returns the locales narrow weekdays
+func (zh *zh) WeekdaysNarrow() []string {
+ return zh.daysNarrow
+}
+
+// WeekdayShort returns the locales short weekday given the 'weekday' provided
+func (zh *zh) WeekdayShort(weekday time.Weekday) string {
+ return zh.daysShort[weekday]
+}
+
+// WeekdaysShort returns the locales short weekdays
+func (zh *zh) WeekdaysShort() []string {
+ return zh.daysShort
+}
+
+// WeekdayWide returns the locales wide weekday given the 'weekday' provided
+func (zh *zh) WeekdayWide(weekday time.Weekday) string {
+ return zh.daysWide[weekday]
+}
+
+// WeekdaysWide returns the locales wide weekdays
+func (zh *zh) WeekdaysWide() []string {
+ return zh.daysWide
+}
+
+// Decimal returns the decimal point of number
+func (zh *zh) Decimal() string {
+ return zh.decimal
+}
+
+// Group returns the group of number
+func (zh *zh) Group() string {
+ return zh.group
+}
+
+// Group returns the minus sign of number
+func (zh *zh) Minus() string {
+ return zh.minus
+}
+
+// FmtNumber returns 'num' with digits/precision of 'v' for 'zh' and handles both Whole and Real numbers based on 'v'
+func (zh *zh) FmtNumber(num float64, v uint64) string {
+
+ s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
+ l := len(s) + 2 + 1*len(s[:len(s)-int(v)-1])/3
+ count := 0
+ inWhole := v == 0
+ b := make([]byte, 0, l)
+
+ for i := len(s) - 1; i >= 0; i-- {
+
+ if s[i] == '.' {
+ b = append(b, zh.decimal[0])
+ inWhole = true
+ continue
+ }
+
+ if inWhole {
+ if count == 3 {
+ b = append(b, zh.group[0])
+ count = 1
+ } else {
+ count++
+ }
+ }
+
+ b = append(b, s[i])
+ }
+
+ if num < 0 {
+ b = append(b, zh.minus[0])
+ }
+
+ // reverse
+ for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
+ b[i], b[j] = b[j], b[i]
+ }
+
+ return string(b)
+}
+
+// FmtPercent returns 'num' with digits/precision of 'v' for 'zh' and handles both Whole and Real numbers based on 'v'
+// NOTE: 'num' passed into FmtPercent is assumed to be in percent already
+func (zh *zh) FmtPercent(num float64, v uint64) string {
+ s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
+ l := len(s) + 3
+ b := make([]byte, 0, l)
+
+ for i := len(s) - 1; i >= 0; i-- {
+
+ if s[i] == '.' {
+ b = append(b, zh.decimal[0])
+ continue
+ }
+
+ b = append(b, s[i])
+ }
+
+ if num < 0 {
+ b = append(b, zh.minus[0])
+ }
+
+ // reverse
+ for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
+ b[i], b[j] = b[j], b[i]
+ }
+
+ b = append(b, zh.percent...)
+
+ return string(b)
+}
+
+// FmtCurrency returns the currency representation of 'num' with digits/precision of 'v' for 'zh'
+func (zh *zh) FmtCurrency(num float64, v uint64, currency currency.Type) string {
+
+ s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
+ symbol := zh.currencies[currency]
+ l := len(s) + len(symbol) + 2 + 1*len(s[:len(s)-int(v)-1])/3
+ count := 0
+ inWhole := v == 0
+ b := make([]byte, 0, l)
+
+ for i := len(s) - 1; i >= 0; i-- {
+
+ if s[i] == '.' {
+ b = append(b, zh.decimal[0])
+ inWhole = true
+ continue
+ }
+
+ if inWhole {
+ if count == 3 {
+ b = append(b, zh.group[0])
+ count = 1
+ } else {
+ count++
+ }
+ }
+
+ b = append(b, s[i])
+ }
+
+ for j := len(symbol) - 1; j >= 0; j-- {
+ b = append(b, symbol[j])
+ }
+
+ if num < 0 {
+ b = append(b, zh.minus[0])
+ }
+
+ // reverse
+ for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
+ b[i], b[j] = b[j], b[i]
+ }
+
+ if int(v) < 2 {
+
+ if v == 0 {
+ b = append(b, zh.decimal...)
+ }
+
+ for i := 0; i < 2-int(v); i++ {
+ b = append(b, '0')
+ }
+ }
+
+ return string(b)
+}
+
+// FmtAccounting returns the currency representation of 'num' with digits/precision of 'v' for 'zh'
+// in accounting notation.
+func (zh *zh) FmtAccounting(num float64, v uint64, currency currency.Type) string {
+
+ s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
+ symbol := zh.currencies[currency]
+ l := len(s) + len(symbol) + 2 + 1*len(s[:len(s)-int(v)-1])/3
+ count := 0
+ inWhole := v == 0
+ b := make([]byte, 0, l)
+
+ for i := len(s) - 1; i >= 0; i-- {
+
+ if s[i] == '.' {
+ b = append(b, zh.decimal[0])
+ inWhole = true
+ continue
+ }
+
+ if inWhole {
+ if count == 3 {
+ b = append(b, zh.group[0])
+ count = 1
+ } else {
+ count++
+ }
+ }
+
+ b = append(b, s[i])
+ }
+
+ if num < 0 {
+
+ for j := len(symbol) - 1; j >= 0; j-- {
+ b = append(b, symbol[j])
+ }
+
+ b = append(b, zh.minus[0])
+
+ } else {
+
+ for j := len(symbol) - 1; j >= 0; j-- {
+ b = append(b, symbol[j])
+ }
+
+ }
+
+ // reverse
+ for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
+ b[i], b[j] = b[j], b[i]
+ }
+
+ if int(v) < 2 {
+
+ if v == 0 {
+ b = append(b, zh.decimal...)
+ }
+
+ for i := 0; i < 2-int(v); i++ {
+ b = append(b, '0')
+ }
+ }
+
+ return string(b)
+}
+
+// FmtDateShort returns the short date representation of 't' for 'zh'
+func (zh *zh) FmtDateShort(t time.Time) string {
+
+ b := make([]byte, 0, 32)
+
+ if t.Year() > 0 {
+ b = strconv.AppendInt(b, int64(t.Year()), 10)
+ } else {
+ b = strconv.AppendInt(b, int64(-t.Year()), 10)
+ }
+
+ b = append(b, []byte{0x2f}...)
+ b = strconv.AppendInt(b, int64(t.Month()), 10)
+ b = append(b, []byte{0x2f}...)
+ b = strconv.AppendInt(b, int64(t.Day()), 10)
+
+ return string(b)
+}
+
+// FmtDateMedium returns the medium date representation of 't' for 'zh'
+func (zh *zh) FmtDateMedium(t time.Time) string {
+
+ b := make([]byte, 0, 32)
+
+ if t.Year() > 0 {
+ b = strconv.AppendInt(b, int64(t.Year()), 10)
+ } else {
+ b = strconv.AppendInt(b, int64(-t.Year()), 10)
+ }
+
+ b = append(b, []byte{0xe5, 0xb9, 0xb4}...)
+ b = strconv.AppendInt(b, int64(t.Month()), 10)
+ b = append(b, []byte{0xe6, 0x9c, 0x88}...)
+ b = strconv.AppendInt(b, int64(t.Day()), 10)
+ b = append(b, []byte{0xe6, 0x97, 0xa5}...)
+
+ return string(b)
+}
+
+// FmtDateLong returns the long date representation of 't' for 'zh'
+func (zh *zh) FmtDateLong(t time.Time) string {
+
+ b := make([]byte, 0, 32)
+
+ if t.Year() > 0 {
+ b = strconv.AppendInt(b, int64(t.Year()), 10)
+ } else {
+ b = strconv.AppendInt(b, int64(-t.Year()), 10)
+ }
+
+ b = append(b, []byte{0xe5, 0xb9, 0xb4}...)
+ b = strconv.AppendInt(b, int64(t.Month()), 10)
+ b = append(b, []byte{0xe6, 0x9c, 0x88}...)
+ b = strconv.AppendInt(b, int64(t.Day()), 10)
+ b = append(b, []byte{0xe6, 0x97, 0xa5}...)
+
+ return string(b)
+}
+
+// FmtDateFull returns the full date representation of 't' for 'zh'
+func (zh *zh) FmtDateFull(t time.Time) string {
+
+ b := make([]byte, 0, 32)
+
+ if t.Year() > 0 {
+ b = strconv.AppendInt(b, int64(t.Year()), 10)
+ } else {
+ b = strconv.AppendInt(b, int64(-t.Year()), 10)
+ }
+
+ b = append(b, []byte{0xe5, 0xb9, 0xb4}...)
+ b = strconv.AppendInt(b, int64(t.Month()), 10)
+ b = append(b, []byte{0xe6, 0x9c, 0x88}...)
+ b = strconv.AppendInt(b, int64(t.Day()), 10)
+ b = append(b, []byte{0xe6, 0x97, 0xa5}...)
+ b = append(b, zh.daysWide[t.Weekday()]...)
+
+ return string(b)
+}
+
+// FmtTimeShort returns the short time representation of 't' for 'zh'
+func (zh *zh) FmtTimeShort(t time.Time) string {
+
+ b := make([]byte, 0, 32)
+
+ if t.Hour() < 12 {
+ b = append(b, zh.periodsAbbreviated[0]...)
+ } else {
+ b = append(b, zh.periodsAbbreviated[1]...)
+ }
+
+ h := t.Hour()
+
+ if h > 12 {
+ h -= 12
+ }
+
+ b = strconv.AppendInt(b, int64(h), 10)
+ b = append(b, zh.timeSeparator...)
+
+ if t.Minute() < 10 {
+ b = append(b, '0')
+ }
+
+ b = strconv.AppendInt(b, int64(t.Minute()), 10)
+
+ return string(b)
+}
+
+// FmtTimeMedium returns the medium time representation of 't' for 'zh'
+func (zh *zh) FmtTimeMedium(t time.Time) string {
+
+ b := make([]byte, 0, 32)
+
+ if t.Hour() < 12 {
+ b = append(b, zh.periodsAbbreviated[0]...)
+ } else {
+ b = append(b, zh.periodsAbbreviated[1]...)
+ }
+
+ h := t.Hour()
+
+ if h > 12 {
+ h -= 12
+ }
+
+ b = strconv.AppendInt(b, int64(h), 10)
+ b = append(b, zh.timeSeparator...)
+
+ if t.Minute() < 10 {
+ b = append(b, '0')
+ }
+
+ b = strconv.AppendInt(b, int64(t.Minute()), 10)
+ b = append(b, zh.timeSeparator...)
+
+ if t.Second() < 10 {
+ b = append(b, '0')
+ }
+
+ b = strconv.AppendInt(b, int64(t.Second()), 10)
+
+ return string(b)
+}
+
+// FmtTimeLong returns the long time representation of 't' for 'zh'
+func (zh *zh) FmtTimeLong(t time.Time) string {
+
+ b := make([]byte, 0, 32)
+
+ tz, _ := t.Zone()
+ b = append(b, tz...)
+
+ b = append(b, []byte{0x20}...)
+
+ if t.Hour() < 12 {
+ b = append(b, zh.periodsAbbreviated[0]...)
+ } else {
+ b = append(b, zh.periodsAbbreviated[1]...)
+ }
+
+ h := t.Hour()
+
+ if h > 12 {
+ h -= 12
+ }
+
+ b = strconv.AppendInt(b, int64(h), 10)
+ b = append(b, zh.timeSeparator...)
+
+ if t.Minute() < 10 {
+ b = append(b, '0')
+ }
+
+ b = strconv.AppendInt(b, int64(t.Minute()), 10)
+ b = append(b, zh.timeSeparator...)
+
+ if t.Second() < 10 {
+ b = append(b, '0')
+ }
+
+ b = strconv.AppendInt(b, int64(t.Second()), 10)
+
+ return string(b)
+}
+
+// FmtTimeFull returns the full time representation of 't' for 'zh'
+func (zh *zh) FmtTimeFull(t time.Time) string {
+
+ b := make([]byte, 0, 32)
+
+ tz, _ := t.Zone()
+
+ if btz, ok := zh.timezones[tz]; ok {
+ b = append(b, btz...)
+ } else {
+ b = append(b, tz...)
+ }
+
+ b = append(b, []byte{0x20}...)
+
+ if t.Hour() < 12 {
+ b = append(b, zh.periodsAbbreviated[0]...)
+ } else {
+ b = append(b, zh.periodsAbbreviated[1]...)
+ }
+
+ h := t.Hour()
+
+ if h > 12 {
+ h -= 12
+ }
+
+ b = strconv.AppendInt(b, int64(h), 10)
+ b = append(b, zh.timeSeparator...)
+
+ if t.Minute() < 10 {
+ b = append(b, '0')
+ }
+
+ b = strconv.AppendInt(b, int64(t.Minute()), 10)
+ b = append(b, zh.timeSeparator...)
+
+ if t.Second() < 10 {
+ b = append(b, '0')
+ }
+
+ b = strconv.AppendInt(b, int64(t.Second()), 10)
+
+ return string(b)
+}
diff --git a/go-playground/universal-translator/.gitignore b/go-playground/universal-translator/.gitignore
new file mode 100644
index 0000000..bc4e07f
--- /dev/null
+++ b/go-playground/universal-translator/.gitignore
@@ -0,0 +1,25 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+*.coverprofile
\ No newline at end of file
diff --git a/go-playground/universal-translator/.travis.yml b/go-playground/universal-translator/.travis.yml
new file mode 100644
index 0000000..39b8b92
--- /dev/null
+++ b/go-playground/universal-translator/.travis.yml
@@ -0,0 +1,27 @@
+language: go
+go:
+ - 1.13.4
+ - tip
+matrix:
+ allow_failures:
+ - go: tip
+
+notifications:
+ email:
+ recipients: dean.karn@gmail.com
+ on_success: change
+ on_failure: always
+
+before_install:
+ - go install github.com/mattn/goveralls
+
+# Only clone the most recent commit.
+git:
+ depth: 1
+
+script:
+ - go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
+
+after_success: |
+ [ $TRAVIS_GO_VERSION = 1.13.4 ] &&
+ goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
\ No newline at end of file
diff --git a/go-playground/universal-translator/LICENSE b/go-playground/universal-translator/LICENSE
new file mode 100644
index 0000000..8d8aba1
--- /dev/null
+++ b/go-playground/universal-translator/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Go Playground
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/go-playground/universal-translator/README.md b/go-playground/universal-translator/README.md
new file mode 100644
index 0000000..42f5f94
--- /dev/null
+++ b/go-playground/universal-translator/README.md
@@ -0,0 +1,89 @@
+## universal-translator
+![Project status](https://img.shields.io/badge/version-0.17.0-green.svg)
+[![Build Status](https://travis-ci.org/go-playground/universal-translator.svg?branch=master)](https://travis-ci.org/go-playground/universal-translator)
+[![Coverage Status](https://coveralls.io/repos/github/go-playground/universal-translator/badge.svg)](https://coveralls.io/github/go-playground/universal-translator)
+[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/universal-translator)](https://goreportcard.com/report/github.com/go-playground/universal-translator)
+[![GoDoc](https://godoc.org/github.com/go-playground/universal-translator?status.svg)](https://godoc.org/github.com/go-playground/universal-translator)
+![License](https://img.shields.io/dub/l/vibe-d.svg)
+[![Gitter](https://badges.gitter.im/go-playground/universal-translator.svg)](https://gitter.im/go-playground/universal-translator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+
+Universal Translator is an i18n Translator for Go/Golang using CLDR data + pluralization rules
+
+Why another i18n library?
+--------------------------
+Because none of the plural rules seem to be correct out there, including the previous implementation of this package,
+so I took it upon myself to create [locales](https://go-playground/locales) for everyone to use; this package
+is a thin wrapper around [locales](https://go-playground/locales) in order to store and translate text for
+use in your applications.
+
+Features
+--------
+- [x] Rules generated from the [CLDR](http://cldr.unicode.org/index/downloads) data, v30.0.3
+- [x] Contains Cardinal, Ordinal and Range Plural Rules
+- [x] Contains Month, Weekday and Timezone translations built in
+- [x] Contains Date & Time formatting functions
+- [x] Contains Number, Currency, Accounting and Percent formatting functions
+- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere )
+- [x] Support loading translations from files
+- [x] Exporting translations to file(s), mainly for getting them professionally translated
+- [ ] Code Generation for translation files -> Go code.. i.e. after it has been professionally translated
+- [ ] Tests for all languages, I need help with this, please see [here](https://go-playground/locales/issues/1)
+
+Installation
+-----------
+
+Use go get
+
+```shell
+go get github.com/go-playground/universal-translator
+```
+
+Usage & Documentation
+-------
+
+Please see https://godoc.org/github.com/go-playground/universal-translator for usage docs
+
+##### Examples:
+
+- [Basic](https://github.com/go-playground/universal-translator/tree/master/_examples/basic)
+- [Full - no files](https://github.com/go-playground/universal-translator/tree/master/_examples/full-no-files)
+- [Full - with files](https://github.com/go-playground/universal-translator/tree/master/_examples/full-with-files)
+
+File formatting
+--------------
+All types, Plain substitution, Cardinal, Ordinal and Range translations can all be contained withing the same file(s);
+they are only separated for easy viewing.
+
+##### Examples:
+
+- [Formats](https://github.com/go-playground/universal-translator/tree/master/_examples/file-formats)
+
+##### Basic Makeup
+NOTE: not all fields are needed for all translation types, see [examples](https://github.com/go-playground/universal-translator/tree/master/_examples/file-formats)
+```json
+{
+ "locale": "en",
+ "key": "days-left",
+ "trans": "You have {0} day left.",
+ "type": "Cardinal",
+ "rule": "One",
+ "override": false
+}
+```
+|Field|Description|
+|---|---|
+|locale|The locale for which the translation is for.|
+|key|The translation key that will be used to store and lookup each translation; normally it is a string or integer.|
+|trans|The actual translation text.|
+|type|The type of translation Cardinal, Ordinal, Range or "" for a plain substitution(not required to be defined if plain used)|
+|rule|The plural rule for which the translation is for eg. One, Two, Few, Many or Other.(not required to be defined if plain used)|
+|override|If you wish to override an existing translation that has already been registered, set this to 'true'. 99% of the time there is no need to define it.|
+
+Help With Tests
+---------------
+To anyone interesting in helping or contributing, I sure could use some help creating tests for each language.
+Please see issue [here](https://go-playground/locales/issues/1) for details.
+
+License
+------
+Distributed under MIT License, please see license file in code for more details.
diff --git a/go-playground/universal-translator/errors.go b/go-playground/universal-translator/errors.go
new file mode 100644
index 0000000..3b4649c
--- /dev/null
+++ b/go-playground/universal-translator/errors.go
@@ -0,0 +1,148 @@
+package ut
+
+import (
+ "errors"
+ "fmt"
+
+ "gin-valid/go-playground/locales"
+)
+
+var (
+ // ErrUnknowTranslation indicates the translation could not be found
+ ErrUnknowTranslation = errors.New("Unknown Translation")
+)
+
+var _ error = new(ErrConflictingTranslation)
+var _ error = new(ErrRangeTranslation)
+var _ error = new(ErrOrdinalTranslation)
+var _ error = new(ErrCardinalTranslation)
+var _ error = new(ErrMissingPluralTranslation)
+var _ error = new(ErrExistingTranslator)
+
+// ErrExistingTranslator is the error representing a conflicting translator
+type ErrExistingTranslator struct {
+ locale string
+}
+
+// Error returns ErrExistingTranslator's internal error text
+func (e *ErrExistingTranslator) Error() string {
+ return fmt.Sprintf("error: conflicting translator for locale '%s'", e.locale)
+}
+
+// ErrConflictingTranslation is the error representing a conflicting translation
+type ErrConflictingTranslation struct {
+ locale string
+ key interface{}
+ rule locales.PluralRule
+ text string
+}
+
+// Error returns ErrConflictingTranslation's internal error text
+func (e *ErrConflictingTranslation) Error() string {
+
+ if _, ok := e.key.(string); !ok {
+ return fmt.Sprintf("error: conflicting key '%#v' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale)
+ }
+
+ return fmt.Sprintf("error: conflicting key '%s' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale)
+}
+
+// ErrRangeTranslation is the error representing a range translation error
+type ErrRangeTranslation struct {
+ text string
+}
+
+// Error returns ErrRangeTranslation's internal error text
+func (e *ErrRangeTranslation) Error() string {
+ return e.text
+}
+
+// ErrOrdinalTranslation is the error representing an ordinal translation error
+type ErrOrdinalTranslation struct {
+ text string
+}
+
+// Error returns ErrOrdinalTranslation's internal error text
+func (e *ErrOrdinalTranslation) Error() string {
+ return e.text
+}
+
+// ErrCardinalTranslation is the error representing a cardinal translation error
+type ErrCardinalTranslation struct {
+ text string
+}
+
+// Error returns ErrCardinalTranslation's internal error text
+func (e *ErrCardinalTranslation) Error() string {
+ return e.text
+}
+
+// ErrMissingPluralTranslation is the error signifying a missing translation given
+// the locales plural rules.
+type ErrMissingPluralTranslation struct {
+ locale string
+ key interface{}
+ rule locales.PluralRule
+ translationType string
+}
+
+// Error returns ErrMissingPluralTranslation's internal error text
+func (e *ErrMissingPluralTranslation) Error() string {
+
+ if _, ok := e.key.(string); !ok {
+ return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%#v' and locale '%s'", e.translationType, e.rule, e.key, e.locale)
+ }
+
+ return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%s' and locale '%s'", e.translationType, e.rule, e.key, e.locale)
+}
+
+// ErrMissingBracket is the error representing a missing bracket in a translation
+// eg. This is a {0 <-- missing ending '}'
+type ErrMissingBracket struct {
+ locale string
+ key interface{}
+ text string
+}
+
+// Error returns ErrMissingBracket error message
+func (e *ErrMissingBracket) Error() string {
+ return fmt.Sprintf("error: missing bracket '{}', in translation. locale: '%s' key: '%v' text: '%s'", e.locale, e.key, e.text)
+}
+
+// ErrBadParamSyntax is the error representing a bad parameter definition in a translation
+// eg. This is a {must-be-int}
+type ErrBadParamSyntax struct {
+ locale string
+ param string
+ key interface{}
+ text string
+}
+
+// Error returns ErrBadParamSyntax error message
+func (e *ErrBadParamSyntax) Error() string {
+ return fmt.Sprintf("error: bad parameter syntax, missing parameter '%s' in translation. locale: '%s' key: '%v' text: '%s'", e.param, e.locale, e.key, e.text)
+}
+
+// import/export errors
+
+// ErrMissingLocale is the error representing an expected locale that could
+// not be found aka locale not registered with the UniversalTranslator Instance
+type ErrMissingLocale struct {
+ locale string
+}
+
+// Error returns ErrMissingLocale's internal error text
+func (e *ErrMissingLocale) Error() string {
+ return fmt.Sprintf("error: locale '%s' not registered.", e.locale)
+}
+
+// ErrBadPluralDefinition is the error representing an incorrect plural definition
+// usually found within translations defined within files during the import process.
+type ErrBadPluralDefinition struct {
+ tl translation
+}
+
+// Error returns ErrBadPluralDefinition's internal error text
+func (e *ErrBadPluralDefinition) Error() string {
+ return fmt.Sprintf("error: bad plural definition '%#v'", e.tl)
+}
diff --git a/go-playground/universal-translator/go.sum b/go-playground/universal-translator/go.sum
new file mode 100644
index 0000000..54b4c8a
--- /dev/null
+++ b/go-playground/universal-translator/go.sum
@@ -0,0 +1,4 @@
+go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/go-playground/universal-translator/import_export.go b/go-playground/universal-translator/import_export.go
new file mode 100644
index 0000000..839d012
--- /dev/null
+++ b/go-playground/universal-translator/import_export.go
@@ -0,0 +1,274 @@
+package ut
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "io"
+
+ "go-playground/locales"
+)
+
+type translation struct {
+ Locale string `json:"locale"`
+ Key interface{} `json:"key"` // either string or integer
+ Translation string `json:"trans"`
+ PluralType string `json:"type,omitempty"`
+ PluralRule string `json:"rule,omitempty"`
+ OverrideExisting bool `json:"override,omitempty"`
+}
+
+const (
+ cardinalType = "Cardinal"
+ ordinalType = "Ordinal"
+ rangeType = "Range"
+)
+
+// ImportExportFormat is the format of the file import or export
+type ImportExportFormat uint8
+
+// supported Export Formats
+const (
+ FormatJSON ImportExportFormat = iota
+)
+
+// Export writes the translations out to a file on disk.
+//
+// NOTE: this currently only works with string or int translations keys.
+func (t *UniversalTranslator) Export(format ImportExportFormat, dirname string) error {
+
+ _, err := os.Stat(dirname)
+ fmt.Println(dirname, err, os.IsNotExist(err))
+ if err != nil {
+
+ if !os.IsNotExist(err) {
+ return err
+ }
+
+ if err = os.MkdirAll(dirname, 0744); err != nil {
+ return err
+ }
+ }
+
+ // build up translations
+ var trans []translation
+ var b []byte
+ var ext string
+
+ for _, locale := range t.translators {
+
+ for k, v := range locale.(*translator).translations {
+ trans = append(trans, translation{
+ Locale: locale.Locale(),
+ Key: k,
+ Translation: v.text,
+ })
+ }
+
+ for k, pluralTrans := range locale.(*translator).cardinalTanslations {
+
+ for i, plural := range pluralTrans {
+
+ // leave enough for all plural rules
+ // but not all are set for all languages.
+ if plural == nil {
+ continue
+ }
+
+ trans = append(trans, translation{
+ Locale: locale.Locale(),
+ Key: k.(string),
+ Translation: plural.text,
+ PluralType: cardinalType,
+ PluralRule: locales.PluralRule(i).String(),
+ })
+ }
+ }
+
+ for k, pluralTrans := range locale.(*translator).ordinalTanslations {
+
+ for i, plural := range pluralTrans {
+
+ // leave enough for all plural rules
+ // but not all are set for all languages.
+ if plural == nil {
+ continue
+ }
+
+ trans = append(trans, translation{
+ Locale: locale.Locale(),
+ Key: k.(string),
+ Translation: plural.text,
+ PluralType: ordinalType,
+ PluralRule: locales.PluralRule(i).String(),
+ })
+ }
+ }
+
+ for k, pluralTrans := range locale.(*translator).rangeTanslations {
+
+ for i, plural := range pluralTrans {
+
+ // leave enough for all plural rules
+ // but not all are set for all languages.
+ if plural == nil {
+ continue
+ }
+
+ trans = append(trans, translation{
+ Locale: locale.Locale(),
+ Key: k.(string),
+ Translation: plural.text,
+ PluralType: rangeType,
+ PluralRule: locales.PluralRule(i).String(),
+ })
+ }
+ }
+
+ switch format {
+ case FormatJSON:
+ b, err = json.MarshalIndent(trans, "", " ")
+ ext = ".json"
+ }
+
+ if err != nil {
+ return err
+ }
+
+ err = ioutil.WriteFile(filepath.Join(dirname, fmt.Sprintf("%s%s", locale.Locale(), ext)), b, 0644)
+ if err != nil {
+ return err
+ }
+
+ trans = trans[0:0]
+ }
+
+ return nil
+}
+
+// Import reads the translations out of a file or directory on disk.
+//
+// NOTE: this currently only works with string or int translations keys.
+func (t *UniversalTranslator) Import(format ImportExportFormat, dirnameOrFilename string) error {
+
+ fi, err := os.Stat(dirnameOrFilename)
+ if err != nil {
+ return err
+ }
+
+ processFn := func(filename string) error {
+
+ f, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ return t.ImportByReader(format, f)
+ }
+
+ if !fi.IsDir() {
+ return processFn(dirnameOrFilename)
+ }
+
+ // recursively go through directory
+ walker := func(path string, info os.FileInfo, err error) error {
+
+ if info.IsDir() {
+ return nil
+ }
+
+ switch format {
+ case FormatJSON:
+ // skip non JSON files
+ if filepath.Ext(info.Name()) != ".json" {
+ return nil
+ }
+ }
+
+ return processFn(path)
+ }
+
+ return filepath.Walk(dirnameOrFilename, walker)
+}
+
+// ImportByReader imports the the translations found within the contents read from the supplied reader.
+//
+// NOTE: generally used when assets have been embedded into the binary and are already in memory.
+func (t *UniversalTranslator) ImportByReader(format ImportExportFormat, reader io.Reader) error {
+
+ b, err := ioutil.ReadAll(reader)
+ if err != nil {
+ return err
+ }
+
+ var trans []translation
+
+ switch format {
+ case FormatJSON:
+ err = json.Unmarshal(b, &trans)
+ }
+
+ if err != nil {
+ return err
+ }
+
+ for _, tl := range trans {
+
+ locale, found := t.FindTranslator(tl.Locale)
+ if !found {
+ return &ErrMissingLocale{locale: tl.Locale}
+ }
+
+ pr := stringToPR(tl.PluralRule)
+
+ if pr == locales.PluralRuleUnknown {
+
+ err = locale.Add(tl.Key, tl.Translation, tl.OverrideExisting)
+ if err != nil {
+ return err
+ }
+
+ continue
+ }
+
+ switch tl.PluralType {
+ case cardinalType:
+ err = locale.AddCardinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
+ case ordinalType:
+ err = locale.AddOrdinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
+ case rangeType:
+ err = locale.AddRange(tl.Key, tl.Translation, pr, tl.OverrideExisting)
+ default:
+ return &ErrBadPluralDefinition{tl: tl}
+ }
+
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func stringToPR(s string) locales.PluralRule {
+
+ switch s {
+ case "One":
+ return locales.PluralRuleOne
+ case "Two":
+ return locales.PluralRuleTwo
+ case "Few":
+ return locales.PluralRuleFew
+ case "Many":
+ return locales.PluralRuleMany
+ case "Other":
+ return locales.PluralRuleOther
+ default:
+ return locales.PluralRuleUnknown
+ }
+
+}
diff --git a/go-playground/universal-translator/logo.png b/go-playground/universal-translator/logo.png
new file mode 100644
index 0000000..a37aa8c
Binary files /dev/null and b/go-playground/universal-translator/logo.png differ
diff --git a/go-playground/universal-translator/translator.go b/go-playground/universal-translator/translator.go
new file mode 100644
index 0000000..a63ec1f
--- /dev/null
+++ b/go-playground/universal-translator/translator.go
@@ -0,0 +1,420 @@
+package ut
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "gin-valid/go-playground/locales"
+)
+
+const (
+ paramZero = "{0}"
+ paramOne = "{1}"
+ unknownTranslation = ""
+)
+
+// Translator is universal translators
+// translator instance which is a thin wrapper
+// around locales.Translator instance providing
+// some extra functionality
+type Translator interface {
+ locales.Translator
+
+ // adds a normal translation for a particular language/locale
+ // {#} is the only replacement type accepted and are ad infinitum
+ // eg. one: '{0} day left' other: '{0} days left'
+ Add(key interface{}, text string, override bool) error
+
+ // adds a cardinal plural translation for a particular language/locale
+ // {0} is the only replacement type accepted and only one variable is accepted as
+ // multiple cannot be used for a plural rule determination, unless it is a range;
+ // see AddRange below.
+ // eg. in locale 'en' one: '{0} day left' other: '{0} days left'
+ AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error
+
+ // adds an ordinal plural translation for a particular language/locale
+ // {0} is the only replacement type accepted and only one variable is accepted as
+ // multiple cannot be used for a plural rule determination, unless it is a range;
+ // see AddRange below.
+ // eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring'
+ // - 1st, 2nd, 3rd...
+ AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error
+
+ // adds a range plural translation for a particular language/locale
+ // {0} and {1} are the only replacement types accepted and only these are accepted.
+ // eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left'
+ AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error
+
+ // creates the translation for the locale given the 'key' and params passed in
+ T(key interface{}, params ...string) (string, error)
+
+ // creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments
+ // and param passed in
+ C(key interface{}, num float64, digits uint64, param string) (string, error)
+
+ // creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments
+ // and param passed in
+ O(key interface{}, num float64, digits uint64, param string) (string, error)
+
+ // creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and
+ // 'digit2' arguments and 'param1' and 'param2' passed in
+ R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error)
+
+ // VerifyTranslations checks to ensures that no plural rules have been
+ // missed within the translations.
+ VerifyTranslations() error
+}
+
+var _ Translator = new(translator)
+var _ locales.Translator = new(translator)
+
+type translator struct {
+ locales.Translator
+ translations map[interface{}]*transText
+ cardinalTanslations map[interface{}][]*transText // array index is mapped to locales.PluralRule index + the locales.PluralRuleUnknown
+ ordinalTanslations map[interface{}][]*transText
+ rangeTanslations map[interface{}][]*transText
+}
+
+type transText struct {
+ text string
+ indexes []int //记录每个占位符的始末位置,也就是 { 和 } 的位置
+}
+
+func newTranslator(trans locales.Translator) Translator {
+ return &translator{
+ Translator: trans,
+ translations: make(map[interface{}]*transText), // translation text broken up by byte index
+ cardinalTanslations: make(map[interface{}][]*transText),
+ ordinalTanslations: make(map[interface{}][]*transText),
+ rangeTanslations: make(map[interface{}][]*transText),
+ }
+}
+
+// Add adds a normal translation for a particular language/locale
+// {#} is the only replacement type accepted and are ad infinitum
+// eg. one: '{0} day left' other: '{0} days left'
+func (t *translator) Add(key interface{}, text string, override bool) error {
+
+ if _, ok := t.translations[key]; ok && !override {
+ return &ErrConflictingTranslation{locale: t.Locale(), key: key, text: text}
+ }
+
+ lb := strings.Count(text, "{")
+ rb := strings.Count(text, "}")
+
+ if lb != rb {
+ return &ErrMissingBracket{locale: t.Locale(), key: key, text: text}
+ }
+
+ trans := &transText{
+ text: text,
+ }
+
+ var idx int
+
+ for i := 0; i < lb; i++ {
+ s := "{" + strconv.Itoa(i) + "}"
+ idx = strings.Index(text, s)
+ if idx == -1 {
+ return &ErrBadParamSyntax{locale: t.Locale(), param: s, key: key, text: text}
+ }
+
+ trans.indexes = append(trans.indexes, idx)
+ trans.indexes = append(trans.indexes, idx+len(s))
+ }
+
+ t.translations[key] = trans
+
+ return nil
+}
+
+// AddCardinal adds a cardinal plural translation for a particular language/locale
+// {0} is the only replacement type accepted and only one variable is accepted as
+// multiple cannot be used for a plural rule determination, unless it is a range;
+// see AddRange below.
+// eg. in locale 'en' one: '{0} day left' other: '{0} days left'
+func (t *translator) AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error {
+
+ var verified bool
+
+ // verify plural rule exists for locale
+ for _, pr := range t.PluralsCardinal() {
+ if pr == rule {
+ verified = true
+ break
+ }
+ }
+
+ if !verified {
+ return &ErrCardinalTranslation{text: fmt.Sprintf("error: cardinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
+ }
+
+ tarr, ok := t.cardinalTanslations[key]
+ if ok {
+ // verify not adding a conflicting record
+ if len(tarr) > 0 && tarr[rule] != nil && !override {
+ return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
+ }
+
+ } else {
+ tarr = make([]*transText, 7, 7)
+ t.cardinalTanslations[key] = tarr
+ }
+
+ trans := &transText{
+ text: text,
+ indexes: make([]int, 2, 2),
+ }
+
+ tarr[rule] = trans
+
+ idx := strings.Index(text, paramZero)
+ if idx == -1 {
+ tarr[rule] = nil
+ return &ErrCardinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
+ }
+
+ trans.indexes[0] = idx
+ trans.indexes[1] = idx + len(paramZero)
+
+ return nil
+}
+
+// AddOrdinal adds an ordinal plural translation for a particular language/locale
+// {0} is the only replacement type accepted and only one variable is accepted as
+// multiple cannot be used for a plural rule determination, unless it is a range;
+// see AddRange below.
+// eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring' - 1st, 2nd, 3rd...
+func (t *translator) AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error {
+
+ var verified bool
+
+ // verify plural rule exists for locale
+ for _, pr := range t.PluralsOrdinal() {
+ if pr == rule {
+ verified = true
+ break
+ }
+ }
+
+ if !verified {
+ return &ErrOrdinalTranslation{text: fmt.Sprintf("error: ordinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
+ }
+
+ tarr, ok := t.ordinalTanslations[key]
+ if ok {
+ // verify not adding a conflicting record
+ if len(tarr) > 0 && tarr[rule] != nil && !override {
+ return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
+ }
+
+ } else {
+ tarr = make([]*transText, 7, 7)
+ t.ordinalTanslations[key] = tarr
+ }
+
+ trans := &transText{
+ text: text,
+ indexes: make([]int, 2, 2),
+ }
+
+ tarr[rule] = trans
+
+ idx := strings.Index(text, paramZero)
+ if idx == -1 {
+ tarr[rule] = nil
+ return &ErrOrdinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
+ }
+
+ trans.indexes[0] = idx
+ trans.indexes[1] = idx + len(paramZero)
+
+ return nil
+}
+
+// AddRange adds a range plural translation for a particular language/locale
+// {0} and {1} are the only replacement types accepted and only these are accepted.
+// eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left'
+func (t *translator) AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error {
+
+ var verified bool
+
+ // verify plural rule exists for locale
+ for _, pr := range t.PluralsRange() {
+ if pr == rule {
+ verified = true
+ break
+ }
+ }
+
+ if !verified {
+ return &ErrRangeTranslation{text: fmt.Sprintf("error: range plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
+ }
+
+ tarr, ok := t.rangeTanslations[key]
+ if ok {
+ // verify not adding a conflicting record
+ if len(tarr) > 0 && tarr[rule] != nil && !override {
+ return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
+ }
+
+ } else {
+ tarr = make([]*transText, 7, 7)
+ t.rangeTanslations[key] = tarr
+ }
+
+ trans := &transText{
+ text: text,
+ indexes: make([]int, 4, 4),
+ }
+
+ tarr[rule] = trans
+
+ idx := strings.Index(text, paramZero)
+ if idx == -1 {
+ tarr[rule] = nil
+ return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, are you sure you're adding a Range Translation? locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
+ }
+
+ trans.indexes[0] = idx
+ trans.indexes[1] = idx + len(paramZero)
+
+ idx = strings.Index(text, paramOne)
+ if idx == -1 {
+ tarr[rule] = nil
+ return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, a Range Translation requires two parameters. locale: '%s' key: '%v' text: '%s'", paramOne, t.Locale(), key, text)}
+ }
+
+ trans.indexes[2] = idx
+ trans.indexes[3] = idx + len(paramOne)
+
+ return nil
+}
+
+// T creates the translation for the locale given the 'key' and params passed in
+func (t *translator) T(key interface{}, params ...string) (string, error) {
+
+ trans, ok := t.translations[key]
+ if !ok {
+ return unknownTranslation, ErrUnknowTranslation
+ }
+
+ b := make([]byte, 0, 64)
+
+ var start, end, count int
+
+ for i := 0; i < len(trans.indexes); i++ {
+ end = trans.indexes[i]
+ b = append(b, trans.text[start:end]...)
+ b = append(b, params[count]...)
+ i++
+ start = trans.indexes[i]
+ count++
+ }
+
+ b = append(b, trans.text[start:]...)
+
+ return string(b), nil
+}
+
+// C creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in
+func (t *translator) C(key interface{}, num float64, digits uint64, param string) (string, error) {
+
+ tarr, ok := t.cardinalTanslations[key]
+ if !ok {
+ return unknownTranslation, ErrUnknowTranslation
+ }
+
+ rule := t.CardinalPluralRule(num, digits)
+
+ trans := tarr[rule]
+
+ b := make([]byte, 0, 64)
+ b = append(b, trans.text[:trans.indexes[0]]...)
+ b = append(b, param...)
+ b = append(b, trans.text[trans.indexes[1]:]...)
+
+ return string(b), nil
+}
+
+// O creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in
+func (t *translator) O(key interface{}, num float64, digits uint64, param string) (string, error) {
+
+ tarr, ok := t.ordinalTanslations[key]
+ if !ok {
+ return unknownTranslation, ErrUnknowTranslation
+ }
+
+ rule := t.OrdinalPluralRule(num, digits)
+
+ trans := tarr[rule]
+
+ b := make([]byte, 0, 64)
+ b = append(b, trans.text[:trans.indexes[0]]...)
+ b = append(b, param...)
+ b = append(b, trans.text[trans.indexes[1]:]...)
+
+ return string(b), nil
+}
+
+// R creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and 'digit2' arguments
+// and 'param1' and 'param2' passed in
+func (t *translator) R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error) {
+
+ tarr, ok := t.rangeTanslations[key]
+ if !ok {
+ return unknownTranslation, ErrUnknowTranslation
+ }
+
+ rule := t.RangePluralRule(num1, digits1, num2, digits2)
+
+ trans := tarr[rule]
+
+ b := make([]byte, 0, 64)
+ b = append(b, trans.text[:trans.indexes[0]]...)
+ b = append(b, param1...)
+ b = append(b, trans.text[trans.indexes[1]:trans.indexes[2]]...)
+ b = append(b, param2...)
+ b = append(b, trans.text[trans.indexes[3]:]...)
+
+ return string(b), nil
+}
+
+// VerifyTranslations checks to ensures that no plural rules have been
+// missed within the translations.
+func (t *translator) VerifyTranslations() error {
+
+ for k, v := range t.cardinalTanslations {
+
+ for _, rule := range t.PluralsCardinal() {
+
+ if v[rule] == nil {
+ return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "plural", rule: rule, key: k}
+ }
+ }
+ }
+
+ for k, v := range t.ordinalTanslations {
+
+ for _, rule := range t.PluralsOrdinal() {
+
+ if v[rule] == nil {
+ return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "ordinal", rule: rule, key: k}
+ }
+ }
+ }
+
+ for k, v := range t.rangeTanslations {
+
+ for _, rule := range t.PluralsRange() {
+
+ if v[rule] == nil {
+ return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "range", rule: rule, key: k}
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/go-playground/universal-translator/universal_translator.go b/go-playground/universal-translator/universal_translator.go
new file mode 100644
index 0000000..32c619e
--- /dev/null
+++ b/go-playground/universal-translator/universal_translator.go
@@ -0,0 +1,113 @@
+package ut
+
+import (
+ "strings"
+
+ "go-playground/locales"
+)
+
+// UniversalTranslator holds all locale & translation data
+type UniversalTranslator struct {
+ translators map[string]Translator
+ fallback Translator
+}
+
+// New returns a new UniversalTranslator instance set with
+// the fallback locale and locales it should support
+func New(fallback locales.Translator, supportedLocales ...locales.Translator) *UniversalTranslator {
+
+ t := &UniversalTranslator{
+ translators: make(map[string]Translator),
+ }
+
+ for _, v := range supportedLocales {
+
+ trans := newTranslator(v)
+ t.translators[strings.ToLower(trans.Locale())] = trans
+
+ if fallback.Locale() == v.Locale() {
+ t.fallback = trans
+ }
+ }
+
+ if t.fallback == nil && fallback != nil {
+ t.fallback = newTranslator(fallback)
+ }
+
+ return t
+}
+
+// FindTranslator trys to find a Translator based on an array of locales
+// and returns the first one it can find, otherwise returns the
+// fallback translator.
+func (t *UniversalTranslator) FindTranslator(locales ...string) (trans Translator, found bool) {
+
+ for _, locale := range locales {
+
+ if trans, found = t.translators[strings.ToLower(locale)]; found {
+ return
+ }
+ }
+
+ return t.fallback, false
+}
+
+// GetTranslator returns the specified translator for the given locale,
+// or fallback if not found
+func (t *UniversalTranslator) GetTranslator(locale string) (trans Translator, found bool) {
+
+ if trans, found = t.translators[strings.ToLower(locale)]; found {
+ return
+ }
+
+ return t.fallback, false
+}
+
+// GetFallback returns the fallback locale
+func (t *UniversalTranslator) GetFallback() Translator {
+ return t.fallback
+}
+
+// AddTranslator adds the supplied translator, if it already exists the override param
+// will be checked and if false an error will be returned, otherwise the translator will be
+// overridden; if the fallback matches the supplied translator it will be overridden as well
+// NOTE: this is normally only used when translator is embedded within a library
+func (t *UniversalTranslator) AddTranslator(translator locales.Translator, override bool) error {
+
+ lc := strings.ToLower(translator.Locale())
+ _, ok := t.translators[lc]
+ if ok && !override {
+ return &ErrExistingTranslator{locale: translator.Locale()}
+ }
+
+ trans := newTranslator(translator)
+
+ if t.fallback.Locale() == translator.Locale() {
+
+ // because it's optional to have a fallback, I don't impose that limitation
+ // don't know why you wouldn't but...
+ if !override {
+ return &ErrExistingTranslator{locale: translator.Locale()}
+ }
+
+ t.fallback = trans
+ }
+
+ t.translators[lc] = trans
+
+ return nil
+}
+
+// VerifyTranslations runs through all locales and identifies any issues
+// eg. missing plural rules for a locale
+func (t *UniversalTranslator) VerifyTranslations() (err error) {
+
+ for _, trans := range t.translators {
+ err = trans.VerifyTranslations()
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
diff --git a/go-playground/validator/v10/.github/CONTRIBUTING.md b/go-playground/validator/v10/.github/CONTRIBUTING.md
new file mode 100644
index 0000000..6d3811c
--- /dev/null
+++ b/go-playground/validator/v10/.github/CONTRIBUTING.md
@@ -0,0 +1,9 @@
+# Contribution Guidelines
+
+## Quality Standard
+
+To ensure the continued stability of this package, tests are required to be written or already exist in order for a pull request to be merged.
+
+## Reporting issues
+
+Please open an issue or join the gitter chat [![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) for any issues, questions or possible enhancements to the package.
diff --git a/go-playground/validator/v10/.github/ISSUE_TEMPLATE.md b/go-playground/validator/v10/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..8afb259
--- /dev/null
+++ b/go-playground/validator/v10/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,13 @@
+### Package version eg. v8, v9:
+
+
+
+### Issue, Question or Enhancement:
+
+
+
+### Code sample, to showcase or reproduce:
+
+```go
+
+```
diff --git a/go-playground/validator/v10/.github/PULL_REQUEST_TEMPLATE.md b/go-playground/validator/v10/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..7bebde3
--- /dev/null
+++ b/go-playground/validator/v10/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,13 @@
+Fixes Or Enhances # .
+
+**Make sure that you've checked the boxes below before you submit PR:**
+- [ ] Tests exist or have been written that cover this particular change.
+
+Change Details:
+
+-
+-
+-
+
+
+@go-playground/admins
\ No newline at end of file
diff --git a/go-playground/validator/v10/.gitignore b/go-playground/validator/v10/.gitignore
new file mode 100644
index 0000000..6e43fac
--- /dev/null
+++ b/go-playground/validator/v10/.gitignore
@@ -0,0 +1,30 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+bin
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+*.test
+*.out
+*.txt
+cover.html
+README.html
diff --git a/go-playground/validator/v10/.travis.yml b/go-playground/validator/v10/.travis.yml
new file mode 100644
index 0000000..85a7be3
--- /dev/null
+++ b/go-playground/validator/v10/.travis.yml
@@ -0,0 +1,29 @@
+language: go
+go:
+ - 1.15.2
+ - tip
+matrix:
+ allow_failures:
+ - go: tip
+
+notifications:
+ email:
+ recipients: dean.karn@gmail.com
+ on_success: change
+ on_failure: always
+
+before_install:
+ - go install github.com/mattn/goveralls
+ - mkdir -p $GOPATH/src/gopkg.in
+ - ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/validator.v9
+
+# Only clone the most recent commit.
+git:
+ depth: 1
+
+script:
+ - go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
+
+after_success: |
+ [ $TRAVIS_GO_VERSION = 1.15.2 ] &&
+ goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
diff --git a/go-playground/validator/v10/LICENSE b/go-playground/validator/v10/LICENSE
new file mode 100644
index 0000000..6a2ae9a
--- /dev/null
+++ b/go-playground/validator/v10/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Dean Karn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/go-playground/validator/v10/Makefile b/go-playground/validator/v10/Makefile
new file mode 100644
index 0000000..19c91ed
--- /dev/null
+++ b/go-playground/validator/v10/Makefile
@@ -0,0 +1,18 @@
+GOCMD=GO111MODULE=on go
+
+linters-install:
+ @golangci-lint --version >/dev/null 2>&1 || { \
+ echo "installing linting tools..."; \
+ curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.21.0; \
+ }
+
+lint: linters-install
+ $(PWD)/bin/golangci-lint run
+
+test:
+ $(GOCMD) test -cover -race ./...
+
+bench:
+ $(GOCMD) test -bench=. -benchmem ./...
+
+.PHONY: test lint linters-install
\ No newline at end of file
diff --git a/go-playground/validator/v10/README.md b/go-playground/validator/v10/README.md
new file mode 100644
index 0000000..de72348
--- /dev/null
+++ b/go-playground/validator/v10/README.md
@@ -0,0 +1,299 @@
+Package validator
+================
+[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+![Project status](https://img.shields.io/badge/version-10.4.1-green.svg)
+[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
+[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
+[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
+[![GoDoc](https://godoc.org/github.com/go-playground/validator?status.svg)](https://pkg.go.dev/gin-valid/go-playground/validator/v10)
+![License](https://img.shields.io/dub/l/vibe-d.svg)
+
+Package validator implements value validations for structs and individual fields based on tags.
+
+It has the following **unique** features:
+
+- Cross Field and Cross Struct validations by using validation tags or custom validators.
+- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
+- Ability to dive into both map keys and values for validation
+- Handles type interface by determining it's underlying type prior to validation.
+- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29)
+- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs
+- Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError
+- Customizable i18n aware error messages.
+- Default validator for the [gin](https://github.com/gin-gonic/gin) web framework; upgrading from v8 to v9 in gin see [here](https://github.com/go-playground/validator/tree/master/_examples/gin-upgrading-overriding)
+
+Installation
+------------
+
+Use go get.
+
+ go get gin-valid/go-playground/validator/v10
+
+Then import the validator package into your own code.
+
+ import "gin-valid/go-playground/validator/v10"
+
+Error Return Value
+-------
+
+Validation functions return type error
+
+They return type error to avoid the issue discussed in the following, where err is always != nil:
+
+* http://stackoverflow.com/a/29138676/3158232
+* https://github.com/go-playground/validator/issues/134
+
+Validator only InvalidValidationError for bad validation input, nil or ValidationErrors as type error; so, in your code all you need to do is check if the error returned is not nil, and if it's not check if error is InvalidValidationError ( if necessary, most of the time it isn't ) type cast it to type ValidationErrors like so:
+
+```go
+err := validate.Struct(mystruct)
+validationErrors := err.(validator.ValidationErrors)
+ ```
+
+Usage and documentation
+------
+
+Please see https://godoc.org/github.com/go-playground/validator for detailed usage docs.
+
+##### Examples:
+
+- [Simple](https://github.com/go-playground/validator/blob/master/_examples/simple/main.go)
+- [Custom Field Types](https://github.com/go-playground/validator/blob/master/_examples/custom/main.go)
+- [Struct Level](https://github.com/go-playground/validator/blob/master/_examples/struct-level/main.go)
+- [Translations & Custom Errors](https://github.com/go-playground/validator/blob/master/_examples/translations/main.go)
+- [Gin upgrade and/or override validator](https://github.com/go-playground/validator/tree/v9/_examples/gin-upgrading-overriding)
+- [wash - an example application putting it all together](https://github.com/bluesuncorp/wash)
+
+Baked-in Validations
+------
+
+### Fields:
+
+| Tag | Description |
+| - | - |
+| eqcsfield | Field Equals Another Field (relative)|
+| eqfield | Field Equals Another Field |
+| fieldcontains | NOT DOCUMENTED IN doc.go |
+| fieldexcludes | NOT DOCUMENTED IN doc.go |
+| gtcsfield | Field Greater Than Another Relative Field |
+| gtecsfield | Field Greater Than or Equal To Another Relative Field |
+| gtefield | Field Greater Than or Equal To Another Field |
+| gtfield | Field Greater Than Another Field |
+| ltcsfield | Less Than Another Relative Field |
+| ltecsfield | Less Than or Equal To Another Relative Field |
+| ltefield | Less Than or Equal To Another Field |
+| ltfield | Less Than Another Field |
+| necsfield | Field Does Not Equal Another Field (relative) |
+| nefield | Field Does Not Equal Another Field |
+
+### Network:
+
+| Tag | Description |
+| - | - |
+| cidr | Classless Inter-Domain Routing CIDR |
+| cidrv4 | Classless Inter-Domain Routing CIDRv4 |
+| cidrv6 | Classless Inter-Domain Routing CIDRv6 |
+| datauri | Data URL |
+| fqdn | Full Qualified Domain Name (FQDN) |
+| hostname | Hostname RFC 952 |
+| hostname_port | HostPort |
+| hostname_rfc1123 | Hostname RFC 1123 |
+| ip | Internet Protocol Address IP |
+| ip4_addr | Internet Protocol Address IPv4 |
+| ip6_addr |Internet Protocol Address IPv6 |
+| ip_addr | Internet Protocol Address IP |
+| ipv4 | Internet Protocol Address IPv4 |
+| ipv6 | Internet Protocol Address IPv6 |
+| mac | Media Access Control Address MAC |
+| tcp4_addr | Transmission Control Protocol Address TCPv4 |
+| tcp6_addr | Transmission Control Protocol Address TCPv6 |
+| tcp_addr | Transmission Control Protocol Address TCP |
+| udp4_addr | User Datagram Protocol Address UDPv4 |
+| udp6_addr | User Datagram Protocol Address UDPv6 |
+| udp_addr | User Datagram Protocol Address UDP |
+| unix_addr | Unix domain socket end point Address |
+| uri | URI String |
+| url | URL String |
+| url_encoded | URL Encoded |
+| urn_rfc2141 | Urn RFC 2141 String |
+
+### Strings:
+
+| Tag | Description |
+| - | - |
+| alpha | Alpha Only |
+| alphanum | Alphanumeric |
+| alphanumunicode | Alphanumeric Unicode |
+| alphaunicode | Alpha Unicode |
+| ascii | ASCII |
+| contains | Contains |
+| containsany | Contains Any |
+| containsrune | Contains Rune |
+| endswith | Ends With |
+| lowercase | Lowercase |
+| multibyte | Multi-Byte Characters |
+| number | NOT DOCUMENTED IN doc.go |
+| numeric | Numeric |
+| printascii | Printable ASCII |
+| startswith | Starts With |
+| uppercase | Uppercase |
+
+### Format:
+| Tag | Description |
+| - | - |
+| base64 | Base64 String |
+| base64url | Base64URL String |
+| btc_addr | Bitcoin Address |
+| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
+| datetime | Datetime |
+| e164 | e164 formatted phone number |
+| email | E-mail String
+| eth_addr | Ethereum Address |
+| hexadecimal | Hexadecimal String |
+| hexcolor | Hexcolor String |
+| hsl | HSL String |
+| hsla | HSLA String |
+| html | HTML Tags |
+| html_encoded | HTML Encoded |
+| isbn | International Standard Book Number |
+| isbn10 | International Standard Book Number 10 |
+| isbn13 | International Standard Book Number 13 |
+| json | JSON |
+| latitude | Latitude |
+| longitude | Longitude |
+| rgb | RGB String |
+| rgba | RGBA String |
+| ssn | Social Security Number SSN |
+| uuid | Universally Unique Identifier UUID |
+| uuid3 | Universally Unique Identifier UUID v3 |
+| uuid3_rfc4122 | Universally Unique Identifier UUID v3 RFC4122 |
+| uuid4 | Universally Unique Identifier UUID v4 |
+| uuid4_rfc4122 | Universally Unique Identifier UUID v4 RFC4122 |
+| uuid5 | Universally Unique Identifier UUID v5 |
+| uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 |
+| uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 |
+
+### Comparisons:
+| Tag | Description |
+| - | - |
+| eq | Equals |
+| gt | Greater than|
+| gte |Greater than or equal |
+| lt | Less Than |
+| lte | Less Than or Equal |
+| ne | Not Equal |
+
+### Other:
+| Tag | Description |
+| - | - |
+| dir | Directory |
+| endswith | Ends With |
+| excludes | Excludes |
+| excludesall | Excludes All |
+| excludesrune | Excludes Rune |
+| file | File path |
+| isdefault | Is Default |
+| len | Length |
+| max | Maximum |
+| min | Minimum |
+| oneof | One Of |
+| required | Required |
+| required_if | Required If |
+| required_unless | Required Unless |
+| required_with | Required With |
+| required_with_all | Required With All |
+| required_without | Required Without |
+| required_without_all | Required Without All |
+| excluded_with | Excluded With |
+| excluded_with_all | Excluded With All |
+| excluded_without | Excluded Without |
+| excluded_without_all | Excluded Without All |
+| unique | Unique |
+
+Benchmarks
+------
+###### Run on MacBook Pro (15-inch, 2017) go version go1.10.2 darwin/amd64
+```go
+goos: darwin
+goarch: amd64
+pkg: github.com/go-playground/validator
+BenchmarkFieldSuccess-8 20000000 83.6 ns/op 0 B/op 0 allocs/op
+BenchmarkFieldSuccessParallel-8 50000000 26.8 ns/op 0 B/op 0 allocs/op
+BenchmarkFieldFailure-8 5000000 291 ns/op 208 B/op 4 allocs/op
+BenchmarkFieldFailureParallel-8 20000000 107 ns/op 208 B/op 4 allocs/op
+BenchmarkFieldArrayDiveSuccess-8 2000000 623 ns/op 201 B/op 11 allocs/op
+BenchmarkFieldArrayDiveSuccessParallel-8 10000000 237 ns/op 201 B/op 11 allocs/op
+BenchmarkFieldArrayDiveFailure-8 2000000 859 ns/op 412 B/op 16 allocs/op
+BenchmarkFieldArrayDiveFailureParallel-8 5000000 335 ns/op 413 B/op 16 allocs/op
+BenchmarkFieldMapDiveSuccess-8 1000000 1292 ns/op 432 B/op 18 allocs/op
+BenchmarkFieldMapDiveSuccessParallel-8 3000000 467 ns/op 432 B/op 18 allocs/op
+BenchmarkFieldMapDiveFailure-8 1000000 1082 ns/op 512 B/op 16 allocs/op
+BenchmarkFieldMapDiveFailureParallel-8 5000000 425 ns/op 512 B/op 16 allocs/op
+BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1539 ns/op 480 B/op 21 allocs/op
+BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 613 ns/op 480 B/op 21 allocs/op
+BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1413 ns/op 721 B/op 21 allocs/op
+BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 575 ns/op 721 B/op 21 allocs/op
+BenchmarkFieldCustomTypeSuccess-8 10000000 216 ns/op 32 B/op 2 allocs/op
+BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.2 ns/op 32 B/op 2 allocs/op
+BenchmarkFieldCustomTypeFailure-8 5000000 274 ns/op 208 B/op 4 allocs/op
+BenchmarkFieldCustomTypeFailureParallel-8 20000000 116 ns/op 208 B/op 4 allocs/op
+BenchmarkFieldOrTagSuccess-8 2000000 740 ns/op 16 B/op 1 allocs/op
+BenchmarkFieldOrTagSuccessParallel-8 3000000 474 ns/op 16 B/op 1 allocs/op
+BenchmarkFieldOrTagFailure-8 3000000 471 ns/op 224 B/op 5 allocs/op
+BenchmarkFieldOrTagFailureParallel-8 3000000 414 ns/op 224 B/op 5 allocs/op
+BenchmarkStructLevelValidationSuccess-8 10000000 213 ns/op 32 B/op 2 allocs/op
+BenchmarkStructLevelValidationSuccessParallel-8 20000000 91.8 ns/op 32 B/op 2 allocs/op
+BenchmarkStructLevelValidationFailure-8 3000000 473 ns/op 304 B/op 8 allocs/op
+BenchmarkStructLevelValidationFailureParallel-8 10000000 234 ns/op 304 B/op 8 allocs/op
+BenchmarkStructSimpleCustomTypeSuccess-8 5000000 385 ns/op 32 B/op 2 allocs/op
+BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 161 ns/op 32 B/op 2 allocs/op
+BenchmarkStructSimpleCustomTypeFailure-8 2000000 640 ns/op 424 B/op 9 allocs/op
+BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 318 ns/op 440 B/op 10 allocs/op
+BenchmarkStructFilteredSuccess-8 2000000 597 ns/op 288 B/op 9 allocs/op
+BenchmarkStructFilteredSuccessParallel-8 10000000 266 ns/op 288 B/op 9 allocs/op
+BenchmarkStructFilteredFailure-8 3000000 454 ns/op 256 B/op 7 allocs/op
+BenchmarkStructFilteredFailureParallel-8 10000000 214 ns/op 256 B/op 7 allocs/op
+BenchmarkStructPartialSuccess-8 3000000 502 ns/op 256 B/op 6 allocs/op
+BenchmarkStructPartialSuccessParallel-8 10000000 225 ns/op 256 B/op 6 allocs/op
+BenchmarkStructPartialFailure-8 2000000 702 ns/op 480 B/op 11 allocs/op
+BenchmarkStructPartialFailureParallel-8 5000000 329 ns/op 480 B/op 11 allocs/op
+BenchmarkStructExceptSuccess-8 2000000 793 ns/op 496 B/op 12 allocs/op
+BenchmarkStructExceptSuccessParallel-8 10000000 193 ns/op 240 B/op 5 allocs/op
+BenchmarkStructExceptFailure-8 2000000 639 ns/op 464 B/op 10 allocs/op
+BenchmarkStructExceptFailureParallel-8 5000000 300 ns/op 464 B/op 10 allocs/op
+BenchmarkStructSimpleCrossFieldSuccess-8 3000000 417 ns/op 72 B/op 3 allocs/op
+BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 163 ns/op 72 B/op 3 allocs/op
+BenchmarkStructSimpleCrossFieldFailure-8 2000000 645 ns/op 304 B/op 8 allocs/op
+BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 285 ns/op 304 B/op 8 allocs/op
+BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 588 ns/op 80 B/op 4 allocs/op
+BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 221 ns/op 80 B/op 4 allocs/op
+BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 868 ns/op 320 B/op 9 allocs/op
+BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 337 ns/op 320 B/op 9 allocs/op
+BenchmarkStructSimpleSuccess-8 5000000 260 ns/op 0 B/op 0 allocs/op
+BenchmarkStructSimpleSuccessParallel-8 20000000 90.6 ns/op 0 B/op 0 allocs/op
+BenchmarkStructSimpleFailure-8 2000000 619 ns/op 424 B/op 9 allocs/op
+BenchmarkStructSimpleFailureParallel-8 5000000 296 ns/op 424 B/op 9 allocs/op
+BenchmarkStructComplexSuccess-8 1000000 1454 ns/op 128 B/op 8 allocs/op
+BenchmarkStructComplexSuccessParallel-8 3000000 579 ns/op 128 B/op 8 allocs/op
+BenchmarkStructComplexFailure-8 300000 4140 ns/op 3041 B/op 53 allocs/op
+BenchmarkStructComplexFailureParallel-8 1000000 2127 ns/op 3041 B/op 53 allocs/op
+BenchmarkOneof-8 10000000 140 ns/op 0 B/op 0 allocs/op
+BenchmarkOneofParallel-8 20000000 70.1 ns/op 0 B/op 0 allocs/op
+```
+
+Complementary Software
+----------------------
+
+Here is a list of software that complements using this library either pre or post validation.
+
+* [form](https://github.com/go-playground/form) - Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support.
+* [mold](https://github.com/go-playground/mold) - A general library to help modify or set data within data structures and other objects
+
+How to Contribute
+------
+
+Make a pull request...
+
+License
+------
+Distributed under MIT License, please see license file within the code for more details.
diff --git a/go-playground/validator/v10/_examples/custom-validation/main.go b/go-playground/validator/v10/_examples/custom-validation/main.go
new file mode 100644
index 0000000..db8822e
--- /dev/null
+++ b/go-playground/validator/v10/_examples/custom-validation/main.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+ "fmt"
+
+ "gin-valid/go-playground/validator/v10"
+)
+
+// MyStruct ..
+type MyStruct struct {
+ String string `validate:"is-awesome"`
+}
+
+// use a single instance of Validate, it caches struct info
+var validate *validator.Validate
+
+func main() {
+
+ validate = validator.New()
+ validate.RegisterValidation("is-awesome", ValidateMyVal)
+
+ s := MyStruct{String: "awesome"}
+
+ err := validate.Struct(s)
+ if err != nil {
+ fmt.Printf("Err(s):\n%+v\n", err)
+ }
+
+ s.String = "not awesome"
+ err = validate.Struct(s)
+ if err != nil {
+ fmt.Printf("Err(s):\n%+v\n", err)
+ }
+}
+
+// ValidateMyVal implements validator.Func
+func ValidateMyVal(fl validator.FieldLevel) bool {
+ return fl.Field().String() == "awesome"
+}
diff --git a/go-playground/validator/v10/_examples/custom/main.go b/go-playground/validator/v10/_examples/custom/main.go
new file mode 100644
index 0000000..ebaa6b8
--- /dev/null
+++ b/go-playground/validator/v10/_examples/custom/main.go
@@ -0,0 +1,51 @@
+package main
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "fmt"
+ "reflect"
+
+ "gin-valid/go-playground/validator/v10"
+)
+
+// DbBackedUser User struct
+type DbBackedUser struct {
+ Name sql.NullString `validate:"required"`
+ Age sql.NullInt64 `validate:"required"`
+}
+
+// use a single instance of Validate, it caches struct info
+var validate *validator.Validate
+
+func main() {
+
+ validate = validator.New()
+
+ // register all sql.Null* types to use the ValidateValuer CustomTypeFunc
+ validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
+
+ // build object for validation
+ x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
+
+ err := validate.Struct(x)
+
+ if err != nil {
+ fmt.Printf("Err(s):\n%+v\n", err)
+ }
+}
+
+// ValidateValuer implements validator.CustomTypeFunc
+func ValidateValuer(field reflect.Value) interface{} {
+
+ if valuer, ok := field.Interface().(driver.Valuer); ok {
+
+ val, err := valuer.Value()
+ if err == nil {
+ return val
+ }
+ // handle the error how you want
+ }
+
+ return nil
+}
diff --git a/go-playground/validator/v10/_examples/dive/main.go b/go-playground/validator/v10/_examples/dive/main.go
new file mode 100644
index 0000000..f67592e
--- /dev/null
+++ b/go-playground/validator/v10/_examples/dive/main.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+ "fmt"
+
+ "gin-valid/go-playground/validator/v10"
+)
+
+// Test ...
+type Test struct {
+ Array []string `validate:"required,gt=0,dive,required"`
+ Map map[string]string `validate:"required,gt=0,dive,keys,keymax,endkeys,required,max=1000"`
+}
+
+// use a single instance of Validate, it caches struct info
+var validate *validator.Validate
+
+func main() {
+
+ validate = validator.New()
+
+ // registering alias so we can see the differences between
+ // map key, value validation errors
+ validate.RegisterAlias("keymax", "max=10")
+
+ var test Test
+
+ val(test)
+
+ test.Array = []string{""}
+ test.Map = map[string]string{"test > than 10": ""}
+ val(test)
+}
+
+func val(test Test) {
+ fmt.Println("testing")
+ err := validate.Struct(test)
+ fmt.Println(err)
+}
diff --git a/go-playground/validator/v10/_examples/gin-upgrading-overriding/main.go b/go-playground/validator/v10/_examples/gin-upgrading-overriding/main.go
new file mode 100644
index 0000000..99a1ddf
--- /dev/null
+++ b/go-playground/validator/v10/_examples/gin-upgrading-overriding/main.go
@@ -0,0 +1,10 @@
+package main
+
+import "gin-valid/gin/binding"
+
+func main() {
+
+ binding.Validator = new(defaultValidator)
+
+ // regular gin logic
+}
diff --git a/go-playground/validator/v10/_examples/gin-upgrading-overriding/v8_to_v9.go b/go-playground/validator/v10/_examples/gin-upgrading-overriding/v8_to_v9.go
new file mode 100644
index 0000000..c049989
--- /dev/null
+++ b/go-playground/validator/v10/_examples/gin-upgrading-overriding/v8_to_v9.go
@@ -0,0 +1,55 @@
+package main
+
+import (
+ "reflect"
+ "sync"
+
+ "gin-valid/gin/binding"
+ "gin-valid/go-playground/validator/v10"
+)
+
+type defaultValidator struct {
+ once sync.Once
+ validate *validator.Validate
+}
+
+var _ binding.StructValidator = &defaultValidator{}
+
+func (v *defaultValidator) ValidateStruct(obj interface{}) error {
+
+ if kindOfData(obj) == reflect.Struct {
+
+ v.lazyinit()
+
+ if err := v.validate.Struct(obj); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (v *defaultValidator) Engine() interface{} {
+ v.lazyinit()
+ return v.validate
+}
+
+func (v *defaultValidator) lazyinit() {
+ v.once.Do(func() {
+ v.validate = validator.New()
+ v.validate.SetTagName("binding")
+
+ // add any custom validations etc. here
+ })
+}
+
+func kindOfData(data interface{}) reflect.Kind {
+
+ value := reflect.ValueOf(data)
+ valueType := value.Kind()
+
+ if valueType == reflect.Ptr {
+ valueType = value.Elem().Kind()
+ }
+ return valueType
+}
diff --git a/go-playground/validator/v10/_examples/simple/main.go b/go-playground/validator/v10/_examples/simple/main.go
new file mode 100644
index 0000000..02242d8
--- /dev/null
+++ b/go-playground/validator/v10/_examples/simple/main.go
@@ -0,0 +1,101 @@
+package main
+
+import (
+ "fmt"
+
+ "gin-valid/go-playground/validator/v10"
+)
+
+// User contains user information
+type User struct {
+ FirstName string `validate:"required"`
+ LastName string `validate:"required"`
+ Age uint8 `validate:"gte=0,lte=130"`
+ Email string `validate:"required,email"`
+ FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla'
+ Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
+}
+
+// Address houses a users address information
+type Address struct {
+ Street string `validate:"required"`
+ City string `validate:"required"`
+ Planet string `validate:"required"`
+ Phone string `validate:"required"`
+}
+
+// use a single instance of Validate, it caches struct info
+var validate *validator.Validate
+
+func main() {
+
+ validate = validator.New()
+
+ validateStruct()
+ validateVariable()
+}
+
+func validateStruct() {
+
+ address := &Address{
+ Street: "Eavesdown Docks",
+ Planet: "Persphone",
+ Phone: "none",
+ }
+
+ user := &User{
+ FirstName: "Badger",
+ LastName: "Smith",
+ Age: 135,
+ Email: "Badger.Smith@gmail.com",
+ FavouriteColor: "#000-",
+ Addresses: []*Address{address},
+ }
+
+ // returns nil or ValidationErrors ( []FieldError )
+ err := validate.Struct(user)
+ if err != nil {
+
+ // this check is only needed when your code could produce
+ // an invalid value for validation such as interface with nil
+ // value most including myself do not usually have code like this.
+ if _, ok := err.(*validator.InvalidValidationError); ok {
+ fmt.Println(err)
+ return
+ }
+
+ for _, err := range err.(validator.ValidationErrors) {
+
+ fmt.Println(err.Namespace())
+ fmt.Println(err.Field())
+ fmt.Println(err.StructNamespace())
+ fmt.Println(err.StructField())
+ fmt.Println(err.Tag())
+ fmt.Println(err.ActualTag())
+ fmt.Println(err.Kind())
+ fmt.Println(err.Type())
+ fmt.Println(err.Value())
+ fmt.Println(err.Param())
+ fmt.Println()
+ }
+
+ // from here you can create your own error messages in whatever language you wish
+ return
+ }
+
+ // save user to database
+}
+
+func validateVariable() {
+
+ myEmail := "joeybloggs.gmail.com"
+
+ errs := validate.Var(myEmail, "required,email")
+
+ if errs != nil {
+ fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
+ return
+ }
+
+ // email ok, move on
+}
diff --git a/go-playground/validator/v10/_examples/struct-level/main.go b/go-playground/validator/v10/_examples/struct-level/main.go
new file mode 100644
index 0000000..4472623
--- /dev/null
+++ b/go-playground/validator/v10/_examples/struct-level/main.go
@@ -0,0 +1,120 @@
+package main
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+
+ "gin-valid/go-playground/validator/v10"
+)
+
+// User contains user information
+type User struct {
+ FirstName string `json:"fname"`
+ LastName string `json:"lname"`
+ Age uint8 `validate:"gte=0,lte=130"`
+ Email string `json:"e-mail" validate:"required,email"`
+ FavouriteColor string `validate:"hexcolor|rgb|rgba"`
+ Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
+}
+
+// Address houses a users address information
+type Address struct {
+ Street string `validate:"required"`
+ City string `validate:"required"`
+ Planet string `validate:"required"`
+ Phone string `validate:"required"`
+}
+
+// use a single instance of Validate, it caches struct info
+var validate *validator.Validate
+
+func main() {
+
+ validate = validator.New()
+
+ // register function to get tag name from json tags.
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+ if name == "-" {
+ return ""
+ }
+ return name
+ })
+
+ // register validation for 'User'
+ // NOTE: only have to register a non-pointer type for 'User', validator
+ // interanlly dereferences during it's type checks.
+ validate.RegisterStructValidation(UserStructLevelValidation, User{})
+
+ // build 'User' info, normally posted data etc...
+ address := &Address{
+ Street: "Eavesdown Docks",
+ Planet: "Persphone",
+ Phone: "none",
+ City: "Unknown",
+ }
+
+ user := &User{
+ FirstName: "",
+ LastName: "",
+ Age: 45,
+ Email: "Badger.Smith@gmail",
+ FavouriteColor: "#000",
+ Addresses: []*Address{address},
+ }
+
+ // returns InvalidValidationError for bad validation input, nil or ValidationErrors ( []FieldError )
+ err := validate.Struct(user)
+ if err != nil {
+
+ // this check is only needed when your code could produce
+ // an invalid value for validation such as interface with nil
+ // value most including myself do not usually have code like this.
+ if _, ok := err.(*validator.InvalidValidationError); ok {
+ fmt.Println(err)
+ return
+ }
+
+ for _, err := range err.(validator.ValidationErrors) {
+
+ fmt.Println(err.Namespace()) // can differ when a custom TagNameFunc is registered or
+ fmt.Println(err.Field()) // by passing alt name to ReportError like below
+ fmt.Println(err.StructNamespace())
+ fmt.Println(err.StructField())
+ fmt.Println(err.Tag())
+ fmt.Println(err.ActualTag())
+ fmt.Println(err.Kind())
+ fmt.Println(err.Type())
+ fmt.Println(err.Value())
+ fmt.Println(err.Param())
+ fmt.Println()
+ }
+
+ // from here you can create your own error messages in whatever language you wish
+ return
+ }
+
+ // save user to database
+}
+
+// UserStructLevelValidation contains custom struct level validations that don't always
+// make sense at the field validation level. For Example this function validates that either
+// FirstName or LastName exist; could have done that with a custom field validation but then
+// would have had to add it to both fields duplicating the logic + overhead, this way it's
+// only validated once.
+//
+// NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
+// hooks right into validator and you can combine with validation tags and still have a
+// common error output format.
+func UserStructLevelValidation(sl validator.StructLevel) {
+
+ user := sl.Current().Interface().(User)
+
+ if len(user.FirstName) == 0 && len(user.LastName) == 0 {
+ sl.ReportError(user.FirstName, "fname", "FirstName", "fnameorlname", "")
+ sl.ReportError(user.LastName, "lname", "LastName", "fnameorlname", "")
+ }
+
+ // plus can do more, even with different tag than "fnameorlname"
+}
diff --git a/go-playground/validator/v10/_examples/translations/main.go b/go-playground/validator/v10/_examples/translations/main.go
new file mode 100644
index 0000000..61d4f02
--- /dev/null
+++ b/go-playground/validator/v10/_examples/translations/main.go
@@ -0,0 +1,129 @@
+package main
+
+import (
+ "fmt"
+
+ "gin-valid/go-playground/locales/en"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ en_translations "gin-valid/go-playground/validator/v10/translations/en"
+)
+
+// User contains user information
+type User struct {
+ FirstName string `validate:"required"`
+ LastName string `validate:"required"`
+ Age uint8 `validate:"gte=0,lte=130"`
+ Email string `validate:"required,email"`
+ FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla'
+ Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
+}
+
+// Address houses a users address information
+type Address struct {
+ Street string `validate:"required"`
+ City string `validate:"required"`
+ Planet string `validate:"required"`
+ Phone string `validate:"required"`
+}
+
+// use a single instance , it caches struct info
+var (
+ uni *ut.UniversalTranslator
+ validate *validator.Validate
+)
+
+func main() {
+
+ // NOTE: ommitting allot of error checking for brevity
+
+ en := en.New()
+ uni = ut.New(en, en)
+
+ // this is usually know or extracted from http 'Accept-Language' header
+ // also see uni.FindTranslator(...)
+ trans, _ := uni.GetTranslator("en")
+
+ validate = validator.New()
+ en_translations.RegisterDefaultTranslations(validate, trans)
+
+ translateAll(trans)
+ translateIndividual(trans)
+ translateOverride(trans) // yep you can specify your own in whatever locale you want!
+}
+
+func translateAll(trans ut.Translator) {
+
+ type User struct {
+ Username string `validate:"required"`
+ Tagline string `validate:"required,lt=10"`
+ Tagline2 string `validate:"required,gt=1"`
+ }
+
+ user := User{
+ Username: "Joeybloggs",
+ Tagline: "This tagline is way too long.",
+ Tagline2: "1",
+ }
+
+ err := validate.Struct(user)
+ if err != nil {
+
+ // translate all error at once
+ errs := err.(validator.ValidationErrors)
+
+ // returns a map with key = namespace & value = translated error
+ // NOTICE: 2 errors are returned and you'll see something surprising
+ // translations are i18n aware!!!!
+ // eg. '10 characters' vs '1 character'
+ fmt.Println(errs.Translate(trans))
+ }
+}
+
+func translateIndividual(trans ut.Translator) {
+
+ type User struct {
+ Username string `validate:"required"`
+ }
+
+ var user User
+
+ err := validate.Struct(user)
+ if err != nil {
+
+ errs := err.(validator.ValidationErrors)
+
+ for _, e := range errs {
+ // can translate each error one at a time.
+ fmt.Println(e.Translate(trans))
+ }
+ }
+}
+
+func translateOverride(trans ut.Translator) {
+
+ validate.RegisterTranslation("required", trans, func(ut ut.Translator) error {
+ return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details
+ }, func(ut ut.Translator, fe validator.FieldError) string {
+ t, _ := ut.T("required", fe.Field())
+
+ return t
+ })
+
+ type User struct {
+ Username string `validate:"required"`
+ }
+
+ var user User
+
+ err := validate.Struct(user)
+ if err != nil {
+
+ errs := err.(validator.ValidationErrors)
+
+ for _, e := range errs {
+ // can translate each error one at a time.
+ fmt.Println(e.Translate(trans))
+ }
+ }
+}
diff --git a/go-playground/validator/v10/baked_in.go b/go-playground/validator/v10/baked_in.go
new file mode 100644
index 0000000..8e41f62
--- /dev/null
+++ b/go-playground/validator/v10/baked_in.go
@@ -0,0 +1,2285 @@
+package validator
+
+import (
+ "bytes"
+ "context"
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "net"
+ "net/url"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+ "unicode/utf8"
+
+ "golang.org/x/crypto/sha3"
+
+ urn "github.com/leodido/go-urn"
+)
+
+// Func accepts a FieldLevel interface for all validation needs. The return
+// value should be true when validation succeeds.
+type Func func(fl FieldLevel) bool
+
+// FuncCtx accepts a context.Context and FieldLevel interface for all
+// validation needs. The return value should be true when validation succeeds.
+type FuncCtx func(ctx context.Context, fl FieldLevel) bool
+
+// wrapFunc wraps noramal Func makes it compatible with FuncCtx
+func wrapFunc(fn Func) FuncCtx {
+ if fn == nil {
+ return nil // be sure not to wrap a bad function.
+ }
+ return func(ctx context.Context, fl FieldLevel) bool {
+ return fn(fl)
+ }
+}
+
+var (
+ restrictedTags = map[string]struct{}{
+ diveTag: {},
+ keysTag: {},
+ endKeysTag: {},
+ structOnlyTag: {},
+ omitempty: {},
+ skipValidationTag: {},
+ utf8HexComma: {},
+ utf8Pipe: {},
+ noStructLevelTag: {},
+ requiredTag: {},
+ isdefault: {},
+ }
+
+ // BakedInAliasValidators is a default mapping of a single validation tag that
+ // defines a common or complex set of validation(s) to simplify
+ // adding validation to structs.
+ bakedInAliases = map[string]string{
+ "iscolor": "hexcolor|rgb|rgba|hsl|hsla",
+ "country_code": "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric",
+ }
+
+ // BakedInValidators is the default map of ValidationFunc
+ // you can add, remove or even replace items to suite your needs,
+ // or even disregard and use your own map if so desired.
+ bakedInValidators = map[string]Func{
+ "required": hasValue,
+ "required_if": requiredIf,
+ "required_unless": requiredUnless,
+ "required_with": requiredWith,
+ "required_with_all": requiredWithAll,
+ "required_without": requiredWithout,
+ "required_without_all": requiredWithoutAll,
+ "excluded_with": excludedWith,
+ "excluded_with_all": excludedWithAll,
+ "excluded_without": excludedWithout,
+ "excluded_without_all": excludedWithoutAll,
+ "isdefault": isDefault,
+ "len": hasLengthOf,
+ "min": hasMinOf,
+ "max": hasMaxOf,
+ "eq": isEq,
+ "ne": isNe,
+ "lt": isLt,
+ "lte": isLte,
+ "gt": isGt,
+ "gte": isGte,
+ "eqfield": isEqField,
+ "eqcsfield": isEqCrossStructField,
+ "necsfield": isNeCrossStructField,
+ "gtcsfield": isGtCrossStructField,
+ "gtecsfield": isGteCrossStructField,
+ "ltcsfield": isLtCrossStructField,
+ "ltecsfield": isLteCrossStructField,
+ "nefield": isNeField,
+ "gtefield": isGteField,
+ "gtfield": isGtField,
+ "ltefield": isLteField,
+ "ltfield": isLtField,
+ "fieldcontains": fieldContains,
+ "fieldexcludes": fieldExcludes,
+ "alpha": isAlpha,
+ "alphanum": isAlphanum,
+ "alphaunicode": isAlphaUnicode,
+ "alphanumunicode": isAlphanumUnicode,
+ "numeric": isNumeric,
+ "number": isNumber,
+ "hexadecimal": isHexadecimal,
+ "hexcolor": isHEXColor,
+ "rgb": isRGB,
+ "rgba": isRGBA,
+ "hsl": isHSL,
+ "hsla": isHSLA,
+ "e164": isE164,
+ "email": isEmail,
+ "url": isURL,
+ "uri": isURI,
+ "urn_rfc2141": isUrnRFC2141, // RFC 2141
+ "file": isFile,
+ "base64": isBase64,
+ "base64url": isBase64URL,
+ "contains": contains,
+ "containsany": containsAny,
+ "containsrune": containsRune,
+ "excludes": excludes,
+ "excludesall": excludesAll,
+ "excludesrune": excludesRune,
+ "startswith": startsWith,
+ "endswith": endsWith,
+ "startsnotwith": startsNotWith,
+ "endsnotwith": endsNotWith,
+ "isbn": isISBN,
+ "isbn10": isISBN10,
+ "isbn13": isISBN13,
+ "eth_addr": isEthereumAddress,
+ "btc_addr": isBitcoinAddress,
+ "btc_addr_bech32": isBitcoinBech32Address,
+ "uuid": isUUID,
+ "uuid3": isUUID3,
+ "uuid4": isUUID4,
+ "uuid5": isUUID5,
+ "uuid_rfc4122": isUUIDRFC4122,
+ "uuid3_rfc4122": isUUID3RFC4122,
+ "uuid4_rfc4122": isUUID4RFC4122,
+ "uuid5_rfc4122": isUUID5RFC4122,
+ "ascii": isASCII,
+ "printascii": isPrintableASCII,
+ "multibyte": hasMultiByteCharacter,
+ "datauri": isDataURI,
+ "latitude": isLatitude,
+ "longitude": isLongitude,
+ "ssn": isSSN,
+ "ipv4": isIPv4,
+ "ipv6": isIPv6,
+ "ip": isIP,
+ "cidrv4": isCIDRv4,
+ "cidrv6": isCIDRv6,
+ "cidr": isCIDR,
+ "tcp4_addr": isTCP4AddrResolvable,
+ "tcp6_addr": isTCP6AddrResolvable,
+ "tcp_addr": isTCPAddrResolvable,
+ "udp4_addr": isUDP4AddrResolvable,
+ "udp6_addr": isUDP6AddrResolvable,
+ "udp_addr": isUDPAddrResolvable,
+ "ip4_addr": isIP4AddrResolvable,
+ "ip6_addr": isIP6AddrResolvable,
+ "ip_addr": isIPAddrResolvable,
+ "unix_addr": isUnixAddrResolvable,
+ "mac": isMAC,
+ "hostname": isHostnameRFC952, // RFC 952
+ "hostname_rfc1123": isHostnameRFC1123, // RFC 1123
+ "fqdn": isFQDN,
+ "unique": isUnique,
+ "oneof": isOneOf,
+ "html": isHTML,
+ "html_encoded": isHTMLEncoded,
+ "url_encoded": isURLEncoded,
+ "dir": isDir,
+ "json": isJSON,
+ "hostname_port": isHostnamePort,
+ "lowercase": isLowercase,
+ "uppercase": isUppercase,
+ "datetime": isDatetime,
+ "timezone": isTimeZone,
+ "iso3166_1_alpha2": isIso3166Alpha2,
+ "iso3166_1_alpha3": isIso3166Alpha3,
+ "iso3166_1_alpha_numeric": isIso3166AlphaNumeric,
+ }
+)
+
+var oneofValsCache = map[string][]string{}
+var oneofValsCacheRWLock = sync.RWMutex{}
+
+func parseOneOfParam2(s string) []string {
+ oneofValsCacheRWLock.RLock()
+ vals, ok := oneofValsCache[s]
+ oneofValsCacheRWLock.RUnlock()
+ if !ok {
+ oneofValsCacheRWLock.Lock()
+ vals = splitParamsRegex.FindAllString(s, -1)
+ for i := 0; i < len(vals); i++ {
+ vals[i] = strings.Replace(vals[i], "'", "", -1)
+ }
+ oneofValsCache[s] = vals
+ oneofValsCacheRWLock.Unlock()
+ }
+ return vals
+}
+
+func isURLEncoded(fl FieldLevel) bool {
+ return uRLEncodedRegex.MatchString(fl.Field().String())
+}
+
+func isHTMLEncoded(fl FieldLevel) bool {
+ return hTMLEncodedRegex.MatchString(fl.Field().String())
+}
+
+func isHTML(fl FieldLevel) bool {
+ return hTMLRegex.MatchString(fl.Field().String())
+}
+
+func isOneOf(fl FieldLevel) bool {
+ vals := parseOneOfParam2(fl.Param())
+
+ field := fl.Field()
+
+ var v string
+ switch field.Kind() {
+ case reflect.String:
+ v = field.String()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ v = strconv.FormatInt(field.Int(), 10)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ v = strconv.FormatUint(field.Uint(), 10)
+ default:
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ }
+ for i := 0; i < len(vals); i++ {
+ if vals[i] == v {
+ return true
+ }
+ }
+ return false
+}
+
+// isUnique is the validation function for validating if each array|slice|map value is unique
+func isUnique(fl FieldLevel) bool {
+
+ field := fl.Field()
+ param := fl.Param()
+ v := reflect.ValueOf(struct{}{})
+
+ switch field.Kind() {
+ case reflect.Slice, reflect.Array:
+ elem := field.Type().Elem()
+ if elem.Kind() == reflect.Ptr {
+ elem = elem.Elem()
+ }
+
+ if param == "" {
+ m := reflect.MakeMap(reflect.MapOf(elem, v.Type()))
+
+ for i := 0; i < field.Len(); i++ {
+ m.SetMapIndex(reflect.Indirect(field.Index(i)), v)
+ }
+ return field.Len() == m.Len()
+ }
+
+ sf, ok := elem.FieldByName(param)
+ if !ok {
+ panic(fmt.Sprintf("Bad field name %s", param))
+ }
+
+ sfTyp := sf.Type
+ if sfTyp.Kind() == reflect.Ptr {
+ sfTyp = sfTyp.Elem()
+ }
+
+ m := reflect.MakeMap(reflect.MapOf(sfTyp, v.Type()))
+ for i := 0; i < field.Len(); i++ {
+ m.SetMapIndex(reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param)), v)
+ }
+ return field.Len() == m.Len()
+ case reflect.Map:
+ m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type()))
+
+ for _, k := range field.MapKeys() {
+ m.SetMapIndex(field.MapIndex(k), v)
+ }
+ return field.Len() == m.Len()
+ default:
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ }
+}
+
+// IsMAC is the validation function for validating if the field's value is a valid MAC address.
+func isMAC(fl FieldLevel) bool {
+
+ _, err := net.ParseMAC(fl.Field().String())
+
+ return err == nil
+}
+
+// IsCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address.
+func isCIDRv4(fl FieldLevel) bool {
+
+ ip, _, err := net.ParseCIDR(fl.Field().String())
+
+ return err == nil && ip.To4() != nil
+}
+
+// IsCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address.
+func isCIDRv6(fl FieldLevel) bool {
+
+ ip, _, err := net.ParseCIDR(fl.Field().String())
+
+ return err == nil && ip.To4() == nil
+}
+
+// IsCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address.
+func isCIDR(fl FieldLevel) bool {
+
+ _, _, err := net.ParseCIDR(fl.Field().String())
+
+ return err == nil
+}
+
+// IsIPv4 is the validation function for validating if a value is a valid v4 IP address.
+func isIPv4(fl FieldLevel) bool {
+
+ ip := net.ParseIP(fl.Field().String())
+
+ return ip != nil && ip.To4() != nil
+}
+
+// IsIPv6 is the validation function for validating if the field's value is a valid v6 IP address.
+func isIPv6(fl FieldLevel) bool {
+
+ ip := net.ParseIP(fl.Field().String())
+
+ return ip != nil && ip.To4() == nil
+}
+
+// IsIP is the validation function for validating if the field's value is a valid v4 or v6 IP address.
+func isIP(fl FieldLevel) bool {
+
+ ip := net.ParseIP(fl.Field().String())
+
+ return ip != nil
+}
+
+// IsSSN is the validation function for validating if the field's value is a valid SSN.
+func isSSN(fl FieldLevel) bool {
+
+ field := fl.Field()
+
+ if field.Len() != 11 {
+ return false
+ }
+
+ return sSNRegex.MatchString(field.String())
+}
+
+// IsLongitude is the validation function for validating if the field's value is a valid longitude coordinate.
+func isLongitude(fl FieldLevel) bool {
+ field := fl.Field()
+
+ var v string
+ switch field.Kind() {
+ case reflect.String:
+ v = field.String()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ v = strconv.FormatInt(field.Int(), 10)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ v = strconv.FormatUint(field.Uint(), 10)
+ case reflect.Float32:
+ v = strconv.FormatFloat(field.Float(), 'f', -1, 32)
+ case reflect.Float64:
+ v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
+ default:
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ }
+
+ return longitudeRegex.MatchString(v)
+}
+
+// IsLatitude is the validation function for validating if the field's value is a valid latitude coordinate.
+func isLatitude(fl FieldLevel) bool {
+ field := fl.Field()
+
+ var v string
+ switch field.Kind() {
+ case reflect.String:
+ v = field.String()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ v = strconv.FormatInt(field.Int(), 10)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ v = strconv.FormatUint(field.Uint(), 10)
+ case reflect.Float32:
+ v = strconv.FormatFloat(field.Float(), 'f', -1, 32)
+ case reflect.Float64:
+ v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
+ default:
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ }
+
+ return latitudeRegex.MatchString(v)
+}
+
+// IsDataURI is the validation function for validating if the field's value is a valid data URI.
+func isDataURI(fl FieldLevel) bool {
+
+ uri := strings.SplitN(fl.Field().String(), ",", 2)
+
+ if len(uri) != 2 {
+ return false
+ }
+
+ if !dataURIRegex.MatchString(uri[0]) {
+ return false
+ }
+
+ return base64Regex.MatchString(uri[1])
+}
+
+// HasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character.
+func hasMultiByteCharacter(fl FieldLevel) bool {
+
+ field := fl.Field()
+
+ if field.Len() == 0 {
+ return true
+ }
+
+ return multibyteRegex.MatchString(field.String())
+}
+
+// IsPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character.
+func isPrintableASCII(fl FieldLevel) bool {
+ return printableASCIIRegex.MatchString(fl.Field().String())
+}
+
+// IsASCII is the validation function for validating if the field's value is a valid ASCII character.
+func isASCII(fl FieldLevel) bool {
+ return aSCIIRegex.MatchString(fl.Field().String())
+}
+
+// IsUUID5 is the validation function for validating if the field's value is a valid v5 UUID.
+func isUUID5(fl FieldLevel) bool {
+ return uUID5Regex.MatchString(fl.Field().String())
+}
+
+// IsUUID4 is the validation function for validating if the field's value is a valid v4 UUID.
+func isUUID4(fl FieldLevel) bool {
+ return uUID4Regex.MatchString(fl.Field().String())
+}
+
+// IsUUID3 is the validation function for validating if the field's value is a valid v3 UUID.
+func isUUID3(fl FieldLevel) bool {
+ return uUID3Regex.MatchString(fl.Field().String())
+}
+
+// IsUUID is the validation function for validating if the field's value is a valid UUID of any version.
+func isUUID(fl FieldLevel) bool {
+ return uUIDRegex.MatchString(fl.Field().String())
+}
+
+// IsUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID.
+func isUUID5RFC4122(fl FieldLevel) bool {
+ return uUID5RFC4122Regex.MatchString(fl.Field().String())
+}
+
+// IsUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID.
+func isUUID4RFC4122(fl FieldLevel) bool {
+ return uUID4RFC4122Regex.MatchString(fl.Field().String())
+}
+
+// IsUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID.
+func isUUID3RFC4122(fl FieldLevel) bool {
+ return uUID3RFC4122Regex.MatchString(fl.Field().String())
+}
+
+// IsUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version.
+func isUUIDRFC4122(fl FieldLevel) bool {
+ return uUIDRFC4122Regex.MatchString(fl.Field().String())
+}
+
+// IsISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN.
+func isISBN(fl FieldLevel) bool {
+ return isISBN10(fl) || isISBN13(fl)
+}
+
+// IsISBN13 is the validation function for validating if the field's value is a valid v13 ISBN.
+func isISBN13(fl FieldLevel) bool {
+
+ s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 4), " ", "", 4)
+
+ if !iSBN13Regex.MatchString(s) {
+ return false
+ }
+
+ var checksum int32
+ var i int32
+
+ factor := []int32{1, 3}
+
+ for i = 0; i < 12; i++ {
+ checksum += factor[i%2] * int32(s[i]-'0')
+ }
+
+ return (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0
+}
+
+// IsISBN10 is the validation function for validating if the field's value is a valid v10 ISBN.
+func isISBN10(fl FieldLevel) bool {
+
+ s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 3), " ", "", 3)
+
+ if !iSBN10Regex.MatchString(s) {
+ return false
+ }
+
+ var checksum int32
+ var i int32
+
+ for i = 0; i < 9; i++ {
+ checksum += (i + 1) * int32(s[i]-'0')
+ }
+
+ if s[9] == 'X' {
+ checksum += 10 * 10
+ } else {
+ checksum += 10 * int32(s[9]-'0')
+ }
+
+ return checksum%11 == 0
+}
+
+// IsEthereumAddress is the validation function for validating if the field's value is a valid Ethereum address.
+func isEthereumAddress(fl FieldLevel) bool {
+ address := fl.Field().String()
+
+ if !ethAddressRegex.MatchString(address) {
+ return false
+ }
+
+ if ethaddressRegexUpper.MatchString(address) || ethAddressRegexLower.MatchString(address) {
+ return true
+ }
+
+ // Checksum validation. Reference: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
+ address = address[2:] // Skip "0x" prefix.
+ h := sha3.NewLegacyKeccak256()
+ // hash.Hash's io.Writer implementation says it never returns an error. https://golang.org/pkg/hash/#Hash
+ _, _ = h.Write([]byte(strings.ToLower(address)))
+ hash := hex.EncodeToString(h.Sum(nil))
+
+ for i := 0; i < len(address); i++ {
+ if address[i] <= '9' { // Skip 0-9 digits: they don't have upper/lower-case.
+ continue
+ }
+ if hash[i] > '7' && address[i] >= 'a' || hash[i] <= '7' && address[i] <= 'F' {
+ return false
+ }
+ }
+
+ return true
+}
+
+// IsBitcoinAddress is the validation function for validating if the field's value is a valid btc address
+func isBitcoinAddress(fl FieldLevel) bool {
+ address := fl.Field().String()
+
+ if !btcAddressRegex.MatchString(address) {
+ return false
+ }
+
+ alphabet := []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
+
+ decode := [25]byte{}
+
+ for _, n := range []byte(address) {
+ d := bytes.IndexByte(alphabet, n)
+
+ for i := 24; i >= 0; i-- {
+ d += 58 * int(decode[i])
+ decode[i] = byte(d % 256)
+ d /= 256
+ }
+ }
+
+ h := sha256.New()
+ _, _ = h.Write(decode[:21])
+ d := h.Sum([]byte{})
+ h = sha256.New()
+ _, _ = h.Write(d)
+
+ validchecksum := [4]byte{}
+ computedchecksum := [4]byte{}
+
+ copy(computedchecksum[:], h.Sum(d[:0]))
+ copy(validchecksum[:], decode[21:])
+
+ return validchecksum == computedchecksum
+}
+
+// IsBitcoinBech32Address is the validation function for validating if the field's value is a valid bech32 btc address
+func isBitcoinBech32Address(fl FieldLevel) bool {
+ address := fl.Field().String()
+
+ if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address) {
+ return false
+ }
+
+ am := len(address) % 8
+
+ if am == 0 || am == 3 || am == 5 {
+ return false
+ }
+
+ address = strings.ToLower(address)
+
+ alphabet := "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
+
+ hr := []int{3, 3, 0, 2, 3} // the human readable part will always be bc
+ addr := address[3:]
+ dp := make([]int, 0, len(addr))
+
+ for _, c := range addr {
+ dp = append(dp, strings.IndexRune(alphabet, c))
+ }
+
+ ver := dp[0]
+
+ if ver < 0 || ver > 16 {
+ return false
+ }
+
+ if ver == 0 {
+ if len(address) != 42 && len(address) != 62 {
+ return false
+ }
+ }
+
+ values := append(hr, dp...)
+
+ GEN := []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3}
+
+ p := 1
+
+ for _, v := range values {
+ b := p >> 25
+ p = (p&0x1ffffff)<<5 ^ v
+
+ for i := 0; i < 5; i++ {
+ if (b>>uint(i))&1 == 1 {
+ p ^= GEN[i]
+ }
+ }
+ }
+
+ if p != 1 {
+ return false
+ }
+
+ b := uint(0)
+ acc := 0
+ mv := (1 << 5) - 1
+ var sw []int
+
+ for _, v := range dp[1 : len(dp)-6] {
+ acc = (acc << 5) | v
+ b += 5
+ for b >= 8 {
+ b -= 8
+ sw = append(sw, (acc>>b)&mv)
+ }
+ }
+
+ if len(sw) < 2 || len(sw) > 40 {
+ return false
+ }
+
+ return true
+}
+
+// ExcludesRune is the validation function for validating that the field's value does not contain the rune specified within the param.
+func excludesRune(fl FieldLevel) bool {
+ return !containsRune(fl)
+}
+
+// ExcludesAll is the validation function for validating that the field's value does not contain any of the characters specified within the param.
+func excludesAll(fl FieldLevel) bool {
+ return !containsAny(fl)
+}
+
+// Excludes is the validation function for validating that the field's value does not contain the text specified within the param.
+func excludes(fl FieldLevel) bool {
+ return !contains(fl)
+}
+
+// ContainsRune is the validation function for validating that the field's value contains the rune specified within the param.
+func containsRune(fl FieldLevel) bool {
+
+ r, _ := utf8.DecodeRuneInString(fl.Param())
+
+ return strings.ContainsRune(fl.Field().String(), r)
+}
+
+// ContainsAny is the validation function for validating that the field's value contains any of the characters specified within the param.
+func containsAny(fl FieldLevel) bool {
+ return strings.ContainsAny(fl.Field().String(), fl.Param())
+}
+
+// Contains is the validation function for validating that the field's value contains the text specified within the param.
+func contains(fl FieldLevel) bool {
+ return strings.Contains(fl.Field().String(), fl.Param())
+}
+
+// StartsWith is the validation function for validating that the field's value starts with the text specified within the param.
+func startsWith(fl FieldLevel) bool {
+ return strings.HasPrefix(fl.Field().String(), fl.Param())
+}
+
+// EndsWith is the validation function for validating that the field's value ends with the text specified within the param.
+func endsWith(fl FieldLevel) bool {
+ return strings.HasSuffix(fl.Field().String(), fl.Param())
+}
+
+// StartsNotWith is the validation function for validating that the field's value does not start with the text specified within the param.
+func startsNotWith(fl FieldLevel) bool {
+ return !startsWith(fl)
+}
+
+// EndsNotWith is the validation function for validating that the field's value does not end with the text specified within the param.
+func endsNotWith(fl FieldLevel) bool {
+ return !endsWith(fl)
+}
+
+// FieldContains is the validation function for validating if the current field's value contains the field specified by the param's value.
+func fieldContains(fl FieldLevel) bool {
+ field := fl.Field()
+
+ currentField, _, ok := fl.GetStructFieldOK()
+
+ if !ok {
+ return false
+ }
+
+ return strings.Contains(field.String(), currentField.String())
+}
+
+// FieldExcludes is the validation function for validating if the current field's value excludes the field specified by the param's value.
+func fieldExcludes(fl FieldLevel) bool {
+ field := fl.Field()
+
+ currentField, _, ok := fl.GetStructFieldOK()
+ if !ok {
+ return true
+ }
+
+ return !strings.Contains(field.String(), currentField.String())
+}
+
+// IsNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value.
+func isNeField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ currentField, currentKind, ok := fl.GetStructFieldOK()
+
+ if !ok || currentKind != kind {
+ return true
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return field.Int() != currentField.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return field.Uint() != currentField.Uint()
+
+ case reflect.Float32, reflect.Float64:
+ return field.Float() != currentField.Float()
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ return int64(field.Len()) != int64(currentField.Len())
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != currentField.Type() {
+ return true
+ }
+
+ if fieldType == timeType {
+
+ t := currentField.Interface().(time.Time)
+ fieldTime := field.Interface().(time.Time)
+
+ return !fieldTime.Equal(t)
+ }
+
+ }
+
+ // default reflect.String:
+ return field.String() != currentField.String()
+}
+
+// IsNe is the validation function for validating that the field's value does not equal the provided param value.
+func isNe(fl FieldLevel) bool {
+ return !isEq(fl)
+}
+
+// IsLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value.
+func isLteCrossStructField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ topField, topKind, ok := fl.GetStructFieldOK()
+ if !ok || topKind != kind {
+ return false
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return field.Int() <= topField.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return field.Uint() <= topField.Uint()
+
+ case reflect.Float32, reflect.Float64:
+ return field.Float() <= topField.Float()
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ return int64(field.Len()) <= int64(topField.Len())
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != topField.Type() {
+ return false
+ }
+
+ if fieldType == timeType {
+
+ fieldTime := field.Interface().(time.Time)
+ topTime := topField.Interface().(time.Time)
+
+ return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
+ }
+ }
+
+ // default reflect.String:
+ return field.String() <= topField.String()
+}
+
+// IsLtCrossStructField is the validation function for validating if the current field's value is less than the field, within a separate struct, specified by the param's value.
+// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
+func isLtCrossStructField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ topField, topKind, ok := fl.GetStructFieldOK()
+ if !ok || topKind != kind {
+ return false
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return field.Int() < topField.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return field.Uint() < topField.Uint()
+
+ case reflect.Float32, reflect.Float64:
+ return field.Float() < topField.Float()
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ return int64(field.Len()) < int64(topField.Len())
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != topField.Type() {
+ return false
+ }
+
+ if fieldType == timeType {
+
+ fieldTime := field.Interface().(time.Time)
+ topTime := topField.Interface().(time.Time)
+
+ return fieldTime.Before(topTime)
+ }
+ }
+
+ // default reflect.String:
+ return field.String() < topField.String()
+}
+
+// IsGteCrossStructField is the validation function for validating if the current field's value is greater than or equal to the field, within a separate struct, specified by the param's value.
+func isGteCrossStructField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ topField, topKind, ok := fl.GetStructFieldOK()
+ if !ok || topKind != kind {
+ return false
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return field.Int() >= topField.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return field.Uint() >= topField.Uint()
+
+ case reflect.Float32, reflect.Float64:
+ return field.Float() >= topField.Float()
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ return int64(field.Len()) >= int64(topField.Len())
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != topField.Type() {
+ return false
+ }
+
+ if fieldType == timeType {
+
+ fieldTime := field.Interface().(time.Time)
+ topTime := topField.Interface().(time.Time)
+
+ return fieldTime.After(topTime) || fieldTime.Equal(topTime)
+ }
+ }
+
+ // default reflect.String:
+ return field.String() >= topField.String()
+}
+
+// IsGtCrossStructField is the validation function for validating if the current field's value is greater than the field, within a separate struct, specified by the param's value.
+func isGtCrossStructField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ topField, topKind, ok := fl.GetStructFieldOK()
+ if !ok || topKind != kind {
+ return false
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return field.Int() > topField.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return field.Uint() > topField.Uint()
+
+ case reflect.Float32, reflect.Float64:
+ return field.Float() > topField.Float()
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ return int64(field.Len()) > int64(topField.Len())
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != topField.Type() {
+ return false
+ }
+
+ if fieldType == timeType {
+
+ fieldTime := field.Interface().(time.Time)
+ topTime := topField.Interface().(time.Time)
+
+ return fieldTime.After(topTime)
+ }
+ }
+
+ // default reflect.String:
+ return field.String() > topField.String()
+}
+
+// IsNeCrossStructField is the validation function for validating that the current field's value is not equal to the field, within a separate struct, specified by the param's value.
+func isNeCrossStructField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ topField, currentKind, ok := fl.GetStructFieldOK()
+ if !ok || currentKind != kind {
+ return true
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return topField.Int() != field.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return topField.Uint() != field.Uint()
+
+ case reflect.Float32, reflect.Float64:
+ return topField.Float() != field.Float()
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ return int64(topField.Len()) != int64(field.Len())
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != topField.Type() {
+ return true
+ }
+
+ if fieldType == timeType {
+
+ t := field.Interface().(time.Time)
+ fieldTime := topField.Interface().(time.Time)
+
+ return !fieldTime.Equal(t)
+ }
+ }
+
+ // default reflect.String:
+ return topField.String() != field.String()
+}
+
+// IsEqCrossStructField is the validation function for validating that the current field's value is equal to the field, within a separate struct, specified by the param's value.
+func isEqCrossStructField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ topField, topKind, ok := fl.GetStructFieldOK()
+ if !ok || topKind != kind {
+ return false
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return topField.Int() == field.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return topField.Uint() == field.Uint()
+
+ case reflect.Float32, reflect.Float64:
+ return topField.Float() == field.Float()
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ return int64(topField.Len()) == int64(field.Len())
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != topField.Type() {
+ return false
+ }
+
+ if fieldType == timeType {
+
+ t := field.Interface().(time.Time)
+ fieldTime := topField.Interface().(time.Time)
+
+ return fieldTime.Equal(t)
+ }
+ }
+
+ // default reflect.String:
+ return topField.String() == field.String()
+}
+
+// IsEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value.
+func isEqField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ currentField, currentKind, ok := fl.GetStructFieldOK()
+ if !ok || currentKind != kind {
+ return false
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return field.Int() == currentField.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return field.Uint() == currentField.Uint()
+
+ case reflect.Float32, reflect.Float64:
+ return field.Float() == currentField.Float()
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ return int64(field.Len()) == int64(currentField.Len())
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != currentField.Type() {
+ return false
+ }
+
+ if fieldType == timeType {
+
+ t := currentField.Interface().(time.Time)
+ fieldTime := field.Interface().(time.Time)
+
+ return fieldTime.Equal(t)
+ }
+
+ }
+
+ // default reflect.String:
+ return field.String() == currentField.String()
+}
+
+// IsEq is the validation function for validating if the current field's value is equal to the param's value.
+func isEq(fl FieldLevel) bool {
+
+ field := fl.Field()
+ param := fl.Param()
+
+ switch field.Kind() {
+
+ case reflect.String:
+ return field.String() == param
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(field.Len()) == p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asIntFromType(field.Type(), param)
+
+ return field.Int() == p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return field.Uint() == p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return field.Float() == p
+
+ case reflect.Bool:
+ p := asBool(param)
+
+ return field.Bool() == p
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// IsBase64 is the validation function for validating if the current field's value is a valid base 64.
+func isBase64(fl FieldLevel) bool {
+ return base64Regex.MatchString(fl.Field().String())
+}
+
+// IsBase64URL is the validation function for validating if the current field's value is a valid base64 URL safe string.
+func isBase64URL(fl FieldLevel) bool {
+ return base64URLRegex.MatchString(fl.Field().String())
+}
+
+// IsURI is the validation function for validating if the current field's value is a valid URI.
+func isURI(fl FieldLevel) bool {
+
+ field := fl.Field()
+
+ switch field.Kind() {
+
+ case reflect.String:
+
+ s := field.String()
+
+ // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195
+ // emulate browser and strip the '#' suffix prior to validation. see issue-#237
+ if i := strings.Index(s, "#"); i > -1 {
+ s = s[:i]
+ }
+
+ if len(s) == 0 {
+ return false
+ }
+
+ _, err := url.ParseRequestURI(s)
+
+ return err == nil
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// IsURL is the validation function for validating if the current field's value is a valid URL.
+func isURL(fl FieldLevel) bool {
+
+ field := fl.Field()
+
+ switch field.Kind() {
+
+ case reflect.String:
+
+ var i int
+ s := field.String()
+
+ // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195
+ // emulate browser and strip the '#' suffix prior to validation. see issue-#237
+ if i = strings.Index(s, "#"); i > -1 {
+ s = s[:i]
+ }
+
+ if len(s) == 0 {
+ return false
+ }
+
+ url, err := url.ParseRequestURI(s)
+
+ if err != nil || url.Scheme == "" {
+ return false
+ }
+
+ return true
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141.
+func isUrnRFC2141(fl FieldLevel) bool {
+ field := fl.Field()
+
+ switch field.Kind() {
+
+ case reflect.String:
+
+ str := field.String()
+
+ _, match := urn.Parse([]byte(str))
+
+ return match
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// IsFile is the validation function for validating if the current field's value is a valid file path.
+func isFile(fl FieldLevel) bool {
+ field := fl.Field()
+
+ switch field.Kind() {
+ case reflect.String:
+ fileInfo, err := os.Stat(field.String())
+ if err != nil {
+ return false
+ }
+
+ return !fileInfo.IsDir()
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// IsE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number.
+func isE164(fl FieldLevel) bool {
+ return e164Regex.MatchString(fl.Field().String())
+}
+
+// IsEmail is the validation function for validating if the current field's value is a valid email address.
+func isEmail(fl FieldLevel) bool {
+ return emailRegex.MatchString(fl.Field().String())
+}
+
+// IsHSLA is the validation function for validating if the current field's value is a valid HSLA color.
+func isHSLA(fl FieldLevel) bool {
+ return hslaRegex.MatchString(fl.Field().String())
+}
+
+// IsHSL is the validation function for validating if the current field's value is a valid HSL color.
+func isHSL(fl FieldLevel) bool {
+ return hslRegex.MatchString(fl.Field().String())
+}
+
+// IsRGBA is the validation function for validating if the current field's value is a valid RGBA color.
+func isRGBA(fl FieldLevel) bool {
+ return rgbaRegex.MatchString(fl.Field().String())
+}
+
+// IsRGB is the validation function for validating if the current field's value is a valid RGB color.
+func isRGB(fl FieldLevel) bool {
+ return rgbRegex.MatchString(fl.Field().String())
+}
+
+// IsHEXColor is the validation function for validating if the current field's value is a valid HEX color.
+func isHEXColor(fl FieldLevel) bool {
+ return hexcolorRegex.MatchString(fl.Field().String())
+}
+
+// IsHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal.
+func isHexadecimal(fl FieldLevel) bool {
+ return hexadecimalRegex.MatchString(fl.Field().String())
+}
+
+// IsNumber is the validation function for validating if the current field's value is a valid number.
+func isNumber(fl FieldLevel) bool {
+ switch fl.Field().Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
+ return true
+ default:
+ return numberRegex.MatchString(fl.Field().String())
+ }
+}
+
+// IsNumeric is the validation function for validating if the current field's value is a valid numeric value.
+func isNumeric(fl FieldLevel) bool {
+ switch fl.Field().Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
+ return true
+ default:
+ return numericRegex.MatchString(fl.Field().String())
+ }
+}
+
+// IsAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value.
+func isAlphanum(fl FieldLevel) bool {
+ return alphaNumericRegex.MatchString(fl.Field().String())
+}
+
+// IsAlpha is the validation function for validating if the current field's value is a valid alpha value.
+func isAlpha(fl FieldLevel) bool {
+ return alphaRegex.MatchString(fl.Field().String())
+}
+
+// IsAlphanumUnicode is the validation function for validating if the current field's value is a valid alphanumeric unicode value.
+func isAlphanumUnicode(fl FieldLevel) bool {
+ return alphaUnicodeNumericRegex.MatchString(fl.Field().String())
+}
+
+// IsAlphaUnicode is the validation function for validating if the current field's value is a valid alpha unicode value.
+func isAlphaUnicode(fl FieldLevel) bool {
+ return alphaUnicodeRegex.MatchString(fl.Field().String())
+}
+
+// isDefault is the opposite of required aka hasValue
+func isDefault(fl FieldLevel) bool {
+ return !hasValue(fl)
+}
+
+// HasValue is the validation function for validating if the current field's value is not the default static value.
+func hasValue(fl FieldLevel) bool {
+ field := fl.Field()
+ switch field.Kind() {
+ case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
+ return !field.IsNil()
+ default:
+ if fl.(*validate).fldIsPointer && field.Interface() != nil {
+ return true
+ }
+ return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
+ }
+}
+
+// requireCheckField is a func for check field kind
+func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool {
+ field := fl.Field()
+ kind := field.Kind()
+ var nullable, found bool
+ if len(param) > 0 {
+ field, kind, nullable, found = fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
+ if !found {
+ return defaultNotFoundValue
+ }
+ }
+ switch kind {
+ case reflect.Invalid:
+ return defaultNotFoundValue
+ case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
+ return field.IsNil()
+ default:
+ if nullable && field.Interface() != nil {
+ return false
+ }
+ return field.IsValid() && field.Interface() == reflect.Zero(field.Type()).Interface()
+ }
+}
+
+// requireCheckFieldValue is a func for check field value
+func requireCheckFieldValue(fl FieldLevel, param string, value string, defaultNotFoundValue bool) bool {
+ field, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
+ if !found {
+ return defaultNotFoundValue
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return field.Int() == asInt(value)
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return field.Uint() == asUint(value)
+
+ case reflect.Float32, reflect.Float64:
+ return field.Float() == asFloat(value)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ return int64(field.Len()) == asInt(value)
+ }
+
+ // default reflect.String:
+ return field.String() == value
+}
+
+// requiredIf is the validation function
+// The field under validation must be present and not empty only if all the other specified fields are equal to the value following with the specified field.
+func requiredIf(fl FieldLevel) bool {
+ params := parseOneOfParam2(fl.Param())
+ if len(params)%2 != 0 {
+ panic(fmt.Sprintf("Bad param number for required_if %s", fl.FieldName()))
+ }
+ for i := 0; i < len(params); i += 2 {
+ if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
+ return true
+ }
+ }
+ return hasValue(fl)
+}
+
+// requiredUnless is the validation function
+// The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
+func requiredUnless(fl FieldLevel) bool {
+ params := parseOneOfParam2(fl.Param())
+ if len(params)%2 != 0 {
+ panic(fmt.Sprintf("Bad param number for required_unless %s", fl.FieldName()))
+ }
+
+ for i := 0; i < len(params); i += 2 {
+ if requireCheckFieldValue(fl, params[i], params[i+1], false) {
+ return true
+ }
+ }
+ return hasValue(fl)
+}
+
+// ExcludedWith is the validation function
+// The field under validation must not be present or is empty if any of the other specified fields are present.
+func excludedWith(fl FieldLevel) bool {
+ params := parseOneOfParam2(fl.Param())
+ for _, param := range params {
+ if !requireCheckFieldKind(fl, param, true) {
+ return !hasValue(fl)
+ }
+ }
+ return true
+}
+
+// RequiredWith is the validation function
+// The field under validation must be present and not empty only if any of the other specified fields are present.
+func requiredWith(fl FieldLevel) bool {
+ params := parseOneOfParam2(fl.Param())
+ for _, param := range params {
+ if !requireCheckFieldKind(fl, param, true) {
+ return hasValue(fl)
+ }
+ }
+ return true
+}
+
+// ExcludedWithAll is the validation function
+// The field under validation must not be present or is empty if all of the other specified fields are present.
+func excludedWithAll(fl FieldLevel) bool {
+ params := parseOneOfParam2(fl.Param())
+ for _, param := range params {
+ if requireCheckFieldKind(fl, param, true) {
+ return true
+ }
+ }
+ return !hasValue(fl)
+}
+
+// RequiredWithAll is the validation function
+// The field under validation must be present and not empty only if all of the other specified fields are present.
+func requiredWithAll(fl FieldLevel) bool {
+ params := parseOneOfParam2(fl.Param())
+ for _, param := range params {
+ if requireCheckFieldKind(fl, param, true) {
+ return true
+ }
+ }
+ return hasValue(fl)
+}
+
+// ExcludedWithout is the validation function
+// The field under validation must not be present or is empty when any of the other specified fields are not present.
+func excludedWithout(fl FieldLevel) bool {
+ if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
+ return !hasValue(fl)
+ }
+ return true
+}
+
+// RequiredWithout is the validation function
+// The field under validation must be present and not empty only when any of the other specified fields are not present.
+func requiredWithout(fl FieldLevel) bool {
+ if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
+ return hasValue(fl)
+ }
+ return true
+}
+
+// RequiredWithoutAll is the validation function
+// The field under validation must not be present or is empty when all of the other specified fields are not present.
+func excludedWithoutAll(fl FieldLevel) bool {
+ params := parseOneOfParam2(fl.Param())
+ for _, param := range params {
+ if !requireCheckFieldKind(fl, param, true) {
+ return true
+ }
+ }
+ return !hasValue(fl)
+}
+
+// RequiredWithoutAll is the validation function
+// The field under validation must be present and not empty only when all of the other specified fields are not present.
+func requiredWithoutAll(fl FieldLevel) bool {
+ params := parseOneOfParam2(fl.Param())
+ for _, param := range params {
+ if !requireCheckFieldKind(fl, param, true) {
+ return true
+ }
+ }
+ return hasValue(fl)
+}
+
+// IsGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value.
+func isGteField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ currentField, currentKind, ok := fl.GetStructFieldOK()
+ if !ok || currentKind != kind {
+ return false
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+
+ return field.Int() >= currentField.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+
+ return field.Uint() >= currentField.Uint()
+
+ case reflect.Float32, reflect.Float64:
+
+ return field.Float() >= currentField.Float()
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != currentField.Type() {
+ return false
+ }
+
+ if fieldType == timeType {
+
+ t := currentField.Interface().(time.Time)
+ fieldTime := field.Interface().(time.Time)
+
+ return fieldTime.After(t) || fieldTime.Equal(t)
+ }
+ }
+
+ // default reflect.String
+ return len(field.String()) >= len(currentField.String())
+}
+
+// IsGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value.
+func isGtField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ currentField, currentKind, ok := fl.GetStructFieldOK()
+ if !ok || currentKind != kind { // 首先会 比较找到的 field 和现在的类型作比较
+ return false
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+
+ return field.Int() > currentField.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+
+ return field.Uint() > currentField.Uint()
+
+ case reflect.Float32, reflect.Float64:
+
+ return field.Float() > currentField.Float()
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != currentField.Type() {
+ return false
+ }
+
+ if fieldType == timeType {
+
+ t := currentField.Interface().(time.Time)
+ fieldTime := field.Interface().(time.Time)
+
+ return fieldTime.After(t)
+ }
+ }
+
+ // default reflect.String
+ return len(field.String()) > len(currentField.String())
+}
+
+// IsGte is the validation function for validating if the current field's value is greater than or equal to the param's value.
+func isGte(fl FieldLevel) bool {
+
+ field := fl.Field()
+ param := fl.Param()
+
+ switch field.Kind() {
+
+ case reflect.String:
+ p := asInt(param)
+
+ return int64(utf8.RuneCountInString(field.String())) >= p
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(field.Len()) >= p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asIntFromType(field.Type(), param)
+
+ return field.Int() >= p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return field.Uint() >= p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return field.Float() >= p
+
+ case reflect.Struct:
+
+ if field.Type() == timeType {
+
+ now := time.Now().UTC()
+ t := field.Interface().(time.Time)
+
+ return t.After(now) || t.Equal(now)
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// IsGt is the validation function for validating if the current field's value is greater than the param's value.
+func isGt(fl FieldLevel) bool {
+
+ field := fl.Field()
+ param := fl.Param()
+
+ switch field.Kind() {
+
+ case reflect.String:
+ p := asInt(param)
+
+ return int64(utf8.RuneCountInString(field.String())) > p
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(field.Len()) > p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asIntFromType(field.Type(), param)
+
+ return field.Int() > p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return field.Uint() > p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return field.Float() > p
+ case reflect.Struct:
+
+ if field.Type() == timeType {
+
+ return field.Interface().(time.Time).After(time.Now().UTC())
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// HasLengthOf is the validation function for validating if the current field's value is equal to the param's value.
+func hasLengthOf(fl FieldLevel) bool {
+
+ field := fl.Field()
+ param := fl.Param()
+
+ switch field.Kind() {
+
+ case reflect.String:
+ p := asInt(param)
+
+ return int64(utf8.RuneCountInString(field.String())) == p
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(field.Len()) == p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asIntFromType(field.Type(), param)
+
+ return field.Int() == p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return field.Uint() == p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return field.Float() == p
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// HasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value.
+func hasMinOf(fl FieldLevel) bool {
+ return isGte(fl)
+}
+
+// IsLteField is the validation function for validating if the current field's value is less than or equal to the field specified by the param's value.
+func isLteField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ currentField, currentKind, ok := fl.GetStructFieldOK()
+ if !ok || currentKind != kind {
+ return false
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+
+ return field.Int() <= currentField.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+
+ return field.Uint() <= currentField.Uint()
+
+ case reflect.Float32, reflect.Float64:
+
+ return field.Float() <= currentField.Float()
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != currentField.Type() {
+ return false
+ }
+
+ if fieldType == timeType {
+
+ t := currentField.Interface().(time.Time)
+ fieldTime := field.Interface().(time.Time)
+
+ return fieldTime.Before(t) || fieldTime.Equal(t)
+ }
+ }
+
+ // default reflect.String
+ return len(field.String()) <= len(currentField.String())
+}
+
+// IsLtField is the validation function for validating if the current field's value is less than the field specified by the param's value.
+func isLtField(fl FieldLevel) bool {
+
+ field := fl.Field()
+ kind := field.Kind()
+
+ currentField, currentKind, ok := fl.GetStructFieldOK()
+ if !ok || currentKind != kind {
+ return false
+ }
+
+ switch kind {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+
+ return field.Int() < currentField.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+
+ return field.Uint() < currentField.Uint()
+
+ case reflect.Float32, reflect.Float64:
+
+ return field.Float() < currentField.Float()
+
+ case reflect.Struct:
+
+ fieldType := field.Type()
+
+ // Not Same underlying type i.e. struct and time
+ if fieldType != currentField.Type() {
+ return false
+ }
+
+ if fieldType == timeType {
+
+ t := currentField.Interface().(time.Time)
+ fieldTime := field.Interface().(time.Time)
+
+ return fieldTime.Before(t)
+ }
+ }
+
+ // default reflect.String
+ return len(field.String()) < len(currentField.String())
+}
+
+// IsLte is the validation function for validating if the current field's value is less than or equal to the param's value.
+func isLte(fl FieldLevel) bool {
+
+ field := fl.Field()
+ param := fl.Param()
+
+ switch field.Kind() {
+
+ case reflect.String:
+ p := asInt(param)
+
+ return int64(utf8.RuneCountInString(field.String())) <= p
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(field.Len()) <= p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asIntFromType(field.Type(), param)
+
+ return field.Int() <= p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return field.Uint() <= p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return field.Float() <= p
+
+ case reflect.Struct:
+
+ if field.Type() == timeType {
+
+ now := time.Now().UTC()
+ t := field.Interface().(time.Time)
+
+ return t.Before(now) || t.Equal(now)
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// IsLt is the validation function for validating if the current field's value is less than the param's value.
+func isLt(fl FieldLevel) bool {
+
+ field := fl.Field()
+ param := fl.Param()
+
+ switch field.Kind() {
+
+ case reflect.String:
+ p := asInt(param)
+
+ return int64(utf8.RuneCountInString(field.String())) < p
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(field.Len()) < p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asIntFromType(field.Type(), param)
+
+ return field.Int() < p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return field.Uint() < p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return field.Float() < p
+
+ case reflect.Struct:
+
+ if field.Type() == timeType {
+
+ return field.Interface().(time.Time).Before(time.Now().UTC())
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// HasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value.
+func hasMaxOf(fl FieldLevel) bool {
+ return isLte(fl)
+}
+
+// IsTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address.
+func isTCP4AddrResolvable(fl FieldLevel) bool {
+
+ if !isIP4Addr(fl) {
+ return false
+ }
+
+ _, err := net.ResolveTCPAddr("tcp4", fl.Field().String())
+ return err == nil
+}
+
+// IsTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address.
+func isTCP6AddrResolvable(fl FieldLevel) bool {
+
+ if !isIP6Addr(fl) {
+ return false
+ }
+
+ _, err := net.ResolveTCPAddr("tcp6", fl.Field().String())
+
+ return err == nil
+}
+
+// IsTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address.
+func isTCPAddrResolvable(fl FieldLevel) bool {
+
+ if !isIP4Addr(fl) && !isIP6Addr(fl) {
+ return false
+ }
+
+ _, err := net.ResolveTCPAddr("tcp", fl.Field().String())
+
+ return err == nil
+}
+
+// IsUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address.
+func isUDP4AddrResolvable(fl FieldLevel) bool {
+
+ if !isIP4Addr(fl) {
+ return false
+ }
+
+ _, err := net.ResolveUDPAddr("udp4", fl.Field().String())
+
+ return err == nil
+}
+
+// IsUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address.
+func isUDP6AddrResolvable(fl FieldLevel) bool {
+
+ if !isIP6Addr(fl) {
+ return false
+ }
+
+ _, err := net.ResolveUDPAddr("udp6", fl.Field().String())
+
+ return err == nil
+}
+
+// IsUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address.
+func isUDPAddrResolvable(fl FieldLevel) bool {
+
+ if !isIP4Addr(fl) && !isIP6Addr(fl) {
+ return false
+ }
+
+ _, err := net.ResolveUDPAddr("udp", fl.Field().String())
+
+ return err == nil
+}
+
+// IsIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address.
+func isIP4AddrResolvable(fl FieldLevel) bool {
+
+ if !isIPv4(fl) {
+ return false
+ }
+
+ _, err := net.ResolveIPAddr("ip4", fl.Field().String())
+
+ return err == nil
+}
+
+// IsIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address.
+func isIP6AddrResolvable(fl FieldLevel) bool {
+
+ if !isIPv6(fl) {
+ return false
+ }
+
+ _, err := net.ResolveIPAddr("ip6", fl.Field().String())
+
+ return err == nil
+}
+
+// IsIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address.
+func isIPAddrResolvable(fl FieldLevel) bool {
+
+ if !isIP(fl) {
+ return false
+ }
+
+ _, err := net.ResolveIPAddr("ip", fl.Field().String())
+
+ return err == nil
+}
+
+// IsUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address.
+func isUnixAddrResolvable(fl FieldLevel) bool {
+
+ _, err := net.ResolveUnixAddr("unix", fl.Field().String())
+
+ return err == nil
+}
+
+func isIP4Addr(fl FieldLevel) bool {
+
+ val := fl.Field().String()
+
+ if idx := strings.LastIndex(val, ":"); idx != -1 {
+ val = val[0:idx]
+ }
+
+ ip := net.ParseIP(val)
+
+ return ip != nil && ip.To4() != nil
+}
+
+func isIP6Addr(fl FieldLevel) bool {
+
+ val := fl.Field().String()
+
+ if idx := strings.LastIndex(val, ":"); idx != -1 {
+ if idx != 0 && val[idx-1:idx] == "]" {
+ val = val[1 : idx-1]
+ }
+ }
+
+ ip := net.ParseIP(val)
+
+ return ip != nil && ip.To4() == nil
+}
+
+func isHostnameRFC952(fl FieldLevel) bool {
+ return hostnameRegexRFC952.MatchString(fl.Field().String())
+}
+
+func isHostnameRFC1123(fl FieldLevel) bool {
+ return hostnameRegexRFC1123.MatchString(fl.Field().String())
+}
+
+func isFQDN(fl FieldLevel) bool {
+ val := fl.Field().String()
+
+ if val == "" {
+ return false
+ }
+
+ return fqdnRegexRFC1123.MatchString(val)
+}
+
+// IsDir is the validation function for validating if the current field's value is a valid directory.
+func isDir(fl FieldLevel) bool {
+ field := fl.Field()
+
+ if field.Kind() == reflect.String {
+ fileInfo, err := os.Stat(field.String())
+ if err != nil {
+ return false
+ }
+
+ return fileInfo.IsDir()
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// isJSON is the validation function for validating if the current field's value is a valid json string.
+func isJSON(fl FieldLevel) bool {
+ field := fl.Field()
+
+ if field.Kind() == reflect.String {
+ val := field.String()
+ return json.Valid([]byte(val))
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// isHostnamePort validates a : combination for fields typically used for socket address.
+func isHostnamePort(fl FieldLevel) bool {
+ val := fl.Field().String()
+ host, port, err := net.SplitHostPort(val)
+ if err != nil {
+ return false
+ }
+ // Port must be a iny <= 65535.
+ if portNum, err := strconv.ParseInt(port, 10, 32); err != nil || portNum > 65535 || portNum < 1 {
+ return false
+ }
+
+ // If host is specified, it should match a DNS name
+ if host != "" {
+ return hostnameRegexRFC1123.MatchString(host)
+ }
+ return true
+}
+
+// isLowercase is the validation function for validating if the current field's value is a lowercase string.
+func isLowercase(fl FieldLevel) bool {
+ field := fl.Field()
+
+ if field.Kind() == reflect.String {
+ if field.String() == "" {
+ return false
+ }
+ return field.String() == strings.ToLower(field.String())
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// isUppercase is the validation function for validating if the current field's value is an uppercase string.
+func isUppercase(fl FieldLevel) bool {
+ field := fl.Field()
+
+ if field.Kind() == reflect.String {
+ if field.String() == "" {
+ return false
+ }
+ return field.String() == strings.ToUpper(field.String())
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// isDatetime is the validation function for validating if the current field's value is a valid datetime string.
+func isDatetime(fl FieldLevel) bool {
+ field := fl.Field()
+ param := fl.Param()
+
+ if field.Kind() == reflect.String {
+ _, err := time.Parse(param, field.String())
+
+ return err == nil
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// isTimeZone is the validation function for validating if the current field's value is a valid time zone string.
+func isTimeZone(fl FieldLevel) bool {
+ field := fl.Field()
+
+ if field.Kind() == reflect.String {
+ // empty value is converted to UTC by time.LoadLocation but disallow it as it is not a valid time zone name
+ if field.String() == "" {
+ return false
+ }
+
+ // Local value is converted to the current system time zone by time.LoadLocation but disallow it as it is not a valid time zone name
+ if strings.ToLower(field.String()) == "local" {
+ return false
+ }
+
+ _, err := time.LoadLocation(field.String())
+ return err == nil
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+}
+
+// isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 country code.
+func isIso3166Alpha2(fl FieldLevel) bool {
+ val := fl.Field().String()
+ return iso3166_1_alpha2[val]
+}
+
+// isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 country code.
+func isIso3166Alpha3(fl FieldLevel) bool {
+ val := fl.Field().String()
+ return iso3166_1_alpha3[val]
+}
+
+// isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric country code.
+func isIso3166AlphaNumeric(fl FieldLevel) bool {
+ field := fl.Field()
+
+ var code int
+ switch field.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ code = int(field.Int() % 1000)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ code = int(field.Uint() % 1000)
+ default:
+ panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ }
+ return iso3166_1_alpha_numeric[code]
+}
diff --git a/go-playground/validator/v10/benchmarks_test.go b/go-playground/validator/v10/benchmarks_test.go
new file mode 100644
index 0000000..ee70f95
--- /dev/null
+++ b/go-playground/validator/v10/benchmarks_test.go
@@ -0,0 +1,1099 @@
+package validator
+
+import (
+ "bytes"
+ sql "database/sql/driver"
+ "testing"
+ "time"
+)
+
+func BenchmarkFieldSuccess(b *testing.B) {
+ validate := New()
+ s := "1"
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(&s, "len=1")
+ }
+}
+
+func BenchmarkFieldSuccessParallel(b *testing.B) {
+ validate := New()
+ s := "1"
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(&s, "len=1")
+ }
+ })
+}
+
+func BenchmarkFieldFailure(b *testing.B) {
+ validate := New()
+ s := "12"
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(&s, "len=1")
+ }
+}
+
+func BenchmarkFieldFailureParallel(b *testing.B) {
+ validate := New()
+ s := "12"
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(&s, "len=1")
+ }
+ })
+}
+
+func BenchmarkFieldArrayDiveSuccess(b *testing.B) {
+ validate := New()
+ m := []string{"val1", "val2", "val3"}
+
+ b.ResetTimer()
+
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(m, "required,dive,required")
+ }
+}
+
+func BenchmarkFieldArrayDiveSuccessParallel(b *testing.B) {
+ validate := New()
+ m := []string{"val1", "val2", "val3"}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(m, "required,dive,required")
+ }
+ })
+}
+
+func BenchmarkFieldArrayDiveFailure(b *testing.B) {
+ validate := New()
+ m := []string{"val1", "", "val3"}
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(m, "required,dive,required")
+ }
+}
+
+func BenchmarkFieldArrayDiveFailureParallel(b *testing.B) {
+ validate := New()
+ m := []string{"val1", "", "val3"}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(m, "required,dive,required")
+ }
+ })
+}
+
+func BenchmarkFieldMapDiveSuccess(b *testing.B) {
+ validate := New()
+ m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
+
+ b.ResetTimer()
+
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(m, "required,dive,required")
+ }
+}
+
+func BenchmarkFieldMapDiveSuccessParallel(b *testing.B) {
+ validate := New()
+ m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(m, "required,dive,required")
+ }
+ })
+}
+
+func BenchmarkFieldMapDiveFailure(b *testing.B) {
+ validate := New()
+ m := map[string]string{"": "", "val3": "val3"}
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(m, "required,dive,required")
+ }
+}
+
+func BenchmarkFieldMapDiveFailureParallel(b *testing.B) {
+ validate := New()
+ m := map[string]string{"": "", "val3": "val3"}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(m, "required,dive,required")
+ }
+ })
+}
+
+func BenchmarkFieldMapDiveWithKeysSuccess(b *testing.B) {
+ validate := New()
+ m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
+
+ b.ResetTimer()
+
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(m, "required,dive,keys,required,endkeys,required")
+ }
+}
+
+func BenchmarkFieldMapDiveWithKeysSuccessParallel(b *testing.B) {
+ validate := New()
+ m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(m, "required,dive,keys,required,endkeys,required")
+ }
+ })
+}
+
+func BenchmarkFieldMapDiveWithKeysFailure(b *testing.B) {
+ validate := New()
+ m := map[string]string{"": "", "val3": "val3"}
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(m, "required,dive,keys,required,endkeys,required")
+ }
+}
+
+func BenchmarkFieldMapDiveWithKeysFailureParallel(b *testing.B) {
+ validate := New()
+ m := map[string]string{"": "", "val3": "val3"}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(m, "required,dive,keys,required,endkeys,required")
+ }
+ })
+}
+
+func BenchmarkFieldCustomTypeSuccess(b *testing.B) {
+ validate := New()
+ validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
+ val := valuer{
+ Name: "1",
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(val, "len=1")
+ }
+}
+
+func BenchmarkFieldCustomTypeSuccessParallel(b *testing.B) {
+ validate := New()
+ validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
+ val := valuer{
+ Name: "1",
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(val, "len=1")
+ }
+ })
+}
+
+func BenchmarkFieldCustomTypeFailure(b *testing.B) {
+ validate := New()
+ validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
+ val := valuer{}
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(val, "len=1")
+ }
+}
+
+func BenchmarkFieldCustomTypeFailureParallel(b *testing.B) {
+ validate := New()
+ validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
+ val := valuer{}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(val, "len=1")
+ }
+ })
+}
+
+func BenchmarkFieldOrTagSuccess(b *testing.B) {
+ validate := New()
+ s := "rgba(0,0,0,1)"
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(s, "rgb|rgba")
+ }
+}
+
+func BenchmarkFieldOrTagSuccessParallel(b *testing.B) {
+ validate := New()
+ s := "rgba(0,0,0,1)"
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(s, "rgb|rgba")
+ }
+ })
+}
+
+func BenchmarkFieldOrTagFailure(b *testing.B) {
+ validate := New()
+ s := "#000"
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Var(s, "rgb|rgba")
+ }
+}
+
+func BenchmarkFieldOrTagFailureParallel(b *testing.B) {
+ validate := New()
+ s := "#000"
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Var(s, "rgb|rgba")
+ }
+ })
+}
+
+func BenchmarkStructLevelValidationSuccess(b *testing.B) {
+ validate := New()
+ validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{})
+
+ tst := TestStruct{
+ String: "good value",
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(tst)
+ }
+}
+
+func BenchmarkStructLevelValidationSuccessParallel(b *testing.B) {
+ validate := New()
+ validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{})
+
+ tst := TestStruct{
+ String: "good value",
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(tst)
+ }
+ })
+}
+
+func BenchmarkStructLevelValidationFailure(b *testing.B) {
+ validate := New()
+ validate.RegisterStructValidation(StructValidationTestStruct, TestStruct{})
+
+ tst := TestStruct{
+ String: "good value",
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(tst)
+ }
+}
+
+func BenchmarkStructLevelValidationFailureParallel(b *testing.B) {
+ validate := New()
+ validate.RegisterStructValidation(StructValidationTestStruct, TestStruct{})
+
+ tst := TestStruct{
+ String: "good value",
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(tst)
+ }
+ })
+}
+
+func BenchmarkStructSimpleCustomTypeSuccess(b *testing.B) {
+ validate := New()
+ validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
+
+ val := valuer{
+ Name: "1",
+ }
+
+ type Foo struct {
+ Valuer valuer `validate:"len=1"`
+ IntValue int `validate:"min=5,max=10"`
+ }
+
+ validFoo := &Foo{Valuer: val, IntValue: 7}
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(validFoo)
+ }
+}
+
+func BenchmarkStructSimpleCustomTypeSuccessParallel(b *testing.B) {
+ validate := New()
+ validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
+ val := valuer{
+ Name: "1",
+ }
+
+ type Foo struct {
+ Valuer valuer `validate:"len=1"`
+ IntValue int `validate:"min=5,max=10"`
+ }
+ validFoo := &Foo{Valuer: val, IntValue: 7}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(validFoo)
+ }
+ })
+}
+
+func BenchmarkStructSimpleCustomTypeFailure(b *testing.B) {
+ validate := New()
+ validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
+
+ val := valuer{}
+
+ type Foo struct {
+ Valuer valuer `validate:"len=1"`
+ IntValue int `validate:"min=5,max=10"`
+ }
+ validFoo := &Foo{Valuer: val, IntValue: 3}
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(validFoo)
+ }
+}
+
+func BenchmarkStructSimpleCustomTypeFailureParallel(b *testing.B) {
+ validate := New()
+ validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
+
+ val := valuer{}
+
+ type Foo struct {
+ Valuer valuer `validate:"len=1"`
+ IntValue int `validate:"min=5,max=10"`
+ }
+ validFoo := &Foo{Valuer: val, IntValue: 3}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(validate.Struct(validFoo))
+ }
+ })
+}
+
+func BenchmarkStructFilteredSuccess(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+ byts := []byte("Name")
+ fn := func(ns []byte) bool {
+ return !bytes.HasSuffix(ns, byts)
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.StructFiltered(test, fn)
+ }
+}
+
+func BenchmarkStructFilteredSuccessParallel(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+ byts := []byte("Name")
+ fn := func(ns []byte) bool {
+ return !bytes.HasSuffix(ns, byts)
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.StructFiltered(test, fn)
+ }
+ })
+}
+
+func BenchmarkStructFilteredFailure(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+
+ byts := []byte("NickName")
+
+ fn := func(ns []byte) bool {
+ return !bytes.HasSuffix(ns, byts)
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.StructFiltered(test, fn)
+ }
+}
+
+func BenchmarkStructFilteredFailureParallel(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+ byts := []byte("NickName")
+ fn := func(ns []byte) bool {
+ return !bytes.HasSuffix(ns, byts)
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.StructFiltered(test, fn)
+ }
+ })
+}
+
+func BenchmarkStructPartialSuccess(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.StructPartial(test, "Name")
+ }
+}
+
+func BenchmarkStructPartialSuccessParallel(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.StructPartial(test, "Name")
+ }
+ })
+}
+
+func BenchmarkStructPartialFailure(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.StructPartial(test, "NickName")
+ }
+}
+
+func BenchmarkStructPartialFailureParallel(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.StructPartial(test, "NickName")
+ }
+ })
+}
+
+func BenchmarkStructExceptSuccess(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.StructExcept(test, "Nickname")
+ }
+}
+
+func BenchmarkStructExceptSuccessParallel(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.StructExcept(test, "NickName")
+ }
+ })
+}
+
+func BenchmarkStructExceptFailure(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.StructExcept(test, "Name")
+ }
+}
+
+func BenchmarkStructExceptFailureParallel(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Name string `validate:"required"`
+ NickName string `validate:"required"`
+ }
+
+ test := &Test{
+ Name: "Joey Bloggs",
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.StructExcept(test, "Name")
+ }
+ })
+}
+
+func BenchmarkStructSimpleCrossFieldSuccess(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Start time.Time
+ End time.Time `validate:"gtfield=Start"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * 5)
+ test := &Test{
+ Start: now,
+ End: then,
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(test)
+ }
+}
+
+func BenchmarkStructSimpleCrossFieldSuccessParallel(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Start time.Time
+ End time.Time `validate:"gtfield=Start"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * 5)
+ test := &Test{
+ Start: now,
+ End: then,
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(test)
+ }
+ })
+}
+
+func BenchmarkStructSimpleCrossFieldFailure(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Start time.Time
+ End time.Time `validate:"gtfield=Start"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * -5)
+
+ test := &Test{
+ Start: now,
+ End: then,
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(test)
+ }
+}
+
+func BenchmarkStructSimpleCrossFieldFailureParallel(b *testing.B) {
+ validate := New()
+
+ type Test struct {
+ Start time.Time
+ End time.Time `validate:"gtfield=Start"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * -5)
+ test := &Test{
+ Start: now,
+ End: then,
+ }
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(test)
+ }
+ })
+}
+
+func BenchmarkStructSimpleCrossStructCrossFieldSuccess(b *testing.B) {
+ validate := New()
+
+ type Inner struct {
+ Start time.Time
+ }
+
+ type Outer struct {
+ Inner *Inner
+ CreatedAt time.Time `validate:"eqcsfield=Inner.Start"`
+ }
+
+ now := time.Now().UTC()
+ inner := &Inner{
+ Start: now,
+ }
+ outer := &Outer{
+ Inner: inner,
+ CreatedAt: now,
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(outer)
+ }
+}
+
+func BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel(b *testing.B) {
+ validate := New()
+
+ type Inner struct {
+ Start time.Time
+ }
+
+ type Outer struct {
+ Inner *Inner
+ CreatedAt time.Time `validate:"eqcsfield=Inner.Start"`
+ }
+
+ now := time.Now().UTC()
+ inner := &Inner{
+ Start: now,
+ }
+ outer := &Outer{
+ Inner: inner,
+ CreatedAt: now,
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(outer)
+ }
+ })
+}
+
+func BenchmarkStructSimpleCrossStructCrossFieldFailure(b *testing.B) {
+ validate := New()
+ type Inner struct {
+ Start time.Time
+ }
+
+ type Outer struct {
+ Inner *Inner
+ CreatedAt time.Time `validate:"eqcsfield=Inner.Start"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * 5)
+
+ inner := &Inner{
+ Start: then,
+ }
+
+ outer := &Outer{
+ Inner: inner,
+ CreatedAt: now,
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(outer)
+ }
+}
+
+func BenchmarkStructSimpleCrossStructCrossFieldFailureParallel(b *testing.B) {
+ validate := New()
+
+ type Inner struct {
+ Start time.Time
+ }
+
+ type Outer struct {
+ Inner *Inner
+ CreatedAt time.Time `validate:"eqcsfield=Inner.Start"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * 5)
+
+ inner := &Inner{
+ Start: then,
+ }
+
+ outer := &Outer{
+ Inner: inner,
+ CreatedAt: now,
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(outer)
+ }
+ })
+}
+
+func BenchmarkStructSimpleSuccess(b *testing.B) {
+ validate := New()
+ type Foo struct {
+ StringValue string `validate:"min=5,max=10"`
+ IntValue int `validate:"min=5,max=10"`
+ }
+
+ validFoo := &Foo{StringValue: "Foobar", IntValue: 7}
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(validFoo)
+ }
+}
+
+func BenchmarkStructSimpleSuccessParallel(b *testing.B) {
+ validate := New()
+ type Foo struct {
+ StringValue string `validate:"min=5,max=10"`
+ IntValue int `validate:"min=5,max=10"`
+ }
+ validFoo := &Foo{StringValue: "Foobar", IntValue: 7}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(validFoo)
+ }
+ })
+}
+
+func BenchmarkStructSimpleFailure(b *testing.B) {
+ validate := New()
+ type Foo struct {
+ StringValue string `validate:"min=5,max=10"`
+ IntValue int `validate:"min=5,max=10"`
+ }
+
+ invalidFoo := &Foo{StringValue: "Fo", IntValue: 3}
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(invalidFoo)
+ }
+}
+
+func BenchmarkStructSimpleFailureParallel(b *testing.B) {
+ validate := New()
+ type Foo struct {
+ StringValue string `validate:"min=5,max=10"`
+ IntValue int `validate:"min=5,max=10"`
+ }
+
+ invalidFoo := &Foo{StringValue: "Fo", IntValue: 3}
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(invalidFoo)
+ }
+ })
+}
+
+func BenchmarkStructComplexSuccess(b *testing.B) {
+ validate := New()
+ tSuccess := &TestString{
+ Required: "Required",
+ Len: "length==10",
+ Min: "min=1",
+ Max: "1234567890",
+ MinMax: "12345",
+ Lt: "012345678",
+ Lte: "0123456789",
+ Gt: "01234567890",
+ Gte: "0123456789",
+ OmitEmpty: "",
+ Sub: &SubTest{
+ Test: "1",
+ },
+ SubIgnore: &SubTest{
+ Test: "",
+ },
+ Anonymous: struct {
+ A string `validate:"required"`
+ }{
+ A: "1",
+ },
+ Iface: &Impl{
+ F: "123",
+ },
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(tSuccess)
+ }
+}
+
+func BenchmarkStructComplexSuccessParallel(b *testing.B) {
+ validate := New()
+ tSuccess := &TestString{
+ Required: "Required",
+ Len: "length==10",
+ Min: "min=1",
+ Max: "1234567890",
+ MinMax: "12345",
+ Lt: "012345678",
+ Lte: "0123456789",
+ Gt: "01234567890",
+ Gte: "0123456789",
+ OmitEmpty: "",
+ Sub: &SubTest{
+ Test: "1",
+ },
+ SubIgnore: &SubTest{
+ Test: "",
+ },
+ Anonymous: struct {
+ A string `validate:"required"`
+ }{
+ A: "1",
+ },
+ Iface: &Impl{
+ F: "123",
+ },
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(tSuccess)
+ }
+ })
+}
+
+func BenchmarkStructComplexFailure(b *testing.B) {
+ validate := New()
+ tFail := &TestString{
+ Required: "",
+ Len: "",
+ Min: "",
+ Max: "12345678901",
+ MinMax: "",
+ Lt: "0123456789",
+ Lte: "01234567890",
+ Gt: "1",
+ Gte: "1",
+ OmitEmpty: "12345678901",
+ Sub: &SubTest{
+ Test: "",
+ },
+ Anonymous: struct {
+ A string `validate:"required"`
+ }{
+ A: "",
+ },
+ Iface: &Impl{
+ F: "12",
+ },
+ }
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _ = validate.Struct(tFail)
+ }
+}
+
+func BenchmarkStructComplexFailureParallel(b *testing.B) {
+ validate := New()
+ tFail := &TestString{
+ Required: "",
+ Len: "",
+ Min: "",
+ Max: "12345678901",
+ MinMax: "",
+ Lt: "0123456789",
+ Lte: "01234567890",
+ Gt: "1",
+ Gte: "1",
+ OmitEmpty: "12345678901",
+ Sub: &SubTest{
+ Test: "",
+ },
+ Anonymous: struct {
+ A string `validate:"required"`
+ }{
+ A: "",
+ },
+ Iface: &Impl{
+ F: "12",
+ },
+ }
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = validate.Struct(tFail)
+ }
+ })
+}
+
+type TestOneof struct {
+ Color string `validate:"oneof=red green"`
+}
+
+func BenchmarkOneof(b *testing.B) {
+ w := &TestOneof{Color: "green"}
+ val := New()
+ for i := 0; i < b.N; i++ {
+ _ = val.Struct(w)
+ }
+}
+
+func BenchmarkOneofParallel(b *testing.B) {
+ w := &TestOneof{Color: "green"}
+ val := New()
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _ = val.Struct(w)
+ }
+ })
+}
diff --git a/go-playground/validator/v10/cache.go b/go-playground/validator/v10/cache.go
new file mode 100644
index 0000000..df48ce1
--- /dev/null
+++ b/go-playground/validator/v10/cache.go
@@ -0,0 +1,322 @@
+package validator
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "sync"
+ "sync/atomic"
+)
+
+type tagType uint8
+
+const (
+ typeDefault tagType = iota
+ typeOmitEmpty
+ typeIsDefault
+ typeNoStructLevel
+ typeStructOnly
+ typeDive
+ typeOr
+ typeKeys
+ typeEndKeys
+)
+
+const (
+ invalidValidation = "Invalid validation tag on field '%s'"
+ undefinedValidation = "Undefined validation function '%s' on field '%s'"
+ keysTagNotDefined = "'" + endKeysTag + "' tag encountered without a corresponding '" + keysTag + "' tag"
+)
+
+type structCache struct {
+ lock sync.Mutex
+ m atomic.Value // map[reflect.Type]*cStruct
+}
+
+func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) {
+ c, found = sc.m.Load().(map[reflect.Type]*cStruct)[key] //有一个断言的封装
+ return
+}
+
+func (sc *structCache) Set(key reflect.Type, value *cStruct) {
+ m := sc.m.Load().(map[reflect.Type]*cStruct)
+ nm := make(map[reflect.Type]*cStruct, len(m)+1)
+ for k, v := range m {
+ nm[k] = v
+ }
+ nm[key] = value
+ sc.m.Store(nm)
+}
+
+type tagCache struct {
+ lock sync.Mutex
+ m atomic.Value // map[string]*cTag
+}
+
+func (tc *tagCache) Get(key string) (c *cTag, found bool) {
+ c, found = tc.m.Load().(map[string]*cTag)[key]
+ return
+}
+
+func (tc *tagCache) Set(key string, value *cTag) {
+ m := tc.m.Load().(map[string]*cTag)
+ nm := make(map[string]*cTag, len(m)+1)
+ for k, v := range m {
+ nm[k] = v
+ }
+ nm[key] = value
+ tc.m.Store(nm)
+}
+
+type cStruct struct {
+ name string
+ fields []*cField
+ fn StructLevelFuncCtx
+}
+
+type cField struct {
+ idx int
+ name string // field.name
+ altName string // 如果没有自定义 tagNameFunc,就是field.name, 如果有,比如可以按 json_tag中的, 报错的时候就会按照这个 报错
+ namesEqual bool // field.name 和 altName是否相同
+ cTags *cTag //以链表的形式存在
+}
+
+type cTag struct {
+ tag string // (gte=10) 实际是 gte,不带参数
+ aliasTag string // 应该是别名, 不过在没有别名的时候等同于 tag
+ actualAliasTag string // 实际名字,而不是在func *Validate.parseFieldTagsRecursive 解析后的 别名
+ param string
+ keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation
+ next *cTag
+ fn FuncCtx
+ typeof tagType
+ hasTag bool
+ hasAlias bool
+ hasParam bool // true if parameter used eg. eq= where the equal sign has been set
+ isBlockEnd bool // indicates the current tag represents the last validation in the block , 表示当前field的最后一个,比如 `require,gte=10`,那么到gte就是true
+ runValidationWhenNil bool
+}
+
+func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct {
+ v.structCache.lock.Lock()
+ defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise!
+
+ typ := current.Type()
+
+ // could have been multiple trying to access, but once first is done this ensures struct
+ // isn't parsed again.
+ cs, ok := v.structCache.Get(typ)
+ if ok {
+ return cs
+ }
+
+ cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]}
+
+ numFields := current.NumField()
+
+ var ctag *cTag
+ var fld reflect.StructField
+ var tag string
+ var customName string
+
+ for i := 0; i < numFields; i++ {
+
+ fld = typ.Field(i)
+
+ if !fld.Anonymous && len(fld.PkgPath) > 0 { // 如果不是 嵌套field 且是 未导出字段, (大写的字段 pakPath为空)
+ continue
+ }
+
+ tag = fld.Tag.Get(v.tagName)
+
+ if tag == skipValidationTag {
+ continue
+ }
+
+ customName = fld.Name
+
+ if v.hasTagNameFunc { // 自定义的获取 名字的func
+ name := v.tagNameFunc(fld)
+ if len(name) > 0 {
+ customName = name
+ }
+ }
+
+ // NOTE: cannot use shared tag cache, because tags may be equal, but things like alias may be different
+ // and so only struct level caching can be used instead of combined with Field tag caching
+
+ if len(tag) > 0 {
+ ctag, _ = v.parseFieldTagsRecursive(tag, fld.Name, "", false) //返回链头
+ } else {
+ // even if field doesn't have validations need cTag for traversing to potential inner/nested
+ // elements of the field.
+ ctag = new(cTag)
+ }
+
+ cs.fields = append(cs.fields, &cField{
+ idx: i,
+ name: fld.Name,
+ altName: customName,
+ cTags: ctag,
+ namesEqual: fld.Name == customName,
+ })
+ }
+ v.structCache.Set(typ, cs)
+ return cs
+}
+
+func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) {
+ var t string
+ noAlias := len(alias) == 0
+ tags := strings.Split(tag, tagSeparator)
+
+ for i := 0; i < len(tags); i++ {
+ t = tags[i]
+ if noAlias {
+ alias = t
+ }
+
+ // check map for alias and process new tags, otherwise process as usual
+ if tagsVal, found := v.aliases[t]; found {
+ if i == 0 {
+ firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
+ } else {
+ next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
+ current.next, current = next, curr
+
+ }
+ continue
+ }
+
+ var prevTag tagType
+
+ if i == 0 {
+ current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true, typeof: typeDefault}
+ firstCtag = current
+ } else {
+ prevTag = current.typeof // 此刻之前current 还是 上一个的状态
+ current.next = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true}
+ current = current.next
+ }
+
+ switch t {
+ case diveTag:
+ current.typeof = typeDive
+ continue
+
+ case keysTag:
+ current.typeof = typeKeys
+
+ if i == 0 || prevTag != typeDive {
+ panic(fmt.Sprintf("'%s' tag must be immediately preceded by the '%s' tag", keysTag, diveTag))
+ }
+
+ current.typeof = typeKeys
+
+ // need to pass along only keys tag
+ // need to increment i to skip over the keys tags
+ b := make([]byte, 0, 64)
+
+ i++
+
+ for ; i < len(tags); i++ {
+
+ b = append(b, tags[i]...)
+ b = append(b, ',')
+
+ if tags[i] == endKeysTag {
+ break
+ }
+ }
+
+ current.keys, _ = v.parseFieldTagsRecursive(string(b[:len(b)-1]), fieldName, "", false)
+ continue
+
+ case endKeysTag:
+ current.typeof = typeEndKeys
+
+ // if there are more in tags then there was no keysTag defined
+ // and an error should be thrown
+ if i != len(tags)-1 {
+ panic(keysTagNotDefined)
+ }
+ return
+
+ case omitempty:
+ current.typeof = typeOmitEmpty
+ continue
+
+ case structOnlyTag:
+ current.typeof = typeStructOnly
+ continue
+
+ case noStructLevelTag:
+ current.typeof = typeNoStructLevel
+ continue
+
+ default:
+ if t == isdefault {
+ current.typeof = typeIsDefault
+ }
+ // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
+ orVals := strings.Split(t, orSeparator)
+
+ for j := 0; j < len(orVals); j++ {
+ vals := strings.SplitN(orVals[j], tagKeySeparator, 2) // binding:"gte=10" 的 =
+ if noAlias {
+ alias = vals[0] //如果没有别名就 gte , 前面的是 gte=10
+ current.aliasTag = alias
+ } else {
+ current.actualAliasTag = t // 原始名字 gte=10
+ }
+
+ if j > 0 { //这里的current.next 不应该是 .pre 前一个么,不过前一个也不对啊,后面的赋值还是给 current赋值啊
+ current.next = &cTag{aliasTag: alias, actualAliasTag: current.actualAliasTag, hasAlias: hasAlias, hasTag: true}
+ current = current.next
+ }
+ current.hasParam = len(vals) > 1
+
+ current.tag = vals[0]
+ if len(current.tag) == 0 {
+ panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName)))
+ }
+
+ if wrapper, ok := v.validations[current.tag]; ok {
+ current.fn = wrapper.fn
+ current.runValidationWhenNil = wrapper.runValidatinOnNil
+ } else {
+ panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName)))
+ }
+
+ if len(orVals) > 1 {
+ current.typeof = typeOr
+ }
+
+ if len(vals) > 1 {
+ current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
+ }
+ }
+ current.isBlockEnd = true
+ }
+ }
+ return
+}
+
+func (v *Validate) fetchCacheTag(tag string) *cTag {
+ // find cached tag
+ ctag, found := v.tagCache.Get(tag)
+ if !found {
+ v.tagCache.lock.Lock()
+ defer v.tagCache.lock.Unlock()
+
+ // could have been multiple trying to access, but once first is done this ensures tag
+ // isn't parsed again.
+ ctag, found = v.tagCache.Get(tag)
+ if !found {
+ ctag, _ = v.parseFieldTagsRecursive(tag, "", "", false)
+ v.tagCache.Set(tag, ctag)
+ }
+ }
+ return ctag
+}
diff --git a/go-playground/validator/v10/country_codes.go b/go-playground/validator/v10/country_codes.go
new file mode 100644
index 0000000..ef81ead
--- /dev/null
+++ b/go-playground/validator/v10/country_codes.go
@@ -0,0 +1,162 @@
+package validator
+
+var iso3166_1_alpha2 = map[string]bool{
+ // see: https://www.iso.org/iso-3166-country-codes.html
+ "AF": true, "AX": true, "AL": true, "DZ": true, "AS": true,
+ "AD": true, "AO": true, "AI": true, "AQ": true, "AG": true,
+ "AR": true, "AM": true, "AW": true, "AU": true, "AT": true,
+ "AZ": true, "BS": true, "BH": true, "BD": true, "BB": true,
+ "BY": true, "BE": true, "BZ": true, "BJ": true, "BM": true,
+ "BT": true, "BO": true, "BQ": true, "BA": true, "BW": true,
+ "BV": true, "BR": true, "IO": true, "BN": true, "BG": true,
+ "BF": true, "BI": true, "KH": true, "CM": true, "CA": true,
+ "CV": true, "KY": true, "CF": true, "TD": true, "CL": true,
+ "CN": true, "CX": true, "CC": true, "CO": true, "KM": true,
+ "CG": true, "CD": true, "CK": true, "CR": true, "CI": true,
+ "HR": true, "CU": true, "CW": true, "CY": true, "CZ": true,
+ "DK": true, "DJ": true, "DM": true, "DO": true, "EC": true,
+ "EG": true, "SV": true, "GQ": true, "ER": true, "EE": true,
+ "ET": true, "FK": true, "FO": true, "FJ": true, "FI": true,
+ "FR": true, "GF": true, "PF": true, "TF": true, "GA": true,
+ "GM": true, "GE": true, "DE": true, "GH": true, "GI": true,
+ "GR": true, "GL": true, "GD": true, "GP": true, "GU": true,
+ "GT": true, "GG": true, "GN": true, "GW": true, "GY": true,
+ "HT": true, "HM": true, "VA": true, "HN": true, "HK": true,
+ "HU": true, "IS": true, "IN": true, "ID": true, "IR": true,
+ "IQ": true, "IE": true, "IM": true, "IL": true, "IT": true,
+ "JM": true, "JP": true, "JE": true, "JO": true, "KZ": true,
+ "KE": true, "KI": true, "KP": true, "KR": true, "KW": true,
+ "KG": true, "LA": true, "LV": true, "LB": true, "LS": true,
+ "LR": true, "LY": true, "LI": true, "LT": true, "LU": true,
+ "MO": true, "MK": true, "MG": true, "MW": true, "MY": true,
+ "MV": true, "ML": true, "MT": true, "MH": true, "MQ": true,
+ "MR": true, "MU": true, "YT": true, "MX": true, "FM": true,
+ "MD": true, "MC": true, "MN": true, "ME": true, "MS": true,
+ "MA": true, "MZ": true, "MM": true, "NA": true, "NR": true,
+ "NP": true, "NL": true, "NC": true, "NZ": true, "NI": true,
+ "NE": true, "NG": true, "NU": true, "NF": true, "MP": true,
+ "NO": true, "OM": true, "PK": true, "PW": true, "PS": true,
+ "PA": true, "PG": true, "PY": true, "PE": true, "PH": true,
+ "PN": true, "PL": true, "PT": true, "PR": true, "QA": true,
+ "RE": true, "RO": true, "RU": true, "RW": true, "BL": true,
+ "SH": true, "KN": true, "LC": true, "MF": true, "PM": true,
+ "VC": true, "WS": true, "SM": true, "ST": true, "SA": true,
+ "SN": true, "RS": true, "SC": true, "SL": true, "SG": true,
+ "SX": true, "SK": true, "SI": true, "SB": true, "SO": true,
+ "ZA": true, "GS": true, "SS": true, "ES": true, "LK": true,
+ "SD": true, "SR": true, "SJ": true, "SZ": true, "SE": true,
+ "CH": true, "SY": true, "TW": true, "TJ": true, "TZ": true,
+ "TH": true, "TL": true, "TG": true, "TK": true, "TO": true,
+ "TT": true, "TN": true, "TR": true, "TM": true, "TC": true,
+ "TV": true, "UG": true, "UA": true, "AE": true, "GB": true,
+ "US": true, "UM": true, "UY": true, "UZ": true, "VU": true,
+ "VE": true, "VN": true, "VG": true, "VI": true, "WF": true,
+ "EH": true, "YE": true, "ZM": true, "ZW": true,
+}
+
+var iso3166_1_alpha3 = map[string]bool{
+ // see: https://www.iso.org/iso-3166-country-codes.html
+ "AFG": true, "ALB": true, "DZA": true, "ASM": true, "AND": true,
+ "AGO": true, "AIA": true, "ATA": true, "ATG": true, "ARG": true,
+ "ARM": true, "ABW": true, "AUS": true, "AUT": true, "AZE": true,
+ "BHS": true, "BHR": true, "BGD": true, "BRB": true, "BLR": true,
+ "BEL": true, "BLZ": true, "BEN": true, "BMU": true, "BTN": true,
+ "BOL": true, "BES": true, "BIH": true, "BWA": true, "BVT": true,
+ "BRA": true, "IOT": true, "BRN": true, "BGR": true, "BFA": true,
+ "BDI": true, "CPV": true, "KHM": true, "CMR": true, "CAN": true,
+ "CYM": true, "CAF": true, "TCD": true, "CHL": true, "CHN": true,
+ "CXR": true, "CCK": true, "COL": true, "COM": true, "COD": true,
+ "COG": true, "COK": true, "CRI": true, "HRV": true, "CUB": true,
+ "CUW": true, "CYP": true, "CZE": true, "CIV": true, "DNK": true,
+ "DJI": true, "DMA": true, "DOM": true, "ECU": true, "EGY": true,
+ "SLV": true, "GNQ": true, "ERI": true, "EST": true, "SWZ": true,
+ "ETH": true, "FLK": true, "FRO": true, "FJI": true, "FIN": true,
+ "FRA": true, "GUF": true, "PYF": true, "ATF": true, "GAB": true,
+ "GMB": true, "GEO": true, "DEU": true, "GHA": true, "GIB": true,
+ "GRC": true, "GRL": true, "GRD": true, "GLP": true, "GUM": true,
+ "GTM": true, "GGY": true, "GIN": true, "GNB": true, "GUY": true,
+ "HTI": true, "HMD": true, "VAT": true, "HND": true, "HKG": true,
+ "HUN": true, "ISL": true, "IND": true, "IDN": true, "IRN": true,
+ "IRQ": true, "IRL": true, "IMN": true, "ISR": true, "ITA": true,
+ "JAM": true, "JPN": true, "JEY": true, "JOR": true, "KAZ": true,
+ "KEN": true, "KIR": true, "PRK": true, "KOR": true, "KWT": true,
+ "KGZ": true, "LAO": true, "LVA": true, "LBN": true, "LSO": true,
+ "LBR": true, "LBY": true, "LIE": true, "LTU": true, "LUX": true,
+ "MAC": true, "MDG": true, "MWI": true, "MYS": true, "MDV": true,
+ "MLI": true, "MLT": true, "MHL": true, "MTQ": true, "MRT": true,
+ "MUS": true, "MYT": true, "MEX": true, "FSM": true, "MDA": true,
+ "MCO": true, "MNG": true, "MNE": true, "MSR": true, "MAR": true,
+ "MOZ": true, "MMR": true, "NAM": true, "NRU": true, "NPL": true,
+ "NLD": true, "NCL": true, "NZL": true, "NIC": true, "NER": true,
+ "NGA": true, "NIU": true, "NFK": true, "MKD": true, "MNP": true,
+ "NOR": true, "OMN": true, "PAK": true, "PLW": true, "PSE": true,
+ "PAN": true, "PNG": true, "PRY": true, "PER": true, "PHL": true,
+ "PCN": true, "POL": true, "PRT": true, "PRI": true, "QAT": true,
+ "ROU": true, "RUS": true, "RWA": true, "REU": true, "BLM": true,
+ "SHN": true, "KNA": true, "LCA": true, "MAF": true, "SPM": true,
+ "VCT": true, "WSM": true, "SMR": true, "STP": true, "SAU": true,
+ "SEN": true, "SRB": true, "SYC": true, "SLE": true, "SGP": true,
+ "SXM": true, "SVK": true, "SVN": true, "SLB": true, "SOM": true,
+ "ZAF": true, "SGS": true, "SSD": true, "ESP": true, "LKA": true,
+ "SDN": true, "SUR": true, "SJM": true, "SWE": true, "CHE": true,
+ "SYR": true, "TWN": true, "TJK": true, "TZA": true, "THA": true,
+ "TLS": true, "TGO": true, "TKL": true, "TON": true, "TTO": true,
+ "TUN": true, "TUR": true, "TKM": true, "TCA": true, "TUV": true,
+ "UGA": true, "UKR": true, "ARE": true, "GBR": true, "UMI": true,
+ "USA": true, "URY": true, "UZB": true, "VUT": true, "VEN": true,
+ "VNM": true, "VGB": true, "VIR": true, "WLF": true, "ESH": true,
+ "YEM": true, "ZMB": true, "ZWE": true, "ALA": true,
+}
+var iso3166_1_alpha_numeric = map[int]bool{
+ // see: https://www.iso.org/iso-3166-country-codes.html
+ 4: true, 8: true, 12: true, 16: true, 20: true,
+ 24: true, 660: true, 10: true, 28: true, 32: true,
+ 51: true, 533: true, 36: true, 40: true, 31: true,
+ 44: true, 48: true, 50: true, 52: true, 112: true,
+ 56: true, 84: true, 204: true, 60: true, 64: true,
+ 68: true, 535: true, 70: true, 72: true, 74: true,
+ 76: true, 86: true, 96: true, 100: true, 854: true,
+ 108: true, 132: true, 116: true, 120: true, 124: true,
+ 136: true, 140: true, 148: true, 152: true, 156: true,
+ 162: true, 166: true, 170: true, 174: true, 180: true,
+ 178: true, 184: true, 188: true, 191: true, 192: true,
+ 531: true, 196: true, 203: true, 384: true, 208: true,
+ 262: true, 212: true, 214: true, 218: true, 818: true,
+ 222: true, 226: true, 232: true, 233: true, 748: true,
+ 231: true, 238: true, 234: true, 242: true, 246: true,
+ 250: true, 254: true, 258: true, 260: true, 266: true,
+ 270: true, 268: true, 276: true, 288: true, 292: true,
+ 300: true, 304: true, 308: true, 312: true, 316: true,
+ 320: true, 831: true, 324: true, 624: true, 328: true,
+ 332: true, 334: true, 336: true, 340: true, 344: true,
+ 348: true, 352: true, 356: true, 360: true, 364: true,
+ 368: true, 372: true, 833: true, 376: true, 380: true,
+ 388: true, 392: true, 832: true, 400: true, 398: true,
+ 404: true, 296: true, 408: true, 410: true, 414: true,
+ 417: true, 418: true, 428: true, 422: true, 426: true,
+ 430: true, 434: true, 438: true, 440: true, 442: true,
+ 446: true, 450: true, 454: true, 458: true, 462: true,
+ 466: true, 470: true, 584: true, 474: true, 478: true,
+ 480: true, 175: true, 484: true, 583: true, 498: true,
+ 492: true, 496: true, 499: true, 500: true, 504: true,
+ 508: true, 104: true, 516: true, 520: true, 524: true,
+ 528: true, 540: true, 554: true, 558: true, 562: true,
+ 566: true, 570: true, 574: true, 807: true, 580: true,
+ 578: true, 512: true, 586: true, 585: true, 275: true,
+ 591: true, 598: true, 600: true, 604: true, 608: true,
+ 612: true, 616: true, 620: true, 630: true, 634: true,
+ 642: true, 643: true, 646: true, 638: true, 652: true,
+ 654: true, 659: true, 662: true, 663: true, 666: true,
+ 670: true, 882: true, 674: true, 678: true, 682: true,
+ 686: true, 688: true, 690: true, 694: true, 702: true,
+ 534: true, 703: true, 705: true, 90: true, 706: true,
+ 710: true, 239: true, 728: true, 724: true, 144: true,
+ 729: true, 740: true, 744: true, 752: true, 756: true,
+ 760: true, 158: true, 762: true, 834: true, 764: true,
+ 626: true, 768: true, 772: true, 776: true, 780: true,
+ 788: true, 792: true, 795: true, 796: true, 798: true,
+ 800: true, 804: true, 784: true, 826: true, 581: true,
+ 840: true, 858: true, 860: true, 548: true, 862: true,
+ 704: true, 92: true, 850: true, 876: true, 732: true,
+ 887: true, 894: true, 716: true, 248: true,
+}
diff --git a/go-playground/validator/v10/doc.go b/go-playground/validator/v10/doc.go
new file mode 100644
index 0000000..34f22ad
--- /dev/null
+++ b/go-playground/validator/v10/doc.go
@@ -0,0 +1,1308 @@
+/*
+Package validator implements value validations for structs and individual fields
+based on tags.
+
+It can also handle Cross-Field and Cross-Struct validation for nested structs
+and has the ability to dive into arrays and maps of any type.
+
+see more examples https://github.com/go-playground/validator/tree/master/_examples
+
+Validation Functions Return Type error
+
+Doing things this way is actually the way the standard library does, see the
+file.Open method here:
+
+ https://golang.org/pkg/os/#Open.
+
+The authors return type "error" to avoid the issue discussed in the following,
+where err is always != nil:
+
+ http://stackoverflow.com/a/29138676/3158232
+ https://github.com/go-playground/validator/issues/134
+
+Validator only InvalidValidationError for bad validation input, nil or
+ValidationErrors as type error; so, in your code all you need to do is check
+if the error returned is not nil, and if it's not check if error is
+InvalidValidationError ( if necessary, most of the time it isn't ) type cast
+it to type ValidationErrors like so err.(validator.ValidationErrors).
+
+Custom Validation Functions
+
+Custom Validation functions can be added. Example:
+
+ // Structure
+ func customFunc(fl validator.FieldLevel) bool {
+
+ if fl.Field().String() == "invalid" {
+ return false
+ }
+
+ return true
+ }
+
+ validate.RegisterValidation("custom tag name", customFunc)
+ // NOTES: using the same tag name as an existing function
+ // will overwrite the existing one
+
+Cross-Field Validation
+
+Cross-Field Validation can be done via the following tags:
+ - eqfield
+ - nefield
+ - gtfield
+ - gtefield
+ - ltfield
+ - ltefield
+ - eqcsfield
+ - necsfield
+ - gtcsfield
+ - gtecsfield
+ - ltcsfield
+ - ltecsfield
+
+If, however, some custom cross-field validation is required, it can be done
+using a custom validation.
+
+Why not just have cross-fields validation tags (i.e. only eqcsfield and not
+eqfield)?
+
+The reason is efficiency. If you want to check a field within the same struct
+"eqfield" only has to find the field on the same struct (1 level). But, if we
+used "eqcsfield" it could be multiple levels down. Example:
+
+ type Inner struct {
+ StartDate time.Time
+ }
+
+ type Outer struct {
+ InnerStructField *Inner
+ CreatedAt time.Time `validate:"ltecsfield=InnerStructField.StartDate"`
+ }
+
+ now := time.Now()
+
+ inner := &Inner{
+ StartDate: now,
+ }
+
+ outer := &Outer{
+ InnerStructField: inner,
+ CreatedAt: now,
+ }
+
+ errs := validate.Struct(outer)
+
+ // NOTE: when calling validate.Struct(val) topStruct will be the top level struct passed
+ // into the function
+ // when calling validate.VarWithValue(val, field, tag) val will be
+ // whatever you pass, struct, field...
+ // when calling validate.Field(field, tag) val will be nil
+
+Multiple Validators
+
+Multiple validators on a field will process in the order defined. Example:
+
+ type Test struct {
+ Field `validate:"max=10,min=1"`
+ }
+
+ // max will be checked then min
+
+Bad Validator definitions are not handled by the library. Example:
+
+ type Test struct {
+ Field `validate:"min=10,max=0"`
+ }
+
+ // this definition of min max will never succeed
+
+Using Validator Tags
+
+Baked In Cross-Field validation only compares fields on the same struct.
+If Cross-Field + Cross-Struct validation is needed you should implement your
+own custom validator.
+
+Comma (",") is the default separator of validation tags. If you wish to
+have a comma included within the parameter (i.e. excludesall=,) you will need to
+use the UTF-8 hex representation 0x2C, which is replaced in the code as a comma,
+so the above will become excludesall=0x2C.
+
+ type Test struct {
+ Field `validate:"excludesall=,"` // BAD! Do not include a comma.
+ Field `validate:"excludesall=0x2C"` // GOOD! Use the UTF-8 hex representation.
+ }
+
+Pipe ("|") is the 'or' validation tags deparator. If you wish to
+have a pipe included within the parameter i.e. excludesall=| you will need to
+use the UTF-8 hex representation 0x7C, which is replaced in the code as a pipe,
+so the above will become excludesall=0x7C
+
+ type Test struct {
+ Field `validate:"excludesall=|"` // BAD! Do not include a a pipe!
+ Field `validate:"excludesall=0x7C"` // GOOD! Use the UTF-8 hex representation.
+ }
+
+
+Baked In Validators and Tags
+
+Here is a list of the current built in validators:
+
+
+Skip Field
+
+Tells the validation to skip this struct field; this is particularly
+handy in ignoring embedded structs from being validated. (Usage: -)
+ Usage: -
+
+
+Or Operator
+
+This is the 'or' operator allowing multiple validators to be used and
+accepted. (Usage: rgb|rgba) <-- this would allow either rgb or rgba
+colors to be accepted. This can also be combined with 'and' for example
+( Usage: omitempty,rgb|rgba)
+
+ Usage: |
+
+StructOnly
+
+When a field that is a nested struct is encountered, and contains this flag
+any validation on the nested struct will be run, but none of the nested
+struct fields will be validated. This is useful if inside of your program
+you know the struct will be valid, but need to verify it has been assigned.
+NOTE: only "required" and "omitempty" can be used on a struct itself.
+
+ Usage: structonly
+
+NoStructLevel
+
+Same as structonly tag except that any struct level validations will not run.
+
+ Usage: nostructlevel
+
+Omit Empty
+
+Allows conditional validation, for example if a field is not set with
+a value (Determined by the "required" validator) then other validation
+such as min or max won't run, but if a value is set validation will run.
+
+ Usage: omitempty
+
+Dive
+
+This tells the validator to dive into a slice, array or map and validate that
+level of the slice, array or map with the validation tags that follow.
+Multidimensional nesting is also supported, each level you wish to dive will
+require another dive tag. dive has some sub-tags, 'keys' & 'endkeys', please see
+the Keys & EndKeys section just below.
+
+ Usage: dive
+
+Example #1
+
+ [][]string with validation tag "gt=0,dive,len=1,dive,required"
+ // gt=0 will be applied to []
+ // len=1 will be applied to []string
+ // required will be applied to string
+
+Example #2
+
+ [][]string with validation tag "gt=0,dive,dive,required"
+ // gt=0 will be applied to []
+ // []string will be spared validation
+ // required will be applied to string
+
+Keys & EndKeys
+
+These are to be used together directly after the dive tag and tells the validator
+that anything between 'keys' and 'endkeys' applies to the keys of a map and not the
+values; think of it like the 'dive' tag, but for map keys instead of values.
+Multidimensional nesting is also supported, each level you wish to validate will
+require another 'keys' and 'endkeys' tag. These tags are only valid for maps.
+
+ Usage: dive,keys,othertagvalidation(s),endkeys,valuevalidationtags
+
+Example #1
+
+ map[string]string with validation tag "gt=0,dive,keys,eg=1|eq=2,endkeys,required"
+ // gt=0 will be applied to the map itself
+ // eg=1|eq=2 will be applied to the map keys
+ // required will be applied to map values
+
+Example #2
+
+ map[[2]string]string with validation tag "gt=0,dive,keys,dive,eq=1|eq=2,endkeys,required"
+ // gt=0 will be applied to the map itself
+ // eg=1|eq=2 will be applied to each array element in the the map keys
+ // required will be applied to map values
+
+Required
+
+This validates that the value is not the data types default zero value.
+For numbers ensures value is not zero. For strings ensures value is
+not "". For slices, maps, pointers, interfaces, channels and functions
+ensures the value is not nil.
+
+ Usage: required
+
+Required If
+
+The field under validation must be present and not empty only if all
+the other specified fields are equal to the value following the specified
+field. For strings ensures value is not "". For slices, maps, pointers,
+interfaces, channels and functions ensures the value is not nil.
+
+ Usage: required_if
+
+Examples:
+
+ // require the field if the Field1 is equal to the parameter given:
+ Usage: required_if=Field1 foobar
+
+ // require the field if the Field1 and Field2 is equal to the value respectively:
+ Usage: required_if=Field1 foo Field2 bar
+
+Required Unless
+
+The field under validation must be present and not empty unless all
+the other specified fields are equal to the value following the specified
+field. For strings ensures value is not "". For slices, maps, pointers,
+interfaces, channels and functions ensures the value is not nil.
+
+ Usage: required_unless
+
+Examples:
+
+ // require the field unless the Field1 is equal to the parameter given:
+ Usage: required_unless=Field1 foobar
+
+ // require the field unless the Field1 and Field2 is equal to the value respectively:
+ Usage: required_unless=Field1 foo Field2 bar
+
+Required With
+
+The field under validation must be present and not empty only if any
+of the other specified fields are present. For strings ensures value is
+not "". For slices, maps, pointers, interfaces, channels and functions
+ensures the value is not nil.
+
+ Usage: required_with
+
+Examples:
+
+ // require the field if the Field1 is present:
+ Usage: required_with=Field1
+
+ // require the field if the Field1 or Field2 is present:
+ Usage: required_with=Field1 Field2
+
+Required With All
+
+The field under validation must be present and not empty only if all
+of the other specified fields are present. For strings ensures value is
+not "". For slices, maps, pointers, interfaces, channels and functions
+ensures the value is not nil.
+
+ Usage: required_with_all
+
+Example:
+
+ // require the field if the Field1 and Field2 is present:
+ Usage: required_with_all=Field1 Field2
+
+Required Without
+
+The field under validation must be present and not empty only when any
+of the other specified fields are not present. For strings ensures value is
+not "". For slices, maps, pointers, interfaces, channels and functions
+ensures the value is not nil.
+
+ Usage: required_without
+
+Examples:
+
+ // require the field if the Field1 is not present:
+ Usage: required_without=Field1
+
+ // require the field if the Field1 or Field2 is not present:
+ Usage: required_without=Field1 Field2
+
+Required Without All
+
+The field under validation must be present and not empty only when all
+of the other specified fields are not present. For strings ensures value is
+not "". For slices, maps, pointers, interfaces, channels and functions
+ensures the value is not nil.
+
+ Usage: required_without_all
+
+Example:
+
+ // require the field if the Field1 and Field2 is not present:
+ Usage: required_without_all=Field1 Field2
+
+Is Default
+
+This validates that the value is the default value and is almost the
+opposite of required.
+
+ Usage: isdefault
+
+Length
+
+For numbers, length will ensure that the value is
+equal to the parameter given. For strings, it checks that
+the string length is exactly that number of characters. For slices,
+arrays, and maps, validates the number of items.
+
+Example #1
+
+ Usage: len=10
+
+Example #2 (time.Duration)
+
+For time.Duration, len will ensure that the value is equal to the duration given
+in the parameter.
+
+ Usage: len=1h30m
+
+Maximum
+
+For numbers, max will ensure that the value is
+less than or equal to the parameter given. For strings, it checks
+that the string length is at most that number of characters. For
+slices, arrays, and maps, validates the number of items.
+
+Example #1
+
+ Usage: max=10
+
+Example #2 (time.Duration)
+
+For time.Duration, max will ensure that the value is less than or equal to the
+duration given in the parameter.
+
+ Usage: max=1h30m
+
+Minimum
+
+For numbers, min will ensure that the value is
+greater or equal to the parameter given. For strings, it checks that
+the string length is at least that number of characters. For slices,
+arrays, and maps, validates the number of items.
+
+Example #1
+
+ Usage: min=10
+
+Example #2 (time.Duration)
+
+For time.Duration, min will ensure that the value is greater than or equal to
+the duration given in the parameter.
+
+ Usage: min=1h30m
+
+Equals
+
+For strings & numbers, eq will ensure that the value is
+equal to the parameter given. For slices, arrays, and maps,
+validates the number of items.
+
+Example #1
+
+ Usage: eq=10
+
+Example #2 (time.Duration)
+
+For time.Duration, eq will ensure that the value is equal to the duration given
+in the parameter.
+
+ Usage: eq=1h30m
+
+Not Equal
+
+For strings & numbers, ne will ensure that the value is not
+equal to the parameter given. For slices, arrays, and maps,
+validates the number of items.
+
+Example #1
+
+ Usage: ne=10
+
+Example #2 (time.Duration)
+
+For time.Duration, ne will ensure that the value is not equal to the duration
+given in the parameter.
+
+ Usage: ne=1h30m
+
+One Of
+
+For strings, ints, and uints, oneof will ensure that the value
+is one of the values in the parameter. The parameter should be
+a list of values separated by whitespace. Values may be
+strings or numbers. To match strings with spaces in them, include
+the target string between single quotes.
+
+ Usage: oneof=red green
+ oneof='red green' 'blue yellow'
+ oneof=5 7 9
+
+Greater Than
+
+For numbers, this will ensure that the value is greater than the
+parameter given. For strings, it checks that the string length
+is greater than that number of characters. For slices, arrays
+and maps it validates the number of items.
+
+Example #1
+
+ Usage: gt=10
+
+Example #2 (time.Time)
+
+For time.Time ensures the time value is greater than time.Now.UTC().
+
+ Usage: gt
+
+Example #3 (time.Duration)
+
+For time.Duration, gt will ensure that the value is greater than the duration
+given in the parameter.
+
+ Usage: gt=1h30m
+
+Greater Than or Equal
+
+Same as 'min' above. Kept both to make terminology with 'len' easier.
+
+Example #1
+
+ Usage: gte=10
+
+Example #2 (time.Time)
+
+For time.Time ensures the time value is greater than or equal to time.Now.UTC().
+
+ Usage: gte
+
+Example #3 (time.Duration)
+
+For time.Duration, gte will ensure that the value is greater than or equal to
+the duration given in the parameter.
+
+ Usage: gte=1h30m
+
+Less Than
+
+For numbers, this will ensure that the value is less than the parameter given.
+For strings, it checks that the string length is less than that number of
+characters. For slices, arrays, and maps it validates the number of items.
+
+Example #1
+
+ Usage: lt=10
+
+Example #2 (time.Time)
+
+For time.Time ensures the time value is less than time.Now.UTC().
+
+ Usage: lt
+
+Example #3 (time.Duration)
+
+For time.Duration, lt will ensure that the value is less than the duration given
+in the parameter.
+
+ Usage: lt=1h30m
+
+Less Than or Equal
+
+Same as 'max' above. Kept both to make terminology with 'len' easier.
+
+Example #1
+
+ Usage: lte=10
+
+Example #2 (time.Time)
+
+For time.Time ensures the time value is less than or equal to time.Now.UTC().
+
+ Usage: lte
+
+Example #3 (time.Duration)
+
+For time.Duration, lte will ensure that the value is less than or equal to the
+duration given in the parameter.
+
+ Usage: lte=1h30m
+
+Field Equals Another Field
+
+This will validate the field value against another fields value either within
+a struct or passed in field.
+
+Example #1:
+
+ // Validation on Password field using:
+ Usage: eqfield=ConfirmPassword
+
+Example #2:
+
+ // Validating by field:
+ validate.VarWithValue(password, confirmpassword, "eqfield")
+
+Field Equals Another Field (relative)
+
+This does the same as eqfield except that it validates the field provided relative
+to the top level struct.
+
+ Usage: eqcsfield=InnerStructField.Field)
+
+Field Does Not Equal Another Field
+
+This will validate the field value against another fields value either within
+a struct or passed in field.
+
+Examples:
+
+ // Confirm two colors are not the same:
+ //
+ // Validation on Color field:
+ Usage: nefield=Color2
+
+ // Validating by field:
+ validate.VarWithValue(color1, color2, "nefield")
+
+Field Does Not Equal Another Field (relative)
+
+This does the same as nefield except that it validates the field provided
+relative to the top level struct.
+
+ Usage: necsfield=InnerStructField.Field
+
+Field Greater Than Another Field
+
+Only valid for Numbers, time.Duration and time.Time types, this will validate
+the field value against another fields value either within a struct or passed in
+field. usage examples are for validation of a Start and End date:
+
+Example #1:
+
+ // Validation on End field using:
+ validate.Struct Usage(gtfield=Start)
+
+Example #2:
+
+ // Validating by field:
+ validate.VarWithValue(start, end, "gtfield")
+
+Field Greater Than Another Relative Field
+
+This does the same as gtfield except that it validates the field provided
+relative to the top level struct.
+
+ Usage: gtcsfield=InnerStructField.Field
+
+Field Greater Than or Equal To Another Field
+
+Only valid for Numbers, time.Duration and time.Time types, this will validate
+the field value against another fields value either within a struct or passed in
+field. usage examples are for validation of a Start and End date:
+
+Example #1:
+
+ // Validation on End field using:
+ validate.Struct Usage(gtefield=Start)
+
+Example #2:
+
+ // Validating by field:
+ validate.VarWithValue(start, end, "gtefield")
+
+Field Greater Than or Equal To Another Relative Field
+
+This does the same as gtefield except that it validates the field provided relative
+to the top level struct.
+
+ Usage: gtecsfield=InnerStructField.Field
+
+Less Than Another Field
+
+Only valid for Numbers, time.Duration and time.Time types, this will validate
+the field value against another fields value either within a struct or passed in
+field. usage examples are for validation of a Start and End date:
+
+Example #1:
+
+ // Validation on End field using:
+ validate.Struct Usage(ltfield=Start)
+
+Example #2:
+
+ // Validating by field:
+ validate.VarWithValue(start, end, "ltfield")
+
+Less Than Another Relative Field
+
+This does the same as ltfield except that it validates the field provided relative
+to the top level struct.
+
+ Usage: ltcsfield=InnerStructField.Field
+
+Less Than or Equal To Another Field
+
+Only valid for Numbers, time.Duration and time.Time types, this will validate
+the field value against another fields value either within a struct or passed in
+field. usage examples are for validation of a Start and End date:
+
+Example #1:
+
+ // Validation on End field using:
+ validate.Struct Usage(ltefield=Start)
+
+Example #2:
+
+ // Validating by field:
+ validate.VarWithValue(start, end, "ltefield")
+
+Less Than or Equal To Another Relative Field
+
+This does the same as ltefield except that it validates the field provided relative
+to the top level struct.
+
+ Usage: ltecsfield=InnerStructField.Field
+
+Field Contains Another Field
+
+This does the same as contains except for struct fields. It should only be used
+with string types. See the behavior of reflect.Value.String() for behavior on
+other types.
+
+ Usage: containsfield=InnerStructField.Field
+
+Field Excludes Another Field
+
+This does the same as excludes except for struct fields. It should only be used
+with string types. See the behavior of reflect.Value.String() for behavior on
+other types.
+
+ Usage: excludesfield=InnerStructField.Field
+
+Unique
+
+For arrays & slices, unique will ensure that there are no duplicates.
+For maps, unique will ensure that there are no duplicate values.
+For slices of struct, unique will ensure that there are no duplicate values
+in a field of the struct specified via a parameter.
+
+ // For arrays, slices, and maps:
+ Usage: unique
+
+ // For slices of struct:
+ Usage: unique=field
+
+Alpha Only
+
+This validates that a string value contains ASCII alpha characters only
+
+ Usage: alpha
+
+Alphanumeric
+
+This validates that a string value contains ASCII alphanumeric characters only
+
+ Usage: alphanum
+
+Alpha Unicode
+
+This validates that a string value contains unicode alpha characters only
+
+ Usage: alphaunicode
+
+Alphanumeric Unicode
+
+This validates that a string value contains unicode alphanumeric characters only
+
+ Usage: alphanumunicode
+
+Number
+
+This validates that a string value contains number values only.
+For integers or float it returns true.
+
+ Usage: number
+
+Numeric
+
+This validates that a string value contains a basic numeric value.
+basic excludes exponents etc...
+for integers or float it returns true.
+
+ Usage: numeric
+
+Hexadecimal String
+
+This validates that a string value contains a valid hexadecimal.
+
+ Usage: hexadecimal
+
+Hexcolor String
+
+This validates that a string value contains a valid hex color including
+hashtag (#)
+
+ Usage: hexcolor
+
+Lowercase String
+
+This validates that a string value contains only lowercase characters. An empty string is not a valid lowercase string.
+
+ Usage: lowercase
+
+Uppercase String
+
+This validates that a string value contains only uppercase characters. An empty string is not a valid uppercase string.
+
+ Usage: uppercase
+
+RGB String
+
+This validates that a string value contains a valid rgb color
+
+ Usage: rgb
+
+RGBA String
+
+This validates that a string value contains a valid rgba color
+
+ Usage: rgba
+
+HSL String
+
+This validates that a string value contains a valid hsl color
+
+ Usage: hsl
+
+HSLA String
+
+This validates that a string value contains a valid hsla color
+
+ Usage: hsla
+
+E.164 Phone Number String
+
+This validates that a string value contains a valid E.164 Phone number
+https://en.wikipedia.org/wiki/E.164 (ex. +1123456789)
+
+ Usage: e164
+
+E-mail String
+
+This validates that a string value contains a valid email
+This may not conform to all possibilities of any rfc standard, but neither
+does any email provider accept all possibilities.
+
+ Usage: email
+
+JSON String
+
+This validates that a string value is valid JSON
+
+ Usage: json
+
+File path
+
+This validates that a string value contains a valid file path and that
+the file exists on the machine.
+This is done using os.Stat, which is a platform independent function.
+
+ Usage: file
+
+URL String
+
+This validates that a string value contains a valid url
+This will accept any url the golang request uri accepts but must contain
+a schema for example http:// or rtmp://
+
+ Usage: url
+
+URI String
+
+This validates that a string value contains a valid uri
+This will accept any uri the golang request uri accepts
+
+ Usage: uri
+
+Urn RFC 2141 String
+
+This validataes that a string value contains a valid URN
+according to the RFC 2141 spec.
+
+ Usage: urn_rfc2141
+
+Base64 String
+
+This validates that a string value contains a valid base64 value.
+Although an empty string is valid base64 this will report an empty string
+as an error, if you wish to accept an empty string as valid you can use
+this with the omitempty tag.
+
+ Usage: base64
+
+Base64URL String
+
+This validates that a string value contains a valid base64 URL safe value
+according the the RFC4648 spec.
+Although an empty string is a valid base64 URL safe value, this will report
+an empty string as an error, if you wish to accept an empty string as valid
+you can use this with the omitempty tag.
+
+ Usage: base64url
+
+Bitcoin Address
+
+This validates that a string value contains a valid bitcoin address.
+The format of the string is checked to ensure it matches one of the three formats
+P2PKH, P2SH and performs checksum validation.
+
+ Usage: btc_addr
+
+Bitcoin Bech32 Address (segwit)
+
+This validates that a string value contains a valid bitcoin Bech32 address as defined
+by bip-0173 (https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)
+Special thanks to Pieter Wuille for providng reference implementations.
+
+ Usage: btc_addr_bech32
+
+Ethereum Address
+
+This validates that a string value contains a valid ethereum address.
+The format of the string is checked to ensure it matches the standard Ethereum address format.
+
+ Usage: eth_addr
+
+Contains
+
+This validates that a string value contains the substring value.
+
+ Usage: contains=@
+
+Contains Any
+
+This validates that a string value contains any Unicode code points
+in the substring value.
+
+ Usage: containsany=!@#?
+
+Contains Rune
+
+This validates that a string value contains the supplied rune value.
+
+ Usage: containsrune=@
+
+Excludes
+
+This validates that a string value does not contain the substring value.
+
+ Usage: excludes=@
+
+Excludes All
+
+This validates that a string value does not contain any Unicode code
+points in the substring value.
+
+ Usage: excludesall=!@#?
+
+Excludes Rune
+
+This validates that a string value does not contain the supplied rune value.
+
+ Usage: excludesrune=@
+
+Starts With
+
+This validates that a string value starts with the supplied string value
+
+ Usage: startswith=hello
+
+Ends With
+
+This validates that a string value ends with the supplied string value
+
+ Usage: endswith=goodbye
+
+Does Not Start With
+
+This validates that a string value does not start with the supplied string value
+
+ Usage: startsnotwith=hello
+
+Does Not End With
+
+This validates that a string value does not end with the supplied string value
+
+ Usage: endsnotwith=goodbye
+
+International Standard Book Number
+
+This validates that a string value contains a valid isbn10 or isbn13 value.
+
+ Usage: isbn
+
+International Standard Book Number 10
+
+This validates that a string value contains a valid isbn10 value.
+
+ Usage: isbn10
+
+International Standard Book Number 13
+
+This validates that a string value contains a valid isbn13 value.
+
+ Usage: isbn13
+
+Universally Unique Identifier UUID
+
+This validates that a string value contains a valid UUID. Uppercase UUID values will not pass - use `uuid_rfc4122` instead.
+
+ Usage: uuid
+
+Universally Unique Identifier UUID v3
+
+This validates that a string value contains a valid version 3 UUID. Uppercase UUID values will not pass - use `uuid3_rfc4122` instead.
+
+ Usage: uuid3
+
+Universally Unique Identifier UUID v4
+
+This validates that a string value contains a valid version 4 UUID. Uppercase UUID values will not pass - use `uuid4_rfc4122` instead.
+
+ Usage: uuid4
+
+Universally Unique Identifier UUID v5
+
+This validates that a string value contains a valid version 5 UUID. Uppercase UUID values will not pass - use `uuid5_rfc4122` instead.
+
+ Usage: uuid5
+
+ASCII
+
+This validates that a string value contains only ASCII characters.
+NOTE: if the string is blank, this validates as true.
+
+ Usage: ascii
+
+Printable ASCII
+
+This validates that a string value contains only printable ASCII characters.
+NOTE: if the string is blank, this validates as true.
+
+ Usage: printascii
+
+Multi-Byte Characters
+
+This validates that a string value contains one or more multibyte characters.
+NOTE: if the string is blank, this validates as true.
+
+ Usage: multibyte
+
+Data URL
+
+This validates that a string value contains a valid DataURI.
+NOTE: this will also validate that the data portion is valid base64
+
+ Usage: datauri
+
+Latitude
+
+This validates that a string value contains a valid latitude.
+
+ Usage: latitude
+
+Longitude
+
+This validates that a string value contains a valid longitude.
+
+ Usage: longitude
+
+Social Security Number SSN
+
+This validates that a string value contains a valid U.S. Social Security Number.
+
+ Usage: ssn
+
+Internet Protocol Address IP
+
+This validates that a string value contains a valid IP Address.
+
+ Usage: ip
+
+Internet Protocol Address IPv4
+
+This validates that a string value contains a valid v4 IP Address.
+
+ Usage: ipv4
+
+Internet Protocol Address IPv6
+
+This validates that a string value contains a valid v6 IP Address.
+
+ Usage: ipv6
+
+Classless Inter-Domain Routing CIDR
+
+This validates that a string value contains a valid CIDR Address.
+
+ Usage: cidr
+
+Classless Inter-Domain Routing CIDRv4
+
+This validates that a string value contains a valid v4 CIDR Address.
+
+ Usage: cidrv4
+
+Classless Inter-Domain Routing CIDRv6
+
+This validates that a string value contains a valid v6 CIDR Address.
+
+ Usage: cidrv6
+
+Transmission Control Protocol Address TCP
+
+This validates that a string value contains a valid resolvable TCP Address.
+
+ Usage: tcp_addr
+
+Transmission Control Protocol Address TCPv4
+
+This validates that a string value contains a valid resolvable v4 TCP Address.
+
+ Usage: tcp4_addr
+
+Transmission Control Protocol Address TCPv6
+
+This validates that a string value contains a valid resolvable v6 TCP Address.
+
+ Usage: tcp6_addr
+
+User Datagram Protocol Address UDP
+
+This validates that a string value contains a valid resolvable UDP Address.
+
+ Usage: udp_addr
+
+User Datagram Protocol Address UDPv4
+
+This validates that a string value contains a valid resolvable v4 UDP Address.
+
+ Usage: udp4_addr
+
+User Datagram Protocol Address UDPv6
+
+This validates that a string value contains a valid resolvable v6 UDP Address.
+
+ Usage: udp6_addr
+
+Internet Protocol Address IP
+
+This validates that a string value contains a valid resolvable IP Address.
+
+ Usage: ip_addr
+
+Internet Protocol Address IPv4
+
+This validates that a string value contains a valid resolvable v4 IP Address.
+
+ Usage: ip4_addr
+
+Internet Protocol Address IPv6
+
+This validates that a string value contains a valid resolvable v6 IP Address.
+
+ Usage: ip6_addr
+
+Unix domain socket end point Address
+
+This validates that a string value contains a valid Unix Address.
+
+ Usage: unix_addr
+
+Media Access Control Address MAC
+
+This validates that a string value contains a valid MAC Address.
+
+ Usage: mac
+
+Note: See Go's ParseMAC for accepted formats and types:
+
+ http://golang.org/src/net/mac.go?s=866:918#L29
+
+Hostname RFC 952
+
+This validates that a string value is a valid Hostname according to RFC 952 https://tools.ietf.org/html/rfc952
+
+ Usage: hostname
+
+Hostname RFC 1123
+
+This validates that a string value is a valid Hostname according to RFC 1123 https://tools.ietf.org/html/rfc1123
+
+ Usage: hostname_rfc1123 or if you want to continue to use 'hostname' in your tags, create an alias.
+
+Full Qualified Domain Name (FQDN)
+
+This validates that a string value contains a valid FQDN.
+
+ Usage: fqdn
+
+HTML Tags
+
+This validates that a string value appears to be an HTML element tag
+including those described at https://developer.mozilla.org/en-US/docs/Web/HTML/Element
+
+ Usage: html
+
+HTML Encoded
+
+This validates that a string value is a proper character reference in decimal
+or hexadecimal format
+
+ Usage: html_encoded
+
+URL Encoded
+
+This validates that a string value is percent-encoded (URL encoded) according
+to https://tools.ietf.org/html/rfc3986#section-2.1
+
+ Usage: url_encoded
+
+Directory
+
+This validates that a string value contains a valid directory and that
+it exists on the machine.
+This is done using os.Stat, which is a platform independent function.
+
+ Usage: dir
+
+HostPort
+
+This validates that a string value contains a valid DNS hostname and port that
+can be used to valiate fields typically passed to sockets and connections.
+
+ Usage: hostname_port
+
+Datetime
+
+This validates that a string value is a valid datetime based on the supplied datetime format.
+Supplied format must match the official Go time format layout as documented in https://golang.org/pkg/time/
+
+ Usage: datetime=2006-01-02
+
+Iso3166-1 alpha-2
+
+This validates that a string value is a valid country code based on iso3166-1 alpha-2 standard.
+see: https://www.iso.org/iso-3166-country-codes.html
+
+ Usage: iso3166_1_alpha2
+
+Iso3166-1 alpha-3
+
+This validates that a string value is a valid country code based on iso3166-1 alpha-3 standard.
+see: https://www.iso.org/iso-3166-country-codes.html
+
+ Usage: iso3166_1_alpha3
+
+Iso3166-1 alpha-numeric
+
+This validates that a string value is a valid country code based on iso3166-1 alpha-numeric standard.
+see: https://www.iso.org/iso-3166-country-codes.html
+
+ Usage: iso3166_1_alpha3
+
+TimeZone
+
+This validates that a string value is a valid time zone based on the time zone database present on the system.
+Although empty value and Local value are allowed by time.LoadLocation golang function, they are not allowed by this validator.
+More information on https://golang.org/pkg/time/#LoadLocation
+
+ Usage: timezone
+
+
+Alias Validators and Tags
+
+NOTE: When returning an error, the tag returned in "FieldError" will be
+the alias tag unless the dive tag is part of the alias. Everything after the
+dive tag is not reported as the alias tag. Also, the "ActualTag" in the before
+case will be the actual tag within the alias that failed.
+
+Here is a list of the current built in alias tags:
+
+ "iscolor"
+ alias is "hexcolor|rgb|rgba|hsl|hsla" (Usage: iscolor)
+ "country_code"
+ alias is "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric" (Usage: country_code)
+
+Validator notes:
+
+ regex
+ a regex validator won't be added because commas and = signs can be part
+ of a regex which conflict with the validation definitions. Although
+ workarounds can be made, they take away from using pure regex's.
+ Furthermore it's quick and dirty but the regex's become harder to
+ maintain and are not reusable, so it's as much a programming philosophy
+ as anything.
+
+ In place of this new validator functions should be created; a regex can
+ be used within the validator function and even be precompiled for better
+ efficiency within regexes.go.
+
+ And the best reason, you can submit a pull request and we can keep on
+ adding to the validation library of this package!
+
+Non standard validators
+
+A collection of validation rules that are frequently needed but are more
+complex than the ones found in the baked in validators.
+A non standard validator must be registered manually like you would
+with your own custom validation functions.
+
+Example of registration and use:
+
+ type Test struct {
+ TestField string `validate:"yourtag"`
+ }
+
+ t := &Test{
+ TestField: "Test"
+ }
+
+ validate := validator.New()
+ validate.RegisterValidation("yourtag", validators.NotBlank)
+
+Here is a list of the current non standard validators:
+
+ NotBlank
+ This validates that the value is not blank or with length zero.
+ For strings ensures they do not contain only spaces. For channels, maps, slices and arrays
+ ensures they don't have zero length. For others, a non empty value is required.
+
+ Usage: notblank
+
+Panics
+
+This package panics when bad input is provided, this is by design, bad code like
+that should not make it to production.
+
+ type Test struct {
+ TestField string `validate:"nonexistantfunction=1"`
+ }
+
+ t := &Test{
+ TestField: "Test"
+ }
+
+ validate.Struct(t) // this will panic
+*/
+package validator
diff --git a/go-playground/validator/v10/errors.go b/go-playground/validator/v10/errors.go
new file mode 100644
index 0000000..776029c
--- /dev/null
+++ b/go-playground/validator/v10/errors.go
@@ -0,0 +1,295 @@
+package validator
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "strings"
+
+ ut "gin-valid/go-playground/universal-translator"
+)
+
+const (
+ fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag"
+)
+
+// ValidationErrorsTranslations is the translation return type
+type ValidationErrorsTranslations map[string]string
+
+// InvalidValidationError describes an invalid argument passed to
+// `Struct`, `StructExcept`, StructPartial` or `Field`
+type InvalidValidationError struct {
+ Type reflect.Type
+}
+
+// Error returns InvalidValidationError message
+func (e *InvalidValidationError) Error() string {
+
+ if e.Type == nil {
+ return "validator: (nil)"
+ }
+
+ return "validator: (nil " + e.Type.String() + ")"
+}
+
+// ValidationErrors is an array of FieldError's
+// for use in custom error messages post validation.
+type ValidationErrors []FieldError
+
+// Error is intended for use in development + debugging and not intended to be a production error message.
+// It allows ValidationErrors to subscribe to the Error interface.
+// All information to create an error message specific to your application is contained within
+// the FieldError found within the ValidationErrors array
+func (ve ValidationErrors) Error() string {
+
+ buff := bytes.NewBufferString("")
+
+ var fe *fieldError
+
+ for i := 0; i < len(ve); i++ {
+
+ fe = ve[i].(*fieldError)
+ buff.WriteString(fe.Error())
+ buff.WriteString("\n")
+ }
+
+ return strings.TrimSpace(buff.String())
+}
+
+// yang 修改
+// Translate translates all of the ValidationErrors
+//func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations {
+//
+// trans := make(ValidationErrorsTranslations)
+//
+// var fe *fieldError
+//
+// for i := 0; i < len(ve); i++ {
+// fe = ve[i].(*fieldError)
+//
+// // // in case an Anonymous struct was used, ensure that the key
+// // // would be 'Username' instead of ".Username"
+// // if len(fe.ns) > 0 && fe.ns[:1] == "." {
+// // trans[fe.ns[1:]] = fe.Translate(ut)
+// // continue
+// // }
+//
+// trans[fe.ns] = fe.Translate(ut)
+// }
+//
+// return trans
+//}
+type TransValidError struct {
+ ErrorString string
+}
+
+func (e TransValidError) Error() string {
+ return e.ErrorString
+}
+func (ve ValidationErrors) Translate(ut ut.Translator) TransValidError {
+ var result TransValidError
+ var fe *fieldError
+ if len(ve) == 0 {
+ return result
+ }
+ fe = ve[0].(*fieldError)
+ result.ErrorString = fe.Translate(ut)
+ return result
+}
+
+// yang 修改结束
+
+// FieldError contains all functions to get error details
+type FieldError interface {
+
+ // returns the validation tag that failed. if the
+ // validation was an alias, this will return the
+ // alias name and not the underlying tag that failed.
+ //
+ // eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
+ // will return "iscolor"
+ Tag() string
+
+ // returns the validation tag that failed, even if an
+ // alias the actual tag within the alias will be returned.
+ // If an 'or' validation fails the entire or will be returned.
+ //
+ // eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
+ // will return "hexcolor|rgb|rgba|hsl|hsla"
+ ActualTag() string
+
+ // returns the namespace for the field error, with the tag
+ // name taking precedence over the field's actual name.
+ //
+ // eg. JSON name "User.fname"
+ //
+ // See StructNamespace() for a version that returns actual names.
+ //
+ // NOTE: this field can be blank when validating a single primitive field
+ // using validate.Field(...) as there is no way to extract it's name
+ Namespace() string
+
+ // returns the namespace for the field error, with the field's
+ // actual name.
+ //
+ // eq. "User.FirstName" see Namespace for comparison
+ //
+ // NOTE: this field can be blank when validating a single primitive field
+ // using validate.Field(...) as there is no way to extract its name
+ StructNamespace() string
+
+ // returns the fields name with the tag name taking precedence over the
+ // field's actual name.
+ //
+ // eq. JSON name "fname"
+ // see StructField for comparison
+ Field() string
+
+ // returns the field's actual name from the struct, when able to determine.
+ //
+ // eq. "FirstName"
+ // see Field for comparison
+ StructField() string
+
+ // returns the actual field's value in case needed for creating the error
+ // message
+ Value() interface{}
+
+ // returns the param value, in string form for comparison; this will also
+ // help with generating an error message
+ Param() string
+
+ // Kind returns the Field's reflect Kind
+ //
+ // eg. time.Time's kind is a struct
+ Kind() reflect.Kind
+
+ // Type returns the Field's reflect Type
+ //
+ // // eg. time.Time's type is time.Time
+ Type() reflect.Type
+
+ // returns the FieldError's translated error
+ // from the provided 'ut.Translator' and registered 'TranslationFunc'
+ //
+ // NOTE: if no registered translator can be found it returns the same as
+ // calling fe.Error()
+ Translate(ut ut.Translator) string
+
+ // Error returns the FieldError's message
+ Error() string
+}
+
+// compile time interface checks
+var _ FieldError = new(fieldError)
+var _ error = new(fieldError)
+
+// fieldError contains a single field's validation error along
+// with other properties that may be needed for error message creation
+// it complies with the FieldError interface
+type fieldError struct {
+ v *Validate
+ tag string
+ actualTag string
+ ns string
+ structNs string
+ fieldLen uint8
+ structfieldLen uint8
+ value interface{}
+ param string
+ kind reflect.Kind
+ typ reflect.Type
+}
+
+// Tag returns the validation tag that failed.
+func (fe *fieldError) Tag() string {
+ return fe.tag
+}
+
+// ActualTag returns the validation tag that failed, even if an
+// alias the actual tag within the alias will be returned.
+func (fe *fieldError) ActualTag() string {
+ return fe.actualTag
+}
+
+// Namespace returns the namespace for the field error, with the tag
+// name taking precedence over the field's actual name.
+func (fe *fieldError) Namespace() string {
+ return fe.ns
+}
+
+// StructNamespace returns the namespace for the field error, with the field's
+// actual name.
+func (fe *fieldError) StructNamespace() string {
+ return fe.structNs
+}
+
+// Field returns the field's name with the tag name taking precedence over the
+// field's actual name.
+func (fe *fieldError) Field() string {
+
+ return fe.ns[len(fe.ns)-int(fe.fieldLen):]
+ // // return fe.field
+ // fld := fe.ns[len(fe.ns)-int(fe.fieldLen):]
+
+ // log.Println("FLD:", fld)
+
+ // if len(fld) > 0 && fld[:1] == "." {
+ // return fld[1:]
+ // }
+
+ // return fld
+}
+
+// returns the field's actual name from the struct, when able to determine.
+func (fe *fieldError) StructField() string {
+ // return fe.structField
+ return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):]
+}
+
+// Value returns the actual field's value in case needed for creating the error
+// message
+func (fe *fieldError) Value() interface{} {
+ return fe.value
+}
+
+// Param returns the param value, in string form for comparison; this will
+// also help with generating an error message
+func (fe *fieldError) Param() string {
+ return fe.param
+}
+
+// Kind returns the Field's reflect Kind
+func (fe *fieldError) Kind() reflect.Kind {
+ return fe.kind
+}
+
+// Type returns the Field's reflect Type
+func (fe *fieldError) Type() reflect.Type {
+ return fe.typ
+}
+
+// Error returns the fieldError's error message
+func (fe *fieldError) Error() string {
+ return fmt.Sprintf(fieldErrMsg, fe.ns, fe.Field(), fe.tag)
+}
+
+// Translate returns the FieldError's translated error
+// from the provided 'ut.Translator' and registered 'TranslationFunc'
+//
+// NOTE: if no registered translation can be found, it returns the original
+// untranslated error message.
+func (fe *fieldError) Translate(ut ut.Translator) string {
+
+ m, ok := fe.v.transTagFunc[ut]
+ if !ok {
+ return fe.Error()
+ }
+
+ fn, ok := m[fe.tag]
+ if !ok {
+ return fe.Error()
+ }
+
+ return fn(ut, fe)
+}
diff --git a/go-playground/validator/v10/field_level.go b/go-playground/validator/v10/field_level.go
new file mode 100644
index 0000000..f0e2a9a
--- /dev/null
+++ b/go-playground/validator/v10/field_level.go
@@ -0,0 +1,119 @@
+package validator
+
+import "reflect"
+
+// FieldLevel contains all the information and helper functions
+// to validate a field
+type FieldLevel interface {
+ // returns the top level struct, if any
+ Top() reflect.Value
+
+ // returns the current fields parent struct, if any or
+ // the comparison value if called 'VarWithValue'
+ Parent() reflect.Value
+
+ // returns current field for validation
+ Field() reflect.Value
+
+ // returns the field's name with the tag
+ // name taking precedence over the fields actual name.
+ FieldName() string
+
+ // returns the struct field's name
+ StructFieldName() string
+
+ // returns param for validation against current field
+ Param() string
+
+ // GetTag returns the current validations tag name
+ GetTag() string
+
+ // ExtractType gets the actual underlying type of field value.
+ // It will dive into pointers, customTypes and return you the
+ // underlying value and it's kind.
+ ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)
+
+ // traverses the parent struct to retrieve a specific field denoted by the provided namespace
+ // in the param and returns the field, field kind and whether is was successful in retrieving
+ // the field at all.
+ //
+ // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
+ // could not be retrieved because it didn't exist.
+ //
+ // Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
+ GetStructFieldOK() (reflect.Value, reflect.Kind, bool)
+
+ // GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
+ // the field and namespace allowing more extensibility for validators.
+ //
+ // Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
+ GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool)
+
+ // traverses the parent struct to retrieve a specific field denoted by the provided namespace
+ // in the param and returns the field, field kind, if it's a nullable type and whether is was successful in retrieving
+ // the field at all.
+ //
+ // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
+ // could not be retrieved because it didn't exist.
+ GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool)
+
+ // GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
+ // the field and namespace allowing more extensibility for validators.
+ GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool)
+}
+
+var _ FieldLevel = new(validate)
+
+// Field returns current field for validation
+func (v *validate) Field() reflect.Value {
+ return v.flField
+}
+
+// FieldName returns the field's name with the tag
+// name taking precedence over the fields actual name.
+func (v *validate) FieldName() string {
+ return v.cf.altName
+}
+
+// GetTag returns the current validations tag name
+func (v *validate) GetTag() string {
+ return v.ct.tag
+}
+
+// StructFieldName returns the struct field's name
+func (v *validate) StructFieldName() string {
+ return v.cf.name
+}
+
+// Param returns param for validation against current field
+func (v *validate) Param() string {
+ return v.ct.param
+}
+
+// GetStructFieldOK returns Param returns param for validation against current field
+//
+// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
+func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) {
+ current, kind, _, found := v.getStructFieldOKInternal(v.slflParent, v.ct.param)
+ return current, kind, found
+}
+
+// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
+// the field and namespace allowing more extensibility for validators.
+//
+// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
+func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) {
+ current, kind, _, found := v.GetStructFieldOKAdvanced2(val, namespace)
+ return current, kind, found
+}
+
+// GetStructFieldOK returns Param returns param for validation against current field
+func (v *validate) GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) {
+ return v.getStructFieldOKInternal(v.slflParent, v.ct.param)
+}
+
+// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
+// the field and namespace allowing more extensibility for validators.
+func (v *validate) GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) {
+ return v.getStructFieldOKInternal(val, namespace)
+}
diff --git a/go-playground/validator/v10/go.sum b/go-playground/validator/v10/go.sum
new file mode 100644
index 0000000..3dc4e15
--- /dev/null
+++ b/go-playground/validator/v10/go.sum
@@ -0,0 +1,48 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
+github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
+github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+gin-valid/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+gin-valid/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+gin-valid/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+gin-valid/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+gin-valid/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
+github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/go-playground/validator/v10/logo.png b/go-playground/validator/v10/logo.png
new file mode 100644
index 0000000..355000f
Binary files /dev/null and b/go-playground/validator/v10/logo.png differ
diff --git a/go-playground/validator/v10/non-standard/validators/notblank.go b/go-playground/validator/v10/non-standard/validators/notblank.go
new file mode 100644
index 0000000..9aa4c96
--- /dev/null
+++ b/go-playground/validator/v10/non-standard/validators/notblank.go
@@ -0,0 +1,25 @@
+package validators
+
+import (
+ "reflect"
+ "strings"
+
+ "gin-valid/go-playground/validator/v10"
+)
+
+// NotBlank is the validation function for validating if the current field
+// has a value or length greater than zero, or is not a space only string.
+func NotBlank(fl validator.FieldLevel) bool {
+ field := fl.Field()
+
+ switch field.Kind() {
+ case reflect.String:
+ return len(strings.TrimSpace(field.String())) > 0
+ case reflect.Chan, reflect.Map, reflect.Slice, reflect.Array:
+ return field.Len() > 0
+ case reflect.Ptr, reflect.Interface, reflect.Func:
+ return !field.IsNil()
+ default:
+ return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
+ }
+}
diff --git a/go-playground/validator/v10/non-standard/validators/notblank_test.go b/go-playground/validator/v10/non-standard/validators/notblank_test.go
new file mode 100644
index 0000000..5ef53dd
--- /dev/null
+++ b/go-playground/validator/v10/non-standard/validators/notblank_test.go
@@ -0,0 +1,65 @@
+package validators
+
+import (
+ "testing"
+
+ "gin-valid/go-playground/validator/v10"
+ "github.com/go-playground/assert/v2"
+)
+
+type test struct {
+ String string `validate:"notblank"`
+ Array []int `validate:"notblank"`
+ Pointer *int `validate:"notblank"`
+ Number int `validate:"notblank"`
+ Interface interface{} `validate:"notblank"`
+ Func func() `validate:"notblank"`
+}
+
+func TestNotBlank(t *testing.T) {
+ v := validator.New()
+ err := v.RegisterValidation("notblank", NotBlank)
+ assert.Equal(t, nil, err)
+
+ // Errors
+ var x *int
+ invalid := test{
+ String: " ",
+ Array: []int{},
+ Pointer: x,
+ Number: 0,
+ Interface: nil,
+ Func: nil,
+ }
+ fieldsWithError := []string{
+ "String",
+ "Array",
+ "Pointer",
+ "Number",
+ "Interface",
+ "Func",
+ }
+
+ errors := v.Struct(invalid).(validator.ValidationErrors)
+ var fields []string
+ for _, err := range errors {
+ fields = append(fields, err.Field())
+ }
+
+ assert.Equal(t, fieldsWithError, fields)
+
+ // No errors
+ y := 1
+ x = &y
+ valid := test{
+ String: "str",
+ Array: []int{1},
+ Pointer: x,
+ Number: 1,
+ Interface: "value",
+ Func: func() {},
+ }
+
+ err = v.Struct(valid)
+ assert.Equal(t, nil, err)
+}
diff --git a/go-playground/validator/v10/regexes.go b/go-playground/validator/v10/regexes.go
new file mode 100644
index 0000000..b741f4e
--- /dev/null
+++ b/go-playground/validator/v10/regexes.go
@@ -0,0 +1,101 @@
+package validator
+
+import "regexp"
+
+const (
+ alphaRegexString = "^[a-zA-Z]+$"
+ alphaNumericRegexString = "^[a-zA-Z0-9]+$"
+ alphaUnicodeRegexString = "^[\\p{L}]+$"
+ alphaUnicodeNumericRegexString = "^[\\p{L}\\p{N}]+$"
+ numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$"
+ numberRegexString = "^[0-9]+$"
+ hexadecimalRegexString = "^(0[xX])?[0-9a-fA-F]+$"
+ hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
+ rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$"
+ rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
+ hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$"
+ hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
+ emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
+ e164RegexString = "^\\+[1-9]?[0-9]{7,14}$"
+ base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
+ base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$"
+ iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$"
+ iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$"
+ uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
+ uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
+ uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
+ uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
+ uUID3RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-3[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
+ uUID4RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
+ uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
+ uUIDRFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
+ aSCIIRegexString = "^[\x00-\x7F]*$"
+ printableASCIIRegexString = "^[\x20-\x7E]*$"
+ multibyteRegexString = "[^\x00-\x7F]"
+ dataURIRegexString = `^data:((?:\w+\/(?:([^;]|;[^;]).)+)?)`
+ latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
+ longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
+ sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$`
+ hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
+ hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
+ fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62})(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
+ btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
+ btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
+ btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
+ ethAddressRegexString = `^0x[0-9a-fA-F]{40}$`
+ ethAddressUpperRegexString = `^0x[0-9A-F]{40}$`
+ ethAddressLowerRegexString = `^0x[0-9a-f]{40}$`
+ uRLEncodedRegexString = `(%[A-Fa-f0-9]{2})`
+ hTMLEncodedRegexString = `[x]?([0-9a-fA-F]{2})|(>)|(<)|(")|(&)+[;]?`
+ hTMLRegexString = `<[/]?([a-zA-Z]+).*?>`
+ splitParamsRegexString = `'[^']*'|\S+`
+)
+
+var (
+ alphaRegex = regexp.MustCompile(alphaRegexString)
+ alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString)
+ alphaUnicodeRegex = regexp.MustCompile(alphaUnicodeRegexString)
+ alphaUnicodeNumericRegex = regexp.MustCompile(alphaUnicodeNumericRegexString)
+ numericRegex = regexp.MustCompile(numericRegexString)
+ numberRegex = regexp.MustCompile(numberRegexString)
+ hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString)
+ hexcolorRegex = regexp.MustCompile(hexcolorRegexString)
+ rgbRegex = regexp.MustCompile(rgbRegexString)
+ rgbaRegex = regexp.MustCompile(rgbaRegexString)
+ hslRegex = regexp.MustCompile(hslRegexString)
+ hslaRegex = regexp.MustCompile(hslaRegexString)
+ e164Regex = regexp.MustCompile(e164RegexString)
+ emailRegex = regexp.MustCompile(emailRegexString)
+ base64Regex = regexp.MustCompile(base64RegexString)
+ base64URLRegex = regexp.MustCompile(base64URLRegexString)
+ iSBN10Regex = regexp.MustCompile(iSBN10RegexString)
+ iSBN13Regex = regexp.MustCompile(iSBN13RegexString)
+ uUID3Regex = regexp.MustCompile(uUID3RegexString)
+ uUID4Regex = regexp.MustCompile(uUID4RegexString)
+ uUID5Regex = regexp.MustCompile(uUID5RegexString)
+ uUIDRegex = regexp.MustCompile(uUIDRegexString)
+ uUID3RFC4122Regex = regexp.MustCompile(uUID3RFC4122RegexString)
+ uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString)
+ uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString)
+ uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString)
+ aSCIIRegex = regexp.MustCompile(aSCIIRegexString)
+ printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString)
+ multibyteRegex = regexp.MustCompile(multibyteRegexString)
+ dataURIRegex = regexp.MustCompile(dataURIRegexString)
+ latitudeRegex = regexp.MustCompile(latitudeRegexString)
+ longitudeRegex = regexp.MustCompile(longitudeRegexString)
+ sSNRegex = regexp.MustCompile(sSNRegexString)
+ hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952)
+ hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123)
+ fqdnRegexRFC1123 = regexp.MustCompile(fqdnRegexStringRFC1123)
+ btcAddressRegex = regexp.MustCompile(btcAddressRegexString)
+ btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32)
+ btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32)
+ ethAddressRegex = regexp.MustCompile(ethAddressRegexString)
+ ethaddressRegexUpper = regexp.MustCompile(ethAddressUpperRegexString)
+ ethAddressRegexLower = regexp.MustCompile(ethAddressLowerRegexString)
+ uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString)
+ hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString)
+ hTMLRegex = regexp.MustCompile(hTMLRegexString)
+ splitParamsRegex = regexp.MustCompile(splitParamsRegexString)
+)
diff --git a/go-playground/validator/v10/struct_level.go b/go-playground/validator/v10/struct_level.go
new file mode 100644
index 0000000..57691ee
--- /dev/null
+++ b/go-playground/validator/v10/struct_level.go
@@ -0,0 +1,175 @@
+package validator
+
+import (
+ "context"
+ "reflect"
+)
+
+// StructLevelFunc accepts all values needed for struct level validation
+type StructLevelFunc func(sl StructLevel)
+
+// StructLevelFuncCtx accepts all values needed for struct level validation
+// but also allows passing of contextual validation information via context.Context.
+type StructLevelFuncCtx func(ctx context.Context, sl StructLevel)
+
+// wrapStructLevelFunc wraps normal StructLevelFunc makes it compatible with StructLevelFuncCtx
+func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx {
+ return func(ctx context.Context, sl StructLevel) {
+ fn(sl)
+ }
+}
+
+// StructLevel contains all the information and helper functions
+// to validate a struct
+type StructLevel interface {
+
+ // returns the main validation object, in case one wants to call validations internally.
+ // this is so you don't have to use anonymous functions to get access to the validate
+ // instance.
+ Validator() *Validate
+
+ // returns the top level struct, if any
+ Top() reflect.Value
+
+ // returns the current fields parent struct, if any
+ Parent() reflect.Value
+
+ // returns the current struct.
+ Current() reflect.Value
+
+ // ExtractType gets the actual underlying type of field value.
+ // It will dive into pointers, customTypes and return you the
+ // underlying value and its kind.
+ ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)
+
+ // reports an error just by passing the field and tag information
+ //
+ // NOTES:
+ //
+ // fieldName and altName get appended to the existing namespace that
+ // validator is on. e.g. pass 'FirstName' or 'Names[0]' depending
+ // on the nesting
+ //
+ // tag can be an existing validation tag or just something you make up
+ // and process on the flip side it's up to you.
+ ReportError(field interface{}, fieldName, structFieldName string, tag, param string)
+
+ // reports an error just by passing ValidationErrors
+ //
+ // NOTES:
+ //
+ // relativeNamespace and relativeActualNamespace get appended to the
+ // existing namespace that validator is on.
+ // e.g. pass 'User.FirstName' or 'Users[0].FirstName' depending
+ // on the nesting. most of the time they will be blank, unless you validate
+ // at a level lower the the current field depth
+ ReportValidationErrors(relativeNamespace, relativeActualNamespace string, errs ValidationErrors)
+}
+
+var _ StructLevel = new(validate)
+
+// Top returns the top level struct
+//
+// NOTE: this can be the same as the current struct being validated
+// if not is a nested struct.
+//
+// this is only called when within Struct and Field Level validation and
+// should not be relied upon for an acurate value otherwise.
+func (v *validate) Top() reflect.Value {
+ return v.top
+}
+
+// Parent returns the current structs parent
+//
+// NOTE: this can be the same as the current struct being validated
+// if not is a nested struct.
+//
+// this is only called when within Struct and Field Level validation and
+// should not be relied upon for an acurate value otherwise.
+func (v *validate) Parent() reflect.Value {
+ return v.slflParent
+}
+
+// Current returns the current struct.
+func (v *validate) Current() reflect.Value {
+ return v.slCurrent
+}
+
+// Validator returns the main validation object, in case one want to call validations internally.
+func (v *validate) Validator() *Validate {
+ return v.v
+}
+
+// ExtractType gets the actual underlying type of field value.
+func (v *validate) ExtractType(field reflect.Value) (reflect.Value, reflect.Kind, bool) {
+ return v.extractTypeInternal(field, false)
+}
+
+// ReportError reports an error just by passing the field and tag information
+func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag, param string) {
+
+ fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false)
+
+ if len(structFieldName) == 0 {
+ structFieldName = fieldName
+ }
+
+ v.str1 = string(append(v.ns, fieldName...))
+
+ if v.v.hasTagNameFunc || fieldName != structFieldName {
+ v.str2 = string(append(v.actualNs, structFieldName...))
+ } else {
+ v.str2 = v.str1
+ }
+
+ if kind == reflect.Invalid {
+
+ v.errs = append(v.errs,
+ &fieldError{
+ v: v.v,
+ tag: tag,
+ actualTag: tag,
+ ns: v.str1,
+ structNs: v.str2,
+ fieldLen: uint8(len(fieldName)),
+ structfieldLen: uint8(len(structFieldName)),
+ param: param,
+ kind: kind,
+ },
+ )
+ return
+ }
+
+ v.errs = append(v.errs,
+ &fieldError{
+ v: v.v,
+ tag: tag,
+ actualTag: tag,
+ ns: v.str1,
+ structNs: v.str2,
+ fieldLen: uint8(len(fieldName)),
+ structfieldLen: uint8(len(structFieldName)),
+ value: fv.Interface(),
+ param: param,
+ kind: kind,
+ typ: fv.Type(),
+ },
+ )
+}
+
+// ReportValidationErrors reports ValidationErrors obtained from running validations within the Struct Level validation.
+//
+// NOTE: this function prepends the current namespace to the relative ones.
+func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNamespace string, errs ValidationErrors) {
+
+ var err *fieldError
+
+ for i := 0; i < len(errs); i++ {
+
+ err = errs[i].(*fieldError)
+ err.ns = string(append(append(v.ns, relativeNamespace...), err.ns...))
+ err.structNs = string(append(append(v.actualNs, relativeStructNamespace...), err.structNs...))
+
+ v.errs = append(v.errs, err)
+ }
+}
diff --git a/go-playground/validator/v10/testdata/a.go b/go-playground/validator/v10/testdata/a.go
new file mode 100644
index 0000000..69d29d3
--- /dev/null
+++ b/go-playground/validator/v10/testdata/a.go
@@ -0,0 +1 @@
+package testdata
diff --git a/go-playground/validator/v10/translations.go b/go-playground/validator/v10/translations.go
new file mode 100644
index 0000000..1ea55ac
--- /dev/null
+++ b/go-playground/validator/v10/translations.go
@@ -0,0 +1,11 @@
+package validator
+
+import ut "gin-valid/go-playground/universal-translator"
+
+// TranslationFunc is the function type used to register or override
+// custom translations
+type TranslationFunc func(ut ut.Translator, fe FieldError) string
+
+// RegisterTranslationsFunc allows for registering of translations
+// for a 'ut.Translator' for use within the 'TranslationFunc'
+type RegisterTranslationsFunc func(ut ut.Translator) error
diff --git a/go-playground/validator/v10/translations/en/en.go b/go-playground/validator/v10/translations/en/en.go
new file mode 100644
index 0000000..c56466b
--- /dev/null
+++ b/go-playground/validator/v10/translations/en/en.go
@@ -0,0 +1,1405 @@
+package en
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0} is a required field",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0} must be {1} in length", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} character", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} characters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0} must be equal to {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0} must contain {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("len-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0} must be at least {1} in length", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} character", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} characters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0} must be {1} or greater", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "{0} must contain at least {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("min-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0} must be a maximum of {1} in length", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} character", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} characters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0} must be {1} or less", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0} must contain at maximum {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("max-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0} is not equal to {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0} should not be equal to {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0} must be less than {1} in length", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} character", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} characters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0} must be less than {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0} must contain less than {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0} must be less than the current Date & Time", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0} must be at maximum {1} in length", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} character", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} characters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0} must be {1} or less", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0} must contain at maximum {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0} must be less than or equal to the current Date & Time", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0} must be greater than {1} in length", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} character", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} characters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0} must be greater than {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0} must contain more than {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0} must be greater than the current Date & Time", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0} must be at least {1} in length", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} character", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} characters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0} must be {1} or greater", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0} must contain at least {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0} must be greater than or equal to the current Date & Time", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0} must be equal to {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0} must be equal to {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0} cannot be equal to {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0} must be greater than {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0} must be greater than or equal to {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0} must be less than {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0} must be less than or equal to {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0} cannot be equal to {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0} must be greater than {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0} must be greater than or equal to {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0} must be less than {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0} must be less than or equal to {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0} can only contain alphabetic characters",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0} can only contain alphanumeric characters",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0} must be a valid numeric value",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0} must be a valid number",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0} must be a valid hexadecimal",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0} must be a valid HEX color",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0} must be a valid RGB color",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0} must be a valid RGBA color",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0} must be a valid HSL color",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0} must be a valid HSLA color",
+ override: false,
+ },
+ {
+ tag: "e164",
+ translation: "{0} must be a valid E.164 formatted phone number",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0} must be a valid email address",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0} must be a valid URL",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0} must be a valid URI",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0} must be a valid Base64 string",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0} must contain the text '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0} must contain at least one of the following characters '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0} cannot contain the text '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0} cannot contain any of the following characters '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0} cannot contain the following '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0} must be a valid ISBN number",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0} must be a valid ISBN-10 number",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0} must be a valid ISBN-13 number",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0} must be a valid UUID",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0} must be a valid version 3 UUID",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0} must be a valid version 4 UUID",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0} must be a valid version 5 UUID",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0} must contain only ascii characters",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0} must contain only printable ascii characters",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0} must contain multibyte characters",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0} must contain a valid Data URI",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0} must contain valid latitude coordinates",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0} must contain a valid longitude coordinates",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0} must be a valid SSN number",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0} must be a valid IPv4 address",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0} must be a valid IPv6 address",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0} must be a valid IP address",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0} must contain a valid CIDR notation",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0} must contain a valid CIDR notation for an IPv4 address",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0} must contain a valid CIDR notation for an IPv6 address",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0} must be a valid TCP address",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0} must be a valid IPv4 TCP address",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0} must be a valid IPv6 TCP address",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0} must be a valid UDP address",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0} must be a valid IPv4 UDP address",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0} must be a valid IPv6 UDP address",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0} must be a resolvable IP address",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0} must be a resolvable IPv4 address",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0} must be a resolvable IPv6 address",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0} must be a resolvable UNIX address",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0} must contain a valid MAC address",
+ override: false,
+ },
+ {
+ tag: "unique",
+ translation: "{0} must contain unique values",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0} must be a valid color",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0} must be one of [{1}]",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ {
+ tag: "json",
+ translation: "{0} must be a valid json string",
+ override: false,
+ },
+ {
+ tag: "lowercase",
+ translation: "{0} must be a lowercase string",
+ override: false,
+ },
+ {
+ tag: "uppercase",
+ translation: "{0} must be an uppercase string",
+ override: false,
+ },
+ {
+ tag: "datetime",
+ translation: "{0} does not match the {1} format",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/en/en_test.go b/go-playground/validator/v10/translations/en/en_test.go
new file mode 100644
index 0000000..9b6be95
--- /dev/null
+++ b/go-playground/validator/v10/translations/en/en_test.go
@@ -0,0 +1,676 @@
+package en
+
+import (
+ "testing"
+ "time"
+
+ english "gin-valid/go-playground/locales/en"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ eng := english.New()
+ uni := ut.New(eng, eng)
+ trans, _ := uni.GetTranslator("en")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ UniqueSlice []string `validate:"unique"`
+ UniqueArray [3]string `validate:"unique"`
+ UniqueMap map[string]string `validate:"unique"`
+ JSONString string `validate:"json"`
+ LowercaseString string `validate:"lowercase"`
+ UppercaseString string `validate:"uppercase"`
+ Datetime string `validate:"datetime=2006-01-02"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ test.LowercaseString = "ABCDEFG"
+ test.UppercaseString = "abcdefg"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ test.UniqueSlice = []string{"1234", "1234"}
+ test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
+ test.Datetime = "2008-Feb-01"
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor must be a valid color",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC must contain a valid MAC address",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr must be a resolvable IP address",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4 must be a resolvable IPv4 address",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6 must be a resolvable IPv6 address",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr must be a valid UDP address",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4 must be a valid IPv4 UDP address",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6 must be a valid IPv6 UDP address",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr must be a valid TCP address",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4 must be a valid IPv4 TCP address",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6 must be a valid IPv6 TCP address",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR must contain a valid CIDR notation",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4 must contain a valid CIDR notation for an IPv4 address",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6 must contain a valid CIDR notation for an IPv6 address",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN must be a valid SSN number",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP must be a valid IP address",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4 must be a valid IPv4 address",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6 must be a valid IPv6 address",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI must contain a valid Data URI",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude must contain valid latitude coordinates",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude must contain a valid longitude coordinates",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte must contain multibyte characters",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII must contain only ascii characters",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII must contain only printable ascii characters",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID must be a valid UUID",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3 must be a valid version 3 UUID",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4 must be a valid version 4 UUID",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5 must be a valid version 5 UUID",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN must be a valid ISBN number",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10 must be a valid ISBN-10 number",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13 must be a valid ISBN-13 number",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes cannot contain the text 'text'",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll cannot contain any of the following characters '!@#$'",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune cannot contain the following '☻'",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny must contain at least one of the following characters '!@#$'",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains must contain the text 'purpose'",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64 must be a valid Base64 string",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email must be a valid email address",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL must be a valid URL",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI must be a valid URI",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString must be a valid RGB color",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString must be a valid RGBA color",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString must be a valid HSL color",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString must be a valid HSLA color",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString must be a valid hexadecimal",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString must be a valid HEX color",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString must be a valid number",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString must be a valid numeric value",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString can only contain alphanumeric characters",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString can only contain alphabetic characters",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString must be less than MaxString",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString must be less than or equal to MaxString",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString must be greater than MaxString",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString must be greater than or equal to MaxString",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString cannot be equal to EqFieldString",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString must be less than Inner.LtCSFieldString",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString must be less than or equal to Inner.LteCSFieldString",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString must be greater than Inner.GtCSFieldString",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString must be greater than or equal to Inner.GteCSFieldString",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString cannot be equal to Inner.NeCSFieldString",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString must be equal to Inner.EqCSFieldString",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString must be equal to MaxString",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteString must be at least 3 characters in length",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber must be 5.56 or greater",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple must contain at least 2 items",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime must be greater than or equal to the current Date & Time",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtString must be greater than 3 characters in length",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber must be greater than 5.56",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple must contain more than 2 items",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime must be greater than the current Date & Time",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteString must be at maximum 3 characters in length",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber must be 5.56 or less",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple must contain at maximum 2 items",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime must be less than or equal to the current Date & Time",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtString must be less than 3 characters in length",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber must be less than 5.56",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple must contain less than 2 items",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime must be less than the current Date & Time",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString should not be equal to ",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber should not be equal to 0.00",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple should not be equal to 0",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString is not equal to 3",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber is not equal to 2.33",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple is not equal to 7",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxString must be a maximum of 3 characters in length",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber must be 1,113.00 or less",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple must contain at maximum 7 items",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinString must be at least 1 character in length",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber must be 1,113.00 or greater",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultiple must contain at least 7 items",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenString must be 1 character in length",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber must be equal to 1,113.00",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple must contain 7 items",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString is a required field",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber is a required field",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple is a required field",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLen must be at least 10 characters in length",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLen must be a maximum of 1 character in length",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLen must be 2 characters in length",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLt must be less than 1 character in length",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLte must be at maximum 1 character in length",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGt must be greater than 10 characters in length",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGte must be at least 10 characters in length",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString must be one of [red green]",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt must be one of [5 63]",
+ },
+ {
+ ns: "Test.UniqueSlice",
+ expected: "UniqueSlice must contain unique values",
+ },
+ {
+ ns: "Test.UniqueArray",
+ expected: "UniqueArray must contain unique values",
+ },
+ {
+ ns: "Test.UniqueMap",
+ expected: "UniqueMap must contain unique values",
+ },
+ {
+ ns: "Test.JSONString",
+ expected: "JSONString must be a valid json string",
+ },
+ {
+ ns: "Test.LowercaseString",
+ expected: "LowercaseString must be a lowercase string",
+ },
+ {
+ ns: "Test.UppercaseString",
+ expected: "UppercaseString must be an uppercase string",
+ },
+ {
+ ns: "Test.Datetime",
+ expected: "Datetime does not match the 2006-01-02 format",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/es/es.go b/go-playground/validator/v10/translations/es/es.go
new file mode 100644
index 0000000..7722aac
--- /dev/null
+++ b/go-playground/validator/v10/translations/es/es.go
@@ -0,0 +1,1375 @@
+package es
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0} es un campo requerido",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0} debe tener {1} de longitud", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} carácter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0} debe ser igual a {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0} debe contener {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("len-items-item", "{0} elemento", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-items-item", "{0} elementos", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0} debe tener al menos {1} de longitud", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} carácter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0} debe ser {1} o más", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "{0} debe contener al menos {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("min-items-item", "{0} elemento", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-items-item", "{0} elementos", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0} debe tener un máximo de {1} de longitud", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} carácter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0} debe ser {1} o menos", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0} debe contener como máximo {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("max-items-item", "{0} elemento", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-items-item", "{0} elementos", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0} no es igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0} no debería ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0} debe tener menos de {1} de longitud", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} carácter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0} debe ser menos de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0} debe contener menos de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} elemento", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} elementos", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0} debe ser antes de la fecha y hora actual", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0} debe tener un máximo de {1} de longitud", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} carácter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0} debe ser {1} o menos", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0} debe contener como máximo {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} elemento", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} elementos", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0} debe ser antes o durante la fecha y hora actual", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0} debe ser mayor que {1} en longitud", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} carácter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0} debe ser mayor que {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0} debe contener más de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} elemento", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} elementos", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0} debe ser después de la fecha y hora actual", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0} debe tener al menos {1} de longitud", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} carácter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0} debe ser {1} o mayor", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0} debe contener al menos {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} elemento", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} elementos", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0} debe ser después o durante la fecha y hora actuales", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0} debe ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0} debe ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0} no puede ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0} debe ser mayor que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0} debe ser mayor o igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0} debe ser menor que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0} debe ser menor o igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0} no puede ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0} debe ser mayor que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0} debe ser mayor o igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0} debe ser menor que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0} debe ser menor o igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0} sólo puede contener caracteres alfabéticos",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0} sólo puede contener caracteres alfanuméricos",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0} debe ser un valor numérico válido",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0} debe ser un número válido",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0} debe ser un hexadecimal válido",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0} debe ser un color HEX válido",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0} debe ser un color RGB válido",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0} debe ser un color RGBA válido",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0} debe ser un color HSL válido",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0} debe ser un color HSL válido",
+ override: false,
+ },
+ {
+ tag: "e164",
+ translation: "{0} debe ser un número de teléfono válido con formato E.164",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0} debe ser una dirección de correo electrónico válida",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0} debe ser un URL válido",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0} debe ser una URI válida",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0} debe ser una cadena de Base64 válida",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0} debe contener el texto '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0} debe contener al menos uno de los siguientes caracteres '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0} no puede contener el texto '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0} no puede contener ninguno de los siguientes caracteres '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0} no puede contener lo siguiente '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0} debe ser un número ISBN válido",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0} debe ser un número ISBN-10 válido",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0} debe ser un número ISBN-13 válido",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0} debe ser un UUID válido",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0} debe ser una versión válida 3 UUID",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0} debe ser una versión válida 4 UUID",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0} debe ser una versión válida 5 UUID",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0} debe contener sólo caracteres ascii",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0} debe contener sólo caracteres ASCII imprimibles",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0} debe contener caracteres multibyte",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0} debe contener un URI de datos válido",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0} debe contener coordenadas de latitud válidas",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0} debe contener unas coordenadas de longitud válidas",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0} debe ser un número válido de SSN",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0} debe ser una dirección IPv4 válida",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0} debe ser una dirección IPv6 válida",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0} debe ser una dirección IP válida",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0} debe contener una anotación válida del CIDR",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0} debe contener una notación CIDR válida para una dirección IPv4",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0} debe contener una notación CIDR válida para una dirección IPv6",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0} debe ser una dirección TCP válida",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0} debe ser una dirección IPv4 TCP válida",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0} debe ser una dirección IPv6 TCP válida",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0} debe ser una dirección UDP válida",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0} debe ser una dirección IPv4 UDP válida",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0} debe ser una dirección IPv6 UDP válida",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0} debe ser una dirección IP resoluble",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0} debe ser una dirección IPv4 resoluble",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0} debe ser una dirección IPv6 resoluble",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0} debe ser una dirección UNIX resoluble",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0} debe contener una dirección MAC válida",
+ override: false,
+ },
+ {
+ tag: "unique",
+ translation: "{0} debe contener valores únicos",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0} debe ser un color válido",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0} debe ser uno de [{1}]",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/es/es_test.go b/go-playground/validator/v10/translations/es/es_test.go
new file mode 100644
index 0000000..5985c38
--- /dev/null
+++ b/go-playground/validator/v10/translations/es/es_test.go
@@ -0,0 +1,652 @@
+package es
+
+import (
+ "testing"
+ "time"
+
+ spanish "gin-valid/go-playground/locales/es"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ spa := spanish.New()
+ uni := ut.New(spa, spa)
+ trans, _ := uni.GetTranslator("es")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ UniqueSlice []string `validate:"unique"`
+ UniqueArray [3]string `validate:"unique"`
+ UniqueMap map[string]string `validate:"unique"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ test.UniqueSlice = []string{"1234", "1234"}
+ test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor debe ser un color válido",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC debe contener una dirección MAC válida",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr debe ser una dirección IP resoluble",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4 debe ser una dirección IPv4 resoluble",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6 debe ser una dirección IPv6 resoluble",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr debe ser una dirección UDP válida",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4 debe ser una dirección IPv4 UDP válida",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6 debe ser una dirección IPv6 UDP válida",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr debe ser una dirección TCP válida",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4 debe ser una dirección IPv4 TCP válida",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6 debe ser una dirección IPv6 TCP válida",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR debe contener una anotación válida del CIDR",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4 debe contener una notación CIDR válida para una dirección IPv4",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6 debe contener una notación CIDR válida para una dirección IPv6",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN debe ser un número válido de SSN",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP debe ser una dirección IP válida",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4 debe ser una dirección IPv4 válida",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6 debe ser una dirección IPv6 válida",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI debe contener un URI de datos válido",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude debe contener coordenadas de latitud válidas",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude debe contener unas coordenadas de longitud válidas",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte debe contener caracteres multibyte",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII debe contener sólo caracteres ascii",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII debe contener sólo caracteres ASCII imprimibles",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID debe ser un UUID válido",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3 debe ser una versión válida 3 UUID",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4 debe ser una versión válida 4 UUID",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5 debe ser una versión válida 5 UUID",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN debe ser un número ISBN válido",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10 debe ser un número ISBN-10 válido",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13 debe ser un número ISBN-13 válido",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes no puede contener el texto 'text'",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll no puede contener ninguno de los siguientes caracteres '!@#$'",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune no puede contener lo siguiente '☻'",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny debe contener al menos uno de los siguientes caracteres '!@#$'",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains debe contener el texto 'purpose'",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64 debe ser una cadena de Base64 válida",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email debe ser una dirección de correo electrónico válida",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL debe ser un URL válido",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI debe ser una URI válida",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString debe ser un color RGB válido",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString debe ser un color RGBA válido",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString debe ser un color HSL válido",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString debe ser un color HSL válido",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString debe ser un hexadecimal válido",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString debe ser un color HEX válido",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString debe ser un número válido",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString debe ser un valor numérico válido",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString sólo puede contener caracteres alfanuméricos",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString sólo puede contener caracteres alfabéticos",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString debe ser menor que MaxString",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString debe ser menor o igual a MaxString",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString debe ser mayor que MaxString",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString debe ser mayor o igual a MaxString",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString no puede ser igual a EqFieldString",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString debe ser menor que Inner.LtCSFieldString",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString debe ser menor o igual a Inner.LteCSFieldString",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString debe ser mayor que Inner.GtCSFieldString",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString debe ser mayor o igual a Inner.GteCSFieldString",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString no puede ser igual a Inner.NeCSFieldString",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString debe ser igual a Inner.EqCSFieldString",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString debe ser igual a MaxString",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteString debe tener al menos 3 caracteres de longitud",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber debe ser 5,56 o mayor",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple debe contener al menos 2 elementos",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime debe ser después o durante la fecha y hora actuales",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtString debe ser mayor que 3 caracteres en longitud",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber debe ser mayor que 5,56",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple debe contener más de 2 elementos",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime debe ser después de la fecha y hora actual",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteString debe tener un máximo de 3 caracteres de longitud",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber debe ser 5,56 o menos",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple debe contener como máximo 2 elementos",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime debe ser antes o durante la fecha y hora actual",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtString debe tener menos de 3 caracteres de longitud",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber debe ser menos de 5,56",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple debe contener menos de 2 elementos",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime debe ser antes de la fecha y hora actual",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString no debería ser igual a ",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber no debería ser igual a 0.00",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple no debería ser igual a 0",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString no es igual a 3",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber no es igual a 2.33",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple no es igual a 7",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxString debe tener un máximo de 3 caracteres de longitud",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber debe ser 1.113,00 o menos",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple debe contener como máximo 7 elementos",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinString debe tener al menos 1 carácter de longitud",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber debe ser 1.113,00 o más",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultiple debe contener al menos 7 elementos",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenString debe tener 1 carácter de longitud",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber debe ser igual a 1.113,00",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple debe contener 7 elementos",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString es un campo requerido",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber es un campo requerido",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple es un campo requerido",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLen debe tener al menos 10 caracteres de longitud",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLen debe tener un máximo de 1 carácter de longitud",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLen debe tener 2 caracteres de longitud",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLt debe tener menos de 1 carácter de longitud",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLte debe tener un máximo de 1 carácter de longitud",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGt debe ser mayor que 10 caracteres en longitud",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGte debe tener al menos 10 caracteres de longitud",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString debe ser uno de [red green]",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt debe ser uno de [5 63]",
+ },
+ {
+ ns: "Test.UniqueSlice",
+ expected: "UniqueSlice debe contener valores únicos",
+ },
+ {
+ ns: "Test.UniqueArray",
+ expected: "UniqueArray debe contener valores únicos",
+ },
+ {
+ ns: "Test.UniqueMap",
+ expected: "UniqueMap debe contener valores únicos",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/fr/fr.go b/go-playground/validator/v10/translations/fr/fr.go
new file mode 100644
index 0000000..82dbacf
--- /dev/null
+++ b/go-playground/validator/v10/translations/fr/fr.go
@@ -0,0 +1,1365 @@
+package fr
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0} est un champ obligatoire",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0} doit faire une taille de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} caractère", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} caractères", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0} doit être égal à {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-elements", "{0} doit contenir {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("len-elements-element", "{0} element", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-elements-element", "{0} elements", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-elements-element", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-elements", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0} doit faire une taille minimum de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} caractère", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} caractères", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0} doit être égal à {1} ou plus", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-elements", "{0} doit contenir au moins {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("min-elements-element", "{0} element", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-elements-element", "{0} elements", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-elements-element", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-elements", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0} doit faire une taille maximum de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} caractère", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} caractères", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0} doit être égal à {1} ou moins", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-elements", "{0} doit contenir au maximum {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("max-elements-element", "{0} element", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-elements-element", "{0} elements", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-elements-element", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-elements", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0} n'est pas égal à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0} ne doit pas être égal à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0} doit avoir une taille inférieure à {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} caractère", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} caractères", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0} doit être inférieur à {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-elements", "{0} doit contenir mois de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-elements-element", "{0} element", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-elements-element", "{0} elements", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0} doit être avant la date et l'heure actuelle", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-elements-element", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-elements", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0} doit faire une taille maximum de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} caractère", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} caractères", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0} doit faire {1} ou moins", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-elements", "{0} doit contenir un maximum de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-elements-element", "{0} element", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-elements-element", "{0} elements", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0} doit être avant ou pendant la date et l'heure actuelle", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-elements-element", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-elements", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0} doit avoir une taille supérieur à {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} caractère", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} caractères", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0} doit être supérieur à {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-elements", "{0} doit contenir plus de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-elements-element", "{0} element", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-elements-element", "{0} elements", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0} doit être après la date et l'heure actuelle", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-elements-element", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-elements", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0} doit faire une taille d'au moins {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} caractère", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} caractères", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0} doit être {1} ou plus", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-elements", "{0} doit contenir au moins {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-elements-element", "{0} element", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-elements-element", "{0} elements", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0} doit être après ou pendant la date et l'heure actuelle", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-elements-element", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-elements", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0} doit être égal à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0} doit être égal à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0} ne doit pas être égal à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0} doit être supérieur à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0} doit être supérieur ou égal à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0} doit être inférieur à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0} doit être inférieur ou égal à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0} ne doit pas être égal à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0} doit être supérieur à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0} doit être supérieur ou égal à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0} doit être inférieur à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0} doit être inférieur ou égal à {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0} ne doit contenir que des caractères alphabétiques",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0} ne doit contenir que des caractères alphanumériques",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0} doit être une valeur numérique valide",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0} doit être un nombre valid",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0} doit être une chaîne de caractères au format hexadécimal valide",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0} doit être une couleur au format HEX valide",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0} doit être une couleur au format RGB valide",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0} doit être une couleur au format RGBA valide",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0} doit être une couleur au format HSL valide",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0} doit être une couleur au format HSLA valide",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0} doit être une adresse email valide",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0} doit être une URL valide",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0} doit être une URI valide",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0} doit être une chaîne de caractères au format Base64 valide",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0} doit contenir le texte '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0} doit contenir au moins l' un des caractères suivants '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0} ne doit pas contenir le texte '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0} ne doit pas contenir l'un des caractères suivants '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0} ne doit pas contenir ce qui suit '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0} doit être un numéro ISBN valid",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0} doit être un numéro ISBN-10 valid",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0} doit être un numéro ISBN-13 valid",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0} doit être un UUID valid",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0} doit être un UUID version 3 valid",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0} doit être un UUID version 4 valid",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0} doit être un UUID version 5 valid",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0} ne doit contenir que des caractères ascii",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0} ne doit contenir que des caractères ascii affichables",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0} doit contenir des caractères multioctets",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0} doit contenir une URI data valide",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0} doit contenir des coordonnées latitude valides",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0} doit contenir des coordonnées longitudes valides",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0} doit être un numéro SSN valide",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0} doit être une adressse IPv4 valide",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0} doit être une adressse IPv6 valide",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0} doit être une adressse IP valide",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0} doit contenir une notation CIDR valide",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0} doit contenir une notation CIDR valide pour une adresse IPv4",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0} doit contenir une notation CIDR valide pour une adresse IPv6",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0} doit être une adressse TCP valide",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0} doit être une adressse IPv4 TCP valide",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0} doit être une adressse IPv6 TCP valide",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0} doit être une adressse UDP valide",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0} doit être une adressse IPv4 UDP valide",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0} doit être une adressse IPv6 UDP valide",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0} doit être une adresse IP résolvable",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0} doit être une adresse IPv4 résolvable",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0} doit être une adresse IPv6 résolvable",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0} doit être une adresse UNIX résolvable",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0} doit contenir une adresse MAC valide",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0} doit être une couleur valide",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0} doit être l'un des choix suivants [{1}]",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/fr/fr_test.go b/go-playground/validator/v10/translations/fr/fr_test.go
new file mode 100644
index 0000000..08c8a63
--- /dev/null
+++ b/go-playground/validator/v10/translations/fr/fr_test.go
@@ -0,0 +1,634 @@
+package fr
+
+import (
+ "testing"
+ "time"
+
+ french "gin-valid/go-playground/locales/fr"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ fre := french.New()
+ uni := ut.New(fre, fre)
+ trans, _ := uni.GetTranslator("fr")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor doit être une couleur valide",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC doit contenir une adresse MAC valide",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr doit être une adresse IP résolvable",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4 doit être une adresse IPv4 résolvable",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6 doit être une adresse IPv6 résolvable",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr doit être une adressse UDP valide",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4 doit être une adressse IPv4 UDP valide",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6 doit être une adressse IPv6 UDP valide",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr doit être une adressse TCP valide",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4 doit être une adressse IPv4 TCP valide",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6 doit être une adressse IPv6 TCP valide",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR doit contenir une notation CIDR valide",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4 doit contenir une notation CIDR valide pour une adresse IPv4",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6 doit contenir une notation CIDR valide pour une adresse IPv6",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN doit être un numéro SSN valide",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP doit être une adressse IP valide",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4 doit être une adressse IPv4 valide",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6 doit être une adressse IPv6 valide",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI doit contenir une URI data valide",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude doit contenir des coordonnées latitude valides",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude doit contenir des coordonnées longitudes valides",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte doit contenir des caractères multioctets",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII ne doit contenir que des caractères ascii",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII ne doit contenir que des caractères ascii affichables",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID doit être un UUID valid",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3 doit être un UUID version 3 valid",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4 doit être un UUID version 4 valid",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5 doit être un UUID version 5 valid",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN doit être un numéro ISBN valid",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10 doit être un numéro ISBN-10 valid",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13 doit être un numéro ISBN-13 valid",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes ne doit pas contenir le texte 'text'",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll ne doit pas contenir l'un des caractères suivants '!@#$'",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune ne doit pas contenir ce qui suit '☻'",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny doit contenir au moins l' un des caractères suivants '!@#$'",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains doit contenir le texte 'purpose'",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64 doit être une chaîne de caractères au format Base64 valide",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email doit être une adresse email valide",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL doit être une URL valide",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI doit être une URI valide",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString doit être une couleur au format RGB valide",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString doit être une couleur au format RGBA valide",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString doit être une couleur au format HSL valide",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString doit être une couleur au format HSLA valide",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString doit être une chaîne de caractères au format hexadécimal valide",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString doit être une couleur au format HEX valide",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString doit être un nombre valid",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString doit être une valeur numérique valide",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString ne doit contenir que des caractères alphanumériques",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString ne doit contenir que des caractères alphabétiques",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString doit être inférieur à MaxString",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString doit être inférieur ou égal à MaxString",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString doit être supérieur à MaxString",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString doit être supérieur ou égal à MaxString",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString ne doit pas être égal à EqFieldString",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString doit être inférieur à Inner.LtCSFieldString",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString doit être inférieur ou égal à Inner.LteCSFieldString",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString doit être supérieur à Inner.GtCSFieldString",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString doit être supérieur ou égal à Inner.GteCSFieldString",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString ne doit pas être égal à Inner.NeCSFieldString",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString doit être égal à Inner.EqCSFieldString",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString doit être égal à MaxString",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteString doit faire une taille d'au moins 3 caractères",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber doit être 5,56 ou plus",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple doit contenir au moins 2 elements",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime doit être après ou pendant la date et l'heure actuelle",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtString doit avoir une taille supérieur à 3 caractères",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber doit être supérieur à 5,56",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple doit contenir plus de 2 elements",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime doit être après la date et l'heure actuelle",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteString doit faire une taille maximum de 3 caractères",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber doit faire 5,56 ou moins",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple doit contenir un maximum de 2 elements",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime doit être avant ou pendant la date et l'heure actuelle",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtString doit avoir une taille inférieure à 3 caractères",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber doit être inférieur à 5,56",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple doit contenir mois de 2 elements",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime doit être avant la date et l'heure actuelle",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString ne doit pas être égal à ",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber ne doit pas être égal à 0.00",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple ne doit pas être égal à 0",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString n'est pas égal à 3",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber n'est pas égal à 2.33",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple n'est pas égal à 7",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxString doit faire une taille maximum de 3 caractères",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber doit être égal à 1 113,00 ou moins",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple doit contenir au maximum 7 elements",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinString doit faire une taille minimum de 1 caractère",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber doit être égal à 1 113,00 ou plus",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultiple doit contenir au moins 7 elements",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenString doit faire une taille de 1 caractère",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber doit être égal à 1 113,00",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple doit contenir 7 elements",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString est un champ obligatoire",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber est un champ obligatoire",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple est un champ obligatoire",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLen doit faire une taille minimum de 10 caractères",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLen doit faire une taille maximum de 1 caractère",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLen doit faire une taille de 2 caractères",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLt doit avoir une taille inférieure à 1 caractère",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLte doit faire une taille maximum de 1 caractère",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGt doit avoir une taille supérieur à 10 caractères",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGte doit faire une taille d'au moins 10 caractères",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString doit être l'un des choix suivants [red green]",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt doit être l'un des choix suivants [5 63]",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/id/id.go b/go-playground/validator/v10/translations/id/id.go
new file mode 100644
index 0000000..e98b61b
--- /dev/null
+++ b/go-playground/validator/v10/translations/id/id.go
@@ -0,0 +1,1365 @@
+package id
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0} wajib diisi",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "panjang {0} harus {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("len-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("len-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0} harus sama dengan {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0} harus berisi {1}", false); err != nil {
+ return
+ }
+ // if err = ut.AddCardinal("len-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("len-items-item", "{0} item", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "panjang minimal {0} adalah {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("min-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("min-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0} harus {1} atau lebih besar", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "panjang minimal {0} adalah {1}", false); err != nil {
+ return
+ }
+ // if err = ut.AddCardinal("min-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("min-items-item", "{0} item", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "panjang maksimal {0} adalah {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("max-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("max-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0} harus {1} atau kurang", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0} harus berisi maksimal {1}", false); err != nil {
+ return
+ }
+ // if err = ut.AddCardinal("max-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("max-items-item", "{0} item", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0} tidak sama dengan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0} tidak sama dengan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "panjang {0} harus kurang dari {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("lt-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0} harus kurang dari {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0} harus berisi kurang dari {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("lt-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} item", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0} harus kurang dari tanggal & waktu saat ini", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "panjang maksimal {0} adalah {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("lte-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0} harus {1} atau kurang", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0} harus berisi maksimal {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("lte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} item", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0} harus kurang dari atau sama dengan tanggal & waktu saat ini", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "panjang {0} harus lebih dari {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("gt-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0} harus lebih besar dari {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0} harus berisi lebih dari {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("gt-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} item", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0} harus lebih besar dari tanggal & waktu saat ini", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "panjang minimal {0} adalah {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("gte-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0} harus {1} atau lebih besar", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0} harus berisi setidaknya {1}", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("gte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} item", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0} harus lebih besar dari atau sama dengan tanggal & waktu saat ini", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0} harus sama dengan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0} harus sama dengan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0} tidak sama dengan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0} harus lebih besar dari {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0} harus lebih besar dari atau sama dengan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0} harus kurang dari {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0} harus kurang dari atau sama dengan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0} tidak sama dengan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0} harus lebih besar dari {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0} harus lebih besar dari atau sama dengan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0} harus kurang dari {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0} harus kurang dari atau sama dengan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0} hanya dapat berisi karakter abjad",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0} hanya dapat berisi karakter alfanumerik",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0} harus berupa nilai numerik yang valid",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0} harus berupa angka yang valid",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0} harus berupa heksadesimal yang valid",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0} harus berupa warna HEX yang valid",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0} harus berupa warna RGB yang valid",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0} harus berupa warna RGBA yang valid",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0} harus berupa warna HSL yang valid",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0} harus berupa warna HSLA yang valid",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0} harus berupa alamat email yang valid",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0} harus berupa URL yang valid",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0} harus berupa URI yang valid",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0} harus berupa string Base64 yang valid",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0} harus berisi teks '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0} harus berisi setidaknya salah satu karakter berikut '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0} tidak boleh berisi teks '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0} tidak boleh berisi salah satu karakter berikut '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0} tidak boleh berisi '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0} harus berupa nomor ISBN yang valid",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0} harus berupa nomor ISBN-10 yang valid",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0} harus berupa nomor ISBN-13 yang valid",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0} harus berupa UUID yang valid",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0} harus berupa UUID versi 3 yang valid",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0} harus berupa UUID versi 4 yang valid",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0} harus berupa UUID versi 5 yang valid",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0} hanya boleh berisi karakter ascii",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0} hanya boleh berisi karakter ascii yang dapat dicetak",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0} harus berisi karakter multibyte",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0} harus berisi URI Data yang valid",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0} harus berisi koordinat lintang yang valid",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0} harus berisi koordinat bujur yang valid",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0} harus berupa nomor SSN yang valid",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0} harus berupa alamat IPv4 yang valid",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0} harus berupa alamat IPv6 yang valid",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0} harus berupa alamat IP yang valid",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0} harus berisi notasi CIDR yang valid",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0} harus berisi notasi CIDR yang valid untuk alamat IPv4",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0} harus berisi notasi CIDR yang valid untuk alamat IPv6",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0} harus berupa alamat TCP yang valid",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0} harus berupa alamat TCP IPv4 yang valid",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0} harus berupa alamat TCP IPv6 yang valid",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0} harus berupa alamat UDP yang valid",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0} harus berupa alamat IPv4 UDP yang valid",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0} harus berupa alamat IPv6 UDP yang valid",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0} harus berupa alamat IP yang dapat dipecahkan",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0} harus berupa alamat IPv4 yang dapat diatasi",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0} harus berupa alamat IPv6 yang dapat diatasi",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0} harus berupa alamat UNIX yang dapat diatasi",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0} harus berisi alamat MAC yang valid",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0} harus berupa warna yang valid",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0} harus berupa salah satu dari [{1}]",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/id/id_test.go b/go-playground/validator/v10/translations/id/id_test.go
new file mode 100644
index 0000000..1305ff6
--- /dev/null
+++ b/go-playground/validator/v10/translations/id/id_test.go
@@ -0,0 +1,634 @@
+package id
+
+import (
+ "testing"
+ "time"
+
+ indonesia "gin-valid/go-playground/locales/id"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ idn := indonesia.New()
+ uni := ut.New(idn, idn)
+ trans, _ := uni.GetTranslator("id")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=tujuan"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=merah hijau"`
+ OneOfInt int `validate:"oneof=5 63"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor harus berupa warna yang valid",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC harus berisi alamat MAC yang valid",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr harus berupa alamat IP yang dapat dipecahkan",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4 harus berupa alamat IPv4 yang dapat diatasi",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6 harus berupa alamat IPv6 yang dapat diatasi",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr harus berupa alamat UDP yang valid",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4 harus berupa alamat IPv4 UDP yang valid",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6 harus berupa alamat IPv6 UDP yang valid",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr harus berupa alamat TCP yang valid",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4 harus berupa alamat TCP IPv4 yang valid",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6 harus berupa alamat TCP IPv6 yang valid",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR harus berisi notasi CIDR yang valid",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4 harus berisi notasi CIDR yang valid untuk alamat IPv4",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6 harus berisi notasi CIDR yang valid untuk alamat IPv6",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN harus berupa nomor SSN yang valid",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP harus berupa alamat IP yang valid",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4 harus berupa alamat IPv4 yang valid",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6 harus berupa alamat IPv6 yang valid",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI harus berisi URI Data yang valid",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude harus berisi koordinat lintang yang valid",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude harus berisi koordinat bujur yang valid",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte harus berisi karakter multibyte",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII hanya boleh berisi karakter ascii",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII hanya boleh berisi karakter ascii yang dapat dicetak",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID harus berupa UUID yang valid",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3 harus berupa UUID versi 3 yang valid",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4 harus berupa UUID versi 4 yang valid",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5 harus berupa UUID versi 5 yang valid",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN harus berupa nomor ISBN yang valid",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10 harus berupa nomor ISBN-10 yang valid",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13 harus berupa nomor ISBN-13 yang valid",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes tidak boleh berisi teks 'text'",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll tidak boleh berisi salah satu karakter berikut '!@#$'",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune tidak boleh berisi '☻'",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny harus berisi setidaknya salah satu karakter berikut '!@#$'",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains harus berisi teks 'tujuan'",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64 harus berupa string Base64 yang valid",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email harus berupa alamat email yang valid",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL harus berupa URL yang valid",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI harus berupa URI yang valid",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString harus berupa warna RGB yang valid",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString harus berupa warna RGBA yang valid",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString harus berupa warna HSL yang valid",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString harus berupa warna HSLA yang valid",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString harus berupa heksadesimal yang valid",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString harus berupa warna HEX yang valid",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString harus berupa angka yang valid",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString harus berupa nilai numerik yang valid",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString hanya dapat berisi karakter alfanumerik",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString hanya dapat berisi karakter abjad",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString harus kurang dari MaxString",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString harus kurang dari atau sama dengan MaxString",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString harus lebih besar dari MaxString",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString harus lebih besar dari atau sama dengan MaxString",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString tidak sama dengan EqFieldString",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString harus kurang dari Inner.LtCSFieldString",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString harus kurang dari atau sama dengan Inner.LteCSFieldString",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString harus lebih besar dari Inner.GtCSFieldString",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString harus lebih besar dari atau sama dengan Inner.GteCSFieldString",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString tidak sama dengan Inner.NeCSFieldString",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString harus sama dengan Inner.EqCSFieldString",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString harus sama dengan MaxString",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "panjang minimal GteString adalah 3 karakter",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber harus 5,56 atau lebih besar",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple harus berisi setidaknya 2 item",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime harus lebih besar dari atau sama dengan tanggal & waktu saat ini",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "panjang GtString harus lebih dari 3 karakter",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber harus lebih besar dari 5,56",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple harus berisi lebih dari 2 item",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime harus lebih besar dari tanggal & waktu saat ini",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "panjang maksimal LteString adalah 3 karakter",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber harus 5,56 atau kurang",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple harus berisi maksimal 2 item",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime harus kurang dari atau sama dengan tanggal & waktu saat ini",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "panjang LtString harus kurang dari 3 karakter",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber harus kurang dari 5,56",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple harus berisi kurang dari 2 item",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime harus kurang dari tanggal & waktu saat ini",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString tidak sama dengan ",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber tidak sama dengan 0.00",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple tidak sama dengan 0",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString tidak sama dengan 3",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber tidak sama dengan 2.33",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple tidak sama dengan 7",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "panjang maksimal MaxString adalah 3 karakter",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber harus 1.113,00 atau kurang",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple harus berisi maksimal 7 item",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "panjang minimal MinString adalah 1 karakter",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber harus 1.113,00 atau lebih besar",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "panjang minimal MinMultiple adalah 7 item",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "panjang LenString harus 1 karakter",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber harus sama dengan 1.113,00",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple harus berisi 7 item",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString wajib diisi",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber wajib diisi",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple wajib diisi",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "panjang minimal StrPtrMinLen adalah 10 karakter",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "panjang maksimal StrPtrMaxLen adalah 1 karakter",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "panjang StrPtrLen harus 2 karakter",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "panjang StrPtrLt harus kurang dari 1 karakter",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "panjang maksimal StrPtrLte adalah 1 karakter",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "panjang StrPtrGt harus lebih dari 10 karakter",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "panjang minimal StrPtrGte adalah 10 karakter",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString harus berupa salah satu dari [merah hijau]",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt harus berupa salah satu dari [5 63]",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/ja/ja.go b/go-playground/validator/v10/translations/ja/ja.go
new file mode 100644
index 0000000..2de8803
--- /dev/null
+++ b/go-playground/validator/v10/translations/ja/ja.go
@@ -0,0 +1,1421 @@
+package ja
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0}は必須フィールドです",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0}の長さは{1}でなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("len-string-character", "{0}文字", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("len-string-character", "{0}文字", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0}は{1}と等しくなければなりません", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0}は{1}を含まなければなりません", false); err != nil {
+ return
+ }
+ // if err = ut.AddCardinal("len-items-item", "{0}つの項目", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("len-items-item", "{0}つの項目", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0}の長さは少なくとも{1}はなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("min-string-character", "{0}文字", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("min-string-character", "{0}文字", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0}は{1}かより大きくなければなりません", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "{0}は少なくとも{1}を含まなければなりません", false); err != nil {
+ return
+ }
+ // if err = ut.AddCardinal("min-items-item", "{0}つの項目", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("min-items-item", "{0}つの項目", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0}の長さは最大でも{1}でなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("max-string-character", "{0}文字", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("max-string-character", "{0}文字", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0}は{1}かより小さくなければなりません", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0}は最大でも{1}を含まなければなりません", false); err != nil {
+ return
+ }
+ // if err = ut.AddCardinal("max-items-item", "{0}つの項目", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("max-items-item", "{0}つの項目", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0}は{1}と等しくありません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+ if err = ut.Add("ne-items", "{0}の項目の数は{1}と異ならなければなりません", false); err != nil {
+ fmt.Printf("ne customRegisFunc #1 error because of %v\n", err)
+ return
+ }
+ // if err = ut.AddCardinal("ne-items-item", "{0}個", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("ne-items-item", "{0}個", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+ if err = ut.Add("ne", "{0}は{1}と異ならなければなりません", false); err != nil {
+ fmt.Printf("ne customRegisFunc #2 error because of %v\n", err)
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.Slice:
+ var c string
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("ne-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+ t, err = ut.T("ne-items", fe.Field(), c)
+ default:
+ t, err = ut.T("ne", fe.Field(), fe.Param())
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0}の長さは{1}よりも少なくなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("lt-string-character", "{0}文字", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("lt-string-character", "{0}文字", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0}は{1}よりも小さくなければなりません", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0}は{1}よりも少ない項目を含まなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("lt-items-item", "{0}つの項目", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("lt-items-item", "{0}つの項目", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0}は現時刻よりも前でなければなりません", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0}の長さは最大でも{1}でなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("lte-string-character", "{0}文字", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("lte-string-character", "{0}文字", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0}は{1}かより小さくなければなりません", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0}は最大でも{1}を含まなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("lte-items-item", "{0}つの項目", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("lte-items-item", "{0}つの項目", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0}は現時刻以前でなければなりません", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0}の長さは{1}よりも多くなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("gt-string-character", "{0}文字", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("gt-string-character", "{0}文字", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0}は{1}よりも大きくなければなりません", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0}は{1}よりも多い項目を含まなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("gt-items-item", "{0}つの項目", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("gt-items-item", "{0}つの項目", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0}は現時刻よりも後でなければなりません", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0}の長さは少なくとも{1}以上はなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("gte-string-character", "{0}文字", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("gte-string-character", "{0}文字", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0}は{1}かより大きくなければなりません", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0}は少なくとも{1}を含まなければなりません", false); err != nil {
+ return
+ }
+
+ // if err = ut.AddCardinal("gte-items-item", "{0}つの項目", locales.PluralRuleOne, false); err != nil {
+ // return
+ // }
+
+ if err = ut.AddCardinal("gte-items-item", "{0}つの項目", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0}は現時刻以降でなければなりません", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0}は{1}と等しくなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0}は{1}と等しくなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0}は{1}とは異ならなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0}は{1}よりも大きくなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0}は{1}以上でなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0}は{1}よりも小さくなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0}は{1}以下でなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0}は{1}とは異ならなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0}は{1}よりも大きくなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0}は{1}以上でなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0}は{1}よりも小さくなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0}は{1}以下でなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0}はアルファベットのみを含むことができます",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0}はアルファベットと数字のみを含むことができます",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0}は正しい数字でなければなりません",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0}は正しい数でなければなりません",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0}は正しい16進表記でなければなりません",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0}は正しいHEXカラーコードでなければなりません",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0}は正しいRGBカラーコードでなければなりません",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0}は正しいRGBAカラーコードでなければなりません",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0}は正しいHSLカラーコードでなければなりません",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0}は正しいHSLAカラーコードでなければなりません",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0}は正しいメールアドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0}は正しいURLでなければなりません",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0}は正しいURIでなければなりません",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0}は正しいBase64文字列でなければなりません",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0}は'{1}'を含まなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0}は'{1}'の少なくとも1つを含まなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0}には'{1}'というテキストを含むことはできません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0}には'{1}'のどれも含めることはできません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0}には'{1}'を含めることはできません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0}は正しいISBN番号でなければなりません",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0}は正しいISBN-10番号でなければなりません",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0}は正しいISBN-13番号でなければなりません",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0}は正しいUUIDでなければなりません",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0}はバージョンが3の正しいUUIDでなければなりません",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0}はバージョンが4の正しいUUIDでなければなりません",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0}はバージョンが4の正しいUUIDでなければなりません",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0}はASCII文字のみを含まなければなりません",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0}は印刷可能なASCII文字のみを含まなければなりません",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0}はマルチバイト文字を含まなければなりません",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0}は正しいデータURIを含まなければなりません",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0}は正しい緯度の座標を含まなければなりません",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0}は正しい経度の座標を含まなければなりません",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0}は正しい社会保障番号でなければなりません",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0}は正しいIPv4アドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0}は正しいIPv6アドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0}は正しいIPアドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0}は正しいCIDR表記を含まなければなりません",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0}はIPv4アドレスの正しいCIDR表記を含まなければなりません",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0}はIPv6アドレスの正しいCIDR表記を含まなければなりません",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0}は正しいTCPアドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0}は正しいIPv4のTCPアドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0}は正しいIPv6のTCPアドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0}は正しいUDPアドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0}は正しいIPv4のUDPアドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0}は正しいIPv6のUDPアドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0}は解決可能なIPアドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0}は解決可能なIPv4アドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0}は解決可能なIPv6アドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0}は解決可能なUNIXアドレスでなければなりません",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0}は正しいMACアドレスを含まなければなりません",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0}は正しい色でなければなりません",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0}は[{1}]のうちのいずれかでなければなりません",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/ja/ja_test.go b/go-playground/validator/v10/translations/ja/ja_test.go
new file mode 100644
index 0000000..d47fe34
--- /dev/null
+++ b/go-playground/validator/v10/translations/ja/ja_test.go
@@ -0,0 +1,634 @@
+package ja
+
+import (
+ "testing"
+ "time"
+
+ ja_locale "gin-valid/go-playground/locales/ja"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ japanese := ja_locale.New()
+ uni := ut.New(japanese, japanese)
+ trans, _ := uni.GetTranslator("ja")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColorは正しい色でなければなりません",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MACは正しいMACアドレスを含まなければなりません",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddrは解決可能なIPアドレスでなければなりません",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4は解決可能なIPv4アドレスでなければなりません",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6は解決可能なIPv6アドレスでなければなりません",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddrは正しいUDPアドレスでなければなりません",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4は正しいIPv4のUDPアドレスでなければなりません",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6は正しいIPv6のUDPアドレスでなければなりません",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddrは正しいTCPアドレスでなければなりません",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4は正しいIPv4のTCPアドレスでなければなりません",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6は正しいIPv6のTCPアドレスでなければなりません",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDRは正しいCIDR表記を含まなければなりません",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4はIPv4アドレスの正しいCIDR表記を含まなければなりません",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6はIPv6アドレスの正しいCIDR表記を含まなければなりません",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSNは正しい社会保障番号でなければなりません",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IPは正しいIPアドレスでなければなりません",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4は正しいIPv4アドレスでなければなりません",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6は正しいIPv6アドレスでなければなりません",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURIは正しいデータURIを含まなければなりません",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitudeは正しい緯度の座標を含まなければなりません",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitudeは正しい経度の座標を含まなければなりません",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByteはマルチバイト文字を含まなければなりません",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCIIはASCII文字のみを含まなければなりません",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCIIは印刷可能なASCII文字のみを含まなければなりません",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUIDは正しいUUIDでなければなりません",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3はバージョンが3の正しいUUIDでなければなりません",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4はバージョンが4の正しいUUIDでなければなりません",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5はバージョンが4の正しいUUIDでなければなりません",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBNは正しいISBN番号でなければなりません",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10は正しいISBN-10番号でなければなりません",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13は正しいISBN-13番号でなければなりません",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludesには'text'というテキストを含むことはできません",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAllには'!@#$'のどれも含めることはできません",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRuneには'☻'を含めることはできません",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAnyは'!@#$'の少なくとも1つを含まなければなりません",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Containsは'purpose'を含まなければなりません",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64は正しいBase64文字列でなければなりません",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Emailは正しいメールアドレスでなければなりません",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URLは正しいURLでなければなりません",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URIは正しいURIでなければなりません",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorStringは正しいRGBカラーコードでなければなりません",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorStringは正しいRGBAカラーコードでなければなりません",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorStringは正しいHSLカラーコードでなければなりません",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorStringは正しいHSLAカラーコードでなければなりません",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalStringは正しい16進表記でなければなりません",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorStringは正しいHEXカラーコードでなければなりません",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberStringは正しい数でなければなりません",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericStringは正しい数字でなければなりません",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumStringはアルファベットと数字のみを含むことができます",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaStringはアルファベットのみを含むことができます",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldStringはMaxStringよりも小さくなければなりません",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldStringはMaxString以下でなければなりません",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldStringはMaxStringよりも大きくなければなりません",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldStringはMaxString以上でなければなりません",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldStringはEqFieldStringとは異ならなければなりません",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldStringはInner.LtCSFieldStringよりも小さくなければなりません",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldStringはInner.LteCSFieldString以下でなければなりません",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldStringはInner.GtCSFieldStringよりも大きくなければなりません",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldStringはInner.GteCSFieldString以上でなければなりません",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldStringはInner.NeCSFieldStringとは異ならなければなりません",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldStringはInner.EqCSFieldStringと等しくなければなりません",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldStringはMaxStringと等しくなければなりません",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteStringの長さは少なくとも3文字以上はなければなりません",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumberは5.56かより大きくなければなりません",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultipleは少なくとも2つの項目を含まなければなりません",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTimeは現時刻以降でなければなりません",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtStringの長さは3文字よりも多くなければなりません",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumberは5.56よりも大きくなければなりません",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultipleは2つの項目よりも多い項目を含まなければなりません",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTimeは現時刻よりも後でなければなりません",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteStringの長さは最大でも3文字でなければなりません",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumberは5.56かより小さくなければなりません",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultipleは最大でも2つの項目を含まなければなりません",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTimeは現時刻以前でなければなりません",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtStringの長さは3文字よりも少なくなければなりません",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumberは5.56よりも小さくなければなりません",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultipleは2つの項目よりも少ない項目を含まなければなりません",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTimeは現時刻よりも前でなければなりません",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeStringはと異ならなければなりません",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumberは0.00と異ならなければなりません",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultipleの項目の数は0個と異ならなければなりません",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqStringは3と等しくありません",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumberは2.33と等しくありません",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultipleは7と等しくありません",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxStringの長さは最大でも3文字でなければなりません",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumberは1,113.00かより小さくなければなりません",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultipleは最大でも7つの項目を含まなければなりません",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinStringの長さは少なくとも1文字はなければなりません",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumberは1,113.00かより大きくなければなりません",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultipleは少なくとも7つの項目を含まなければなりません",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenStringの長さは1文字でなければなりません",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumberは1,113.00と等しくなければなりません",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultipleは7つの項目を含まなければなりません",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredStringは必須フィールドです",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumberは必須フィールドです",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultipleは必須フィールドです",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLenの長さは少なくとも10文字はなければなりません",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLenの長さは最大でも1文字でなければなりません",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLenの長さは2文字でなければなりません",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLtの長さは1文字よりも少なくなければなりません",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLteの長さは最大でも1文字でなければなりません",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGtの長さは10文字よりも多くなければなりません",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGteの長さは少なくとも10文字以上はなければなりません",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfStringは[red green]のうちのいずれかでなければなりません",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfIntは[5 63]のうちのいずれかでなければなりません",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/nl/nl.go b/go-playground/validator/v10/translations/nl/nl.go
new file mode 100644
index 0000000..bb9c544
--- /dev/null
+++ b/go-playground/validator/v10/translations/nl/nl.go
@@ -0,0 +1,1365 @@
+package nl
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0} is een verplicht veld",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0} moet {1} lang zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} karakters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0} moet gelijk zijn aan {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0} moet {1} bevatten", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("len-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0} moet tenminste {1} lang zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} karakters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0} moet {1} of groter zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "{0} moet tenminste {1} bevatten", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("min-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0} mag maximaal {1} lang zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} karakters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0} moet {1} of kleiner zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0} mag maximaal {1} bevatten", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("max-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0} is niet gelijk aan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0} mag niet gelijk zijn aan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0} moet minder dan {1} lang zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} karakters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0} moet kleiner zijn dan {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0} moet minder dan {1} bevatten", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0} moet kleiner zijn dan de huidige datum & tijd", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0} mag maximaal {1} lang zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} karakters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0} moet {1} of minder zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0} mag maximaal {1} bevatten", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0} moet kleiner dan of gelijk aan de huidige datum & tijd zijn", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0} moet langer dan {1} zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} karakters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0} moet groter zijn dan {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0} moet meer dan {1} bevatten", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0} moet groter zijn dan de huidige datum & tijd", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0} moet tenminste {1} lang zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} karakters", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0} moet {1} of groter zijn", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0} moet tenminste {1} bevatten", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0} moet groter dan of gelijk zijn aan de huidige datum & tijd", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0} moet gelijk zijn aan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0} moet gelijk zijn aan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0} mag niet gelijk zijn aan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0} moet groter zijn dan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0} moet groter dan of gelijk aan {1} zijn",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0} moet kleiner zijn dan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0} moet kleiner dan of gelijk aan {1} zijn",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0} mag niet gelijk zijn aan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0} moet groter zijn dan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0} moet groter dan of gelijk aan {1} zijn",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0} moet kleiner zijn dan {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0} moet kleiner dan of gelijk aan {1} zijn",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0} mag alleen alfabetische karakters bevatten",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0} mag alleen alfanumerieke karakters bevatten",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0} moet een geldige numerieke waarde zijn",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0} moet een geldig getal zijn",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0} moet een geldig hexadecimaal getal zijn",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0} moet een geldige HEX kleur zijn",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0} moet een geldige RGB kleur zijn",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0} moet een geldige RGBA kleur zijn",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0} moet een geldige HSL kleur zijn",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0} moet een geldige HSLA kleur zijn",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0} moet een geldig email adres zijn",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0} moet een geldige URL zijn",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0} moet een geldige URI zijn",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0} moet een geldige Base64 string zijn",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0} moet de tekst '{1}' bevatten",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0} moet tenminste een van de volgende karakters bevatten '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0} mag niet de tekst '{1}' bevatten",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0} mag niet een van de volgende karakters bevatten '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0} mag niet het volgende bevatten '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0} moet een geldig ISBN nummer zijn",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0} moet een geldig ISBN-10 nummer zijn",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0} moet een geldig ISBN-13 nummer zijn",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0} moet een geldige UUID zijn",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0} moet een geldige versie 3 UUID zijn",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0} moet een geldige versie 4 UUID zijn",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0} moet een geldige versie 5 UUID zijn",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0} mag alleen ascii karakters bevatten",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0} mag alleen afdrukbare ascii karakters bevatten",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0} moet multibyte karakters bevatten",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0} moet een geldige Data URI bevatten",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0} moet geldige breedtegraadcoördinaten bevatten",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0} moet geldige lengtegraadcoördinaten bevatten",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0} moet een geldig SSN nummer zijn",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0} moet een geldig IPv4 adres zijn",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0} moet een geldig IPv6 adres zijn",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0} moet een geldig IP adres zijn",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0} moet een geldige CIDR notatie bevatten",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0} moet een geldige CIDR notatie voor een IPv4 adres bevatten",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0} moet een geldige CIDR notatie voor een IPv6 adres bevatten",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0} moet een geldig TCP adres zijn",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0} moet een geldig IPv4 TCP adres zijn",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0} moet een geldig IPv6 TCP adres zijn",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0} moet een geldig UDP adres zijn",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0} moet een geldig IPv4 UDP adres zijn",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0} moet een geldig IPv6 UDP adres zijn",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0} moet een oplosbaar IP adres zijn",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0} moet een oplosbaar IPv4 adres zijn",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0} moet een oplosbaar IPv6 adres zijn",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0} moet een oplosbaar UNIX adres zijn",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0} moet een geldig MAC adres bevatten",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0} moet een geldige kleur zijn",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0} moet een van de volgende zijn [{1}]",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/nl/nl_test.go b/go-playground/validator/v10/translations/nl/nl_test.go
new file mode 100644
index 0000000..4ec720e
--- /dev/null
+++ b/go-playground/validator/v10/translations/nl/nl_test.go
@@ -0,0 +1,634 @@
+package nl
+
+import (
+ "testing"
+ "time"
+
+ english "gin-valid/go-playground/locales/en"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ eng := english.New()
+ uni := ut.New(eng, eng)
+ trans, _ := uni.GetTranslator("en")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor moet een geldige kleur zijn",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC moet een geldig MAC adres bevatten",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr moet een oplosbaar IP adres zijn",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4 moet een oplosbaar IPv4 adres zijn",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6 moet een oplosbaar IPv6 adres zijn",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr moet een geldig UDP adres zijn",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4 moet een geldig IPv4 UDP adres zijn",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6 moet een geldig IPv6 UDP adres zijn",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr moet een geldig TCP adres zijn",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4 moet een geldig IPv4 TCP adres zijn",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6 moet een geldig IPv6 TCP adres zijn",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR moet een geldige CIDR notatie bevatten",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4 moet een geldige CIDR notatie voor een IPv4 adres bevatten",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6 moet een geldige CIDR notatie voor een IPv6 adres bevatten",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN moet een geldig SSN nummer zijn",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP moet een geldig IP adres zijn",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4 moet een geldig IPv4 adres zijn",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6 moet een geldig IPv6 adres zijn",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI moet een geldige Data URI bevatten",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude moet geldige breedtegraadcoördinaten bevatten",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude moet geldige lengtegraadcoördinaten bevatten",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte moet multibyte karakters bevatten",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII mag alleen ascii karakters bevatten",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII mag alleen afdrukbare ascii karakters bevatten",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID moet een geldige UUID zijn",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3 moet een geldige versie 3 UUID zijn",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4 moet een geldige versie 4 UUID zijn",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5 moet een geldige versie 5 UUID zijn",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN moet een geldig ISBN nummer zijn",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10 moet een geldig ISBN-10 nummer zijn",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13 moet een geldig ISBN-13 nummer zijn",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes mag niet de tekst 'text' bevatten",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll mag niet een van de volgende karakters bevatten '!@#$'",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune mag niet het volgende bevatten '☻'",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny moet tenminste een van de volgende karakters bevatten '!@#$'",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains moet de tekst 'purpose' bevatten",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64 moet een geldige Base64 string zijn",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email moet een geldig email adres zijn",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL moet een geldige URL zijn",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI moet een geldige URI zijn",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString moet een geldige RGB kleur zijn",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString moet een geldige RGBA kleur zijn",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString moet een geldige HSL kleur zijn",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString moet een geldige HSLA kleur zijn",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString moet een geldig hexadecimaal getal zijn",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString moet een geldige HEX kleur zijn",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString moet een geldig getal zijn",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString moet een geldige numerieke waarde zijn",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString mag alleen alfanumerieke karakters bevatten",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString mag alleen alfabetische karakters bevatten",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString moet kleiner zijn dan MaxString",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString moet kleiner dan of gelijk aan MaxString zijn",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString moet groter zijn dan MaxString",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString moet groter dan of gelijk aan MaxString zijn",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString mag niet gelijk zijn aan EqFieldString",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString moet kleiner zijn dan Inner.LtCSFieldString",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString moet kleiner dan of gelijk aan Inner.LteCSFieldString zijn",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString moet groter zijn dan Inner.GtCSFieldString",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString moet groter dan of gelijk aan Inner.GteCSFieldString zijn",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString mag niet gelijk zijn aan Inner.NeCSFieldString",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString moet gelijk zijn aan Inner.EqCSFieldString",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString moet gelijk zijn aan MaxString",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteString moet tenminste 3 karakters lang zijn",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber moet 5.56 of groter zijn",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple moet tenminste 2 items bevatten",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime moet groter dan of gelijk zijn aan de huidige datum & tijd",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtString moet langer dan 3 karakters zijn",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber moet groter zijn dan 5.56",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple moet meer dan 2 items bevatten",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime moet groter zijn dan de huidige datum & tijd",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteString mag maximaal 3 karakters lang zijn",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber moet 5.56 of minder zijn",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple mag maximaal 2 items bevatten",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime moet kleiner dan of gelijk aan de huidige datum & tijd zijn",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtString moet minder dan 3 karakters lang zijn",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber moet kleiner zijn dan 5.56",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple moet minder dan 2 items bevatten",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime moet kleiner zijn dan de huidige datum & tijd",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString mag niet gelijk zijn aan ",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber mag niet gelijk zijn aan 0.00",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple mag niet gelijk zijn aan 0",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString is niet gelijk aan 3",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber is niet gelijk aan 2.33",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple is niet gelijk aan 7",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxString mag maximaal 3 karakters lang zijn",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber moet 1,113.00 of kleiner zijn",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple mag maximaal 7 items bevatten",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinString moet tenminste 1 karakter lang zijn",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber moet 1,113.00 of groter zijn",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultiple moet tenminste 7 items bevatten",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenString moet 1 karakter lang zijn",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber moet gelijk zijn aan 1,113.00",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple moet 7 items bevatten",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString is een verplicht veld",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber is een verplicht veld",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple is een verplicht veld",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLen moet tenminste 10 karakters lang zijn",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLen mag maximaal 1 karakter lang zijn",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLen moet 2 karakters lang zijn",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLt moet minder dan 1 karakter lang zijn",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLte mag maximaal 1 karakter lang zijn",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGt moet langer dan 10 karakters zijn",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGte moet tenminste 10 karakters lang zijn",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString moet een van de volgende zijn [red green]",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt moet een van de volgende zijn [5 63]",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/pt/pt.go b/go-playground/validator/v10/translations/pt/pt.go
new file mode 100644
index 0000000..e3d24a9
--- /dev/null
+++ b/go-playground/validator/v10/translations/pt/pt.go
@@ -0,0 +1,1405 @@
+package pt
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0} é obrigatório",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0} deve ter {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0} deve ser igual a {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0} deve conter {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("len-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0} deve ter pelo menos {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0} deve ser {1} ou superior", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "{0} deve conter pelo menos {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("min-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0} deve ter no máximo {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0} deve ser {1} ou menos", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0} deve conter no máximo {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("max-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0} não é igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0} não deve ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0} deve ter menos de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0} deve ser menor que {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0} deve conter menos de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0} deve ser anterior à data / hora atual", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0} deve ter no máximo {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0} deve ser menor ou igual a {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0} deve conter no máximo {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0} deve ser anterior ou igual à data/hora atual", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0} deve conter mais de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0} deve ser maior que {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0} deve conter mais de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0} deve ser posterior à data/hora atual", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0} deve ter pelo menos {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0} deve ser maior ou igual a {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0} deve conter pelo menos {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} items", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0} deve ser posterior ou igual à data/hora atual", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0} deve ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0} deve ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0} não deve ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0} deve ser maior que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0} deve ser maior ou igual que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0} deve ser menor que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0} deve ser menor ou igual que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0} não deve ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0} deve ser maior que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0} deve ser maior ou igual que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0} deve ser menor que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0} deve ser menor ou igual que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0} deve conter apenas caracteres alfabéticos",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0} deve conter apenas caracteres alfanuméricos",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0} deve ser um valor numérico válido",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0} deve ser um número válido",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0} deve ser um hexadecimal válido",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0} deve ser uma cor HEX válida",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0} deve ser uma cor RGB válida",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0} deve ser uma cor RGBA válida",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0} deve ser uma cor HSL válida",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0} deve ser uma cor HSLA válida",
+ override: false,
+ },
+ {
+ tag: "e164",
+ translation: "{0} deve ser um número de telefone válido no formato E.164",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0} deve ser um endereço de e-mail válido",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0} deve ser um URL válido",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0} deve ser um URI válido",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0} deve ser uma string Base64 válida",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0} deve conter o texto '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0} deve conter pelo menos um dos seguintes caracteres '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0} não deve conter o texto '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0} não deve conter os seguintes caracteres '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0} não pode conter o seguinte '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0} deve ser um número de ISBN válido",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0} deve ser um número ISBN-10 válido",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0} deve ser um número ISBN-13 válido",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0} deve ser um UUID válido",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0} deve ser um UUID versão 3 válido",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0} deve ser um UUID versão 4 válido",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0} deve ser um UUID versão 5 válido",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0} deve conter apenas caracteres ascii",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0} deve conter apenas caracteres ascii imprimíveis",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0} deve conter caracteres multibyte",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0} deve conter um Data URI válido",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0} deve conter uma coordenada de latitude válida",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0} deve conter uma coordenada de longitude válida",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0} deve ser um número SSN válido",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0} deve ser um endereço IPv4 válido",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0} deve ser um endereço IPv6 válido",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0} deve ser um endereço IP válido",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0} não respeita a notação CIDR",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0} não respeita a notação CIDR para um endereço IPv4",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0} não respeita a notação CIDR para um endereço IPv6",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0} deve ser um endereço TCP válido",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0} deve ser um endereço TCP IPv4 válido",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0} deve ser um endereço TCP IPv6 válido",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0} deve ser um endereço UDP válido",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0} deve ser um endereço UDP IPv4 válido",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0} deve ser um endereço UDP IPv6 válido",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0} deve ser um endereço IP resolvível",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0} deve ser um endereço IPv4 resolvível",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0} deve ser um endereço IPv6 resolvível",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0} deve ser um endereço UNIX resolvível",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0} deve conter um endereço MAC válido",
+ override: false,
+ },
+ {
+ tag: "unique",
+ translation: "{0} deve conter valores únicos",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0} deve ser uma cor válida",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0} deve ser um de [{1}]",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ {
+ tag: "json",
+ translation: "{0} deve ser uma string json válida",
+ override: false,
+ },
+ {
+ tag: "lowercase",
+ translation: "{0} deve estar em minuscúlas",
+ override: false,
+ },
+ {
+ tag: "uppercase",
+ translation: "{0} deve estar em maiúsculas",
+ override: false,
+ },
+ {
+ tag: "datetime",
+ translation: "{0} não está no formato {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/pt/pt_test.go b/go-playground/validator/v10/translations/pt/pt_test.go
new file mode 100644
index 0000000..f8a9c7e
--- /dev/null
+++ b/go-playground/validator/v10/translations/pt/pt_test.go
@@ -0,0 +1,677 @@
+package pt
+
+import (
+ "testing"
+ "time"
+
+ "gin-valid/go-playground/locales/pt"
+ ut "gin-valid/go-playground/universal-translator"
+ . "github.com/go-playground/assert/v2"
+
+ "gin-valid/go-playground/validator/v10"
+)
+
+func TestTranslations(t *testing.T) {
+
+ pt := pt.New()
+ uni := ut.New(pt, pt)
+ trans, _ := uni.GetTranslator("pt")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ UniqueSlice []string `validate:"unique"`
+ UniqueArray [3]string `validate:"unique"`
+ UniqueMap map[string]string `validate:"unique"`
+ JSONString string `validate:"json"`
+ LowercaseString string `validate:"lowercase"`
+ UppercaseString string `validate:"uppercase"`
+ Datetime string `validate:"datetime=2006-01-02"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ test.LowercaseString = "ABCDEFG"
+ test.UppercaseString = "abcdefg"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ test.UniqueSlice = []string{"1234", "1234"}
+ test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
+ test.Datetime = "2008-Feb-01"
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor deve ser uma cor válida",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC deve conter um endereço MAC válido",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr deve ser um endereço IP resolvível",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4 deve ser um endereço IPv4 resolvível",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6 deve ser um endereço IPv6 resolvível",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr deve ser um endereço UDP válido",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4 deve ser um endereço UDP IPv4 válido",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6 deve ser um endereço UDP IPv6 válido",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr deve ser um endereço TCP válido",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4 deve ser um endereço TCP IPv4 válido",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6 deve ser um endereço TCP IPv6 válido",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR não respeita a notação CIDR",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4 não respeita a notação CIDR para um endereço IPv4",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6 não respeita a notação CIDR para um endereço IPv6",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN deve ser um número SSN válido",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP deve ser um endereço IP válido",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4 deve ser um endereço IPv4 válido",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6 deve ser um endereço IPv6 válido",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI deve conter um Data URI válido",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude deve conter uma coordenada de latitude válida",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude deve conter uma coordenada de longitude válida",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte deve conter caracteres multibyte",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII deve conter apenas caracteres ascii",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII deve conter apenas caracteres ascii imprimíveis",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID deve ser um UUID válido",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3 deve ser um UUID versão 3 válido",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4 deve ser um UUID versão 4 válido",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5 deve ser um UUID versão 5 válido",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN deve ser um número de ISBN válido",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10 deve ser um número ISBN-10 válido",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13 deve ser um número ISBN-13 válido",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes não deve conter o texto 'text'",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll não deve conter os seguintes caracteres '!@#$'",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune não pode conter o seguinte '☻'",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny deve conter pelo menos um dos seguintes caracteres '!@#$'",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains deve conter o texto 'purpose'",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64 deve ser uma string Base64 válida",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email deve ser um endereço de e-mail válido",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL deve ser um URL válido",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI deve ser um URI válido",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString deve ser uma cor RGB válida",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString deve ser uma cor RGBA válida",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString deve ser uma cor HSL válida",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString deve ser uma cor HSLA válida",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString deve ser um hexadecimal válido",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString deve ser uma cor HEX válida",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString deve ser um número válido",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString deve ser um valor numérico válido",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString deve conter apenas caracteres alfanuméricos",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString deve conter apenas caracteres alfabéticos",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString deve ser menor que MaxString",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString deve ser menor ou igual que MaxString",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString deve ser maior que MaxString",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString deve ser maior ou igual que MaxString",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString não deve ser igual a EqFieldString",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString deve ser menor que Inner.LtCSFieldString",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString deve ser menor ou igual que Inner.LteCSFieldString",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString deve ser maior que Inner.GtCSFieldString",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString deve ser maior ou igual que Inner.GteCSFieldString",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString não deve ser igual a Inner.NeCSFieldString",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString deve ser igual a Inner.EqCSFieldString",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString deve ser igual a MaxString",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteString deve ter pelo menos 3 caracteres",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber deve ser maior ou igual a 5,56",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple deve conter pelo menos 2 items",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime deve ser posterior ou igual à data/hora atual",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtString deve conter mais de 3 caracteres",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber deve ser maior que 5,56",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple deve conter mais de 2 items",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime deve ser posterior à data/hora atual",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteString deve ter no máximo 3 caracteres",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber deve ser menor ou igual a 5,56",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple deve conter no máximo 2 items",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime deve ser anterior ou igual à data/hora atual",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtString deve ter menos de 3 caracteres",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber deve ser menor que 5,56",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple deve conter menos de 2 items",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime deve ser anterior à data / hora atual",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString não deve ser igual a ",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber não deve ser igual a 0.00",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple não deve ser igual a 0",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString não é igual a 3",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber não é igual a 2.33",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple não é igual a 7",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxString deve ter no máximo 3 caracteres",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber deve ser 1.113,00 ou menos",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple deve conter no máximo 7 items",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinString deve ter pelo menos 1 caractere",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber deve ser 1.113,00 ou superior",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultiple deve conter pelo menos 7 items",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenString deve ter 1 caractere",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber deve ser igual a 1.113,00",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple deve conter 7 items",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString é obrigatório",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber é obrigatório",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple é obrigatório",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLen deve ter pelo menos 10 caracteres",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLen deve ter no máximo 1 caractere",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLen deve ter 2 caracteres",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLt deve ter menos de 1 caractere",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLte deve ter no máximo 1 caractere",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGt deve conter mais de 10 caracteres",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGte deve ter pelo menos 10 caracteres",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString deve ser um de [red green]",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt deve ser um de [5 63]",
+ },
+ {
+ ns: "Test.UniqueSlice",
+ expected: "UniqueSlice deve conter valores únicos",
+ },
+ {
+ ns: "Test.UniqueArray",
+ expected: "UniqueArray deve conter valores únicos",
+ },
+ {
+ ns: "Test.UniqueMap",
+ expected: "UniqueMap deve conter valores únicos",
+ },
+ {
+ ns: "Test.JSONString",
+ expected: "JSONString deve ser uma string json válida",
+ },
+ {
+ ns: "Test.LowercaseString",
+ expected: "LowercaseString deve estar em minuscúlas",
+ },
+ {
+ ns: "Test.UppercaseString",
+ expected: "UppercaseString deve estar em maiúsculas",
+ },
+ {
+ ns: "Test.Datetime",
+ expected: "Datetime não está no formato 2006-01-02",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/pt_BR/pt_BR.go b/go-playground/validator/v10/translations/pt_BR/pt_BR.go
new file mode 100644
index 0000000..eb06572
--- /dev/null
+++ b/go-playground/validator/v10/translations/pt_BR/pt_BR.go
@@ -0,0 +1,1365 @@
+package pt_BR
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0} é um campo requerido",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0} deve ter {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0} deve ser igual a {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0} deve conter {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("len-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-items-item", "{0} itens", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("alerta: erro na tradução FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0} deve ter pelo menos {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0} deve ser {1} ou superior", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "{0} deve conter pelo menos {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("min-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-items-item", "{0} itens", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("alerta: erro na tradução FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0} deve ter no máximo {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0} deve ser {1} ou menor", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0} deve conter no máximo {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("max-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-items-item", "{0} itens", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("alerta: erro na tradução FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0} não é igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0} não deve ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0} deve ter menos de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0} deve ser menor que {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0} deve conter menos de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} itens", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0} deve ser inferior à Data e Hora atual", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("a tag '%s' não pode ser usada em uma struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("alerta: erro na tradução FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0} deve ter no máximo {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0} deve ser {1} ou menor", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0} deve conter no máximo {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} itens", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0} deve ser menor ou igual à Data e Hora atual", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("a tag '%s' não pode ser usado em uma struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("alerta: erro na tradução FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0} deve ter mais de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0} deve ser maior do que {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0} deve conter mais de {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} itens", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0} deve ser maior que a Data e Hora atual", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("a tag '%s' não pode ser usado em uma struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("alerta: erro na tradução FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0} deve ter pelo menos {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} caractere", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} caracteres", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0} deve ser {1} ou superior", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0} deve conter pelo menos {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} itens", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0} deve ser maior ou igual à Data e Hora atual", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("a tag '%s' não pode ser usado em uma struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("alerta: erro na tradução FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0} deve ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0} deve ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0} não deve ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0} deve ser maior do que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0} deve ser maior ou igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0} deve ser menor que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0} deve ser menor ou igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0} não deve ser igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0} deve ser maior do que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0} deve ser maior ou igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0} deve ser menor que {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0} deve ser menor ou igual a {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0} deve conter caracteres alfabéticos",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0} deve conter caracteres alfanuméricos",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0} deve ser um valor numérico válido",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0} deve ser um número válido",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0} deve ser um hexadecimal válido",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0} deve ser uma cor HEX válida",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0} deve ser uma cor RGB válida",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0} deve ser uma cor RGBA válida",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0} deve ser uma cor HSL válida",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0} deve ser uma cor HSLA válida",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0} deve ser um endereço de e-mail válido",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0} deve ser uma URL válida",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0} deve ser uma URI válida",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0} deve ser uma string Base64 válida",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0} deve conter o texto '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0} deve conter pelo menos um dos caracteres '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0} não deve conter o texto '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0} não deve conter nenhum dos caracteres '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0} não deve conter '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0} deve ser um número ISBN válido",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0} deve ser um número ISBN-10 válido",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0} deve ser um número ISBN-13 válido",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0} deve ser um UUID válido",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0} deve ser um UUID versão 3 válido",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0} deve ser um UUID versão 4 válido",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0} deve ser um UUID versão 5 válido",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0} deve conter apenas caracteres ascii",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0} deve conter apenas caracteres ascii imprimíveis",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0} deve conter caracteres multibyte",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0} deve conter um URI data válido",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0} deve conter uma coordenada de latitude válida",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0} deve conter uma coordenada de longitude válida",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0} deve ser um número SSN válido",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0} deve ser um endereço IPv4 válido",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0} deve ser um endereço IPv6 válido",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0} deve ser um endereço de IP válido",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0} deve conter uma notação CIDR válida",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0} deve conter uma notação CIDR válida para um endereço IPv4",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0} deve conter uma notação CIDR válida para um endereço IPv6",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0} deve ser um endereço TCP válido",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0} deve ser um endereço IPv4 TCP válido",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0} deve ser um endereço IPv6 TCP válido",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0} deve ser um endereço UDP válido",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0} deve ser um endereço IPv4 UDP válido",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0} deve ser um endereço IPv6 UDP válido",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0} deve ser um endereço IP resolvível",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0} deve ser um endereço IPv4 resolvível",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0} deve ser um endereço IPv6 resolvível",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0} deve ser um endereço UNIX resolvível",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0} deve conter um endereço MAC válido",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0} deve ser uma cor válida",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0} deve ser um de [{1}]",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("alerta: erro na tradução FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/pt_BR/pt_BR_test.go b/go-playground/validator/v10/translations/pt_BR/pt_BR_test.go
new file mode 100644
index 0000000..8877e95
--- /dev/null
+++ b/go-playground/validator/v10/translations/pt_BR/pt_BR_test.go
@@ -0,0 +1,634 @@
+package pt_BR
+
+import (
+ "testing"
+ "time"
+
+ brazilian_portuguese "gin-valid/go-playground/locales/pt_BR"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ ptbr := brazilian_portuguese.New()
+ uni := ut.New(ptbr, ptbr)
+ trans, _ := uni.GetTranslator("pt_BR")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "este é um texto de teste"
+ test.ExcludesAll = "Isso é Ótimo!"
+ test.ExcludesRune = "Amo isso ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor deve ser uma cor válida",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC deve conter um endereço MAC válido",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr deve ser um endereço IP resolvível",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4 deve ser um endereço IPv4 resolvível",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6 deve ser um endereço IPv6 resolvível",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr deve ser um endereço UDP válido",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4 deve ser um endereço IPv4 UDP válido",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6 deve ser um endereço IPv6 UDP válido",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr deve ser um endereço TCP válido",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4 deve ser um endereço IPv4 TCP válido",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6 deve ser um endereço IPv6 TCP válido",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR deve conter uma notação CIDR válida",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4 deve conter uma notação CIDR válida para um endereço IPv4",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6 deve conter uma notação CIDR válida para um endereço IPv6",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN deve ser um número SSN válido",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP deve ser um endereço de IP válido",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4 deve ser um endereço IPv4 válido",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6 deve ser um endereço IPv6 válido",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI deve conter um URI data válido",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude deve conter uma coordenada de latitude válida",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude deve conter uma coordenada de longitude válida",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte deve conter caracteres multibyte",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII deve conter apenas caracteres ascii",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII deve conter apenas caracteres ascii imprimíveis",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID deve ser um UUID válido",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3 deve ser um UUID versão 3 válido",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4 deve ser um UUID versão 4 válido",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5 deve ser um UUID versão 5 válido",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN deve ser um número ISBN válido",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10 deve ser um número ISBN-10 válido",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13 deve ser um número ISBN-13 válido",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes não deve conter o texto 'text'",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll não deve conter nenhum dos caracteres '!@#$'",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune não deve conter '☻'",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny deve conter pelo menos um dos caracteres '!@#$'",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains deve conter o texto 'purpose'",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64 deve ser uma string Base64 válida",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email deve ser um endereço de e-mail válido",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL deve ser uma URL válida",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI deve ser uma URI válida",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString deve ser uma cor RGB válida",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString deve ser uma cor RGBA válida",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString deve ser uma cor HSL válida",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString deve ser uma cor HSLA válida",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString deve ser um hexadecimal válido",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString deve ser uma cor HEX válida",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString deve ser um número válido",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString deve ser um valor numérico válido",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString deve conter caracteres alfanuméricos",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString deve conter caracteres alfabéticos",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString deve ser menor que MaxString",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString deve ser menor ou igual a MaxString",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString deve ser maior do que MaxString",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString deve ser maior ou igual a MaxString",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString não deve ser igual a EqFieldString",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString deve ser menor que Inner.LtCSFieldString",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString deve ser menor ou igual a Inner.LteCSFieldString",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString deve ser maior do que Inner.GtCSFieldString",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString deve ser maior ou igual a Inner.GteCSFieldString",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString não deve ser igual a Inner.NeCSFieldString",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString deve ser igual a Inner.EqCSFieldString",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString deve ser igual a MaxString",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteString deve ter pelo menos 3 caracteres",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber deve ser 5,56 ou superior",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple deve conter pelo menos 2 itens",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime deve ser maior ou igual à Data e Hora atual",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtString deve ter mais de 3 caracteres",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber deve ser maior do que 5,56",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple deve conter mais de 2 itens",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime deve ser maior que a Data e Hora atual",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteString deve ter no máximo 3 caracteres",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber deve ser 5,56 ou menor",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple deve conter no máximo 2 itens",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime deve ser menor ou igual à Data e Hora atual",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtString deve ter menos de 3 caracteres",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber deve ser menor que 5,56",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple deve conter menos de 2 itens",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime deve ser inferior à Data e Hora atual",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString não deve ser igual a ",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber não deve ser igual a 0.00",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple não deve ser igual a 0",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString não é igual a 3",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber não é igual a 2.33",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple não é igual a 7",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxString deve ter no máximo 3 caracteres",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber deve ser 1.113,00 ou menor",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple deve conter no máximo 7 itens",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinString deve ter pelo menos 1 caractere",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber deve ser 1.113,00 ou superior",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultiple deve conter pelo menos 7 itens",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenString deve ter 1 caractere",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber deve ser igual a 1.113,00",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple deve conter 7 itens",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString é um campo requerido",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber é um campo requerido",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple é um campo requerido",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLen deve ter pelo menos 10 caracteres",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLen deve ter no máximo 1 caractere",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLen deve ter 2 caracteres",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLt deve ter menos de 1 caractere",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLte deve ter no máximo 1 caractere",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGt deve ter mais de 10 caracteres",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGte deve ter pelo menos 10 caracteres",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString deve ser um de [red green]",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt deve ser um de [5 63]",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/ru/ru.go b/go-playground/validator/v10/translations/ru/ru.go
new file mode 100644
index 0000000..53a335a
--- /dev/null
+++ b/go-playground/validator/v10/translations/ru/ru.go
@@ -0,0 +1,1375 @@
+package ru
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0} обязательное поле",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0} должен быть длиной в {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} символ", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0} должен быть равен {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0} должен содержать {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("len-items-item", "{0} элемент", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0} должен содержать минимум {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} символ", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0} должен быть больше или равно {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "{0} должен содержать минимум {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("min-items-item", "{0} элемент", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0} должен содержать максимум {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} символ", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0} должен быть меньше или равно {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0} должен содержать максимум {1}", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("max-items-item", "{0} элемент", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0} не равен {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0} должен быть не равен {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0} должен иметь менее {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} символ", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0} должен быть менее {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0} должен содержать менее {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} элемент", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0} must be less than the current Date & Time", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0} должен содержать максимум {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} символ", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0} должен быть менее или равен {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0} должен содержать максимум {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} элемент", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0} must be less than or equal to the current Date & Time", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0} должен быть длиннее {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} символ", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0} должен быть больше {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0} должен содержать более {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} элемент", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0} должна быть позже текущего момента", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0} должен содержать минимум {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} символ", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0} должен быть больше или равно {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0} должен содержать минимум {1}", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} элемент", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0} должна быть позже или равна текущему моменту", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0} должен быть равен {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0} должен быть равен {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0} не должен быть равен {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0} должен быть больше {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0} должен быть больше или равен {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0} должен быть менее {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0} должен быть менее или равен {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0} не должен быть равен {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0} должен быть больше {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0} должен быть больше или равен {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0} должен быть менее {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0} должен быть менее или равен {1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0} должен содержать только буквы",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0} должен содержать только буквы и цифры",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0} должен быть цифровым значением",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0} должен быть цифрой",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0} должен быть шестнадцатеричной строкой",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0} должен быть HEX цветом",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0} должен быть RGB цветом",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0} должен быть RGBA цветом",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0} должен быть HSL цветом",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0} должен быть HSLA цветом",
+ override: false,
+ },
+ {
+ tag: "e164",
+ translation: "{0} должен быть E.164 formatted phone number",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0} должен быть email адресом",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0} должен быть URL",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0} должен быть URI",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0} должен быть Base64 строкой",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0} должен содержать текст '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0} должен содержать минимум один из символов '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0} не должен содержать текст '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0} не должен содержать символы '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0} не должен содержать '{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0} должен быть ISBN номером",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0} должен быть ISBN-10 номером",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0} должен быть ISBN-13 номером",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0} должен быть UUID",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0} должен быть UUID 3 версии",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0} должен быть UUID 4 версии",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0} должен быть UUID 5 версии",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0} должен содержать только ascii символы",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0} должен содержать только доступные для печати ascii символы",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0} должен содержать мультибайтные символы",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0} должен содержать Data URI",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0} должен содержать координаты широты",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0} должен содержать координаты долготы",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0} должен быть SSN номером",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0} должен быть IPv4 адресом",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0} должен быть IPv6 адресом",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0} должен быть IP адресом",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0} должен содержать CIDR обозначения",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0} должен содержать CIDR обозначения для IPv4 адреса",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0} должен содержать CIDR обозначения для IPv6 адреса",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0} должен быть TCP адресом",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0} должен быть IPv4 TCP адресом",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0} должен быть IPv6 TCP адресом",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0} должен быть UDP адресом",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0} должен быть IPv4 UDP адресом",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0} должен быть IPv6 UDP адресом",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0} должен быть распознаваемым IP адресом",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0} должен быть распознаваемым IPv4 адресом",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0} должен быть распознаваемым IPv6 адресом",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0} должен быть распознаваемым UNIX адресом",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0} должен содержать MAC адрес",
+ override: false,
+ },
+ {
+ tag: "unique",
+ translation: "{0} должен содержать уникальные значения",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0} должен быть цветом",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0} должен быть одним из [{1}]",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/ru/ru_test.go b/go-playground/validator/v10/translations/ru/ru_test.go
new file mode 100644
index 0000000..493c1f4
--- /dev/null
+++ b/go-playground/validator/v10/translations/ru/ru_test.go
@@ -0,0 +1,656 @@
+package ru
+
+import (
+ "log"
+ //"github.com/rustery/validator"
+ "testing"
+ "time"
+
+ russian "gin-valid/go-playground/locales/en"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ ru := russian.New()
+ uni := ut.New(ru, ru)
+ trans, _ := uni.GetTranslator("ru")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ UniqueSlice []string `validate:"unique"`
+ UniqueArray [3]string `validate:"unique"`
+ UniqueMap map[string]string `validate:"unique"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ test.UniqueSlice = []string{"1234", "1234"}
+ test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor должен быть цветом",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC должен содержать MAC адрес",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr должен быть распознаваемым IP адресом",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4 должен быть распознаваемым IPv4 адресом",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6 должен быть распознаваемым IPv6 адресом",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr должен быть UDP адресом",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4 должен быть IPv4 UDP адресом",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6 должен быть IPv6 UDP адресом",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr должен быть TCP адресом",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4 должен быть IPv4 TCP адресом",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6 должен быть IPv6 TCP адресом",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR должен содержать CIDR обозначения",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4 должен содержать CIDR обозначения для IPv4 адреса",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6 должен содержать CIDR обозначения для IPv6 адреса",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN должен быть SSN номером",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP должен быть IP адресом",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4 должен быть IPv4 адресом",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6 должен быть IPv6 адресом",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI должен содержать Data URI",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude должен содержать координаты широты",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude должен содержать координаты долготы",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte должен содержать мультибайтные символы",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII должен содержать только ascii символы",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII должен содержать только доступные для печати ascii символы",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID должен быть UUID",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3 должен быть UUID 3 версии",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4 должен быть UUID 4 версии",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5 должен быть UUID 5 версии",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN должен быть ISBN номером",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10 должен быть ISBN-10 номером",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13 должен быть ISBN-13 номером",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes не должен содержать текст 'text'",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll не должен содержать символы '!@#$'",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune не должен содержать '☻'",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny должен содержать минимум один из символов '!@#$'",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains должен содержать текст 'purpose'",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64 должен быть Base64 строкой",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email должен быть email адресом",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL должен быть URL",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI должен быть URI",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString должен быть RGB цветом",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString должен быть RGBA цветом",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString должен быть HSL цветом",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString должен быть HSLA цветом",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString должен быть шестнадцатеричной строкой",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString должен быть HEX цветом",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString должен быть цифрой",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString должен быть цифровым значением",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString должен содержать только буквы и цифры",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString должен содержать только буквы",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString должен быть менее MaxString",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString должен быть менее или равен MaxString",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString должен быть больше MaxString",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString должен быть больше или равен MaxString",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString не должен быть равен EqFieldString",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString должен быть менее Inner.LtCSFieldString",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString должен быть менее или равен Inner.LteCSFieldString",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString должен быть больше Inner.GtCSFieldString",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString должен быть больше или равен Inner.GteCSFieldString",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString не должен быть равен Inner.NeCSFieldString",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString должен быть равен Inner.EqCSFieldString",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString должен быть равен MaxString",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteString должен содержать минимум 3 символы",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber должен быть больше или равно 5.56",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple должен содержать минимум 2 элементы",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime должна быть позже или равна текущему моменту",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtString должен быть длиннее 3 символы",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber должен быть больше 5.56",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple должен содержать более 2 элементы",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime должна быть позже текущего момента",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteString должен содержать максимум 3 символы",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber должен быть менее или равен 5.56",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple должен содержать максимум 2 элементы",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime must be less than or equal to the current Date & Time",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtString должен иметь менее 3 символы",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber должен быть менее 5.56",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple должен содержать менее 2 элементы",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime must be less than the current Date & Time",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString должен быть не равен ",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber должен быть не равен 0.00",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple должен быть не равен 0",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString не равен 3",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber не равен 2.33",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple не равен 7",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxString должен содержать максимум 3 символы",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber должен быть меньше или равно 1,113.00",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple должен содержать максимум 7 элементы",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinString должен содержать минимум 1 символ",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber должен быть больше или равно 1,113.00",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultiple должен содержать минимум 7 элементы",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenString должен быть длиной в 1 символ",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber должен быть равен 1,113.00",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple должен содержать 7 элементы",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString обязательное поле",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber обязательное поле",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple обязательное поле",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLen должен содержать минимум 10 символы",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLen должен содержать максимум 1 символ",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLen должен быть длиной в 2 символы",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLt должен иметь менее 1 символ",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLte должен содержать максимум 1 символ",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGt должен быть длиннее 10 символы",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGte должен содержать минимум 10 символы",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString должен быть одним из [red green]",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt должен быть одним из [5 63]",
+ },
+ {
+ ns: "Test.UniqueSlice",
+ expected: "UniqueSlice должен содержать уникальные значения",
+ },
+ {
+ ns: "Test.UniqueArray",
+ expected: "UniqueArray должен содержать уникальные значения",
+ },
+ {
+ ns: "Test.UniqueMap",
+ expected: "UniqueMap должен содержать уникальные значения",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ log.Println(fe)
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/tr/tr.go b/go-playground/validator/v10/translations/tr/tr.go
new file mode 100644
index 0000000..72706ed
--- /dev/null
+++ b/go-playground/validator/v10/translations/tr/tr.go
@@ -0,0 +1,1370 @@
+package tr
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0} zorunlu bir alandır",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0} uzunluğu {1} olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0}, {1} değerine eşit olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0}, {1} içermelidir", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("len-items-item", "{0} öğe", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("len-items-item", "{0} öğe", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0} en az {1} uzunluğunda olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0}, {1} veya daha büyük olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "{0} en az {1} içermelidir", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("min-items-item", "{0} öğe", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("min-items-item", "{0} öğe", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0} uzunluğu en fazla {1} olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0}, {1} veya daha az olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0} maksimum {1} içermelidir", false); err != nil {
+ return
+ }
+ if err = ut.AddCardinal("max-items-item", "{0} öğe", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("max-items-item", "{0} öğe", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0}, {1} değerine eşit değil",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0}, {1} değerine eşit olmamalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0}, {1} uzunluğundan daha az olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0}, {1} değerinden küçük olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0}, {1}den daha az içermelidir", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} öğe", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lt-items-item", "{0} öğe", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0} geçerli Tarih ve Saatten daha az olmalıdır", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0} en fazla {1} uzunluğunda olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0}, {1} veya daha az olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0}, maksimum {1} içermelidir", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} öğe", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("lte-items-item", "{0} öğe", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0} geçerli Tarih ve Saate eşit veya daha küçük olmalıdır", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("lte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0}, {1} uzunluğundan fazla olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0}, {1} değerinden büyük olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0}, {1}den daha fazla içermelidir", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} öğe", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gt-items-item", "{0} öğe", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0} geçerli Tarih ve Saatten büyük olmalıdır", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gt-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0} en az {1} uzunluğunda olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} karakter", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-string-character", "{0} karakter", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0}, {1} veya daha büyük olmalıdır", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0} en az {1} içermelidir", false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} öğe", locales.PluralRuleOne, false); err != nil {
+ return
+ }
+
+ if err = ut.AddCardinal("gte-items-item", "{0} öğe", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0} geçerli Tarih ve Saatten büyük veya ona eşit olmalıdır", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag())
+ goto END
+ }
+
+ t, err = ut.T("gte-datetime", fe.Field())
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0}, {1} değerine eşit olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0}, {1} değerine eşit olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0}, {1} değerine eşit olmamalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0}, {1} değerinden büyük olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0}, {1} değerinden küçük veya ona eşit olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0}, {1} değerinden küçük olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0}, {1} değerinden küçük veya ona eşit olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0}, {1} değerine eşit olmamalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0}, {1} değerinden büyük olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0}, {1} değerinden büyük veya ona eşit olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0}, {1} değerinden küçük olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0}, {1} değerinden küçük veya ona eşit olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0} yalnızca alfabetik karakterler içerebilir",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0} yalnızca alfanümerik karakterler içerebilir",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0} geçerli bir sayısal değer olmalıdır",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0} geçerli bir sayı olmalıdır",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0} geçerli bir onaltılık olmalıdır",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0} geçerli bir HEX rengi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0} geçerli bir RGB rengi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0} geçerli bir RGBA rengi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0} geçerli bir HSL rengi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0} geçerli bir HSLA rengi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0} geçerli bir e-posta adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0} geçerli bir URL olmalıdır",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0} geçerli bir URI olmalıdır",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0} geçerli bir Base64 karakter dizesi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0}, '{1}' metnini içermelidir",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0}, '{1}' karakterlerinden en az birini içermelidir",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0}, '{1}' metnini içeremez",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0}, '{1}' karakterlerinden hiçbirini içeremez",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0}, '{1}' ifadesini içeremez",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0} geçerli bir ISBN numarası olmalıdır",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0} geçerli bir ISBN-10 numarası olmalıdır",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0} geçerli bir ISBN-13 numarası olmalıdır",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0} geçerli bir UUID olmalıdır",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0} geçerli bir sürüm 3 UUID olmalıdır",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0} geçerli bir sürüm 4 UUID olmalıdır",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0} geçerli bir sürüm 5 UUID olmalıdır",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0} yalnızca ascii karakterler içermelidir",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0} yalnızca yazdırılabilir ascii karakterleri içermelidir",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0} çok baytlı karakterler içermelidir",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0} geçerli bir Veri URI içermelidir",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0} geçerli bir enlem koordinatı içermelidir",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0} geçerli bir boylam koordinatı içermelidir",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0} geçerli bir SSN numarası olmalıdır",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0} geçerli bir IPv4 adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0} geçerli bir IPv6 adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0} geçerli bir IP adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0} geçerli bir CIDR gösterimi içermelidir",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0} bir IPv4 adresi için geçerli bir CIDR gösterimi içermelidir",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0} bir IPv6 adresi için geçerli bir CIDR gösterimi içermelidir",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0} geçerli bir TCP adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0} geçerli bir IPv4 TCP adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0} geçerli bir IPv6 TCP adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0} geçerli bir UDP adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0} geçerli bir IPv4 UDP adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0} geçerli bir IPv6 UDP adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0} çözülebilir bir IP adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0} çözülebilir bir IPv4 adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0} çözülebilir bir IPv6 adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0} çözülebilir bir UNIX adresi olmalıdır",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0} geçerli bir MAC adresi içermelidir",
+ override: false,
+ },
+ {
+ tag: "unique",
+ translation: "{0} benzersiz değerler içermelidir",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0} geçerli bir renk olmalıdır",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0}, [{1}]'dan biri olmalıdır",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("warning: error translating FieldError: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/tr/tr_test.go b/go-playground/validator/v10/translations/tr/tr_test.go
new file mode 100644
index 0000000..07ee1f7
--- /dev/null
+++ b/go-playground/validator/v10/translations/tr/tr_test.go
@@ -0,0 +1,652 @@
+package tr
+
+import (
+ "testing"
+ "time"
+
+ turkish "gin-valid/go-playground/locales/tr"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ tr := turkish.New()
+ uni := ut.New(tr, tr)
+ trans, _ := uni.GetTranslator("tr")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ UniqueSlice []string `validate:"unique"`
+ UniqueArray [3]string `validate:"unique"`
+ UniqueMap map[string]string `validate:"unique"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ test.UniqueSlice = []string{"1234", "1234"}
+ test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor geçerli bir renk olmalıdır",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC geçerli bir MAC adresi içermelidir",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr çözülebilir bir IP adresi olmalıdır",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4 çözülebilir bir IPv4 adresi olmalıdır",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6 çözülebilir bir IPv6 adresi olmalıdır",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr geçerli bir UDP adresi olmalıdır",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4 geçerli bir IPv4 UDP adresi olmalıdır",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6 geçerli bir IPv6 UDP adresi olmalıdır",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr geçerli bir TCP adresi olmalıdır",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4 geçerli bir IPv4 TCP adresi olmalıdır",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6 geçerli bir IPv6 TCP adresi olmalıdır",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR geçerli bir CIDR gösterimi içermelidir",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4 bir IPv4 adresi için geçerli bir CIDR gösterimi içermelidir",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6 bir IPv6 adresi için geçerli bir CIDR gösterimi içermelidir",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN geçerli bir SSN numarası olmalıdır",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP geçerli bir IP adresi olmalıdır",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4 geçerli bir IPv4 adresi olmalıdır",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6 geçerli bir IPv6 adresi olmalıdır",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI geçerli bir Veri URI içermelidir",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude geçerli bir enlem koordinatı içermelidir",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude geçerli bir boylam koordinatı içermelidir",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte çok baytlı karakterler içermelidir",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII yalnızca ascii karakterler içermelidir",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII yalnızca yazdırılabilir ascii karakterleri içermelidir",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID geçerli bir UUID olmalıdır",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3 geçerli bir sürüm 3 UUID olmalıdır",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4 geçerli bir sürüm 4 UUID olmalıdır",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5 geçerli bir sürüm 5 UUID olmalıdır",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN geçerli bir ISBN numarası olmalıdır",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10 geçerli bir ISBN-10 numarası olmalıdır",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13 geçerli bir ISBN-13 numarası olmalıdır",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes, 'text' metnini içeremez",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll, '!@#$' karakterlerinden hiçbirini içeremez",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune, '☻' ifadesini içeremez",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny, '!@#$' karakterlerinden en az birini içermelidir",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains, 'purpose' metnini içermelidir",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64 geçerli bir Base64 karakter dizesi olmalıdır",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email geçerli bir e-posta adresi olmalıdır",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL geçerli bir URL olmalıdır",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI geçerli bir URI olmalıdır",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString geçerli bir RGB rengi olmalıdır",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString geçerli bir RGBA rengi olmalıdır",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString geçerli bir HSL rengi olmalıdır",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString geçerli bir HSLA rengi olmalıdır",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString geçerli bir onaltılık olmalıdır",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString geçerli bir HEX rengi olmalıdır",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString geçerli bir sayı olmalıdır",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString geçerli bir sayısal değer olmalıdır",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString yalnızca alfanümerik karakterler içerebilir",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString yalnızca alfabetik karakterler içerebilir",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString, MaxString değerinden küçük olmalıdır",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString, MaxString değerinden küçük veya ona eşit olmalıdır",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString, MaxString değerinden büyük olmalıdır",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString, MaxString değerinden büyük veya ona eşit olmalıdır",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString, EqFieldString değerine eşit olmamalıdır",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString, Inner.LtCSFieldString değerinden küçük olmalıdır",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString, Inner.LteCSFieldString değerinden küçük veya ona eşit olmalıdır",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString, Inner.GtCSFieldString değerinden büyük olmalıdır",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString, Inner.GteCSFieldString değerinden küçük veya ona eşit olmalıdır",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString, Inner.NeCSFieldString değerine eşit olmamalıdır",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString, Inner.EqCSFieldString değerine eşit olmalıdır",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString, MaxString değerine eşit olmalıdır",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteString en az 3 karakter uzunluğunda olmalıdır",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber, 5,56 veya daha büyük olmalıdır",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple en az 2 öğe içermelidir",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime geçerli Tarih ve Saatten büyük veya ona eşit olmalıdır",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtString, 3 karakter uzunluğundan fazla olmalıdır",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber, 5,56 değerinden büyük olmalıdır",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple, 2 öğeden daha fazla içermelidir",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime geçerli Tarih ve Saatten büyük olmalıdır",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteString en fazla 3 karakter uzunluğunda olmalıdır",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber, 5,56 veya daha az olmalıdır",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple, maksimum 2 öğe içermelidir",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime geçerli Tarih ve Saate eşit veya daha küçük olmalıdır",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtString, 3 karakter uzunluğundan daha az olmalıdır",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber, 5,56 değerinden küçük olmalıdır",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple, 2 öğeden daha az içermelidir",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime geçerli Tarih ve Saatten daha az olmalıdır",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString, değerine eşit olmamalıdır",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber, 0.00 değerine eşit olmamalıdır",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple, 0 değerine eşit olmamalıdır",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString, 3 değerine eşit değil",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber, 2.33 değerine eşit değil",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple, 7 değerine eşit değil",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxString uzunluğu en fazla 3 karakter olmalıdır",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber, 1.113,00 veya daha az olmalıdır",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple maksimum 7 öğe içermelidir",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinString en az 1 karakter uzunluğunda olmalıdır",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber, 1.113,00 veya daha büyük olmalıdır",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultiple en az 7 öğe içermelidir",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenString uzunluğu 1 karakter olmalıdır",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber, 1.113,00 değerine eşit olmalıdır",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple, 7 öğe içermelidir",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString zorunlu bir alandır",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber zorunlu bir alandır",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple zorunlu bir alandır",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLen en az 10 karakter uzunluğunda olmalıdır",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLen uzunluğu en fazla 1 karakter olmalıdır",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLen uzunluğu 2 karakter olmalıdır",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLt, 1 karakter uzunluğundan daha az olmalıdır",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLte en fazla 1 karakter uzunluğunda olmalıdır",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGt, 10 karakter uzunluğundan fazla olmalıdır",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGte en az 10 karakter uzunluğunda olmalıdır",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString, [red green]'dan biri olmalıdır",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt, [5 63]'dan biri olmalıdır",
+ },
+ {
+ ns: "Test.UniqueSlice",
+ expected: "UniqueSlice benzersiz değerler içermelidir",
+ },
+ {
+ ns: "Test.UniqueArray",
+ expected: "UniqueArray benzersiz değerler içermelidir",
+ },
+ {
+ ns: "Test.UniqueMap",
+ expected: "UniqueMap benzersiz değerler içermelidir",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/zh/zh.go b/go-playground/validator/v10/translations/zh/zh.go
new file mode 100644
index 0000000..f681812
--- /dev/null
+++ b/go-playground/validator/v10/translations/zh/zh.go
@@ -0,0 +1,1392 @@
+package zh
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0}为必填字段",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0}长度必须是{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("len-string-character", "{0}字符", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("len-string-character", "{0}个字符", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0}必须等于{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0}必须包含{1}", false); err != nil {
+ return
+ }
+ //if err = ut.AddCardinal("len-items-item", "{0}项", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("len-items-item", "{0}项", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻译字段错误: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0}长度必须至少为{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("min-string-character", "{0}个字符", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("min-string-character", "{0}个字符", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0}最小只能为{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "{0}必须至少包含{1}", false); err != nil {
+ return
+ }
+ //if err = ut.AddCardinal("min-items-item", "{0}项", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("min-items-item", "{0}项", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻译字段错误: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0}长度不能超过{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("max-string-character", "{0}个字符", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("max-string-character", "{0}个字符", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0}必须小于或等于{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0}最多只能包含{1}", false); err != nil {
+ return
+ }
+ //if err = ut.AddCardinal("max-items-item", "{0}项", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("max-items-item", "{0}项", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻译字段错误: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0}不等于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0}不能等于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0}长度必须小于{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("lt-string-character", "{0}个字符", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("lt-string-character", "{0}个字符", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0}必须小于{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0}必须包含少于{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("lt-items-item", "{0}项", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("lt-items-item", "{0}项", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0}必须小于当前日期和时间", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag())
+ } else {
+ t, err = ut.T("lt-datetime", fe.Field())
+ }
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻译字段错误: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0}长度不能超过{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("lte-string-character", "{0} character", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("lte-string-character", "{0}个字符", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0}必须小于或等于{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0}最多只能包含{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("lte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("lte-items-item", "{0}项", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0}必须小于或等于当前日期和时间", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag())
+ } else {
+ t, err = ut.T("lte-datetime", fe.Field())
+ }
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻译字段错误: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0}长度必须大于{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("gt-string-character", "{0}个字符", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("gt-string-character", "{0}个字符", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0}必须大于{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0}必须大于{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("gt-items-item", "{0}项", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("gt-items-item", "{0}项", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0}必须大于当前日期和时间", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag())
+ } else {
+
+ t, err = ut.T("gt-datetime", fe.Field())
+ }
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻译字段错误: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0}长度必须至少为{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("gte-string-character", "{0}个字符", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("gte-string-character", "{0}个字符", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0}必须大于或等于{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0}必须至少包含{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("gte-items-item", "{0}项", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("gte-items-item", "{0}项", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0}必须大于或等于当前日期和时间", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:])) // 表示小数部分的位数
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag())
+ } else {
+ t, err = ut.T("gte-datetime", fe.Field())
+ }
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻译字段错误: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0}必须等于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0}必须等于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0}不能等于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0}必须大于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0}必须大于或等于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0}必须小于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0}必须小于或等于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0}不能等于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0}必须大于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0}必须大于或等于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0}必须小于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0}必须小于或等于{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0}只能包含字母",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0}只能包含字母和数字",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0}必须是一个有效的数值",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0}必须是一个有效的数字",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0}必须是一个有效的十六进制",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0}必须是一个有效的十六进制颜色",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0}必须是一个有效的RGB颜色",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0}必须是一个有效的RGBA颜色",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0}必须是一个有效的HSL颜色",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0}必须是一个有效的HSLA颜色",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0}必须是一个有效的邮箱",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0}必须是一个有效的URL",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0}必须是一个有效的URI",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0}必须是一个有效的Base64字符串",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0}必须包含文本'{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0}必须包含至少一个以下字符'{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0}不能包含文本'{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0}不能包含以下任何字符'{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0}不能包含'{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0}必须是一个有效的ISBN编号",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0}必须是一个有效的ISBN-10编号",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0}必须是一个有效的ISBN-13编号",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0}必须是一个有效的UUID",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0}必须是一个有效的V3 UUID",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0}必须是一个有效的V4 UUID",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0}必须是一个有效的V5 UUID",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0}必须只包含ascii字符",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0}必须只包含可打印的ascii字符",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0}必须包含多字节字符",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0}必须包含有效的数据URI",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0}必须包含有效的纬度坐标",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0}必须包含有效的经度坐标",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0}必须是一个有效的社会安全号码(SSN)",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0}必须是一个有效的IPv4地址",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0}必须是一个有效的IPv6地址",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0}必须是一个有效的IP地址",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0}必须是一个有效的无类别域间路由(CIDR)",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0}必须是一个包含IPv4地址的有效无类别域间路由(CIDR)",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0}必须是一个包含IPv6地址的有效无类别域间路由(CIDR)",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0}必须是一个有效的TCP地址",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0}必须是一个有效的IPv4 TCP地址",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0}必须是一个有效的IPv6 TCP地址",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0}必须是一个有效的UDP地址",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0}必须是一个有效的IPv4 UDP地址",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0}必须是一个有效的IPv6 UDP地址",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0}必须是一个有效的IP地址",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0}必须是一个有效的IPv4地址",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0}必须是一个有效的IPv6地址",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0}必须是一个有效的UNIX地址",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0}必须是一个有效的MAC地址",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0}必须是一个有效的颜色",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0}必须是[{1}]中的一个",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ {
+ tag: "json",
+ translation: "{0}必须是一个JSON字符串",
+ override: false,
+ },
+ {
+ tag: "lowercase",
+ translation: "{0}必须是小写字母",
+ override: false,
+ },
+ {
+ tag: "uppercase",
+ translation: "{0}必须是大写字母",
+ override: false,
+ },
+ {
+ tag: "datetime",
+ translation: "{0}的格式必须是{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("警告: 翻译字段错误: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/zh/zh_test.go b/go-playground/validator/v10/translations/zh/zh_test.go
new file mode 100644
index 0000000..af7ca7a
--- /dev/null
+++ b/go-playground/validator/v10/translations/zh/zh_test.go
@@ -0,0 +1,661 @@
+package zh
+
+import (
+ "testing"
+ "time"
+
+ zhongwen "gin-valid/go-playground/locales/zh"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ zh := zhongwen.New()
+ uni := ut.New(zh, zh)
+ trans, _ := uni.GetTranslator("zh")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ JsonString string `validate:"json"`
+ LowercaseString string `validate:"lowercase"`
+ UppercaseString string `validate:"uppercase"`
+ Datetime string `validate:"datetime=2006-01-02"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ test.JsonString = "{\"foo\":\"bar\",}"
+
+ test.LowercaseString = "ABCDEFG"
+ test.UppercaseString = "abcdefg"
+
+ test.Datetime = "20060102"
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor必须是一个有效的颜色",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC必须是一个有效的MAC地址",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr必须是一个有效的IP地址",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4必须是一个有效的IPv4地址",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6必须是一个有效的IPv6地址",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr必须是一个有效的UDP地址",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4必须是一个有效的IPv4 UDP地址",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6必须是一个有效的IPv6 UDP地址",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr必须是一个有效的TCP地址",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4必须是一个有效的IPv4 TCP地址",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6必须是一个有效的IPv6 TCP地址",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR必须是一个有效的无类别域间路由(CIDR)",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4必须是一个包含IPv4地址的有效无类别域间路由(CIDR)",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6必须是一个包含IPv6地址的有效无类别域间路由(CIDR)",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN必须是一个有效的社会安全号码(SSN)",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP必须是一个有效的IP地址",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4必须是一个有效的IPv4地址",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6必须是一个有效的IPv6地址",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI必须包含有效的数据URI",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude必须包含有效的纬度坐标",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude必须包含有效的经度坐标",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte必须包含多字节字符",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII必须只包含ascii字符",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII必须只包含可打印的ascii字符",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID必须是一个有效的UUID",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3必须是一个有效的V3 UUID",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4必须是一个有效的V4 UUID",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5必须是一个有效的V5 UUID",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN必须是一个有效的ISBN编号",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10必须是一个有效的ISBN-10编号",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13必须是一个有效的ISBN-13编号",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes不能包含文本'text'",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll不能包含以下任何字符'!@#$'",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune不能包含'☻'",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny必须包含至少一个以下字符'!@#$'",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains必须包含文本'purpose'",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64必须是一个有效的Base64字符串",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email必须是一个有效的邮箱",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL必须是一个有效的URL",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI必须是一个有效的URI",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString必须是一个有效的RGB颜色",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString必须是一个有效的RGBA颜色",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString必须是一个有效的HSL颜色",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString必须是一个有效的HSLA颜色",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString必须是一个有效的十六进制",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString必须是一个有效的十六进制颜色",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString必须是一个有效的数字",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString必须是一个有效的数值",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString只能包含字母和数字",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString只能包含字母",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString必须小于MaxString",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString必须小于或等于MaxString",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString必须大于MaxString",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString必须大于或等于MaxString",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString不能等于EqFieldString",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString必须小于Inner.LtCSFieldString",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString必须小于或等于Inner.LteCSFieldString",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString必须大于Inner.GtCSFieldString",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString必须大于或等于Inner.GteCSFieldString",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString不能等于Inner.NeCSFieldString",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString必须等于Inner.EqCSFieldString",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString必须等于MaxString",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteString长度必须至少为3个字符",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber必须大于或等于5.56",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple必须至少包含2项",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime必须大于或等于当前日期和时间",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtString长度必须大于3个字符",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber必须大于5.56",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple必须大于2项",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime必须大于当前日期和时间",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteString长度不能超过3个字符",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber必须小于或等于5.56",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple最多只能包含2项",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime必须小于或等于当前日期和时间",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtString长度必须小于3个字符",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber必须小于5.56",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple必须包含少于2项",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime必须小于当前日期和时间",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString不能等于",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber不能等于0.00",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple不能等于0",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString不等于3",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber不等于2.33",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple不等于7",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxString长度不能超过3个字符",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber必须小于或等于1,113.00",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple最多只能包含7项",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinString长度必须至少为1个字符",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber最小只能为1,113.00",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultiple必须至少包含7项",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenString长度必须是1个字符",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber必须等于1,113.00",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple必须包含7项",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString为必填字段",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber为必填字段",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple为必填字段",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLen长度必须至少为10个字符",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLen长度不能超过1个字符",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLen长度必须是2个字符",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLt长度必须小于1个字符",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLte长度不能超过1个字符",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGt长度必须大于10个字符",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGte长度必须至少为10个字符",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString必须是[red green]中的一个",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt必须是[5 63]中的一个",
+ },
+ {
+ ns: "Test.JsonString",
+ expected: "JsonString必须是一个JSON字符串",
+ },
+ {
+ ns: "Test.LowercaseString",
+ expected: "LowercaseString必须是小写字母",
+ },
+ {
+ ns: "Test.UppercaseString",
+ expected: "UppercaseString必须是大写字母",
+ },
+ {
+ ns: "Test.Datetime",
+ expected: "Datetime的格式必须是2006-01-02",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/translations/zh_tw/zh_tw.go b/go-playground/validator/v10/translations/zh_tw/zh_tw.go
new file mode 100644
index 0000000..1e49efb
--- /dev/null
+++ b/go-playground/validator/v10/translations/zh_tw/zh_tw.go
@@ -0,0 +1,1373 @@
+package zh_tw
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "gin-valid/go-playground/locales"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+)
+
+// RegisterDefaultTranslations registers a set of default translations
+// for all built in tag's in validator; you may add your own as desired.
+func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
+
+ translations := []struct {
+ tag string
+ translation string
+ override bool
+ customRegisFunc validator.RegisterTranslationsFunc
+ customTransFunc validator.TranslationFunc
+ }{
+ {
+ tag: "required",
+ translation: "{0}為必填欄位",
+ override: false,
+ },
+ {
+ tag: "len",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("len-string", "{0}長度必須為{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("len-string-character", "{0}字元", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("len-string-character", "{0}個字元", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-number", "{0}必須等於{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("len-items", "{0}必須包含{1}", false); err != nil {
+ return
+ }
+ //if err = ut.AddCardinal("len-items-item", "{0}項", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("len-items-item", "{0}項", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("len-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻譯欄位錯誤: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "min",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("min-string", "{0}長度必須至少為{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("min-string-character", "{0}個字元", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("min-string-character", "{0}個字元", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-number", "{0}最小只能為{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("min-items", "{0}必須至少包含{1}", false); err != nil {
+ return
+ }
+ //if err = ut.AddCardinal("min-items-item", "{0}項", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("min-items-item", "{0}項", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("min-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻譯欄位錯誤: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "max",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("max-string", "{0}長度不能超過{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("max-string-character", "{0}個字元", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("max-string-character", "{0}個字元", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-number", "{0}必須小於或等於{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("max-items", "{0}最多只能包含{1}", false); err != nil {
+ return
+ }
+ //if err = ut.AddCardinal("max-items-item", "{0}項", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("max-items-item", "{0}項", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+
+ var digits uint64
+ var kind reflect.Kind
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err := strconv.ParseFloat(fe.Param(), 64)
+ if err != nil {
+ goto END
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("max-items", fe.Field(), c)
+
+ default:
+ t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻譯欄位錯誤: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eq",
+ translation: "{0}不等於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ne",
+ translation: "{0}不能等於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ fmt.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lt-string", "{0}長度必須小於{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("lt-string-character", "{0}個字元", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("lt-string-character", "{0}個字元", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-number", "{0}必須小於{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-items", "{0}必須包含少於{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("lt-items-item", "{0}項", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("lt-items-item", "{0}項", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lt-datetime", "{0}必須小於目前日期和時間", false); err != nil {
+ return
+ }
+
+ return
+
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag())
+ } else {
+ t, err = ut.T("lt-datetime", fe.Field())
+ }
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻譯欄位錯誤: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "lte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("lte-string", "{0}長度不能超過{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("lte-string-character", "{0} character", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("lte-string-character", "{0}個字元", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-number", "{0}必須小於或等於{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-items", "{0}最多只能包含{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("lte-items-item", "{0} item", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("lte-items-item", "{0}項", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("lte-datetime", "{0}必須小於或等於目前日期和時間", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag())
+ } else {
+ t, err = ut.T("lte-datetime", fe.Field())
+ }
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻譯欄位錯誤: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gt",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gt-string", "{0}長度必須大於{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("gt-string-character", "{0}個字元", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("gt-string-character", "{0}個字元", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-number", "{0}必須大於{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-items", "{0}必須大於{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("gt-items-item", "{0}項", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("gt-items-item", "{0}項", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gt-datetime", "{0}必須大於目前日期和時間", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag())
+ } else {
+ t, err = ut.T("gt-datetime", fe.Field())
+ }
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻譯欄位錯誤: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gte",
+ customRegisFunc: func(ut ut.Translator) (err error) {
+
+ if err = ut.Add("gte-string", "{0}長度必須至少為{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("gte-string-character", "{0}個字元", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("gte-string-character", "{0}個字元", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-number", "{0}必須大於或等於{1}", false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-items", "{0}必須至少包含{1}", false); err != nil {
+ return
+ }
+
+ //if err = ut.AddCardinal("gte-items-item", "{0}項", locales.PluralRuleOne, false); err != nil {
+ // return
+ //}
+
+ if err = ut.AddCardinal("gte-items-item", "{0}項", locales.PluralRuleOther, false); err != nil {
+ return
+ }
+
+ if err = ut.Add("gte-datetime", "{0}必須大於或等於目前日期和時間", false); err != nil {
+ return
+ }
+
+ return
+ },
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ var err error
+ var t string
+ var f64 float64
+ var digits uint64
+ var kind reflect.Kind
+
+ fn := func() (err error) {
+
+ if idx := strings.Index(fe.Param(), "."); idx != -1 {
+ digits = uint64(len(fe.Param()[idx+1:]))
+ }
+
+ f64, err = strconv.ParseFloat(fe.Param(), 64)
+
+ return
+ }
+
+ kind = fe.Kind()
+ if kind == reflect.Ptr {
+ kind = fe.Type().Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.String:
+
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-string", fe.Field(), c)
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ var c string
+
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits))
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-items", fe.Field(), c)
+
+ case reflect.Struct:
+ if fe.Type() != reflect.TypeOf(time.Time{}) {
+ err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag())
+ } else {
+ t, err = ut.T("gte-datetime", fe.Field())
+ }
+
+ default:
+ err = fn()
+ if err != nil {
+ goto END
+ }
+
+ t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits))
+ }
+
+ END:
+ if err != nil {
+ fmt.Printf("警告: 翻譯欄位錯誤: %s", err)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqfield",
+ translation: "{0}必須等於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "eqcsfield",
+ translation: "{0}必須等於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "necsfield",
+ translation: "{0}不能等於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtcsfield",
+ translation: "{0}必須大於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtecsfield",
+ translation: "{0}必須大於或等於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltcsfield",
+ translation: "{0}必須小於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltecsfield",
+ translation: "{0}必須小於或等於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "nefield",
+ translation: "{0}不能等於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtfield",
+ translation: "{0}必須大於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "gtefield",
+ translation: "{0}必須大於或等於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltfield",
+ translation: "{0}必須小於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "ltefield",
+ translation: "{0}必須小於或等於{1}",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "alpha",
+ translation: "{0}只能包含字母",
+ override: false,
+ },
+ {
+ tag: "alphanum",
+ translation: "{0}只能包含字母和數字",
+ override: false,
+ },
+ {
+ tag: "numeric",
+ translation: "{0}必須是一個有效的數值",
+ override: false,
+ },
+ {
+ tag: "number",
+ translation: "{0}必須是一個有效的數字",
+ override: false,
+ },
+ {
+ tag: "hexadecimal",
+ translation: "{0}必須是一個有效的十六進制",
+ override: false,
+ },
+ {
+ tag: "hexcolor",
+ translation: "{0}必須是一個有效的十六進制顏色",
+ override: false,
+ },
+ {
+ tag: "rgb",
+ translation: "{0}必須是一個有效的RGB顏色",
+ override: false,
+ },
+ {
+ tag: "rgba",
+ translation: "{0}必須是一個有效的RGBA顏色",
+ override: false,
+ },
+ {
+ tag: "hsl",
+ translation: "{0}必須是一個有效的HSL顏色",
+ override: false,
+ },
+ {
+ tag: "hsla",
+ translation: "{0}必須是一個有效的HSLA顏色",
+ override: false,
+ },
+ {
+ tag: "email",
+ translation: "{0}必須是一個有效的信箱",
+ override: false,
+ },
+ {
+ tag: "url",
+ translation: "{0}必須是一個有效的URL",
+ override: false,
+ },
+ {
+ tag: "uri",
+ translation: "{0}必須是一個有效的URI",
+ override: false,
+ },
+ {
+ tag: "base64",
+ translation: "{0}必須是一個有效的Base64字元串",
+ override: false,
+ },
+ {
+ tag: "contains",
+ translation: "{0}必須包含文字'{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "containsany",
+ translation: "{0}必須包含至少一個以下字元'{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludes",
+ translation: "{0}不能包含文字'{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesall",
+ translation: "{0}不能包含以下任何字元'{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "excludesrune",
+ translation: "{0}不能包含'{1}'",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ {
+ tag: "isbn",
+ translation: "{0}必須是一個有效的ISBN編號",
+ override: false,
+ },
+ {
+ tag: "isbn10",
+ translation: "{0}必須是一個有效的ISBN-10編號",
+ override: false,
+ },
+ {
+ tag: "isbn13",
+ translation: "{0}必須是一個有效的ISBN-13編號",
+ override: false,
+ },
+ {
+ tag: "uuid",
+ translation: "{0}必須是一個有效的UUID",
+ override: false,
+ },
+ {
+ tag: "uuid3",
+ translation: "{0}必須是一個有效的V3 UUID",
+ override: false,
+ },
+ {
+ tag: "uuid4",
+ translation: "{0}必須是一個有效的V4 UUID",
+ override: false,
+ },
+ {
+ tag: "uuid5",
+ translation: "{0}必須是一個有效的V5 UUID",
+ override: false,
+ },
+ {
+ tag: "ascii",
+ translation: "{0}必須只包含ascii字元",
+ override: false,
+ },
+ {
+ tag: "printascii",
+ translation: "{0}必須只包含可輸出的ascii字元",
+ override: false,
+ },
+ {
+ tag: "multibyte",
+ translation: "{0}必須包含多個字元",
+ override: false,
+ },
+ {
+ tag: "datauri",
+ translation: "{0}必須包含有效的數據URI",
+ override: false,
+ },
+ {
+ tag: "latitude",
+ translation: "{0}必須包含有效的緯度座標",
+ override: false,
+ },
+ {
+ tag: "longitude",
+ translation: "{0}必須包含有效的經度座標",
+ override: false,
+ },
+ {
+ tag: "ssn",
+ translation: "{0}必須是一個有效的社會安全編號(SSN)",
+ override: false,
+ },
+ {
+ tag: "ipv4",
+ translation: "{0}必須是一個有效的IPv4地址",
+ override: false,
+ },
+ {
+ tag: "ipv6",
+ translation: "{0}必須是一個有效的IPv6地址",
+ override: false,
+ },
+ {
+ tag: "ip",
+ translation: "{0}必須是一個有效的IP地址",
+ override: false,
+ },
+ {
+ tag: "cidr",
+ translation: "{0}必須是一個有效的無類別域間路由(CIDR)",
+ override: false,
+ },
+ {
+ tag: "cidrv4",
+ translation: "{0}必須是一个包含IPv4地址的有效無類別域間路由(CIDR)",
+ override: false,
+ },
+ {
+ tag: "cidrv6",
+ translation: "{0}必須是一个包含IPv6地址的有效無類別域間路由(CIDR)",
+ override: false,
+ },
+ {
+ tag: "tcp_addr",
+ translation: "{0}必須是一個有效的TCP地址",
+ override: false,
+ },
+ {
+ tag: "tcp4_addr",
+ translation: "{0}必須是一個有效的IPv4 TCP地址",
+ override: false,
+ },
+ {
+ tag: "tcp6_addr",
+ translation: "{0}必須是一個有效的IPv6 TCP地址",
+ override: false,
+ },
+ {
+ tag: "udp_addr",
+ translation: "{0}必須是一個有效的UDP地址",
+ override: false,
+ },
+ {
+ tag: "udp4_addr",
+ translation: "{0}必須是一個有效的IPv4 UDP地址",
+ override: false,
+ },
+ {
+ tag: "udp6_addr",
+ translation: "{0}必須是一個有效的IPv6 UDP地址",
+ override: false,
+ },
+ {
+ tag: "ip_addr",
+ translation: "{0}必須是一個有效的IP地址",
+ override: false,
+ },
+ {
+ tag: "ip4_addr",
+ translation: "{0}必須是一個有效的IPv4地址",
+ override: false,
+ },
+ {
+ tag: "ip6_addr",
+ translation: "{0}必須是一個有效的IPv6地址",
+ override: false,
+ },
+ {
+ tag: "unix_addr",
+ translation: "{0}必須是一個有效的UNIX地址",
+ override: false,
+ },
+ {
+ tag: "mac",
+ translation: "{0}必須是一個有效的MAC地址",
+ override: false,
+ },
+ {
+ tag: "iscolor",
+ translation: "{0}必須是一個有效的顏色",
+ override: false,
+ },
+ {
+ tag: "oneof",
+ translation: "{0}必須是[{1}]中的一個",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+ s, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+ return s
+ },
+ },
+ {
+ tag: "datetime",
+ translation: "{0}與{1}格式不匹配",
+ override: false,
+ customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+ },
+ },
+ }
+
+ for _, t := range translations {
+
+ if t.customTransFunc != nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
+
+ } else if t.customTransFunc != nil && t.customRegisFunc == nil {
+
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
+
+ } else if t.customTransFunc == nil && t.customRegisFunc != nil {
+
+ err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
+
+ } else {
+ err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
+ }
+
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
+
+ return func(ut ut.Translator) (err error) {
+
+ if err = ut.Add(tag, translation, override); err != nil {
+ return
+ }
+
+ return
+
+ }
+
+}
+
+func translateFunc(ut ut.Translator, fe validator.FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
+ return fe.(error).Error()
+ }
+
+ return t
+}
diff --git a/go-playground/validator/v10/translations/zh_tw/zh_tw_test.go b/go-playground/validator/v10/translations/zh_tw/zh_tw_test.go
new file mode 100644
index 0000000..b011ae7
--- /dev/null
+++ b/go-playground/validator/v10/translations/zh_tw/zh_tw_test.go
@@ -0,0 +1,641 @@
+package zh_tw
+
+import (
+ "testing"
+ "time"
+
+ zhongwen "gin-valid/go-playground/locales/zh_Hant_TW"
+ ut "gin-valid/go-playground/universal-translator"
+ "gin-valid/go-playground/validator/v10"
+ . "github.com/go-playground/assert/v2"
+)
+
+func TestTranslations(t *testing.T) {
+
+ zh := zhongwen.New()
+ uni := ut.New(zh, zh)
+ trans, _ := uni.GetTranslator("zh")
+
+ validate := validator.New()
+
+ err := RegisterDefaultTranslations(validate, trans)
+ Equal(t, err, nil)
+
+ type Inner struct {
+ EqCSFieldString string
+ NeCSFieldString string
+ GtCSFieldString string
+ GteCSFieldString string
+ LtCSFieldString string
+ LteCSFieldString string
+ }
+
+ type Test struct {
+ Inner Inner
+ RequiredString string `validate:"required"`
+ RequiredNumber int `validate:"required"`
+ RequiredMultiple []string `validate:"required"`
+ LenString string `validate:"len=1"`
+ LenNumber float64 `validate:"len=1113.00"`
+ LenMultiple []string `validate:"len=7"`
+ MinString string `validate:"min=1"`
+ MinNumber float64 `validate:"min=1113.00"`
+ MinMultiple []string `validate:"min=7"`
+ MaxString string `validate:"max=3"`
+ MaxNumber float64 `validate:"max=1113.00"`
+ MaxMultiple []string `validate:"max=7"`
+ EqString string `validate:"eq=3"`
+ EqNumber float64 `validate:"eq=2.33"`
+ EqMultiple []string `validate:"eq=7"`
+ NeString string `validate:"ne="`
+ NeNumber float64 `validate:"ne=0.00"`
+ NeMultiple []string `validate:"ne=0"`
+ LtString string `validate:"lt=3"`
+ LtNumber float64 `validate:"lt=5.56"`
+ LtMultiple []string `validate:"lt=2"`
+ LtTime time.Time `validate:"lt"`
+ LteString string `validate:"lte=3"`
+ LteNumber float64 `validate:"lte=5.56"`
+ LteMultiple []string `validate:"lte=2"`
+ LteTime time.Time `validate:"lte"`
+ GtString string `validate:"gt=3"`
+ GtNumber float64 `validate:"gt=5.56"`
+ GtMultiple []string `validate:"gt=2"`
+ GtTime time.Time `validate:"gt"`
+ GteString string `validate:"gte=3"`
+ GteNumber float64 `validate:"gte=5.56"`
+ GteMultiple []string `validate:"gte=2"`
+ GteTime time.Time `validate:"gte"`
+ EqFieldString string `validate:"eqfield=MaxString"`
+ EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
+ NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
+ GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
+ GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
+ LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
+ LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
+ NeFieldString string `validate:"nefield=EqFieldString"`
+ GtFieldString string `validate:"gtfield=MaxString"`
+ GteFieldString string `validate:"gtefield=MaxString"`
+ LtFieldString string `validate:"ltfield=MaxString"`
+ LteFieldString string `validate:"ltefield=MaxString"`
+ AlphaString string `validate:"alpha"`
+ AlphanumString string `validate:"alphanum"`
+ NumericString string `validate:"numeric"`
+ NumberString string `validate:"number"`
+ HexadecimalString string `validate:"hexadecimal"`
+ HexColorString string `validate:"hexcolor"`
+ RGBColorString string `validate:"rgb"`
+ RGBAColorString string `validate:"rgba"`
+ HSLColorString string `validate:"hsl"`
+ HSLAColorString string `validate:"hsla"`
+ Email string `validate:"email"`
+ URL string `validate:"url"`
+ URI string `validate:"uri"`
+ Base64 string `validate:"base64"`
+ Contains string `validate:"contains=purpose"`
+ ContainsAny string `validate:"containsany=!@#$"`
+ Excludes string `validate:"excludes=text"`
+ ExcludesAll string `validate:"excludesall=!@#$"`
+ ExcludesRune string `validate:"excludesrune=☻"`
+ ISBN string `validate:"isbn"`
+ ISBN10 string `validate:"isbn10"`
+ ISBN13 string `validate:"isbn13"`
+ UUID string `validate:"uuid"`
+ UUID3 string `validate:"uuid3"`
+ UUID4 string `validate:"uuid4"`
+ UUID5 string `validate:"uuid5"`
+ ASCII string `validate:"ascii"`
+ PrintableASCII string `validate:"printascii"`
+ MultiByte string `validate:"multibyte"`
+ DataURI string `validate:"datauri"`
+ Latitude string `validate:"latitude"`
+ Longitude string `validate:"longitude"`
+ SSN string `validate:"ssn"`
+ IP string `validate:"ip"`
+ IPv4 string `validate:"ipv4"`
+ IPv6 string `validate:"ipv6"`
+ CIDR string `validate:"cidr"`
+ CIDRv4 string `validate:"cidrv4"`
+ CIDRv6 string `validate:"cidrv6"`
+ TCPAddr string `validate:"tcp_addr"`
+ TCPAddrv4 string `validate:"tcp4_addr"`
+ TCPAddrv6 string `validate:"tcp6_addr"`
+ UDPAddr string `validate:"udp_addr"`
+ UDPAddrv4 string `validate:"udp4_addr"`
+ UDPAddrv6 string `validate:"udp6_addr"`
+ IPAddr string `validate:"ip_addr"`
+ IPAddrv4 string `validate:"ip4_addr"`
+ IPAddrv6 string `validate:"ip6_addr"`
+ UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
+ MAC string `validate:"mac"`
+ IsColor string `validate:"iscolor"`
+ StrPtrMinLen *string `validate:"min=10"`
+ StrPtrMaxLen *string `validate:"max=1"`
+ StrPtrLen *string `validate:"len=2"`
+ StrPtrLt *string `validate:"lt=1"`
+ StrPtrLte *string `validate:"lte=1"`
+ StrPtrGt *string `validate:"gt=10"`
+ StrPtrGte *string `validate:"gte=10"`
+ OneOfString string `validate:"oneof=red green"`
+ OneOfInt int `validate:"oneof=5 63"`
+ Datetime string `validate:"datetime=2006-01-02"`
+ }
+
+ var test Test
+
+ test.Inner.EqCSFieldString = "1234"
+ test.Inner.GtCSFieldString = "1234"
+ test.Inner.GteCSFieldString = "1234"
+
+ test.MaxString = "1234"
+ test.MaxNumber = 2000
+ test.MaxMultiple = make([]string, 9)
+
+ test.LtString = "1234"
+ test.LtNumber = 6
+ test.LtMultiple = make([]string, 3)
+ test.LtTime = time.Now().Add(time.Hour * 24)
+
+ test.LteString = "1234"
+ test.LteNumber = 6
+ test.LteMultiple = make([]string, 3)
+ test.LteTime = time.Now().Add(time.Hour * 24)
+
+ test.LtFieldString = "12345"
+ test.LteFieldString = "12345"
+
+ test.LtCSFieldString = "1234"
+ test.LteCSFieldString = "1234"
+
+ test.AlphaString = "abc3"
+ test.AlphanumString = "abc3!"
+ test.NumericString = "12E.00"
+ test.NumberString = "12E"
+
+ test.Excludes = "this is some test text"
+ test.ExcludesAll = "This is Great!"
+ test.ExcludesRune = "Love it ☻"
+
+ test.ASCII = "カタカナ"
+ test.PrintableASCII = "カタカナ"
+
+ test.MultiByte = "1234feerf"
+
+ s := "toolong"
+ test.StrPtrMaxLen = &s
+ test.StrPtrLen = &s
+
+ test.Datetime = "2008-Feb-01"
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(validator.ValidationErrors)
+ Equal(t, ok, true)
+
+ tests := []struct {
+ ns string
+ expected string
+ }{
+ {
+ ns: "Test.IsColor",
+ expected: "IsColor必須是一個有效的顏色",
+ },
+ {
+ ns: "Test.MAC",
+ expected: "MAC必須是一個有效的MAC地址",
+ },
+ {
+ ns: "Test.IPAddr",
+ expected: "IPAddr必須是一個有效的IP地址",
+ },
+ {
+ ns: "Test.IPAddrv4",
+ expected: "IPAddrv4必須是一個有效的IPv4地址",
+ },
+ {
+ ns: "Test.IPAddrv6",
+ expected: "IPAddrv6必須是一個有效的IPv6地址",
+ },
+ {
+ ns: "Test.UDPAddr",
+ expected: "UDPAddr必須是一個有效的UDP地址",
+ },
+ {
+ ns: "Test.UDPAddrv4",
+ expected: "UDPAddrv4必須是一個有效的IPv4 UDP地址",
+ },
+ {
+ ns: "Test.UDPAddrv6",
+ expected: "UDPAddrv6必須是一個有效的IPv6 UDP地址",
+ },
+ {
+ ns: "Test.TCPAddr",
+ expected: "TCPAddr必須是一個有效的TCP地址",
+ },
+ {
+ ns: "Test.TCPAddrv4",
+ expected: "TCPAddrv4必須是一個有效的IPv4 TCP地址",
+ },
+ {
+ ns: "Test.TCPAddrv6",
+ expected: "TCPAddrv6必須是一個有效的IPv6 TCP地址",
+ },
+ {
+ ns: "Test.CIDR",
+ expected: "CIDR必須是一個有效的無類別域間路由(CIDR)",
+ },
+ {
+ ns: "Test.CIDRv4",
+ expected: "CIDRv4必須是一个包含IPv4地址的有效無類別域間路由(CIDR)",
+ },
+ {
+ ns: "Test.CIDRv6",
+ expected: "CIDRv6必須是一个包含IPv6地址的有效無類別域間路由(CIDR)",
+ },
+ {
+ ns: "Test.SSN",
+ expected: "SSN必須是一個有效的社會安全編號(SSN)",
+ },
+ {
+ ns: "Test.IP",
+ expected: "IP必須是一個有效的IP地址",
+ },
+ {
+ ns: "Test.IPv4",
+ expected: "IPv4必須是一個有效的IPv4地址",
+ },
+ {
+ ns: "Test.IPv6",
+ expected: "IPv6必須是一個有效的IPv6地址",
+ },
+ {
+ ns: "Test.DataURI",
+ expected: "DataURI必須包含有效的數據URI",
+ },
+ {
+ ns: "Test.Latitude",
+ expected: "Latitude必須包含有效的緯度座標",
+ },
+ {
+ ns: "Test.Longitude",
+ expected: "Longitude必須包含有效的經度座標",
+ },
+ {
+ ns: "Test.MultiByte",
+ expected: "MultiByte必須包含多個字元",
+ },
+ {
+ ns: "Test.ASCII",
+ expected: "ASCII必須只包含ascii字元",
+ },
+ {
+ ns: "Test.PrintableASCII",
+ expected: "PrintableASCII必須只包含可輸出的ascii字元",
+ },
+ {
+ ns: "Test.UUID",
+ expected: "UUID必須是一個有效的UUID",
+ },
+ {
+ ns: "Test.UUID3",
+ expected: "UUID3必須是一個有效的V3 UUID",
+ },
+ {
+ ns: "Test.UUID4",
+ expected: "UUID4必須是一個有效的V4 UUID",
+ },
+ {
+ ns: "Test.UUID5",
+ expected: "UUID5必須是一個有效的V5 UUID",
+ },
+ {
+ ns: "Test.ISBN",
+ expected: "ISBN必須是一個有效的ISBN編號",
+ },
+ {
+ ns: "Test.ISBN10",
+ expected: "ISBN10必須是一個有效的ISBN-10編號",
+ },
+ {
+ ns: "Test.ISBN13",
+ expected: "ISBN13必須是一個有效的ISBN-13編號",
+ },
+ {
+ ns: "Test.Excludes",
+ expected: "Excludes不能包含文字'text'",
+ },
+ {
+ ns: "Test.ExcludesAll",
+ expected: "ExcludesAll不能包含以下任何字元'!@#$'",
+ },
+ {
+ ns: "Test.ExcludesRune",
+ expected: "ExcludesRune不能包含'☻'",
+ },
+ {
+ ns: "Test.ContainsAny",
+ expected: "ContainsAny必須包含至少一個以下字元'!@#$'",
+ },
+ {
+ ns: "Test.Contains",
+ expected: "Contains必須包含文字'purpose'",
+ },
+ {
+ ns: "Test.Base64",
+ expected: "Base64必須是一個有效的Base64字元串",
+ },
+ {
+ ns: "Test.Email",
+ expected: "Email必須是一個有效的信箱",
+ },
+ {
+ ns: "Test.URL",
+ expected: "URL必須是一個有效的URL",
+ },
+ {
+ ns: "Test.URI",
+ expected: "URI必須是一個有效的URI",
+ },
+ {
+ ns: "Test.RGBColorString",
+ expected: "RGBColorString必須是一個有效的RGB顏色",
+ },
+ {
+ ns: "Test.RGBAColorString",
+ expected: "RGBAColorString必須是一個有效的RGBA顏色",
+ },
+ {
+ ns: "Test.HSLColorString",
+ expected: "HSLColorString必須是一個有效的HSL顏色",
+ },
+ {
+ ns: "Test.HSLAColorString",
+ expected: "HSLAColorString必須是一個有效的HSLA顏色",
+ },
+ {
+ ns: "Test.HexadecimalString",
+ expected: "HexadecimalString必須是一個有效的十六進制",
+ },
+ {
+ ns: "Test.HexColorString",
+ expected: "HexColorString必須是一個有效的十六進制顏色",
+ },
+ {
+ ns: "Test.NumberString",
+ expected: "NumberString必須是一個有效的數字",
+ },
+ {
+ ns: "Test.NumericString",
+ expected: "NumericString必須是一個有效的數值",
+ },
+ {
+ ns: "Test.AlphanumString",
+ expected: "AlphanumString只能包含字母和數字",
+ },
+ {
+ ns: "Test.AlphaString",
+ expected: "AlphaString只能包含字母",
+ },
+ {
+ ns: "Test.LtFieldString",
+ expected: "LtFieldString必須小於MaxString",
+ },
+ {
+ ns: "Test.LteFieldString",
+ expected: "LteFieldString必須小於或等於MaxString",
+ },
+ {
+ ns: "Test.GtFieldString",
+ expected: "GtFieldString必須大於MaxString",
+ },
+ {
+ ns: "Test.GteFieldString",
+ expected: "GteFieldString必須大於或等於MaxString",
+ },
+ {
+ ns: "Test.NeFieldString",
+ expected: "NeFieldString不能等於EqFieldString",
+ },
+ {
+ ns: "Test.LtCSFieldString",
+ expected: "LtCSFieldString必須小於Inner.LtCSFieldString",
+ },
+ {
+ ns: "Test.LteCSFieldString",
+ expected: "LteCSFieldString必須小於或等於Inner.LteCSFieldString",
+ },
+ {
+ ns: "Test.GtCSFieldString",
+ expected: "GtCSFieldString必須大於Inner.GtCSFieldString",
+ },
+ {
+ ns: "Test.GteCSFieldString",
+ expected: "GteCSFieldString必須大於或等於Inner.GteCSFieldString",
+ },
+ {
+ ns: "Test.NeCSFieldString",
+ expected: "NeCSFieldString不能等於Inner.NeCSFieldString",
+ },
+ {
+ ns: "Test.EqCSFieldString",
+ expected: "EqCSFieldString必須等於Inner.EqCSFieldString",
+ },
+ {
+ ns: "Test.EqFieldString",
+ expected: "EqFieldString必須等於MaxString",
+ },
+ {
+ ns: "Test.GteString",
+ expected: "GteString長度必須至少為3個字元",
+ },
+ {
+ ns: "Test.GteNumber",
+ expected: "GteNumber必須大於或等於5.56",
+ },
+ {
+ ns: "Test.GteMultiple",
+ expected: "GteMultiple必須至少包含2項",
+ },
+ {
+ ns: "Test.GteTime",
+ expected: "GteTime必須大於或等於目前日期和時間",
+ },
+ {
+ ns: "Test.GtString",
+ expected: "GtString長度必須大於3個字元",
+ },
+ {
+ ns: "Test.GtNumber",
+ expected: "GtNumber必須大於5.56",
+ },
+ {
+ ns: "Test.GtMultiple",
+ expected: "GtMultiple必須大於2項",
+ },
+ {
+ ns: "Test.GtTime",
+ expected: "GtTime必須大於目前日期和時間",
+ },
+ {
+ ns: "Test.LteString",
+ expected: "LteString長度不能超過3個字元",
+ },
+ {
+ ns: "Test.LteNumber",
+ expected: "LteNumber必須小於或等於5.56",
+ },
+ {
+ ns: "Test.LteMultiple",
+ expected: "LteMultiple最多只能包含2項",
+ },
+ {
+ ns: "Test.LteTime",
+ expected: "LteTime必須小於或等於目前日期和時間",
+ },
+ {
+ ns: "Test.LtString",
+ expected: "LtString長度必須小於3個字元",
+ },
+ {
+ ns: "Test.LtNumber",
+ expected: "LtNumber必須小於5.56",
+ },
+ {
+ ns: "Test.LtMultiple",
+ expected: "LtMultiple必須包含少於2項",
+ },
+ {
+ ns: "Test.LtTime",
+ expected: "LtTime必須小於目前日期和時間",
+ },
+ {
+ ns: "Test.NeString",
+ expected: "NeString不能等於",
+ },
+ {
+ ns: "Test.NeNumber",
+ expected: "NeNumber不能等於0.00",
+ },
+ {
+ ns: "Test.NeMultiple",
+ expected: "NeMultiple不能等於0",
+ },
+ {
+ ns: "Test.EqString",
+ expected: "EqString不等於3",
+ },
+ {
+ ns: "Test.EqNumber",
+ expected: "EqNumber不等於2.33",
+ },
+ {
+ ns: "Test.EqMultiple",
+ expected: "EqMultiple不等於7",
+ },
+ {
+ ns: "Test.MaxString",
+ expected: "MaxString長度不能超過3個字元",
+ },
+ {
+ ns: "Test.MaxNumber",
+ expected: "MaxNumber必須小於或等於1,113.00",
+ },
+ {
+ ns: "Test.MaxMultiple",
+ expected: "MaxMultiple最多只能包含7項",
+ },
+ {
+ ns: "Test.MinString",
+ expected: "MinString長度必須至少為1個字元",
+ },
+ {
+ ns: "Test.MinNumber",
+ expected: "MinNumber最小只能為1,113.00",
+ },
+ {
+ ns: "Test.MinMultiple",
+ expected: "MinMultiple必須至少包含7項",
+ },
+ {
+ ns: "Test.LenString",
+ expected: "LenString長度必須為1個字元",
+ },
+ {
+ ns: "Test.LenNumber",
+ expected: "LenNumber必須等於1,113.00",
+ },
+ {
+ ns: "Test.LenMultiple",
+ expected: "LenMultiple必須包含7項",
+ },
+ {
+ ns: "Test.RequiredString",
+ expected: "RequiredString為必填欄位",
+ },
+ {
+ ns: "Test.RequiredNumber",
+ expected: "RequiredNumber為必填欄位",
+ },
+ {
+ ns: "Test.RequiredMultiple",
+ expected: "RequiredMultiple為必填欄位",
+ },
+ {
+ ns: "Test.StrPtrMinLen",
+ expected: "StrPtrMinLen長度必須至少為10個字元",
+ },
+ {
+ ns: "Test.StrPtrMaxLen",
+ expected: "StrPtrMaxLen長度不能超過1個字元",
+ },
+ {
+ ns: "Test.StrPtrLen",
+ expected: "StrPtrLen長度必須為2個字元",
+ },
+ {
+ ns: "Test.StrPtrLt",
+ expected: "StrPtrLt長度必須小於1個字元",
+ },
+ {
+ ns: "Test.StrPtrLte",
+ expected: "StrPtrLte長度不能超過1個字元",
+ },
+ {
+ ns: "Test.StrPtrGt",
+ expected: "StrPtrGt長度必須大於10個字元",
+ },
+ {
+ ns: "Test.StrPtrGte",
+ expected: "StrPtrGte長度必須至少為10個字元",
+ },
+ {
+ ns: "Test.OneOfString",
+ expected: "OneOfString必須是[red green]中的一個",
+ },
+ {
+ ns: "Test.OneOfInt",
+ expected: "OneOfInt必須是[5 63]中的一個",
+ },
+ {
+ ns: "Test.Datetime",
+ expected: "Datetime與2006-01-02格式不匹配",
+ },
+ }
+
+ for _, tt := range tests {
+
+ var fe validator.FieldError
+
+ for _, e := range errs {
+ if tt.ns == e.Namespace() {
+ fe = e
+ break
+ }
+ }
+
+ NotEqual(t, fe, nil)
+ Equal(t, tt.expected, fe.Translate(trans))
+ }
+
+}
diff --git a/go-playground/validator/v10/util.go b/go-playground/validator/v10/util.go
new file mode 100644
index 0000000..cb564e3
--- /dev/null
+++ b/go-playground/validator/v10/util.go
@@ -0,0 +1,288 @@
+package validator
+
+import (
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// extractTypeInternal gets the actual underlying type of field value.
+// It will dive into pointers, customTypes and return you the
+// underlying value and it's kind.
+func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) {
+
+BEGIN:
+ switch current.Kind() {
+ case reflect.Ptr:
+
+ nullable = true
+
+ if current.IsNil() {
+ return current, reflect.Ptr, nullable
+ }
+
+ current = current.Elem()
+ goto BEGIN
+
+ case reflect.Interface:
+
+ nullable = true
+
+ if current.IsNil() {
+ return current, reflect.Interface, nullable
+ }
+
+ current = current.Elem()
+ goto BEGIN
+
+ case reflect.Invalid:
+ return current, reflect.Invalid, nullable
+
+ default:
+
+ if v.v.hasCustomFuncs {
+
+ if fn, ok := v.v.customFuncs[current.Type()]; ok {
+ current = reflect.ValueOf(fn(current))
+ goto BEGIN
+ }
+ }
+
+ return current, current.Kind(), nullable
+ }
+}
+
+// getStructFieldOKInternal traverses a struct to retrieve a specific field denoted by the provided namespace and
+// returns the field, field kind and whether is was successful in retrieving the field at all.
+//
+// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
+// could not be retrieved because it didn't exist.
+func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) {
+ // val 是结构体, 如果 gtfield=filed1 这种field的比较,那 namespace就是入参 filed1
+BEGIN:
+ current, kind, nullable = v.ExtractType(val)
+ if kind == reflect.Invalid {
+ return
+ }
+
+ if namespace == "" {
+ found = true
+ return
+ }
+
+ switch kind {
+
+ case reflect.Ptr, reflect.Interface:
+ return
+
+ case reflect.Struct:
+
+ typ := current.Type()
+ fld := namespace
+ var ns string
+
+ if typ != timeType {
+
+ idx := strings.Index(namespace, namespaceSeparator)
+
+ if idx != -1 {
+ fld = namespace[:idx]
+ ns = namespace[idx+1:]
+ } else {
+ ns = ""
+ }
+
+ bracketIdx := strings.Index(fld, leftBracket)
+ if bracketIdx != -1 {
+ fld = fld[:bracketIdx]
+
+ ns = namespace[bracketIdx:]
+ }
+
+ val = current.FieldByName(fld)
+ namespace = ns
+ goto BEGIN
+ }
+
+ case reflect.Array, reflect.Slice:
+ idx := strings.Index(namespace, leftBracket)
+ idx2 := strings.Index(namespace, rightBracket)
+
+ arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2])
+
+ if arrIdx >= current.Len() {
+ return
+ }
+
+ startIdx := idx2 + 1
+
+ if startIdx < len(namespace) {
+ if namespace[startIdx:startIdx+1] == namespaceSeparator {
+ startIdx++
+ }
+ }
+
+ val = current.Index(arrIdx)
+ namespace = namespace[startIdx:]
+ goto BEGIN
+
+ case reflect.Map:
+ idx := strings.Index(namespace, leftBracket) + 1
+ idx2 := strings.Index(namespace, rightBracket)
+
+ endIdx := idx2
+
+ if endIdx+1 < len(namespace) {
+ if namespace[endIdx+1:endIdx+2] == namespaceSeparator {
+ endIdx++
+ }
+ }
+
+ key := namespace[idx:idx2]
+
+ switch current.Type().Key().Kind() {
+ case reflect.Int:
+ i, _ := strconv.Atoi(key)
+ val = current.MapIndex(reflect.ValueOf(i))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Int8:
+ i, _ := strconv.ParseInt(key, 10, 8)
+ val = current.MapIndex(reflect.ValueOf(int8(i)))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Int16:
+ i, _ := strconv.ParseInt(key, 10, 16)
+ val = current.MapIndex(reflect.ValueOf(int16(i)))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Int32:
+ i, _ := strconv.ParseInt(key, 10, 32)
+ val = current.MapIndex(reflect.ValueOf(int32(i)))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Int64:
+ i, _ := strconv.ParseInt(key, 10, 64)
+ val = current.MapIndex(reflect.ValueOf(i))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Uint:
+ i, _ := strconv.ParseUint(key, 10, 0)
+ val = current.MapIndex(reflect.ValueOf(uint(i)))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Uint8:
+ i, _ := strconv.ParseUint(key, 10, 8)
+ val = current.MapIndex(reflect.ValueOf(uint8(i)))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Uint16:
+ i, _ := strconv.ParseUint(key, 10, 16)
+ val = current.MapIndex(reflect.ValueOf(uint16(i)))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Uint32:
+ i, _ := strconv.ParseUint(key, 10, 32)
+ val = current.MapIndex(reflect.ValueOf(uint32(i)))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Uint64:
+ i, _ := strconv.ParseUint(key, 10, 64)
+ val = current.MapIndex(reflect.ValueOf(i))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Float32:
+ f, _ := strconv.ParseFloat(key, 32)
+ val = current.MapIndex(reflect.ValueOf(float32(f)))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Float64:
+ f, _ := strconv.ParseFloat(key, 64)
+ val = current.MapIndex(reflect.ValueOf(f))
+ namespace = namespace[endIdx+1:]
+
+ case reflect.Bool:
+ b, _ := strconv.ParseBool(key)
+ val = current.MapIndex(reflect.ValueOf(b))
+ namespace = namespace[endIdx+1:]
+
+ // reflect.Type = string
+ default:
+ val = current.MapIndex(reflect.ValueOf(key))
+ namespace = namespace[endIdx+1:]
+ }
+
+ goto BEGIN
+ }
+
+ // if got here there was more namespace, cannot go any deeper
+ panic("Invalid field namespace")
+}
+
+// asInt returns the parameter as a int64
+// or panics if it can't convert
+func asInt(param string) int64 {
+ i, err := strconv.ParseInt(param, 0, 64)
+ panicIf(err)
+
+ return i
+}
+
+// asIntFromTimeDuration parses param as time.Duration and returns it as int64
+// or panics on error.
+func asIntFromTimeDuration(param string) int64 {
+ d, err := time.ParseDuration(param)
+ if err != nil {
+ // attempt parsing as an an integer assuming nanosecond precision
+ return asInt(param)
+ }
+ return int64(d)
+}
+
+// asIntFromType calls the proper function to parse param as int64,
+// given a field's Type t.
+func asIntFromType(t reflect.Type, param string) int64 {
+ switch t {
+ case timeDurationType:
+ return asIntFromTimeDuration(param)
+ default:
+ return asInt(param)
+ }
+}
+
+// asUint returns the parameter as a uint64
+// or panics if it can't convert
+func asUint(param string) uint64 {
+
+ i, err := strconv.ParseUint(param, 0, 64)
+ panicIf(err)
+
+ return i
+}
+
+// asFloat returns the parameter as a float64
+// or panics if it can't convert
+func asFloat(param string) float64 {
+
+ i, err := strconv.ParseFloat(param, 64)
+ panicIf(err)
+
+ return i
+}
+
+// asBool returns the parameter as a bool
+// or panics if it can't convert
+func asBool(param string) bool {
+
+ i, err := strconv.ParseBool(param)
+ panicIf(err)
+
+ return i
+}
+
+func panicIf(err error) {
+ if err != nil {
+ panic(err.Error())
+ }
+}
diff --git a/go-playground/validator/v10/validator.go b/go-playground/validator/v10/validator.go
new file mode 100644
index 0000000..5baacfe
--- /dev/null
+++ b/go-playground/validator/v10/validator.go
@@ -0,0 +1,482 @@
+package validator
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+ "strconv"
+)
+
+// per validate construct
+type validate struct {
+ v *Validate
+ top reflect.Value
+ ns []byte // ns是命名空间,见
+ actualNs []byte
+ // 如果想修改 错误信息的打印, 直接改这个错误的 .Error()方法应该就可以了, 不对不对,还有一个翻译在执行
+ errs ValidationErrors //校验失败的 记录错误的 结构体的 field[] , 依次验证field,所以可以加个判断,只要有一个错误,就停下来 //每次用完都会清零,在put回pool
+ includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
+ ffn FilterFunc
+ slflParent reflect.Value // StructLevel & FieldLevel
+ slCurrent reflect.Value // StructLevel & FieldLevel
+ flField reflect.Value // StructLevel & FieldLevel
+ cf *cField // StructLevel & FieldLevel
+ ct *cTag // StructLevel & FieldLevel
+ misc []byte // misc reusable
+ str1 string // misc reusable // 真正显示的 错误,有别名就是别名
+ str2 string // misc reusable // 失败的field信息,如 User.Name (结构体名称+field真名称),如果没有别名,就是str1
+ fldIsPointer bool // StructLevel & FieldLevel
+ isPartial bool
+ hasExcludes bool
+}
+
+// parent and current will be the same the first run of validateStruct
+func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
+
+ cs, ok := v.v.structCache.Get(typ)
+ if !ok {
+ cs = v.v.extractStructCache(current, typ.Name())
+ }
+
+ if len(ns) == 0 && len(cs.name) != 0 {
+ // 但实际上,`:` 左边的我都不需要,所以还是改 翻译 比较好 , 而且可能会影响到 未知的错误
+ ns = append(ns, cs.name...) // 去掉这里的ns的内容,出现错误时,应该就就不会输出 struct.field:"err", 而是 field:"err",
+ ns = append(ns, '.')
+
+ structNs = append(structNs, cs.name...)
+ structNs = append(structNs, '.')
+ }
+
+ // ct is nil on top level struct, and structs as fields that have no tag info
+ // so if nil or if not nil and the structonly tag isn't present
+ if ct == nil || ct.typeof != typeStructOnly {
+
+ var f *cField
+
+ for i := 0; i < len(cs.fields); i++ {
+ // yang 加个判断, 有一个验证不通过就退出
+ if len(v.errs) != 0 {
+ break
+ }
+ // yang 修改分割线
+ f = cs.fields[i]
+
+ if v.isPartial {
+
+ if v.ffn != nil {
+ // used with StructFiltered
+ if v.ffn(append(structNs, f.name...)) {
+ continue
+ }
+
+ } else {
+ // used with StructPartial & StructExcept
+ _, ok = v.includeExclude[string(append(structNs, f.name...))]
+
+ if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) {
+ continue
+ }
+ }
+ }
+
+ v.traverseField(ctx, parent, current.Field(f.idx), ns, structNs, f, f.cTags)
+ }
+ }
+
+ // check if any struct level validations, after all field validations already checked.
+ // first iteration will have no info about nostructlevel tag, and is checked prior to
+ // calling the next iteration of validateStruct called from traverseField.
+ if cs.fn != nil {
+
+ v.slflParent = parent
+ v.slCurrent = current
+ v.ns = ns
+ v.actualNs = structNs
+
+ cs.fn(ctx, v)
+ }
+}
+
+// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
+func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
+ var typ reflect.Type
+ var kind reflect.Kind
+
+ current, kind, v.fldIsPointer = v.extractTypeInternal(current, false)
+
+ switch kind {
+ case reflect.Ptr, reflect.Interface, reflect.Invalid:
+
+ if ct == nil {
+ return
+ }
+
+ if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault {
+ return
+ }
+
+ if ct.hasTag {
+ if kind == reflect.Invalid {
+ v.str1 = string(append(ns, cf.altName...))
+ if v.v.hasTagNameFunc {
+ v.str2 = string(append(structNs, cf.name...))
+ } else {
+ v.str2 = v.str1
+ }
+ v.errs = append(v.errs,
+ &fieldError{
+ v: v.v,
+ tag: ct.aliasTag,
+ actualTag: ct.tag,
+ ns: v.str1,
+ structNs: v.str2,
+ fieldLen: uint8(len(cf.altName)),
+ structfieldLen: uint8(len(cf.name)),
+ param: ct.param,
+ kind: kind,
+ },
+ )
+ return
+ }
+
+ v.str1 = string(append(ns, cf.altName...))
+ if v.v.hasTagNameFunc {
+ v.str2 = string(append(structNs, cf.name...))
+ } else {
+ v.str2 = v.str1
+ }
+ if !ct.runValidationWhenNil {
+ v.errs = append(v.errs,
+ &fieldError{
+ v: v.v,
+ tag: ct.aliasTag,
+ actualTag: ct.tag,
+ ns: v.str1,
+ structNs: v.str2,
+ fieldLen: uint8(len(cf.altName)),
+ structfieldLen: uint8(len(cf.name)),
+ value: current.Interface(),
+ param: ct.param,
+ kind: kind,
+ typ: current.Type(),
+ },
+ )
+ return
+ }
+ }
+
+ case reflect.Struct:
+
+ typ = current.Type()
+
+ if typ != timeType {
+
+ if ct != nil {
+
+ if ct.typeof == typeStructOnly {
+ goto CONTINUE
+ } else if ct.typeof == typeIsDefault {
+ // set Field Level fields
+ v.slflParent = parent
+ v.flField = current
+ v.cf = cf
+ v.ct = ct
+
+ if !ct.fn(ctx, v) {
+ v.str1 = string(append(ns, cf.altName...))
+
+ if v.v.hasTagNameFunc {
+ v.str2 = string(append(structNs, cf.name...))
+ } else {
+ v.str2 = v.str1
+ }
+
+ v.errs = append(v.errs,
+ &fieldError{
+ v: v.v,
+ tag: ct.aliasTag,
+ actualTag: ct.tag,
+ ns: v.str1,
+ structNs: v.str2,
+ fieldLen: uint8(len(cf.altName)),
+ structfieldLen: uint8(len(cf.name)),
+ value: current.Interface(),
+ param: ct.param,
+ kind: kind,
+ typ: typ,
+ },
+ )
+ return
+ }
+ }
+
+ ct = ct.next
+ }
+
+ if ct != nil && ct.typeof == typeNoStructLevel {
+ return
+ }
+
+ CONTINUE:
+ // if len == 0 then validating using 'Var' or 'VarWithValue'
+ // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
+ // VarWithField - this allows for validating against each field within the struct against a specific value
+ // pretty handy in certain situations
+ if len(cf.name) > 0 {
+ ns = append(append(ns, cf.altName...), '.')
+ structNs = append(append(structNs, cf.name...), '.')
+ }
+
+ v.validateStruct(ctx, current, current, typ, ns, structNs, ct)
+ return
+ }
+ }
+
+ if !ct.hasTag {
+ return
+ }
+
+ typ = current.Type()
+
+OUTER:
+ for {
+ if ct == nil {
+ return
+ }
+
+ switch ct.typeof {
+
+ case typeOmitEmpty:
+
+ // set Field Level fields
+ v.slflParent = parent
+ v.flField = current
+ v.cf = cf
+ v.ct = ct
+
+ if !hasValue(v) {
+ return
+ }
+
+ ct = ct.next
+ continue
+
+ case typeEndKeys:
+ return
+
+ case typeDive:
+
+ ct = ct.next
+
+ // traverse slice or map here
+ // or panic ;)
+ switch kind {
+ case reflect.Slice, reflect.Array:
+
+ var i64 int64
+ reusableCF := &cField{}
+
+ for i := 0; i < current.Len(); i++ {
+
+ i64 = int64(i)
+
+ v.misc = append(v.misc[0:0], cf.name...)
+ v.misc = append(v.misc, '[')
+ v.misc = strconv.AppendInt(v.misc, i64, 10)
+ v.misc = append(v.misc, ']')
+
+ reusableCF.name = string(v.misc)
+
+ if cf.namesEqual {
+ reusableCF.altName = reusableCF.name
+ } else {
+
+ v.misc = append(v.misc[0:0], cf.altName...)
+ v.misc = append(v.misc, '[')
+ v.misc = strconv.AppendInt(v.misc, i64, 10)
+ v.misc = append(v.misc, ']')
+
+ reusableCF.altName = string(v.misc)
+ }
+ v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
+ }
+
+ case reflect.Map:
+
+ var pv string
+ reusableCF := &cField{}
+
+ for _, key := range current.MapKeys() {
+
+ pv = fmt.Sprintf("%v", key.Interface())
+
+ v.misc = append(v.misc[0:0], cf.name...)
+ v.misc = append(v.misc, '[')
+ v.misc = append(v.misc, pv...)
+ v.misc = append(v.misc, ']')
+
+ reusableCF.name = string(v.misc)
+
+ if cf.namesEqual {
+ reusableCF.altName = reusableCF.name
+ } else {
+ v.misc = append(v.misc[0:0], cf.altName...)
+ v.misc = append(v.misc, '[')
+ v.misc = append(v.misc, pv...)
+ v.misc = append(v.misc, ']')
+
+ reusableCF.altName = string(v.misc)
+ }
+
+ if ct != nil && ct.typeof == typeKeys && ct.keys != nil {
+ v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys)
+ // can be nil when just keys being validated
+ if ct.next != nil {
+ v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next)
+ }
+ } else {
+ v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
+ }
+ }
+
+ default:
+ // throw error, if not a slice or map then should not have gotten here
+ // bad dive tag
+ panic("dive error! can't dive on a non slice or map")
+ }
+
+ return
+
+ case typeOr:
+
+ v.misc = v.misc[0:0]
+
+ for {
+
+ // set Field Level fields
+ v.slflParent = parent
+ v.flField = current
+ v.cf = cf
+ v.ct = ct
+
+ if ct.fn(ctx, v) {
+
+ // drain rest of the 'or' values, then continue or leave
+ for {
+
+ ct = ct.next
+
+ if ct == nil {
+ return
+ }
+
+ if ct.typeof != typeOr {
+ continue OUTER
+ }
+ }
+ }
+
+ v.misc = append(v.misc, '|')
+ v.misc = append(v.misc, ct.tag...)
+
+ if ct.hasParam {
+ v.misc = append(v.misc, '=')
+ v.misc = append(v.misc, ct.param...)
+ }
+
+ if ct.isBlockEnd || ct.next == nil {
+ // if we get here, no valid 'or' value and no more tags
+ v.str1 = string(append(ns, cf.altName...))
+
+ if v.v.hasTagNameFunc {
+ v.str2 = string(append(structNs, cf.name...))
+ } else {
+ v.str2 = v.str1
+ }
+
+ if ct.hasAlias {
+
+ v.errs = append(v.errs,
+ &fieldError{
+ v: v.v,
+ tag: ct.aliasTag,
+ actualTag: ct.actualAliasTag,
+ ns: v.str1,
+ structNs: v.str2,
+ fieldLen: uint8(len(cf.altName)),
+ structfieldLen: uint8(len(cf.name)),
+ value: current.Interface(),
+ param: ct.param,
+ kind: kind,
+ typ: typ,
+ },
+ )
+
+ } else {
+
+ tVal := string(v.misc)[1:]
+
+ v.errs = append(v.errs,
+ &fieldError{
+ v: v.v,
+ tag: tVal,
+ actualTag: tVal,
+ ns: v.str1,
+ structNs: v.str2,
+ fieldLen: uint8(len(cf.altName)),
+ structfieldLen: uint8(len(cf.name)),
+ value: current.Interface(),
+ param: ct.param,
+ kind: kind,
+ typ: typ,
+ },
+ )
+ }
+
+ return
+ }
+
+ ct = ct.next
+ }
+
+ default:
+
+ // set Field Level fields
+ v.slflParent = parent
+ v.flField = current
+ v.cf = cf
+ v.ct = ct
+
+ if !ct.fn(ctx, v) { // 这是 验证函数的进行校验, 不成功就会记录下来
+
+ v.str1 = string(append(ns, cf.altName...)) //如果不想要 命名空间,就不要拼接 ns
+
+ if v.v.hasTagNameFunc {
+ v.str2 = string(append(structNs, cf.name...))
+ } else {
+ v.str2 = v.str1
+ }
+
+ v.errs = append(v.errs,
+ &fieldError{
+ v: v.v,
+ tag: ct.aliasTag,
+ actualTag: ct.tag,
+ ns: v.str1,
+ structNs: v.str2,
+ fieldLen: uint8(len(cf.altName)),
+ structfieldLen: uint8(len(cf.name)),
+ value: current.Interface(),
+ param: ct.param,
+ kind: kind,
+ typ: typ,
+ },
+ )
+
+ return
+ }
+ ct = ct.next
+ }
+ }
+
+}
diff --git a/go-playground/validator/v10/validator_instance.go b/go-playground/validator/v10/validator_instance.go
new file mode 100644
index 0000000..1855290
--- /dev/null
+++ b/go-playground/validator/v10/validator_instance.go
@@ -0,0 +1,619 @@
+package validator
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+ "sync"
+ "time"
+
+ ut "gin-valid/go-playground/universal-translator"
+)
+
+const (
+ defaultTagName = "validate"
+ utf8HexComma = "0x2C"
+ utf8Pipe = "0x7C"
+ tagSeparator = "," // tag参数 的分离符号,
+ orSeparator = "|" // 或者 的分离符号
+ tagKeySeparator = "="
+ structOnlyTag = "structonly"
+ noStructLevelTag = "nostructlevel"
+ omitempty = "omitempty"
+ isdefault = "isdefault"
+ requiredWithoutAllTag = "required_without_all"
+ requiredWithoutTag = "required_without"
+ requiredWithTag = "required_with"
+ requiredWithAllTag = "required_with_all"
+ requiredIfTag = "required_if"
+ requiredUnlessTag = "required_unless"
+ skipValidationTag = "-"
+ diveTag = "dive"
+ keysTag = "keys"
+ endKeysTag = "endkeys"
+ requiredTag = "required"
+ namespaceSeparator = "."
+ leftBracket = "["
+ rightBracket = "]"
+ restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}"
+ restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
+ restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
+)
+
+var (
+ timeDurationType = reflect.TypeOf(time.Duration(0))
+ timeType = reflect.TypeOf(time.Time{})
+
+ defaultCField = &cField{namesEqual: true}
+)
+
+// FilterFunc is the type used to filter fields using
+// StructFiltered(...) function.
+// returning true results in the field being filtered/skiped from
+// validation
+type FilterFunc func(ns []byte) bool
+
+// CustomTypeFunc allows for overriding or adding custom field type handler functions
+// field = field value of the type to return a value to be validated
+// example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
+type CustomTypeFunc func(field reflect.Value) interface{}
+
+// TagNameFunc allows for adding of a custom tag name parser
+type TagNameFunc func(field reflect.StructField) string
+
+type internalValidationFuncWrapper struct {
+ fn FuncCtx
+ runValidatinOnNil bool
+}
+
+// Validate contains the validator settings and cache
+type Validate struct {
+ tagName string
+ pool *sync.Pool
+ hasCustomFuncs bool
+ hasTagNameFunc bool
+ tagNameFunc TagNameFunc
+ structLevelFuncs map[reflect.Type]StructLevelFuncCtx
+ customFuncs map[reflect.Type]CustomTypeFunc
+ aliases map[string]string
+ validations map[string]internalValidationFuncWrapper // 每个 tag如 gte 都会对应一个func处理, 如果这个tag没有函数处理会报错
+ transTagFunc map[ut.Translator]map[string]TranslationFunc // map[]map[]TranslationFunc
+ tagCache *tagCache
+ structCache *structCache
+}
+
+// New returns a new instance of 'validate' with sane defaults.
+func New() *Validate {
+
+ tc := new(tagCache)
+ tc.m.Store(make(map[string]*cTag))
+
+ sc := new(structCache)
+ sc.m.Store(make(map[reflect.Type]*cStruct))
+
+ v := &Validate{
+ tagName: defaultTagName,
+ aliases: make(map[string]string, len(bakedInAliases)),
+ validations: make(map[string]internalValidationFuncWrapper, len(bakedInValidators)),
+ tagCache: tc,
+ structCache: sc,
+ }
+
+ // must copy alias validators for separate validations to be used in each validator instance
+ for k, val := range bakedInAliases {
+ v.RegisterAlias(k, val)
+ }
+
+ // must copy validators for separate validations to be used in each instance
+ for k, val := range bakedInValidators {
+
+ switch k {
+ // these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
+ case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag:
+ _ = v.registerValidation(k, wrapFunc(val), true, true)
+ default:
+ // no need to error check here, baked in will always be valid
+ _ = v.registerValidation(k, wrapFunc(val), true, false)
+ }
+ }
+
+ v.pool = &sync.Pool{
+ New: func() interface{} {
+ return &validate{
+ v: v,
+ ns: make([]byte, 0, 64),
+ actualNs: make([]byte, 0, 64),
+ misc: make([]byte, 32),
+ }
+ },
+ }
+
+ return v
+}
+
+// SetTagName allows for changing of the default tag name of 'validate'
+func (v *Validate) SetTagName(name string) {
+ v.tagName = name
+}
+
+// RegisterTagNameFunc registers a function to get alternate names for StructFields.
+//
+// eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names:
+//
+// validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+// name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+// if name == "-" {
+// return ""
+// }
+// return name
+// })
+func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) {
+ v.tagNameFunc = fn
+ v.hasTagNameFunc = true
+}
+
+// RegisterValidation adds a validation with the given tag
+//
+// NOTES:
+// - if the key already exists, the previous validation function will be replaced.
+// - this method is not thread-safe it is intended that these all be registered prior to any validation
+func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error {
+ return v.RegisterValidationCtx(tag, wrapFunc(fn), callValidationEvenIfNull...)
+}
+
+// RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation
+// allowing context.Context validation support.
+func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationEvenIfNull ...bool) error {
+ var nilCheckable bool
+ if len(callValidationEvenIfNull) > 0 {
+ nilCheckable = callValidationEvenIfNull[0]
+ }
+ return v.registerValidation(tag, fn, false, nilCheckable)
+}
+
+func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
+ if len(tag) == 0 {
+ return errors.New("Function Key cannot be empty")
+ }
+
+ if fn == nil {
+ return errors.New("Function cannot be empty")
+ }
+
+ _, ok := restrictedTags[tag]
+ if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
+ panic(fmt.Sprintf(restrictedTagErr, tag))
+ }
+ v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidatinOnNil: nilCheckable}
+ return nil
+}
+
+// RegisterAlias registers a mapping of a single validation tag that
+// defines a common or complex set of validation(s) to simplify adding validation
+// to structs.
+//
+// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
+func (v *Validate) RegisterAlias(alias, tags string) {
+
+ _, ok := restrictedTags[alias]
+
+ if ok || strings.ContainsAny(alias, restrictedTagChars) {
+ panic(fmt.Sprintf(restrictedAliasErr, alias))
+ }
+
+ v.aliases[alias] = tags
+}
+
+// RegisterStructValidation registers a StructLevelFunc against a number of types.
+//
+// NOTE:
+// - this method is not thread-safe it is intended that these all be registered prior to any validation
+func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) {
+ v.RegisterStructValidationCtx(wrapStructLevelFunc(fn), types...)
+}
+
+// RegisterStructValidationCtx registers a StructLevelFuncCtx against a number of types and allows passing
+// of contextual validation information via context.Context.
+//
+// NOTE:
+// - this method is not thread-safe it is intended that these all be registered prior to any validation
+func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) {
+
+ if v.structLevelFuncs == nil {
+ v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx)
+ }
+
+ for _, t := range types {
+ tv := reflect.ValueOf(t)
+ if tv.Kind() == reflect.Ptr {
+ t = reflect.Indirect(tv).Interface()
+ }
+
+ v.structLevelFuncs[reflect.TypeOf(t)] = fn
+ }
+}
+
+// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
+//
+// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
+func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
+
+ if v.customFuncs == nil {
+ v.customFuncs = make(map[reflect.Type]CustomTypeFunc)
+ }
+
+ for _, t := range types {
+ v.customFuncs[reflect.TypeOf(t)] = fn
+ }
+
+ v.hasCustomFuncs = true
+}
+
+// RegisterTranslation registers translations against the provided tag.
+func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
+
+ if v.transTagFunc == nil {
+ v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
+ }
+
+ if err = registerFn(trans); err != nil {
+ return
+ }
+
+ m, ok := v.transTagFunc[trans]
+ if !ok {
+ m = make(map[string]TranslationFunc)
+ v.transTagFunc[trans] = m
+ }
+
+ m[tag] = translationFn
+
+ return
+}
+
+// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified.
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+func (v *Validate) Struct(s interface{}) error {
+ return v.StructCtx(context.Background(), s)
+}
+
+// StructCtx validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified
+// and also allows passing of context.Context for contextual validation information.
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
+
+ val := reflect.ValueOf(s)
+ top := val
+
+ if val.Kind() == reflect.Ptr && !val.IsNil() {
+ val = val.Elem()
+ }
+
+ if val.Kind() != reflect.Struct || val.Type() == timeType {
+ return &InvalidValidationError{Type: reflect.TypeOf(s)}
+ }
+
+ // good to validate
+ vd := v.pool.Get().(*validate)
+ vd.top = top
+ vd.isPartial = false
+ // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
+
+ vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
+
+ if len(vd.errs) > 0 {
+ err = vd.errs
+ vd.errs = nil
+ }
+
+ v.pool.Put(vd)
+
+ return
+}
+
+// StructFiltered validates a structs exposed fields, that pass the FilterFunc check and automatically validates
+// nested structs, unless otherwise specified.
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) error {
+ return v.StructFilteredCtx(context.Background(), s, fn)
+}
+
+// StructFilteredCtx validates a structs exposed fields, that pass the FilterFunc check and automatically validates
+// nested structs, unless otherwise specified and also allows passing of contextual validation information via
+// context.Context
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn FilterFunc) (err error) {
+ val := reflect.ValueOf(s)
+ top := val
+
+ if val.Kind() == reflect.Ptr && !val.IsNil() {
+ val = val.Elem()
+ }
+
+ if val.Kind() != reflect.Struct || val.Type() == timeType {
+ return &InvalidValidationError{Type: reflect.TypeOf(s)}
+ }
+
+ // good to validate
+ vd := v.pool.Get().(*validate)
+ vd.top = top
+ vd.isPartial = true
+ vd.ffn = fn
+ // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
+
+ vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
+
+ if len(vd.errs) > 0 {
+ err = vd.errs
+ vd.errs = nil
+ }
+
+ v.pool.Put(vd)
+
+ return
+}
+
+// StructPartial validates the fields passed in only, ignoring all others.
+// Fields may be provided in a namespaced fashion relative to the struct provided
+// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+func (v *Validate) StructPartial(s interface{}, fields ...string) error {
+ return v.StructPartialCtx(context.Background(), s, fields...)
+}
+
+// StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual
+// validation validation information via context.Context
+// Fields may be provided in a namespaced fashion relative to the struct provided
+// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields ...string) (err error) {
+ val := reflect.ValueOf(s)
+ top := val
+
+ if val.Kind() == reflect.Ptr && !val.IsNil() {
+ val = val.Elem()
+ }
+
+ if val.Kind() != reflect.Struct || val.Type() == timeType {
+ return &InvalidValidationError{Type: reflect.TypeOf(s)}
+ }
+
+ // good to validate
+ vd := v.pool.Get().(*validate)
+ vd.top = top
+ vd.isPartial = true
+ vd.ffn = nil
+ vd.hasExcludes = false
+ vd.includeExclude = make(map[string]struct{})
+
+ typ := val.Type()
+ name := typ.Name()
+
+ for _, k := range fields {
+
+ flds := strings.Split(k, namespaceSeparator)
+ if len(flds) > 0 {
+
+ vd.misc = append(vd.misc[0:0], name...)
+ vd.misc = append(vd.misc, '.')
+
+ for _, s := range flds {
+
+ idx := strings.Index(s, leftBracket)
+
+ if idx != -1 {
+ for idx != -1 {
+ vd.misc = append(vd.misc, s[:idx]...)
+ vd.includeExclude[string(vd.misc)] = struct{}{}
+
+ idx2 := strings.Index(s, rightBracket)
+ idx2++
+ vd.misc = append(vd.misc, s[idx:idx2]...)
+ vd.includeExclude[string(vd.misc)] = struct{}{}
+ s = s[idx2:]
+ idx = strings.Index(s, leftBracket)
+ }
+ } else {
+
+ vd.misc = append(vd.misc, s...)
+ vd.includeExclude[string(vd.misc)] = struct{}{}
+ }
+
+ vd.misc = append(vd.misc, '.')
+ }
+ }
+ }
+
+ vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
+
+ if len(vd.errs) > 0 {
+ err = vd.errs
+ vd.errs = nil
+ }
+
+ v.pool.Put(vd)
+
+ return
+}
+
+// StructExcept validates all fields except the ones passed in.
+// Fields may be provided in a namespaced fashion relative to the struct provided
+// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+func (v *Validate) StructExcept(s interface{}, fields ...string) error {
+ return v.StructExceptCtx(context.Background(), s, fields...)
+}
+
+// StructExceptCtx validates all fields except the ones passed in and allows passing of contextual
+// validation validation information via context.Context
+// Fields may be provided in a namespaced fashion relative to the struct provided
+// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ...string) (err error) {
+ val := reflect.ValueOf(s)
+ top := val
+
+ if val.Kind() == reflect.Ptr && !val.IsNil() {
+ val = val.Elem()
+ }
+
+ if val.Kind() != reflect.Struct || val.Type() == timeType {
+ return &InvalidValidationError{Type: reflect.TypeOf(s)}
+ }
+
+ // good to validate
+ vd := v.pool.Get().(*validate)
+ vd.top = top
+ vd.isPartial = true
+ vd.ffn = nil
+ vd.hasExcludes = true
+ vd.includeExclude = make(map[string]struct{})
+
+ typ := val.Type()
+ name := typ.Name()
+
+ for _, key := range fields {
+
+ vd.misc = vd.misc[0:0]
+
+ if len(name) > 0 {
+ vd.misc = append(vd.misc, name...)
+ vd.misc = append(vd.misc, '.')
+ }
+
+ vd.misc = append(vd.misc, key...)
+ vd.includeExclude[string(vd.misc)] = struct{}{}
+ }
+
+ vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
+
+ if len(vd.errs) > 0 {
+ err = vd.errs
+ vd.errs = nil
+ }
+
+ v.pool.Put(vd)
+
+ return
+}
+
+// Var validates a single variable using tag style validation.
+// eg.
+// var i int
+// validate.Var(i, "gt=1,lt=10")
+//
+// WARNING: a struct can be passed for validation eg. time.Time is a struct or
+// if you have a custom type and have registered a custom type handler, so must
+// allow it; however unforeseen validations will occur if trying to validate a
+// struct that is meant to be passed to 'validate.Struct'
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+// validate Array, Slice and maps fields which may contain more than one error
+func (v *Validate) Var(field interface{}, tag string) error {
+ return v.VarCtx(context.Background(), field, tag)
+}
+
+// VarCtx validates a single variable using tag style validation and allows passing of contextual
+// validation validation information via context.Context.
+// eg.
+// var i int
+// validate.Var(i, "gt=1,lt=10")
+//
+// WARNING: a struct can be passed for validation eg. time.Time is a struct or
+// if you have a custom type and have registered a custom type handler, so must
+// allow it; however unforeseen validations will occur if trying to validate a
+// struct that is meant to be passed to 'validate.Struct'
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+// validate Array, Slice and maps fields which may contain more than one error
+func (v *Validate) VarCtx(ctx context.Context, field interface{}, tag string) (err error) {
+ if len(tag) == 0 || tag == skipValidationTag {
+ return nil
+ }
+
+ ctag := v.fetchCacheTag(tag)
+ val := reflect.ValueOf(field)
+ vd := v.pool.Get().(*validate)
+ vd.top = val
+ vd.isPartial = false
+ vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
+
+ if len(vd.errs) > 0 {
+ err = vd.errs
+ vd.errs = nil
+ }
+ v.pool.Put(vd)
+ return
+}
+
+// VarWithValue validates a single variable, against another variable/field's value using tag style validation
+// eg.
+// s1 := "abcd"
+// s2 := "abcd"
+// validate.VarWithValue(s1, s2, "eqcsfield") // returns true
+//
+// WARNING: a struct can be passed for validation eg. time.Time is a struct or
+// if you have a custom type and have registered a custom type handler, so must
+// allow it; however unforeseen validations will occur if trying to validate a
+// struct that is meant to be passed to 'validate.Struct'
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+// validate Array, Slice and maps fields which may contain more than one error
+func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string) error {
+ return v.VarWithValueCtx(context.Background(), field, other, tag)
+}
+
+// VarWithValueCtx validates a single variable, against another variable/field's value using tag style validation and
+// allows passing of contextual validation validation information via context.Context.
+// eg.
+// s1 := "abcd"
+// s2 := "abcd"
+// validate.VarWithValue(s1, s2, "eqcsfield") // returns true
+//
+// WARNING: a struct can be passed for validation eg. time.Time is a struct or
+// if you have a custom type and have registered a custom type handler, so must
+// allow it; however unforeseen validations will occur if trying to validate a
+// struct that is meant to be passed to 'validate.Struct'
+//
+// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
+// validate Array, Slice and maps fields which may contain more than one error
+func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other interface{}, tag string) (err error) {
+ if len(tag) == 0 || tag == skipValidationTag {
+ return nil
+ }
+ ctag := v.fetchCacheTag(tag)
+ otherVal := reflect.ValueOf(other)
+ vd := v.pool.Get().(*validate)
+ vd.top = otherVal
+ vd.isPartial = false
+ vd.traverseField(ctx, otherVal, reflect.ValueOf(field), vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
+
+ if len(vd.errs) > 0 {
+ err = vd.errs
+ vd.errs = nil
+ }
+ v.pool.Put(vd)
+ return
+}
diff --git a/go-playground/validator/v10/validator_test.go b/go-playground/validator/v10/validator_test.go
new file mode 100644
index 0000000..6b1f392
--- /dev/null
+++ b/go-playground/validator/v10/validator_test.go
@@ -0,0 +1,11035 @@
+package validator
+
+import (
+ "bytes"
+ "context"
+ "database/sql"
+ "database/sql/driver"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "path/filepath"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+
+ "gin-valid/go-playground/locales/en"
+ "gin-valid/go-playground/locales/fr"
+ "gin-valid/go-playground/locales/nl"
+ ut "gin-valid/go-playground/universal-translator"
+ . "github.com/go-playground/assert/v2"
+)
+
+// NOTES:
+// - Run "go test" to run tests
+// - Run "gocov test | gocov report" to report on test converage by file
+// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
+//
+// or
+//
+// -- may be a good idea to change to output path to somewherelike /tmp
+// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
+//
+//
+// go test -cpuprofile cpu.out
+// ./validator.test -test.bench=. -test.cpuprofile=cpu.prof
+// go tool pprof validator.test cpu.prof
+//
+//
+// go test -memprofile mem.out
+
+type I interface {
+ Foo() string
+}
+
+type Impl struct {
+ F string `validate:"len=3"`
+}
+
+func (i *Impl) Foo() string {
+ return i.F
+}
+
+type SubTest struct {
+ Test string `validate:"required"`
+}
+
+type TestInterface struct {
+ Iface I
+}
+
+type TestString struct {
+ BlankTag string `validate:""`
+ Required string `validate:"required"`
+ Len string `validate:"len=10"`
+ Min string `validate:"min=1"`
+ Max string `validate:"max=10"`
+ MinMax string `validate:"min=1,max=10"`
+ Lt string `validate:"lt=10"`
+ Lte string `validate:"lte=10"`
+ Gt string `validate:"gt=10"`
+ Gte string `validate:"gte=10"`
+ OmitEmpty string `validate:"omitempty,min=1,max=10"`
+ Sub *SubTest
+ SubIgnore *SubTest `validate:"-"`
+ Anonymous struct {
+ A string `validate:"required"`
+ }
+ Iface I
+}
+
+type TestUint64 struct {
+ Required uint64 `validate:"required"`
+ Len uint64 `validate:"len=10"`
+ Min uint64 `validate:"min=1"`
+ Max uint64 `validate:"max=10"`
+ MinMax uint64 `validate:"min=1,max=10"`
+ OmitEmpty uint64 `validate:"omitempty,min=1,max=10"`
+}
+
+type TestFloat64 struct {
+ Required float64 `validate:"required"`
+ Len float64 `validate:"len=10"`
+ Min float64 `validate:"min=1"`
+ Max float64 `validate:"max=10"`
+ MinMax float64 `validate:"min=1,max=10"`
+ Lte float64 `validate:"lte=10"`
+ OmitEmpty float64 `validate:"omitempty,min=1,max=10"`
+}
+
+type TestSlice struct {
+ Required []int `validate:"required"`
+ Len []int `validate:"len=10"`
+ Min []int `validate:"min=1"`
+ Max []int `validate:"max=10"`
+ MinMax []int `validate:"min=1,max=10"`
+ OmitEmpty []int `validate:"omitempty,min=1,max=10"`
+}
+
+func AssertError(t *testing.T, err error, nsKey, structNsKey, field, structField, expectedTag string) {
+
+ errs := err.(ValidationErrors)
+
+ found := false
+ var fe FieldError
+
+ for i := 0; i < len(errs); i++ {
+ if errs[i].Namespace() == nsKey && errs[i].StructNamespace() == structNsKey {
+ found = true
+ fe = errs[i]
+ break
+ }
+ }
+
+ EqualSkip(t, 2, found, true)
+ NotEqualSkip(t, 2, fe, nil)
+ EqualSkip(t, 2, fe.Field(), field)
+ EqualSkip(t, 2, fe.StructField(), structField)
+ EqualSkip(t, 2, fe.Tag(), expectedTag)
+}
+
+func AssertDeepError(t *testing.T, err error, nsKey, structNsKey, field, structField, expectedTag, actualTag string) {
+ errs := err.(ValidationErrors)
+
+ found := false
+ var fe FieldError
+
+ for i := 0; i < len(errs); i++ {
+ if errs[i].Namespace() == nsKey && errs[i].StructNamespace() == structNsKey && errs[i].Tag() == expectedTag && errs[i].ActualTag() == actualTag {
+ found = true
+ fe = errs[i]
+ break
+ }
+ }
+
+ EqualSkip(t, 2, found, true)
+ NotEqualSkip(t, 2, fe, nil)
+ EqualSkip(t, 2, fe.Field(), field)
+ EqualSkip(t, 2, fe.StructField(), structField)
+}
+
+func getError(err error, nsKey, structNsKey string) FieldError {
+
+ errs := err.(ValidationErrors)
+
+ var fe FieldError
+
+ for i := 0; i < len(errs); i++ {
+ if errs[i].Namespace() == nsKey && errs[i].StructNamespace() == structNsKey {
+ fe = errs[i]
+ break
+ }
+ }
+
+ return fe
+}
+
+type valuer struct {
+ Name string
+}
+
+func (v valuer) Value() (driver.Value, error) {
+
+ if v.Name == "errorme" {
+ panic("SQL Driver Valuer error: some kind of error")
+ // return nil, errors.New("some kind of error")
+ }
+
+ if len(v.Name) == 0 {
+ return nil, nil
+ }
+
+ return v.Name, nil
+}
+
+type MadeUpCustomType struct {
+ FirstName string
+ LastName string
+}
+
+func ValidateCustomType(field reflect.Value) interface{} {
+
+ if cust, ok := field.Interface().(MadeUpCustomType); ok {
+
+ if len(cust.FirstName) == 0 || len(cust.LastName) == 0 {
+ return ""
+ }
+
+ return cust.FirstName + " " + cust.LastName
+ }
+
+ return ""
+}
+
+func OverrideIntTypeForSomeReason(field reflect.Value) interface{} {
+
+ if i, ok := field.Interface().(int); ok {
+ if i == 1 {
+ return "1"
+ }
+
+ if i == 2 {
+ return "12"
+ }
+ }
+
+ return ""
+}
+
+type CustomMadeUpStruct struct {
+ MadeUp MadeUpCustomType `validate:"required"`
+ OverriddenInt int `validate:"gt=1"`
+}
+
+func ValidateValuerType(field reflect.Value) interface{} {
+
+ if valuer, ok := field.Interface().(driver.Valuer); ok {
+
+ val, err := valuer.Value()
+ if err != nil {
+ // handle the error how you want
+ return nil
+ }
+
+ return val
+ }
+
+ return nil
+}
+
+type TestPartial struct {
+ NoTag string
+ BlankTag string `validate:""`
+ Required string `validate:"required"`
+ SubSlice []*SubTest `validate:"required,dive"`
+ Sub *SubTest
+ SubIgnore *SubTest `validate:"-"`
+ Anonymous struct {
+ A string `validate:"required"`
+ ASubSlice []*SubTest `validate:"required,dive"`
+
+ SubAnonStruct []struct {
+ Test string `validate:"required"`
+ OtherTest string `validate:"required"`
+ } `validate:"required,dive"`
+ }
+}
+
+type TestStruct struct {
+ String string `validate:"required" json:"StringVal"`
+}
+
+func StructValidationTestStructSuccess(sl StructLevel) {
+
+ st := sl.Current().Interface().(TestStruct)
+
+ if st.String != "good value" {
+ sl.ReportError(st.String, "StringVal", "String", "badvalueteststruct", "good value")
+ }
+}
+
+func StructValidationTestStruct(sl StructLevel) {
+
+ st := sl.Current().Interface().(TestStruct)
+
+ if st.String != "bad value" {
+ sl.ReportError(st.String, "StringVal", "String", "badvalueteststruct", "bad value")
+ }
+}
+
+func StructValidationNoTestStructCustomName(sl StructLevel) {
+
+ st := sl.Current().Interface().(TestStruct)
+
+ if st.String != "bad value" {
+ sl.ReportError(st.String, "String", "", "badvalueteststruct", "bad value")
+ }
+}
+
+func StructValidationTestStructInvalid(sl StructLevel) {
+
+ st := sl.Current().Interface().(TestStruct)
+
+ if st.String != "bad value" {
+ sl.ReportError(nil, "StringVal", "String", "badvalueteststruct", "bad value")
+ }
+}
+
+func StructValidationTestStructReturnValidationErrors(sl StructLevel) {
+
+ s := sl.Current().Interface().(TestStructReturnValidationErrors)
+
+ errs := sl.Validator().Struct(s.Inner1.Inner2)
+ if errs == nil {
+ return
+ }
+
+ sl.ReportValidationErrors("Inner1.", "Inner1.", errs.(ValidationErrors))
+}
+
+func StructValidationTestStructReturnValidationErrors2(sl StructLevel) {
+
+ s := sl.Current().Interface().(TestStructReturnValidationErrors)
+
+ errs := sl.Validator().Struct(s.Inner1.Inner2)
+ if errs == nil {
+ return
+ }
+
+ sl.ReportValidationErrors("Inner1JSON.", "Inner1.", errs.(ValidationErrors))
+}
+
+type TestStructReturnValidationErrorsInner2 struct {
+ String string `validate:"required" json:"JSONString"`
+}
+
+type TestStructReturnValidationErrorsInner1 struct {
+ Inner2 *TestStructReturnValidationErrorsInner2
+}
+
+type TestStructReturnValidationErrors struct {
+ Inner1 *TestStructReturnValidationErrorsInner1 `json:"Inner1JSON"`
+}
+
+type StructLevelInvalidErr struct {
+ Value string
+}
+
+func StructLevelInvalidError(sl StructLevel) {
+
+ top := sl.Top().Interface().(StructLevelInvalidErr)
+ s := sl.Current().Interface().(StructLevelInvalidErr)
+
+ if top.Value == s.Value {
+ sl.ReportError(nil, "Value", "Value", "required", "")
+ }
+}
+
+func stringPtr(v string) *string {
+ return &v
+}
+
+func intPtr(v int) *int {
+ return &v
+}
+
+func float64Ptr(v float64) *float64 {
+ return &v
+}
+
+func TestStructLevelInvalidError(t *testing.T) {
+
+ validate := New()
+ validate.RegisterStructValidation(StructLevelInvalidError, StructLevelInvalidErr{})
+
+ var test StructLevelInvalidErr
+
+ err := validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(ValidationErrors)
+ Equal(t, ok, true)
+
+ fe := errs[0]
+ Equal(t, fe.Field(), "Value")
+ Equal(t, fe.StructField(), "Value")
+ Equal(t, fe.Namespace(), "StructLevelInvalidErr.Value")
+ Equal(t, fe.StructNamespace(), "StructLevelInvalidErr.Value")
+ Equal(t, fe.Tag(), "required")
+ Equal(t, fe.ActualTag(), "required")
+ Equal(t, fe.Kind(), reflect.Invalid)
+ Equal(t, fe.Type(), reflect.TypeOf(nil))
+}
+
+func TestNameNamespace(t *testing.T) {
+
+ type Inner2Namespace struct {
+ String []string `validate:"dive,required" json:"JSONString"`
+ }
+
+ type Inner1Namespace struct {
+ Inner2 *Inner2Namespace `json:"Inner2JSON"`
+ }
+
+ type Namespace struct {
+ Inner1 *Inner1Namespace `json:"Inner1JSON"`
+ }
+
+ validate := New()
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+
+ i2 := &Inner2Namespace{String: []string{"ok", "ok", "ok"}}
+ i1 := &Inner1Namespace{Inner2: i2}
+ ns := &Namespace{Inner1: i1}
+
+ errs := validate.Struct(ns)
+ Equal(t, errs, nil)
+
+ i2.String[1] = ""
+
+ errs = validate.Struct(ns)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 1)
+ AssertError(t, errs, "Namespace.Inner1JSON.Inner2JSON.JSONString[1]", "Namespace.Inner1.Inner2.String[1]", "JSONString[1]", "String[1]", "required")
+
+ fe := getError(ve, "Namespace.Inner1JSON.Inner2JSON.JSONString[1]", "Namespace.Inner1.Inner2.String[1]")
+ NotEqual(t, fe, nil)
+ Equal(t, fe.Field(), "JSONString[1]")
+ Equal(t, fe.StructField(), "String[1]")
+ Equal(t, fe.Namespace(), "Namespace.Inner1JSON.Inner2JSON.JSONString[1]")
+ Equal(t, fe.StructNamespace(), "Namespace.Inner1.Inner2.String[1]")
+}
+
+func TestAnonymous(t *testing.T) {
+
+ validate := New()
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+
+ type Test struct {
+ Anonymous struct {
+ A string `validate:"required" json:"EH"`
+ }
+ AnonymousB struct {
+ B string `validate:"required" json:"BEE"`
+ }
+ anonymousC struct {
+ c string `validate:"required"`
+ }
+ }
+
+ tst := &Test{
+ Anonymous: struct {
+ A string `validate:"required" json:"EH"`
+ }{
+ A: "1",
+ },
+ AnonymousB: struct {
+ B string `validate:"required" json:"BEE"`
+ }{
+ B: "",
+ },
+ anonymousC: struct {
+ c string `validate:"required"`
+ }{
+ c: "",
+ },
+ }
+
+ Equal(t, tst.anonymousC.c, "")
+
+ err := validate.Struct(tst)
+ NotEqual(t, err, nil)
+
+ errs := err.(ValidationErrors)
+
+ Equal(t, len(errs), 1)
+ AssertError(t, errs, "Test.AnonymousB.BEE", "Test.AnonymousB.B", "BEE", "B", "required")
+
+ fe := getError(errs, "Test.AnonymousB.BEE", "Test.AnonymousB.B")
+ NotEqual(t, fe, nil)
+ Equal(t, fe.Field(), "BEE")
+ Equal(t, fe.StructField(), "B")
+
+ s := struct {
+ c string `validate:"required"`
+ }{
+ c: "",
+ }
+
+ err = validate.Struct(s)
+ Equal(t, err, nil)
+}
+
+func TestAnonymousSameStructDifferentTags(t *testing.T) {
+
+ validate := New()
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+
+ type Test struct {
+ A interface{}
+ }
+
+ tst := &Test{
+ A: struct {
+ A string `validate:"required"`
+ }{
+ A: "",
+ },
+ }
+
+ err := validate.Struct(tst)
+ NotEqual(t, err, nil)
+
+ errs := err.(ValidationErrors)
+
+ Equal(t, len(errs), 1)
+ AssertError(t, errs, "Test.A.A", "Test.A.A", "A", "A", "required")
+
+ tst = &Test{
+ A: struct {
+ A string `validate:"omitempty,required"`
+ }{
+ A: "",
+ },
+ }
+
+ err = validate.Struct(tst)
+ Equal(t, err, nil)
+}
+
+func TestStructLevelReturnValidationErrors(t *testing.T) {
+
+ validate := New()
+ validate.RegisterStructValidation(StructValidationTestStructReturnValidationErrors, TestStructReturnValidationErrors{})
+
+ inner2 := &TestStructReturnValidationErrorsInner2{
+ String: "I'm HERE",
+ }
+
+ inner1 := &TestStructReturnValidationErrorsInner1{
+ Inner2: inner2,
+ }
+
+ val := &TestStructReturnValidationErrors{
+ Inner1: inner1,
+ }
+
+ errs := validate.Struct(val)
+ Equal(t, errs, nil)
+
+ inner2.String = ""
+
+ errs = validate.Struct(val)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 2)
+ AssertError(t, errs, "TestStructReturnValidationErrors.Inner1.Inner2.String", "TestStructReturnValidationErrors.Inner1.Inner2.String", "String", "String", "required")
+ // this is an extra error reported from struct validation
+ AssertError(t, errs, "TestStructReturnValidationErrors.Inner1.TestStructReturnValidationErrorsInner2.String", "TestStructReturnValidationErrors.Inner1.TestStructReturnValidationErrorsInner2.String", "String", "String", "required")
+}
+
+func TestStructLevelReturnValidationErrorsWithJSON(t *testing.T) {
+
+ validate := New()
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+ validate.RegisterStructValidation(StructValidationTestStructReturnValidationErrors2, TestStructReturnValidationErrors{})
+
+ inner2 := &TestStructReturnValidationErrorsInner2{
+ String: "I'm HERE",
+ }
+
+ inner1 := &TestStructReturnValidationErrorsInner1{
+ Inner2: inner2,
+ }
+
+ val := &TestStructReturnValidationErrors{
+ Inner1: inner1,
+ }
+
+ errs := validate.Struct(val)
+ Equal(t, errs, nil)
+
+ inner2.String = ""
+
+ errs = validate.Struct(val)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 2)
+ AssertError(t, errs, "TestStructReturnValidationErrors.Inner1JSON.Inner2.JSONString", "TestStructReturnValidationErrors.Inner1.Inner2.String", "JSONString", "String", "required")
+ // this is an extra error reported from struct validation, it's a badly formatted one, but on purpose
+ AssertError(t, errs, "TestStructReturnValidationErrors.Inner1JSON.TestStructReturnValidationErrorsInner2.JSONString", "TestStructReturnValidationErrors.Inner1.TestStructReturnValidationErrorsInner2.String", "JSONString", "String", "required")
+
+ fe := getError(errs, "TestStructReturnValidationErrors.Inner1JSON.Inner2.JSONString", "TestStructReturnValidationErrors.Inner1.Inner2.String")
+ NotEqual(t, fe, nil)
+
+ // check for proper JSON namespace
+ Equal(t, fe.Field(), "JSONString")
+ Equal(t, fe.StructField(), "String")
+ Equal(t, fe.Namespace(), "TestStructReturnValidationErrors.Inner1JSON.Inner2.JSONString")
+ Equal(t, fe.StructNamespace(), "TestStructReturnValidationErrors.Inner1.Inner2.String")
+
+ fe = getError(errs, "TestStructReturnValidationErrors.Inner1JSON.TestStructReturnValidationErrorsInner2.JSONString", "TestStructReturnValidationErrors.Inner1.TestStructReturnValidationErrorsInner2.String")
+ NotEqual(t, fe, nil)
+
+ // check for proper JSON namespace
+ Equal(t, fe.Field(), "JSONString")
+ Equal(t, fe.StructField(), "String")
+ Equal(t, fe.Namespace(), "TestStructReturnValidationErrors.Inner1JSON.TestStructReturnValidationErrorsInner2.JSONString")
+ Equal(t, fe.StructNamespace(), "TestStructReturnValidationErrors.Inner1.TestStructReturnValidationErrorsInner2.String")
+}
+
+func TestStructLevelValidations(t *testing.T) {
+
+ v1 := New()
+ v1.RegisterStructValidation(StructValidationTestStruct, TestStruct{})
+
+ tst := &TestStruct{
+ String: "good value",
+ }
+
+ errs := v1.Struct(tst)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestStruct.StringVal", "TestStruct.String", "StringVal", "String", "badvalueteststruct")
+
+ v2 := New()
+ v2.RegisterStructValidation(StructValidationNoTestStructCustomName, TestStruct{})
+
+ errs = v2.Struct(tst)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestStruct.String", "TestStruct.String", "String", "String", "badvalueteststruct")
+
+ v3 := New()
+ v3.RegisterStructValidation(StructValidationTestStructInvalid, TestStruct{})
+
+ errs = v3.Struct(tst)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestStruct.StringVal", "TestStruct.String", "StringVal", "String", "badvalueteststruct")
+
+ v4 := New()
+ v4.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{})
+
+ errs = v4.Struct(tst)
+ Equal(t, errs, nil)
+}
+
+func TestAliasTags(t *testing.T) {
+
+ validate := New()
+ validate.RegisterAlias("iscoloralias", "hexcolor|rgb|rgba|hsl|hsla")
+
+ s := "rgb(255,255,255)"
+ errs := validate.Var(s, "iscoloralias")
+ Equal(t, errs, nil)
+
+ s = ""
+ errs = validate.Var(s, "omitempty,iscoloralias")
+ Equal(t, errs, nil)
+
+ s = "rgb(255,255,0)"
+ errs = validate.Var(s, "iscoloralias,len=5")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "len")
+
+ type Test struct {
+ Color string `validate:"iscoloralias"`
+ }
+
+ tst := &Test{
+ Color: "#000",
+ }
+
+ errs = validate.Struct(tst)
+ Equal(t, errs, nil)
+
+ tst.Color = "cfvre"
+ errs = validate.Struct(tst)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.Color", "Test.Color", "Color", "Color", "iscoloralias")
+
+ fe := getError(errs, "Test.Color", "Test.Color")
+ NotEqual(t, fe, nil)
+ Equal(t, fe.ActualTag(), "hexcolor|rgb|rgba|hsl|hsla")
+
+ validate.RegisterAlias("req", "required,dive,iscoloralias")
+ arr := []string{"val1", "#fff", "#000"}
+
+ errs = validate.Var(arr, "req")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "[0]", "[0]", "[0]", "[0]", "iscoloralias")
+
+ PanicMatches(t, func() { validate.RegisterAlias("exists!", "gt=5,lt=10") }, "Alias 'exists!' either contains restricted characters or is the same as a restricted tag needed for normal operation")
+}
+
+func TestNilValidator(t *testing.T) {
+
+ type TestStruct struct {
+ Test string `validate:"required"`
+ }
+
+ ts := TestStruct{}
+
+ var val *Validate
+
+ fn := func(fl FieldLevel) bool {
+
+ return fl.Parent().String() == fl.Field().String()
+ }
+
+ PanicMatches(t, func() { val.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{}) }, "runtime error: invalid memory address or nil pointer dereference")
+ PanicMatches(t, func() { _ = val.RegisterValidation("something", fn) }, "runtime error: invalid memory address or nil pointer dereference")
+ PanicMatches(t, func() { _ = val.Var(ts.Test, "required") }, "runtime error: invalid memory address or nil pointer dereference")
+ PanicMatches(t, func() { _ = val.VarWithValue("test", ts.Test, "required") }, "runtime error: invalid memory address or nil pointer dereference")
+ PanicMatches(t, func() { _ = val.Struct(ts) }, "runtime error: invalid memory address or nil pointer dereference")
+ PanicMatches(t, func() { _ = val.StructExcept(ts, "Test") }, "runtime error: invalid memory address or nil pointer dereference")
+ PanicMatches(t, func() { _ = val.StructPartial(ts, "Test") }, "runtime error: invalid memory address or nil pointer dereference")
+}
+
+func TestStructPartial(t *testing.T) {
+ p1 := []string{
+ "NoTag",
+ "Required",
+ }
+
+ p2 := []string{
+ "SubSlice[0].Test",
+ "Sub",
+ "SubIgnore",
+ "Anonymous.A",
+ }
+
+ p3 := []string{
+ "SubTest.Test",
+ }
+
+ p4 := []string{
+ "A",
+ }
+
+ tPartial := &TestPartial{
+ NoTag: "NoTag",
+ Required: "Required",
+
+ SubSlice: []*SubTest{
+ {
+
+ Test: "Required",
+ },
+ {
+
+ Test: "Required",
+ },
+ },
+
+ Sub: &SubTest{
+ Test: "1",
+ },
+ SubIgnore: &SubTest{
+ Test: "",
+ },
+ Anonymous: struct {
+ A string `validate:"required"`
+ ASubSlice []*SubTest `validate:"required,dive"`
+ SubAnonStruct []struct {
+ Test string `validate:"required"`
+ OtherTest string `validate:"required"`
+ } `validate:"required,dive"`
+ }{
+ A: "1",
+ ASubSlice: []*SubTest{
+ {
+ Test: "Required",
+ },
+ {
+ Test: "Required",
+ },
+ },
+
+ SubAnonStruct: []struct {
+ Test string `validate:"required"`
+ OtherTest string `validate:"required"`
+ }{
+ {"Required", "RequiredOther"},
+ {"Required", "RequiredOther"},
+ },
+ },
+ }
+
+ validate := New()
+
+ // the following should all return no errors as everything is valid in
+ // the default state
+ errs := validate.StructPartialCtx(context.Background(), tPartial, p1...)
+ Equal(t, errs, nil)
+
+ errs = validate.StructPartial(tPartial, p2...)
+ Equal(t, errs, nil)
+
+ // this isn't really a robust test, but is ment to illustrate the ANON CASE below
+ errs = validate.StructPartial(tPartial.SubSlice[0], p3...)
+ Equal(t, errs, nil)
+
+ errs = validate.StructExceptCtx(context.Background(), tPartial, p1...)
+ Equal(t, errs, nil)
+
+ errs = validate.StructExcept(tPartial, p2...)
+ Equal(t, errs, nil)
+
+ // mod tParial for required feild and re-test making sure invalid fields are NOT required:
+ tPartial.Required = ""
+
+ errs = validate.StructExcept(tPartial, p1...)
+ Equal(t, errs, nil)
+
+ errs = validate.StructPartial(tPartial, p2...)
+ Equal(t, errs, nil)
+
+ // inversion and retesting Partial to generate failures:
+ errs = validate.StructPartial(tPartial, p1...)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.Required", "TestPartial.Required", "Required", "Required", "required")
+
+ errs = validate.StructExcept(tPartial, p2...)
+ AssertError(t, errs, "TestPartial.Required", "TestPartial.Required", "Required", "Required", "required")
+
+ // reset Required field, and set nested struct
+ tPartial.Required = "Required"
+ tPartial.Anonymous.A = ""
+
+ // will pass as unset feilds is not going to be tested
+ errs = validate.StructPartial(tPartial, p1...)
+ Equal(t, errs, nil)
+
+ errs = validate.StructExcept(tPartial, p2...)
+ Equal(t, errs, nil)
+
+ // ANON CASE the response here is strange, it clearly does what it is being told to
+ errs = validate.StructExcept(tPartial.Anonymous, p4...)
+ Equal(t, errs, nil)
+
+ // will fail as unset feild is tested
+ errs = validate.StructPartial(tPartial, p2...)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.Anonymous.A", "TestPartial.Anonymous.A", "A", "A", "required")
+
+ errs = validate.StructExcept(tPartial, p1...)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.Anonymous.A", "TestPartial.Anonymous.A", "A", "A", "required")
+
+ // reset nested struct and unset struct in slice
+ tPartial.Anonymous.A = "Required"
+ tPartial.SubSlice[0].Test = ""
+
+ // these will pass as unset item is NOT tested
+ errs = validate.StructPartial(tPartial, p1...)
+ Equal(t, errs, nil)
+
+ errs = validate.StructExcept(tPartial, p2...)
+ Equal(t, errs, nil)
+
+ // these will fail as unset item IS tested
+ errs = validate.StructExcept(tPartial, p1...)
+ AssertError(t, errs, "TestPartial.SubSlice[0].Test", "TestPartial.SubSlice[0].Test", "Test", "Test", "required")
+ Equal(t, len(errs.(ValidationErrors)), 1)
+
+ errs = validate.StructPartial(tPartial, p2...)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.SubSlice[0].Test", "TestPartial.SubSlice[0].Test", "Test", "Test", "required")
+ Equal(t, len(errs.(ValidationErrors)), 1)
+
+ // Unset second slice member concurrently to test dive behavior:
+ tPartial.SubSlice[1].Test = ""
+
+ errs = validate.StructPartial(tPartial, p1...)
+ Equal(t, errs, nil)
+
+ // NOTE: When specifying nested items, it is still the users responsibility
+ // to specify the dive tag, the library does not override this.
+ errs = validate.StructExcept(tPartial, p2...)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.SubSlice[1].Test", "TestPartial.SubSlice[1].Test", "Test", "Test", "required")
+
+ errs = validate.StructExcept(tPartial, p1...)
+ Equal(t, len(errs.(ValidationErrors)), 2)
+ AssertError(t, errs, "TestPartial.SubSlice[0].Test", "TestPartial.SubSlice[0].Test", "Test", "Test", "required")
+ AssertError(t, errs, "TestPartial.SubSlice[1].Test", "TestPartial.SubSlice[1].Test", "Test", "Test", "required")
+
+ errs = validate.StructPartial(tPartial, p2...)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "TestPartial.SubSlice[0].Test", "TestPartial.SubSlice[0].Test", "Test", "Test", "required")
+
+ // reset struct in slice, and unset struct in slice in unset posistion
+ tPartial.SubSlice[0].Test = "Required"
+
+ // these will pass as the unset item is NOT tested
+ errs = validate.StructPartial(tPartial, p1...)
+ Equal(t, errs, nil)
+
+ errs = validate.StructPartial(tPartial, p2...)
+ Equal(t, errs, nil)
+
+ // testing for missing item by exception, yes it dives and fails
+ errs = validate.StructExcept(tPartial, p1...)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "TestPartial.SubSlice[1].Test", "TestPartial.SubSlice[1].Test", "Test", "Test", "required")
+
+ errs = validate.StructExcept(tPartial, p2...)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.SubSlice[1].Test", "TestPartial.SubSlice[1].Test", "Test", "Test", "required")
+
+ tPartial.SubSlice[1].Test = "Required"
+
+ tPartial.Anonymous.SubAnonStruct[0].Test = ""
+ // these will pass as the unset item is NOT tested
+ errs = validate.StructPartial(tPartial, p1...)
+ Equal(t, errs, nil)
+
+ errs = validate.StructPartial(tPartial, p2...)
+ Equal(t, errs, nil)
+
+ errs = validate.StructExcept(tPartial, p1...)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.Anonymous.SubAnonStruct[0].Test", "TestPartial.Anonymous.SubAnonStruct[0].Test", "Test", "Test", "required")
+
+ errs = validate.StructExcept(tPartial, p2...)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.Anonymous.SubAnonStruct[0].Test", "TestPartial.Anonymous.SubAnonStruct[0].Test", "Test", "Test", "required")
+
+}
+
+func TestCrossStructLteFieldValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ type Inner struct {
+ CreatedAt *time.Time
+ String string
+ Int int
+ Uint uint
+ Float float64
+ Array []string
+ }
+
+ type Test struct {
+ Inner *Inner
+ CreatedAt *time.Time `validate:"ltecsfield=Inner.CreatedAt"`
+ String string `validate:"ltecsfield=Inner.String"`
+ Int int `validate:"ltecsfield=Inner.Int"`
+ Uint uint `validate:"ltecsfield=Inner.Uint"`
+ Float float64 `validate:"ltecsfield=Inner.Float"`
+ Array []string `validate:"ltecsfield=Inner.Array"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * 5)
+
+ inner := &Inner{
+ CreatedAt: &then,
+ String: "abcd",
+ Int: 13,
+ Uint: 13,
+ Float: 1.13,
+ Array: []string{"val1", "val2"},
+ }
+
+ test := &Test{
+ Inner: inner,
+ CreatedAt: &now,
+ String: "abc",
+ Int: 12,
+ Uint: 12,
+ Float: 1.12,
+ Array: []string{"val1"},
+ }
+
+ errs = validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test.CreatedAt = &then
+ test.String = "abcd"
+ test.Int = 13
+ test.Uint = 13
+ test.Float = 1.13
+ test.Array = []string{"val1", "val2"}
+
+ errs = validate.Struct(test)
+ Equal(t, errs, nil)
+
+ after := now.Add(time.Hour * 10)
+
+ test.CreatedAt = &after
+ test.String = "abce"
+ test.Int = 14
+ test.Uint = 14
+ test.Float = 1.14
+ test.Array = []string{"val1", "val2", "val3"}
+
+ errs = validate.Struct(test)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.CreatedAt", "Test.CreatedAt", "CreatedAt", "CreatedAt", "ltecsfield")
+ AssertError(t, errs, "Test.String", "Test.String", "String", "String", "ltecsfield")
+ AssertError(t, errs, "Test.Int", "Test.Int", "Int", "Int", "ltecsfield")
+ AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "ltecsfield")
+ AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "ltecsfield")
+ AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "ltecsfield")
+
+ errs = validate.VarWithValueCtx(context.Background(), 1, "", "ltecsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltecsfield")
+
+ // this test is for the WARNING about unforeseen validation issues.
+ errs = validate.VarWithValue(test, now, "ltecsfield")
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 6)
+ AssertError(t, errs, "Test.CreatedAt", "Test.CreatedAt", "CreatedAt", "CreatedAt", "ltecsfield")
+ AssertError(t, errs, "Test.String", "Test.String", "String", "String", "ltecsfield")
+ AssertError(t, errs, "Test.Int", "Test.Int", "Int", "Int", "ltecsfield")
+ AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "ltecsfield")
+ AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "ltecsfield")
+ AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "ltecsfield")
+
+ type Other struct {
+ Value string
+ }
+
+ type Test2 struct {
+ Value Other
+ Time time.Time `validate:"ltecsfield=Value"`
+ }
+
+ tst := Test2{
+ Value: Other{Value: "StringVal"},
+ Time: then,
+ }
+
+ errs = validate.Struct(tst)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "ltecsfield")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "ltecsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "ltecsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "ltecsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltecsfield")
+
+ errs = validate.VarWithValue(time.Duration(0), -time.Minute, "omitempty,ltecsfield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct and an inner struct with time.Duration type fields.
+
+ type TimeDurationInner struct {
+ Duration time.Duration
+ }
+ var timeDurationInner *TimeDurationInner
+
+ type TimeDurationTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"ltecsfield=Inner.Duration"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationInner = &TimeDurationInner{time.Hour}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "ltecsfield")
+
+ type TimeDurationOmitemptyTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"omitempty,ltecsfield=Inner.Duration"`
+ }
+ var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+ timeDurationInner = &TimeDurationInner{-time.Minute}
+ timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestCrossStructLtFieldValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ type Inner struct {
+ CreatedAt *time.Time
+ String string
+ Int int
+ Uint uint
+ Float float64
+ Array []string
+ }
+
+ type Test struct {
+ Inner *Inner
+ CreatedAt *time.Time `validate:"ltcsfield=Inner.CreatedAt"`
+ String string `validate:"ltcsfield=Inner.String"`
+ Int int `validate:"ltcsfield=Inner.Int"`
+ Uint uint `validate:"ltcsfield=Inner.Uint"`
+ Float float64 `validate:"ltcsfield=Inner.Float"`
+ Array []string `validate:"ltcsfield=Inner.Array"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * 5)
+
+ inner := &Inner{
+ CreatedAt: &then,
+ String: "abcd",
+ Int: 13,
+ Uint: 13,
+ Float: 1.13,
+ Array: []string{"val1", "val2"},
+ }
+
+ test := &Test{
+ Inner: inner,
+ CreatedAt: &now,
+ String: "abc",
+ Int: 12,
+ Uint: 12,
+ Float: 1.12,
+ Array: []string{"val1"},
+ }
+
+ errs = validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test.CreatedAt = &then
+ test.String = "abcd"
+ test.Int = 13
+ test.Uint = 13
+ test.Float = 1.13
+ test.Array = []string{"val1", "val2"}
+
+ errs = validate.Struct(test)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.CreatedAt", "Test.CreatedAt", "CreatedAt", "CreatedAt", "ltcsfield")
+ AssertError(t, errs, "Test.String", "Test.String", "String", "String", "ltcsfield")
+ AssertError(t, errs, "Test.Int", "Test.Int", "Int", "Int", "ltcsfield")
+ AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "ltcsfield")
+ AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "ltcsfield")
+ AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "ltcsfield")
+
+ errs = validate.VarWithValue(1, "", "ltcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltcsfield")
+
+ // this test is for the WARNING about unforeseen validation issues.
+ errs = validate.VarWithValue(test, now, "ltcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.CreatedAt", "Test.CreatedAt", "CreatedAt", "CreatedAt", "ltcsfield")
+ AssertError(t, errs, "Test.String", "Test.String", "String", "String", "ltcsfield")
+ AssertError(t, errs, "Test.Int", "Test.Int", "Int", "Int", "ltcsfield")
+ AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "ltcsfield")
+ AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "ltcsfield")
+ AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "ltcsfield")
+
+ type Other struct {
+ Value string
+ }
+
+ type Test2 struct {
+ Value Other
+ Time time.Time `validate:"ltcsfield=Value"`
+ }
+
+ tst := Test2{
+ Value: Other{Value: "StringVal"},
+ Time: then,
+ }
+
+ errs = validate.Struct(tst)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "ltcsfield")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "ltcsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "ltcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltcsfield")
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "ltcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltcsfield")
+
+ errs = validate.VarWithValue(time.Duration(0), -time.Minute, "omitempty,ltcsfield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct and an inner struct with time.Duration type fields.
+
+ type TimeDurationInner struct {
+ Duration time.Duration
+ }
+ var timeDurationInner *TimeDurationInner
+
+ type TimeDurationTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"ltcsfield=Inner.Duration"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationInner = &TimeDurationInner{time.Hour}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "ltcsfield")
+
+ timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "ltcsfield")
+
+ type TimeDurationOmitemptyTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"omitempty,ltcsfield=Inner.Duration"`
+ }
+ var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+ timeDurationInner = &TimeDurationInner{-time.Minute}
+ timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestCrossStructGteFieldValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ type Inner struct {
+ CreatedAt *time.Time
+ String string
+ Int int
+ Uint uint
+ Float float64
+ Array []string
+ }
+
+ type Test struct {
+ Inner *Inner
+ CreatedAt *time.Time `validate:"gtecsfield=Inner.CreatedAt"`
+ String string `validate:"gtecsfield=Inner.String"`
+ Int int `validate:"gtecsfield=Inner.Int"`
+ Uint uint `validate:"gtecsfield=Inner.Uint"`
+ Float float64 `validate:"gtecsfield=Inner.Float"`
+ Array []string `validate:"gtecsfield=Inner.Array"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * -5)
+
+ inner := &Inner{
+ CreatedAt: &then,
+ String: "abcd",
+ Int: 13,
+ Uint: 13,
+ Float: 1.13,
+ Array: []string{"val1", "val2"},
+ }
+
+ test := &Test{
+ Inner: inner,
+ CreatedAt: &now,
+ String: "abcde",
+ Int: 14,
+ Uint: 14,
+ Float: 1.14,
+ Array: []string{"val1", "val2", "val3"},
+ }
+
+ errs = validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test.CreatedAt = &then
+ test.String = "abcd"
+ test.Int = 13
+ test.Uint = 13
+ test.Float = 1.13
+ test.Array = []string{"val1", "val2"}
+
+ errs = validate.Struct(test)
+ Equal(t, errs, nil)
+
+ before := now.Add(time.Hour * -10)
+
+ test.CreatedAt = &before
+ test.String = "abc"
+ test.Int = 12
+ test.Uint = 12
+ test.Float = 1.12
+ test.Array = []string{"val1"}
+
+ errs = validate.Struct(test)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.CreatedAt", "Test.CreatedAt", "CreatedAt", "CreatedAt", "gtecsfield")
+ AssertError(t, errs, "Test.String", "Test.String", "String", "String", "gtecsfield")
+ AssertError(t, errs, "Test.Int", "Test.Int", "Int", "Int", "gtecsfield")
+ AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "gtecsfield")
+ AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "gtecsfield")
+ AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "gtecsfield")
+
+ errs = validate.VarWithValue(1, "", "gtecsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtecsfield")
+
+ // this test is for the WARNING about unforeseen validation issues.
+ errs = validate.VarWithValue(test, now, "gtecsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.CreatedAt", "Test.CreatedAt", "CreatedAt", "CreatedAt", "gtecsfield")
+ AssertError(t, errs, "Test.String", "Test.String", "String", "String", "gtecsfield")
+ AssertError(t, errs, "Test.Int", "Test.Int", "Int", "Int", "gtecsfield")
+ AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "gtecsfield")
+ AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "gtecsfield")
+ AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "gtecsfield")
+
+ type Other struct {
+ Value string
+ }
+
+ type Test2 struct {
+ Value Other
+ Time time.Time `validate:"gtecsfield=Value"`
+ }
+
+ tst := Test2{
+ Value: Other{Value: "StringVal"},
+ Time: then,
+ }
+
+ errs = validate.Struct(tst)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "gtecsfield")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "gtecsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "gtecsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "gtecsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtecsfield")
+
+ errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,gtecsfield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct and an inner struct with time.Duration type fields.
+
+ type TimeDurationInner struct {
+ Duration time.Duration
+ }
+ var timeDurationInner *TimeDurationInner
+
+ type TimeDurationTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"gtecsfield=Inner.Duration"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationInner = &TimeDurationInner{time.Hour}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gtecsfield")
+
+ type TimeDurationOmitemptyTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"omitempty,gtecsfield=Inner.Duration"`
+ }
+ var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+ timeDurationInner = &TimeDurationInner{time.Hour}
+ timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestCrossStructGtFieldValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ type Inner struct {
+ CreatedAt *time.Time
+ String string
+ Int int
+ Uint uint
+ Float float64
+ Array []string
+ }
+
+ type Test struct {
+ Inner *Inner
+ CreatedAt *time.Time `validate:"gtcsfield=Inner.CreatedAt"`
+ String string `validate:"gtcsfield=Inner.String"`
+ Int int `validate:"gtcsfield=Inner.Int"`
+ Uint uint `validate:"gtcsfield=Inner.Uint"`
+ Float float64 `validate:"gtcsfield=Inner.Float"`
+ Array []string `validate:"gtcsfield=Inner.Array"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * -5)
+
+ inner := &Inner{
+ CreatedAt: &then,
+ String: "abcd",
+ Int: 13,
+ Uint: 13,
+ Float: 1.13,
+ Array: []string{"val1", "val2"},
+ }
+
+ test := &Test{
+ Inner: inner,
+ CreatedAt: &now,
+ String: "abcde",
+ Int: 14,
+ Uint: 14,
+ Float: 1.14,
+ Array: []string{"val1", "val2", "val3"},
+ }
+
+ errs = validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test.CreatedAt = &then
+ test.String = "abcd"
+ test.Int = 13
+ test.Uint = 13
+ test.Float = 1.13
+ test.Array = []string{"val1", "val2"}
+
+ errs = validate.Struct(test)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.CreatedAt", "Test.CreatedAt", "CreatedAt", "CreatedAt", "gtcsfield")
+ AssertError(t, errs, "Test.String", "Test.String", "String", "String", "gtcsfield")
+ AssertError(t, errs, "Test.Int", "Test.Int", "Int", "Int", "gtcsfield")
+ AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "gtcsfield")
+ AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "gtcsfield")
+ AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "gtcsfield")
+
+ errs = validate.VarWithValue(1, "", "gtcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtcsfield")
+
+ // this test is for the WARNING about unforeseen validation issues.
+ errs = validate.VarWithValue(test, now, "gtcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.CreatedAt", "Test.CreatedAt", "CreatedAt", "CreatedAt", "gtcsfield")
+ AssertError(t, errs, "Test.String", "Test.String", "String", "String", "gtcsfield")
+ AssertError(t, errs, "Test.Int", "Test.Int", "Int", "Int", "gtcsfield")
+ AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "gtcsfield")
+ AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "gtcsfield")
+ AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "gtcsfield")
+
+ type Other struct {
+ Value string
+ }
+
+ type Test2 struct {
+ Value Other
+ Time time.Time `validate:"gtcsfield=Value"`
+ }
+
+ tst := Test2{
+ Value: Other{Value: "StringVal"},
+ Time: then,
+ }
+
+ errs = validate.Struct(tst)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "gtcsfield")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "gtcsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "gtcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtcsfield")
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "gtcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtcsfield")
+
+ errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,gtcsfield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct and an inner struct with time.Duration type fields.
+
+ type TimeDurationInner struct {
+ Duration time.Duration
+ }
+ var timeDurationInner *TimeDurationInner
+
+ type TimeDurationTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"gtcsfield=Inner.Duration"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationInner = &TimeDurationInner{time.Hour}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gtcsfield")
+
+ timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gtcsfield")
+
+ type TimeDurationOmitemptyTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"omitempty,gtcsfield=Inner.Duration"`
+ }
+ var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+ timeDurationInner = &TimeDurationInner{time.Hour}
+ timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestCrossStructNeFieldValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ type Inner struct {
+ CreatedAt *time.Time
+ }
+
+ type Test struct {
+ Inner *Inner
+ CreatedAt *time.Time `validate:"necsfield=Inner.CreatedAt"`
+ }
+
+ now := time.Now().UTC()
+ then := now.Add(time.Hour * 5)
+
+ inner := &Inner{
+ CreatedAt: &then,
+ }
+
+ test := &Test{
+ Inner: inner,
+ CreatedAt: &now,
+ }
+
+ errs = validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test.CreatedAt = &then
+
+ errs = validate.Struct(test)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.CreatedAt", "Test.CreatedAt", "CreatedAt", "CreatedAt", "necsfield")
+
+ var j uint64
+ var k float64
+ var j2 uint64
+ var k2 float64
+ s := "abcd"
+ i := 1
+ j = 1
+ k = 1.543
+ arr := []string{"test"}
+
+ s2 := "abcd"
+ i2 := 1
+ j2 = 1
+ k2 = 1.543
+ arr2 := []string{"test"}
+ arr3 := []string{"test", "test2"}
+ now2 := now
+
+ errs = validate.VarWithValue(s, s2, "necsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "necsfield")
+
+ errs = validate.VarWithValue(i2, i, "necsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "necsfield")
+
+ errs = validate.VarWithValue(j2, j, "necsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "necsfield")
+
+ errs = validate.VarWithValue(k2, k, "necsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "necsfield")
+
+ errs = validate.VarWithValue(arr2, arr, "necsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "necsfield")
+
+ errs = validate.VarWithValue(now2, now, "necsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "necsfield")
+
+ errs = validate.VarWithValue(arr3, arr, "necsfield")
+ Equal(t, errs, nil)
+
+ type SInner struct {
+ Name string
+ }
+
+ type TStruct struct {
+ Inner *SInner
+ CreatedAt *time.Time `validate:"necsfield=Inner"`
+ }
+
+ sinner := &SInner{
+ Name: "NAME",
+ }
+
+ test2 := &TStruct{
+ Inner: sinner,
+ CreatedAt: &now,
+ }
+
+ errs = validate.Struct(test2)
+ Equal(t, errs, nil)
+
+ test2.Inner = nil
+ errs = validate.Struct(test2)
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(nil, 1, "necsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "necsfield")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "necsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "necsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "necsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "necsfield")
+
+ errs = validate.VarWithValue(time.Duration(0), time.Duration(0), "omitempty,necsfield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct and an inner struct with time.Duration type fields.
+
+ type TimeDurationInner struct {
+ Duration time.Duration
+ }
+ var timeDurationInner *TimeDurationInner
+
+ type TimeDurationTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"necsfield=Inner.Duration"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationInner = &TimeDurationInner{time.Hour}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "necsfield")
+
+ type TimeDurationOmitemptyTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"omitempty,necsfield=Inner.Duration"`
+ }
+ var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+ timeDurationInner = &TimeDurationInner{time.Duration(0)}
+ timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestCrossStructEqFieldValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ type Inner struct {
+ CreatedAt *time.Time
+ }
+
+ type Test struct {
+ Inner *Inner
+ CreatedAt *time.Time `validate:"eqcsfield=Inner.CreatedAt"`
+ }
+
+ now := time.Now().UTC()
+
+ inner := &Inner{
+ CreatedAt: &now,
+ }
+
+ test := &Test{
+ Inner: inner,
+ CreatedAt: &now,
+ }
+
+ errs = validate.Struct(test)
+ Equal(t, errs, nil)
+
+ newTime := time.Now().UTC()
+ test.CreatedAt = &newTime
+
+ errs = validate.Struct(test)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.CreatedAt", "Test.CreatedAt", "CreatedAt", "CreatedAt", "eqcsfield")
+
+ var j uint64
+ var k float64
+ s := "abcd"
+ i := 1
+ j = 1
+ k = 1.543
+ arr := []string{"test"}
+
+ var j2 uint64
+ var k2 float64
+ s2 := "abcd"
+ i2 := 1
+ j2 = 1
+ k2 = 1.543
+ arr2 := []string{"test"}
+ arr3 := []string{"test", "test2"}
+ now2 := now
+
+ errs = validate.VarWithValue(s, s2, "eqcsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(i2, i, "eqcsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(j2, j, "eqcsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(k2, k, "eqcsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(arr2, arr, "eqcsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(now2, now, "eqcsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(arr3, arr, "eqcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eqcsfield")
+
+ type SInner struct {
+ Name string
+ }
+
+ type TStruct struct {
+ Inner *SInner
+ CreatedAt *time.Time `validate:"eqcsfield=Inner"`
+ }
+
+ sinner := &SInner{
+ Name: "NAME",
+ }
+
+ test2 := &TStruct{
+ Inner: sinner,
+ CreatedAt: &now,
+ }
+
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TStruct.CreatedAt", "TStruct.CreatedAt", "CreatedAt", "CreatedAt", "eqcsfield")
+
+ test2.Inner = nil
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TStruct.CreatedAt", "TStruct.CreatedAt", "CreatedAt", "CreatedAt", "eqcsfield")
+
+ errs = validate.VarWithValue(nil, 1, "eqcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eqcsfield")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "eqcsfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "eqcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eqcsfield")
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "eqcsfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eqcsfield")
+
+ errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,eqcsfield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct and an inner struct with time.Duration type fields.
+
+ type TimeDurationInner struct {
+ Duration time.Duration
+ }
+ var timeDurationInner *TimeDurationInner
+
+ type TimeDurationTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"eqcsfield=Inner.Duration"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationInner = &TimeDurationInner{time.Hour}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "eqcsfield")
+
+ timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+ timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "eqcsfield")
+
+ type TimeDurationOmitemptyTest struct {
+ Inner *TimeDurationInner
+ Duration time.Duration `validate:"omitempty,eqcsfield=Inner.Duration"`
+ }
+ var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+ timeDurationInner = &TimeDurationInner{time.Hour}
+ timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestCrossNamespaceFieldValidation(t *testing.T) {
+
+ type SliceStruct struct {
+ Name string
+ }
+
+ type Inner struct {
+ CreatedAt *time.Time
+ Slice []string
+ SliceStructs []*SliceStruct
+ SliceSlice [][]string
+ SliceSliceStruct [][]*SliceStruct
+ SliceMap []map[string]string
+ Map map[string]string
+ MapMap map[string]map[string]string
+ MapStructs map[string]*SliceStruct
+ MapMapStruct map[string]map[string]*SliceStruct
+ MapSlice map[string][]string
+ MapInt map[int]string
+ MapInt8 map[int8]string
+ MapInt16 map[int16]string
+ MapInt32 map[int32]string
+ MapInt64 map[int64]string
+ MapUint map[uint]string
+ MapUint8 map[uint8]string
+ MapUint16 map[uint16]string
+ MapUint32 map[uint32]string
+ MapUint64 map[uint64]string
+ MapFloat32 map[float32]string
+ MapFloat64 map[float64]string
+ MapBool map[bool]string
+ }
+
+ type Test struct {
+ Inner *Inner
+ CreatedAt *time.Time
+ }
+
+ now := time.Now()
+
+ inner := &Inner{
+ CreatedAt: &now,
+ Slice: []string{"val1", "val2", "val3"},
+ SliceStructs: []*SliceStruct{{Name: "name1"}, {Name: "name2"}, {Name: "name3"}},
+ SliceSlice: [][]string{{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}},
+ SliceSliceStruct: [][]*SliceStruct{{{Name: "name1"}, {Name: "name2"}, {Name: "name3"}}, {{Name: "name4"}, {Name: "name5"}, {Name: "name6"}}, {{Name: "name7"}, {Name: "name8"}, {Name: "name9"}}},
+ SliceMap: []map[string]string{{"key1": "val1", "key2": "val2", "key3": "val3"}, {"key4": "val4", "key5": "val5", "key6": "val6"}},
+ Map: map[string]string{"key1": "val1", "key2": "val2", "key3": "val3"},
+ MapStructs: map[string]*SliceStruct{"key1": {Name: "name1"}, "key2": {Name: "name2"}, "key3": {Name: "name3"}},
+ MapMap: map[string]map[string]string{"key1": {"key1-1": "val1"}, "key2": {"key2-1": "val2"}, "key3": {"key3-1": "val3"}},
+ MapMapStruct: map[string]map[string]*SliceStruct{"key1": {"key1-1": {Name: "name1"}}, "key2": {"key2-1": {Name: "name2"}}, "key3": {"key3-1": {Name: "name3"}}},
+ MapSlice: map[string][]string{"key1": {"1", "2", "3"}, "key2": {"4", "5", "6"}, "key3": {"7", "8", "9"}},
+ MapInt: map[int]string{1: "val1", 2: "val2", 3: "val3"},
+ MapInt8: map[int8]string{1: "val1", 2: "val2", 3: "val3"},
+ MapInt16: map[int16]string{1: "val1", 2: "val2", 3: "val3"},
+ MapInt32: map[int32]string{1: "val1", 2: "val2", 3: "val3"},
+ MapInt64: map[int64]string{1: "val1", 2: "val2", 3: "val3"},
+ MapUint: map[uint]string{1: "val1", 2: "val2", 3: "val3"},
+ MapUint8: map[uint8]string{1: "val1", 2: "val2", 3: "val3"},
+ MapUint16: map[uint16]string{1: "val1", 2: "val2", 3: "val3"},
+ MapUint32: map[uint32]string{1: "val1", 2: "val2", 3: "val3"},
+ MapUint64: map[uint64]string{1: "val1", 2: "val2", 3: "val3"},
+ MapFloat32: map[float32]string{1.01: "val1", 2.02: "val2", 3.03: "val3"},
+ MapFloat64: map[float64]string{1.01: "val1", 2.02: "val2", 3.03: "val3"},
+ MapBool: map[bool]string{true: "val1", false: "val2"},
+ }
+
+ test := &Test{
+ Inner: inner,
+ CreatedAt: &now,
+ }
+
+ val := reflect.ValueOf(test)
+
+ vd := New()
+ v := &validate{
+ v: vd,
+ }
+
+ current, kind, _, ok := v.getStructFieldOKInternal(val, "Inner.CreatedAt")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.Struct)
+ tm, ok := current.Interface().(time.Time)
+ Equal(t, ok, true)
+ Equal(t, tm, now)
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[1]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, _, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField")
+ Equal(t, ok, false)
+
+ current, _, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]")
+ Equal(t, ok, false)
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.Map[key3]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val3")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapMap[key2][key2-1]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapStructs[key2].Name")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "name2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapMapStruct[key3][key3-1].Name")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "name3")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceSlice[2][0]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "7")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceSliceStruct[2][1].Name")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "name8")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceMap[1][key5]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val5")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapSlice[key3][2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "9")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt8[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt16[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt32[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt64[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint8[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint16[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint32[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint64[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat32[3.03]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val3")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat64[2.02]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val2")
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapBool[true]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.String)
+ Equal(t, current.String(), "val1")
+
+ inner = &Inner{
+ CreatedAt: &now,
+ Slice: []string{"val1", "val2", "val3"},
+ SliceStructs: []*SliceStruct{{Name: "name1"}, {Name: "name2"}, nil},
+ SliceSlice: [][]string{{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}},
+ SliceSliceStruct: [][]*SliceStruct{{{Name: "name1"}, {Name: "name2"}, {Name: "name3"}}, {{Name: "name4"}, {Name: "name5"}, {Name: "name6"}}, {{Name: "name7"}, {Name: "name8"}, {Name: "name9"}}},
+ SliceMap: []map[string]string{{"key1": "val1", "key2": "val2", "key3": "val3"}, {"key4": "val4", "key5": "val5", "key6": "val6"}},
+ Map: map[string]string{"key1": "val1", "key2": "val2", "key3": "val3"},
+ MapStructs: map[string]*SliceStruct{"key1": {Name: "name1"}, "key2": {Name: "name2"}, "key3": {Name: "name3"}},
+ MapMap: map[string]map[string]string{"key1": {"key1-1": "val1"}, "key2": {"key2-1": "val2"}, "key3": {"key3-1": "val3"}},
+ MapMapStruct: map[string]map[string]*SliceStruct{"key1": {"key1-1": {Name: "name1"}}, "key2": {"key2-1": {Name: "name2"}}, "key3": {"key3-1": {Name: "name3"}}},
+ MapSlice: map[string][]string{"key1": {"1", "2", "3"}, "key2": {"4", "5", "6"}, "key3": {"7", "8", "9"}},
+ }
+
+ test = &Test{
+ Inner: inner,
+ CreatedAt: nil,
+ }
+
+ val = reflect.ValueOf(test)
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2]")
+ Equal(t, ok, true)
+ Equal(t, kind, reflect.Ptr)
+ Equal(t, current.String(), "<*validator.SliceStruct Value>")
+ Equal(t, current.IsNil(), true)
+
+ current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2].Name")
+ Equal(t, ok, false)
+ Equal(t, kind, reflect.Ptr)
+ Equal(t, current.String(), "<*validator.SliceStruct Value>")
+ Equal(t, current.IsNil(), true)
+
+ PanicMatches(t, func() { v.getStructFieldOKInternal(reflect.ValueOf(1), "crazyinput") }, "Invalid field namespace")
+}
+
+func TestExistsValidation(t *testing.T) {
+
+ jsonText := "{ \"truthiness2\": true }"
+
+ type Thing struct {
+ Truthiness *bool `json:"truthiness" validate:"required"`
+ }
+
+ var ting Thing
+
+ err := json.Unmarshal([]byte(jsonText), &ting)
+ Equal(t, err, nil)
+ NotEqual(t, ting, nil)
+ Equal(t, ting.Truthiness, nil)
+
+ validate := New()
+ errs := validate.Struct(ting)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Thing.Truthiness", "Thing.Truthiness", "Truthiness", "Truthiness", "required")
+
+ jsonText = "{ \"truthiness\": true }"
+
+ err = json.Unmarshal([]byte(jsonText), &ting)
+ Equal(t, err, nil)
+ NotEqual(t, ting, nil)
+ Equal(t, ting.Truthiness, true)
+
+ errs = validate.Struct(ting)
+ Equal(t, errs, nil)
+}
+
+func TestSQLValue2Validation(t *testing.T) {
+
+ validate := New()
+ validate.RegisterCustomTypeFunc(ValidateValuerType, valuer{}, (*driver.Valuer)(nil), sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
+ validate.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{})
+ validate.RegisterCustomTypeFunc(OverrideIntTypeForSomeReason, 1)
+
+ val := valuer{
+ Name: "",
+ }
+
+ errs := validate.Var(val, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ val.Name = "Valid Name"
+ errs = validate.VarCtx(context.Background(), val, "required")
+ Equal(t, errs, nil)
+
+ val.Name = "errorme"
+
+ PanicMatches(t, func() { _ = validate.Var(val, "required") }, "SQL Driver Valuer error: some kind of error")
+
+ myVal := valuer{
+ Name: "",
+ }
+
+ errs = validate.Var(myVal, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ cust := MadeUpCustomType{
+ FirstName: "Joey",
+ LastName: "Bloggs",
+ }
+
+ c := CustomMadeUpStruct{MadeUp: cust, OverriddenInt: 2}
+
+ errs = validate.Struct(c)
+ Equal(t, errs, nil)
+
+ c.MadeUp.FirstName = ""
+ c.OverriddenInt = 1
+
+ errs = validate.Struct(c)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 2)
+ AssertError(t, errs, "CustomMadeUpStruct.MadeUp", "CustomMadeUpStruct.MadeUp", "MadeUp", "MadeUp", "required")
+ AssertError(t, errs, "CustomMadeUpStruct.OverriddenInt", "CustomMadeUpStruct.OverriddenInt", "OverriddenInt", "OverriddenInt", "gt")
+}
+
+func TestSQLValueValidation(t *testing.T) {
+
+ validate := New()
+ validate.RegisterCustomTypeFunc(ValidateValuerType, (*driver.Valuer)(nil), valuer{})
+ validate.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{})
+ validate.RegisterCustomTypeFunc(OverrideIntTypeForSomeReason, 1)
+
+ val := valuer{
+ Name: "",
+ }
+
+ errs := validate.Var(val, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ val.Name = "Valid Name"
+ errs = validate.Var(val, "required")
+ Equal(t, errs, nil)
+
+ val.Name = "errorme"
+
+ PanicMatches(t, func() { errs = validate.Var(val, "required") }, "SQL Driver Valuer error: some kind of error")
+
+ myVal := valuer{
+ Name: "",
+ }
+
+ errs = validate.Var(myVal, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ cust := MadeUpCustomType{
+ FirstName: "Joey",
+ LastName: "Bloggs",
+ }
+
+ c := CustomMadeUpStruct{MadeUp: cust, OverriddenInt: 2}
+
+ errs = validate.Struct(c)
+ Equal(t, errs, nil)
+
+ c.MadeUp.FirstName = ""
+ c.OverriddenInt = 1
+
+ errs = validate.Struct(c)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 2)
+ AssertError(t, errs, "CustomMadeUpStruct.MadeUp", "CustomMadeUpStruct.MadeUp", "MadeUp", "MadeUp", "required")
+ AssertError(t, errs, "CustomMadeUpStruct.OverriddenInt", "CustomMadeUpStruct.OverriddenInt", "OverriddenInt", "OverriddenInt", "gt")
+}
+
+func TestMACValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"3D:F2:C9:A6:B3:4F", true},
+ {"3D-F2-C9-A6-B3:4F", false},
+ {"123", false},
+ {"", false},
+ {"abacaba", false},
+ {"00:25:96:FF:FE:12:34:56", true},
+ {"0025:96FF:FE12:3456", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "mac")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d mac failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d mac failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "mac" {
+ t.Fatalf("Index: %d mac failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestIPValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"10.0.0.1", true},
+ {"172.16.0.1", true},
+ {"192.168.0.1", true},
+ {"192.168.255.254", true},
+ {"192.168.255.256", false},
+ {"172.16.255.254", true},
+ {"172.16.256.255", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652", true},
+ {"2001:cdba:0:0:0:0:3257:9652", true},
+ {"2001:cdba::3257:9652", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "ip")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ip failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ip failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "ip" {
+ t.Fatalf("Index: %d ip failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestIPv6Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"10.0.0.1", false},
+ {"172.16.0.1", false},
+ {"192.168.0.1", false},
+ {"192.168.255.254", false},
+ {"192.168.255.256", false},
+ {"172.16.255.254", false},
+ {"172.16.256.255", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652", true},
+ {"2001:cdba:0:0:0:0:3257:9652", true},
+ {"2001:cdba::3257:9652", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "ipv6")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ipv6 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ipv6 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "ipv6" {
+ t.Fatalf("Index: %d ipv6 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestIPv4Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"10.0.0.1", true},
+ {"172.16.0.1", true},
+ {"192.168.0.1", true},
+ {"192.168.255.254", true},
+ {"192.168.255.256", false},
+ {"172.16.255.254", true},
+ {"172.16.256.255", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652", false},
+ {"2001:cdba:0:0:0:0:3257:9652", false},
+ {"2001:cdba::3257:9652", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "ipv4")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ipv4 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ipv4 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "ipv4" {
+ t.Fatalf("Index: %d ipv4 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestCIDRValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"10.0.0.0/0", true},
+ {"10.0.0.1/8", true},
+ {"172.16.0.1/16", true},
+ {"192.168.0.1/24", true},
+ {"192.168.255.254/24", true},
+ {"192.168.255.254/48", false},
+ {"192.168.255.256/24", false},
+ {"172.16.255.254/16", true},
+ {"172.16.256.255/16", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652/64", true},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652/256", false},
+ {"2001:cdba:0:0:0:0:3257:9652/32", true},
+ {"2001:cdba::3257:9652/16", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "cidr")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d cidr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d cidr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "cidr" {
+ t.Fatalf("Index: %d cidr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestCIDRv6Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"10.0.0.0/0", false},
+ {"10.0.0.1/8", false},
+ {"172.16.0.1/16", false},
+ {"192.168.0.1/24", false},
+ {"192.168.255.254/24", false},
+ {"192.168.255.254/48", false},
+ {"192.168.255.256/24", false},
+ {"172.16.255.254/16", false},
+ {"172.16.256.255/16", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652/64", true},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652/256", false},
+ {"2001:cdba:0:0:0:0:3257:9652/32", true},
+ {"2001:cdba::3257:9652/16", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "cidrv6")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d cidrv6 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d cidrv6 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "cidrv6" {
+ t.Fatalf("Index: %d cidrv6 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestCIDRv4Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"10.0.0.0/0", true},
+ {"10.0.0.1/8", true},
+ {"172.16.0.1/16", true},
+ {"192.168.0.1/24", true},
+ {"192.168.255.254/24", true},
+ {"192.168.255.254/48", false},
+ {"192.168.255.256/24", false},
+ {"172.16.255.254/16", true},
+ {"172.16.256.255/16", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652/64", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652/256", false},
+ {"2001:cdba:0:0:0:0:3257:9652/32", false},
+ {"2001:cdba::3257:9652/16", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "cidrv4")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d cidrv4 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d cidrv4 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "cidrv4" {
+ t.Fatalf("Index: %d cidrv4 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestTCPAddrValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {":80", false},
+ {"127.0.0.1:80", true},
+ {"[::1]:80", true},
+ {"256.0.0.0:1", false},
+ {"[::1]", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "tcp_addr")
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d tcp_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d tcp_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "tcp_addr" {
+ t.Fatalf("Index: %d tcp_addr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestTCP6AddrValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {":80", false},
+ {"127.0.0.1:80", false},
+ {"[::1]:80", true},
+ {"256.0.0.0:1", false},
+ {"[::1]", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "tcp6_addr")
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d tcp6_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d tcp6_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "tcp6_addr" {
+ t.Fatalf("Index: %d tcp6_addr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestTCP4AddrValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {":80", false},
+ {"127.0.0.1:80", true},
+ {"[::1]:80", false}, // https://github.com/golang/go/issues/14037
+ {"256.0.0.0:1", false},
+ {"[::1]", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "tcp4_addr")
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d tcp4_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Log(test.param, IsEqual(errs, nil))
+ t.Fatalf("Index: %d tcp4_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "tcp4_addr" {
+ t.Fatalf("Index: %d tcp4_addr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUDPAddrValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {":80", false},
+ {"127.0.0.1:80", true},
+ {"[::1]:80", true},
+ {"256.0.0.0:1", false},
+ {"[::1]", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "udp_addr")
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d udp_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d udp_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "udp_addr" {
+ t.Fatalf("Index: %d udp_addr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUDP6AddrValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {":80", false},
+ {"127.0.0.1:80", false},
+ {"[::1]:80", true},
+ {"256.0.0.0:1", false},
+ {"[::1]", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "udp6_addr")
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d udp6_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d udp6_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "udp6_addr" {
+ t.Fatalf("Index: %d udp6_addr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUDP4AddrValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {":80", false},
+ {"127.0.0.1:80", true},
+ {"[::1]:80", false}, // https://github.com/golang/go/issues/14037
+ {"256.0.0.0:1", false},
+ {"[::1]", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "udp4_addr")
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d udp4_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Log(test.param, IsEqual(errs, nil))
+ t.Fatalf("Index: %d udp4_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "udp4_addr" {
+ t.Fatalf("Index: %d udp4_addr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestIPAddrValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"127.0.0.1", true},
+ {"127.0.0.1:80", false},
+ {"::1", true},
+ {"256.0.0.0", false},
+ {"localhost", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "ip_addr")
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ip_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ip_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "ip_addr" {
+ t.Fatalf("Index: %d ip_addr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestIP6AddrValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"127.0.0.1", false}, // https://github.com/golang/go/issues/14037
+ {"127.0.0.1:80", false},
+ {"::1", true},
+ {"0:0:0:0:0:0:0:1", true},
+ {"256.0.0.0", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "ip6_addr")
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ip6_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ip6_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "ip6_addr" {
+ t.Fatalf("Index: %d ip6_addr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestIP4AddrValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"127.0.0.1", true},
+ {"127.0.0.1:80", false},
+ {"::1", false}, // https://github.com/golang/go/issues/14037
+ {"256.0.0.0", false},
+ {"localhost", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "ip4_addr")
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ip4_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Log(test.param, IsEqual(errs, nil))
+ t.Fatalf("Index: %d ip4_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "ip4_addr" {
+ t.Fatalf("Index: %d ip4_addr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUnixAddrValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", true},
+ {"v.sock", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "unix_addr")
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d unix_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Log(test.param, IsEqual(errs, nil))
+ t.Fatalf("Index: %d unix_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "unix_addr" {
+ t.Fatalf("Index: %d unix_addr failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestSliceMapArrayChanFuncPtrInterfaceRequiredValidation(t *testing.T) {
+
+ validate := New()
+
+ var m map[string]string
+
+ errs := validate.Var(m, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ m = map[string]string{}
+ errs = validate.Var(m, "required")
+ Equal(t, errs, nil)
+
+ var arr [5]string
+ errs = validate.Var(arr, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ arr[0] = "ok"
+ errs = validate.Var(arr, "required")
+ Equal(t, errs, nil)
+
+ var s []string
+ errs = validate.Var(s, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ s = []string{}
+ errs = validate.Var(s, "required")
+ Equal(t, errs, nil)
+
+ var c chan string
+ errs = validate.Var(c, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ c = make(chan string)
+ errs = validate.Var(c, "required")
+ Equal(t, errs, nil)
+
+ var tst *int
+ errs = validate.Var(tst, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ one := 1
+ tst = &one
+ errs = validate.Var(tst, "required")
+ Equal(t, errs, nil)
+
+ var iface interface{}
+
+ errs = validate.Var(iface, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ errs = validate.Var(iface, "omitempty,required")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(iface, "")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(nil, iface, "")
+ Equal(t, errs, nil)
+
+ var f func(string)
+
+ errs = validate.Var(f, "required")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "required")
+
+ f = func(name string) {}
+
+ errs = validate.Var(f, "required")
+ Equal(t, errs, nil)
+}
+
+func TestDatePtrValidationIssueValidation(t *testing.T) {
+
+ type Test struct {
+ LastViewed *time.Time
+ Reminder *time.Time
+ }
+
+ test := &Test{}
+
+ validate := New()
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+}
+
+func TestCommaAndPipeObfuscationValidation(t *testing.T) {
+ s := "My Name Is, |joeybloggs|"
+
+ validate := New()
+
+ errs := validate.Var(s, "excludesall=0x2C")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "excludesall")
+
+ errs = validate.Var(s, "excludesall=0x7C")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "excludesall")
+}
+
+func TestBadKeyValidation(t *testing.T) {
+ type Test struct {
+ Name string `validate:"required, "`
+ }
+
+ tst := &Test{
+ Name: "test",
+ }
+
+ validate := New()
+
+ PanicMatches(t, func() { _ = validate.Struct(tst) }, "Undefined validation function ' ' on field 'Name'")
+
+ type Test2 struct {
+ Name string `validate:"required,,len=2"`
+ }
+
+ tst2 := &Test2{
+ Name: "test",
+ }
+
+ PanicMatches(t, func() { _ = validate.Struct(tst2) }, "Invalid validation tag on field 'Name'")
+}
+
+func TestInterfaceErrValidation(t *testing.T) {
+
+ var v2 interface{} = 1
+ var v1 interface{} = v2
+
+ validate := New()
+ errs := validate.Var(v1, "len=1")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(v2, "len=1")
+ Equal(t, errs, nil)
+
+ type ExternalCMD struct {
+ Userid string `json:"userid"`
+ Action uint32 `json:"action"`
+ Data interface{} `json:"data,omitempty" validate:"required"`
+ }
+
+ s := &ExternalCMD{
+ Userid: "123456",
+ Action: 10000,
+ // Data: 1,
+ }
+
+ errs = validate.Struct(s)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "ExternalCMD.Data", "ExternalCMD.Data", "Data", "Data", "required")
+
+ type ExternalCMD2 struct {
+ Userid string `json:"userid"`
+ Action uint32 `json:"action"`
+ Data interface{} `json:"data,omitempty" validate:"len=1"`
+ }
+
+ s2 := &ExternalCMD2{
+ Userid: "123456",
+ Action: 10000,
+ // Data: 1,
+ }
+
+ errs = validate.Struct(s2)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "ExternalCMD2.Data", "ExternalCMD2.Data", "Data", "Data", "len")
+
+ s3 := &ExternalCMD2{
+ Userid: "123456",
+ Action: 10000,
+ Data: 2,
+ }
+
+ errs = validate.Struct(s3)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "ExternalCMD2.Data", "ExternalCMD2.Data", "Data", "Data", "len")
+
+ type Inner struct {
+ Name string `validate:"required"`
+ }
+
+ inner := &Inner{
+ Name: "",
+ }
+
+ s4 := &ExternalCMD{
+ Userid: "123456",
+ Action: 10000,
+ Data: inner,
+ }
+
+ errs = validate.Struct(s4)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "ExternalCMD.Data.Name", "ExternalCMD.Data.Name", "Name", "Name", "required")
+
+ type TestMapStructPtr struct {
+ Errs map[int]interface{} `validate:"gt=0,dive,len=2"`
+ }
+
+ mip := map[int]interface{}{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}}
+
+ msp := &TestMapStructPtr{
+ Errs: mip,
+ }
+
+ errs = validate.Struct(msp)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "TestMapStructPtr.Errs[3]", "TestMapStructPtr.Errs[3]", "Errs[3]", "Errs[3]", "len")
+
+ type TestMultiDimensionalStructs struct {
+ Errs [][]interface{} `validate:"gt=0,dive,dive"`
+ }
+
+ var errStructArray [][]interface{}
+
+ errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}})
+ errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}})
+
+ tms := &TestMultiDimensionalStructs{
+ Errs: errStructArray,
+ }
+
+ errs = validate.Struct(tms)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 4)
+ AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][1].Name", "TestMultiDimensionalStructs.Errs[0][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][2].Name", "TestMultiDimensionalStructs.Errs[0][2].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][1].Name", "TestMultiDimensionalStructs.Errs[1][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][2].Name", "TestMultiDimensionalStructs.Errs[1][2].Name", "Name", "Name", "required")
+
+ type TestMultiDimensionalStructsPtr2 struct {
+ Errs [][]*Inner `validate:"gt=0,dive,dive,required"`
+ }
+
+ var errStructPtr2Array [][]*Inner
+
+ errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, {""}})
+ errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, {""}})
+ errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, nil})
+
+ tmsp2 := &TestMultiDimensionalStructsPtr2{
+ Errs: errStructPtr2Array,
+ }
+
+ errs = validate.Struct(tmsp2)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 6)
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][1].Name", "TestMultiDimensionalStructsPtr2.Errs[0][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][2].Name", "TestMultiDimensionalStructsPtr2.Errs[0][2].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][1].Name", "TestMultiDimensionalStructsPtr2.Errs[1][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][2].Name", "TestMultiDimensionalStructsPtr2.Errs[1][2].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][1].Name", "TestMultiDimensionalStructsPtr2.Errs[2][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][2]", "TestMultiDimensionalStructsPtr2.Errs[2][2]", "Errs[2][2]", "Errs[2][2]", "required")
+
+ m := map[int]interface{}{0: "ok", 3: "", 4: "ok"}
+
+ errs = validate.Var(m, "len=3,dive,len=2")
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "[3]", "[3]", "[3]", "[3]", "len")
+
+ errs = validate.Var(m, "len=2,dive,required")
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "", "", "", "", "len")
+
+ arr := []interface{}{"ok", "", "ok"}
+
+ errs = validate.Var(arr, "len=3,dive,len=2")
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "[1]", "[1]", "[1]", "[1]", "len")
+
+ errs = validate.Var(arr, "len=2,dive,required")
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "", "", "", "", "len")
+
+ type MyStruct struct {
+ A, B string
+ C interface{}
+ }
+
+ var a MyStruct
+
+ a.A = "value"
+ a.C = "nu"
+
+ errs = validate.Struct(a)
+ Equal(t, errs, nil)
+}
+
+func TestMapDiveValidation(t *testing.T) {
+
+ validate := New()
+
+ n := map[int]interface{}{0: nil}
+ errs := validate.Var(n, "omitempty,required")
+ Equal(t, errs, nil)
+
+ m := map[int]string{0: "ok", 3: "", 4: "ok"}
+
+ errs = validate.Var(m, "len=3,dive,required")
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "[3]", "[3]", "[3]", "[3]", "required")
+
+ errs = validate.Var(m, "len=2,dive,required")
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "", "", "", "", "len")
+
+ type Inner struct {
+ Name string `validate:"required"`
+ }
+
+ type TestMapStruct struct {
+ Errs map[int]Inner `validate:"gt=0,dive"`
+ }
+
+ mi := map[int]Inner{0: {"ok"}, 3: {""}, 4: {"ok"}}
+
+ ms := &TestMapStruct{
+ Errs: mi,
+ }
+
+ errs = validate.Struct(ms)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "TestMapStruct.Errs[3].Name", "TestMapStruct.Errs[3].Name", "Name", "Name", "required")
+
+ // for full test coverage
+ s := fmt.Sprint(errs.Error())
+ NotEqual(t, s, "")
+
+ type TestMapTimeStruct struct {
+ Errs map[int]*time.Time `validate:"gt=0,dive,required"`
+ }
+
+ t1 := time.Now().UTC()
+
+ mta := map[int]*time.Time{0: &t1, 3: nil, 4: nil}
+
+ mt := &TestMapTimeStruct{
+ Errs: mta,
+ }
+
+ errs = validate.Struct(mt)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 2)
+ AssertError(t, errs, "TestMapTimeStruct.Errs[3]", "TestMapTimeStruct.Errs[3]", "Errs[3]", "Errs[3]", "required")
+ AssertError(t, errs, "TestMapTimeStruct.Errs[4]", "TestMapTimeStruct.Errs[4]", "Errs[4]", "Errs[4]", "required")
+
+ type TestMapStructPtr struct {
+ Errs map[int]*Inner `validate:"gt=0,dive,required"`
+ }
+
+ mip := map[int]*Inner{0: {"ok"}, 3: nil, 4: {"ok"}}
+
+ msp := &TestMapStructPtr{
+ Errs: mip,
+ }
+
+ errs = validate.Struct(msp)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "TestMapStructPtr.Errs[3]", "TestMapStructPtr.Errs[3]", "Errs[3]", "Errs[3]", "required")
+
+ type TestMapStructPtr2 struct {
+ Errs map[int]*Inner `validate:"gt=0,dive,omitempty,required"`
+ }
+
+ mip2 := map[int]*Inner{0: {"ok"}, 3: nil, 4: {"ok"}}
+
+ msp2 := &TestMapStructPtr2{
+ Errs: mip2,
+ }
+
+ errs = validate.Struct(msp2)
+ Equal(t, errs, nil)
+
+ v2 := New()
+ v2.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+
+ type MapDiveJSONTest struct {
+ Map map[string]string `validate:"required,gte=1,dive,gte=1" json:"MyName"`
+ }
+
+ mdjt := &MapDiveJSONTest{
+ Map: map[string]string{
+ "Key1": "Value1",
+ "Key2": "",
+ },
+ }
+
+ err := v2.Struct(mdjt)
+ NotEqual(t, err, nil)
+
+ errs = err.(ValidationErrors)
+ fe := getError(errs, "MapDiveJSONTest.MyName[Key2]", "MapDiveJSONTest.Map[Key2]")
+ NotEqual(t, fe, nil)
+ Equal(t, fe.Tag(), "gte")
+ Equal(t, fe.ActualTag(), "gte")
+ Equal(t, fe.Field(), "MyName[Key2]")
+ Equal(t, fe.StructField(), "Map[Key2]")
+}
+
+func TestArrayDiveValidation(t *testing.T) {
+
+ validate := New()
+
+ arr := []string{"ok", "", "ok"}
+
+ errs := validate.Var(arr, "len=3,dive,required")
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "[1]", "[1]", "[1]", "[1]", "required")
+
+ errs = validate.Var(arr, "len=2,dive,required")
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "", "", "", "", "len")
+
+ type BadDive struct {
+ Name string `validate:"dive"`
+ }
+
+ bd := &BadDive{
+ Name: "TEST",
+ }
+
+ PanicMatches(t, func() { _ = validate.Struct(bd) }, "dive error! can't dive on a non slice or map")
+
+ type Test struct {
+ Errs []string `validate:"gt=0,dive,required"`
+ }
+
+ test := &Test{
+ Errs: []string{"ok", "", "ok"},
+ }
+
+ errs = validate.Struct(test)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "Test.Errs[1]", "Test.Errs[1]", "Errs[1]", "Errs[1]", "required")
+
+ test = &Test{
+ Errs: []string{"ok", "ok", ""},
+ }
+
+ errs = validate.Struct(test)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "Test.Errs[2]", "Test.Errs[2]", "Errs[2]", "Errs[2]", "required")
+
+ type TestMultiDimensional struct {
+ Errs [][]string `validate:"gt=0,dive,dive,required"`
+ }
+
+ var errArray [][]string
+
+ errArray = append(errArray, []string{"ok", "", ""})
+ errArray = append(errArray, []string{"ok", "", ""})
+
+ tm := &TestMultiDimensional{
+ Errs: errArray,
+ }
+
+ errs = validate.Struct(tm)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 4)
+ AssertError(t, errs, "TestMultiDimensional.Errs[0][1]", "TestMultiDimensional.Errs[0][1]", "Errs[0][1]", "Errs[0][1]", "required")
+ AssertError(t, errs, "TestMultiDimensional.Errs[0][2]", "TestMultiDimensional.Errs[0][2]", "Errs[0][2]", "Errs[0][2]", "required")
+ AssertError(t, errs, "TestMultiDimensional.Errs[1][1]", "TestMultiDimensional.Errs[1][1]", "Errs[1][1]", "Errs[1][1]", "required")
+ AssertError(t, errs, "TestMultiDimensional.Errs[1][2]", "TestMultiDimensional.Errs[1][2]", "Errs[1][2]", "Errs[1][2]", "required")
+
+ type Inner struct {
+ Name string `validate:"required"`
+ }
+
+ type TestMultiDimensionalStructs struct {
+ Errs [][]Inner `validate:"gt=0,dive,dive"`
+ }
+
+ var errStructArray [][]Inner
+
+ errStructArray = append(errStructArray, []Inner{{"ok"}, {""}, {""}})
+ errStructArray = append(errStructArray, []Inner{{"ok"}, {""}, {""}})
+
+ tms := &TestMultiDimensionalStructs{
+ Errs: errStructArray,
+ }
+
+ errs = validate.Struct(tms)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 4)
+ AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][1].Name", "TestMultiDimensionalStructs.Errs[0][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][2].Name", "TestMultiDimensionalStructs.Errs[0][2].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][1].Name", "TestMultiDimensionalStructs.Errs[1][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][2].Name", "TestMultiDimensionalStructs.Errs[1][2].Name", "Name", "Name", "required")
+
+ type TestMultiDimensionalStructsPtr struct {
+ Errs [][]*Inner `validate:"gt=0,dive,dive"`
+ }
+
+ var errStructPtrArray [][]*Inner
+
+ errStructPtrArray = append(errStructPtrArray, []*Inner{{"ok"}, {""}, {""}})
+ errStructPtrArray = append(errStructPtrArray, []*Inner{{"ok"}, {""}, {""}})
+ errStructPtrArray = append(errStructPtrArray, []*Inner{{"ok"}, {""}, nil})
+
+ tmsp := &TestMultiDimensionalStructsPtr{
+ Errs: errStructPtrArray,
+ }
+
+ errs = validate.Struct(tmsp)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 5)
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[0][1].Name", "TestMultiDimensionalStructsPtr.Errs[0][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[0][2].Name", "TestMultiDimensionalStructsPtr.Errs[0][2].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[1][1].Name", "TestMultiDimensionalStructsPtr.Errs[1][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[1][2].Name", "TestMultiDimensionalStructsPtr.Errs[1][2].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[2][1].Name", "TestMultiDimensionalStructsPtr.Errs[2][1].Name", "Name", "Name", "required")
+
+ // for full test coverage
+ s := fmt.Sprint(errs.Error())
+ NotEqual(t, s, "")
+
+ type TestMultiDimensionalStructsPtr2 struct {
+ Errs [][]*Inner `validate:"gt=0,dive,dive,required"`
+ }
+
+ var errStructPtr2Array [][]*Inner
+
+ errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, {""}})
+ errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, {""}})
+ errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, nil})
+
+ tmsp2 := &TestMultiDimensionalStructsPtr2{
+ Errs: errStructPtr2Array,
+ }
+
+ errs = validate.Struct(tmsp2)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 6)
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][1].Name", "TestMultiDimensionalStructsPtr2.Errs[0][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][2].Name", "TestMultiDimensionalStructsPtr2.Errs[0][2].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][1].Name", "TestMultiDimensionalStructsPtr2.Errs[1][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][2].Name", "TestMultiDimensionalStructsPtr2.Errs[1][2].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][1].Name", "TestMultiDimensionalStructsPtr2.Errs[2][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][2]", "TestMultiDimensionalStructsPtr2.Errs[2][2]", "Errs[2][2]", "Errs[2][2]", "required")
+
+ type TestMultiDimensionalStructsPtr3 struct {
+ Errs [][]*Inner `validate:"gt=0,dive,dive,omitempty"`
+ }
+
+ var errStructPtr3Array [][]*Inner
+
+ errStructPtr3Array = append(errStructPtr3Array, []*Inner{{"ok"}, {""}, {""}})
+ errStructPtr3Array = append(errStructPtr3Array, []*Inner{{"ok"}, {""}, {""}})
+ errStructPtr3Array = append(errStructPtr3Array, []*Inner{{"ok"}, {""}, nil})
+
+ tmsp3 := &TestMultiDimensionalStructsPtr3{
+ Errs: errStructPtr3Array,
+ }
+
+ errs = validate.Struct(tmsp3)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 5)
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[0][1].Name", "TestMultiDimensionalStructsPtr3.Errs[0][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[0][2].Name", "TestMultiDimensionalStructsPtr3.Errs[0][2].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[1][1].Name", "TestMultiDimensionalStructsPtr3.Errs[1][1].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[1][2].Name", "TestMultiDimensionalStructsPtr3.Errs[1][2].Name", "Name", "Name", "required")
+ AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[2][1].Name", "TestMultiDimensionalStructsPtr3.Errs[2][1].Name", "Name", "Name", "required")
+
+ type TestMultiDimensionalTimeTime struct {
+ Errs [][]*time.Time `validate:"gt=0,dive,dive,required"`
+ }
+
+ var errTimePtr3Array [][]*time.Time
+
+ t1 := time.Now().UTC()
+ t2 := time.Now().UTC()
+ t3 := time.Now().UTC().Add(time.Hour * 24)
+
+ errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, &t3})
+ errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, nil})
+ errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, nil, nil})
+
+ tmtp3 := &TestMultiDimensionalTimeTime{
+ Errs: errTimePtr3Array,
+ }
+
+ errs = validate.Struct(tmtp3)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 3)
+ AssertError(t, errs, "TestMultiDimensionalTimeTime.Errs[1][2]", "TestMultiDimensionalTimeTime.Errs[1][2]", "Errs[1][2]", "Errs[1][2]", "required")
+ AssertError(t, errs, "TestMultiDimensionalTimeTime.Errs[2][1]", "TestMultiDimensionalTimeTime.Errs[2][1]", "Errs[2][1]", "Errs[2][1]", "required")
+ AssertError(t, errs, "TestMultiDimensionalTimeTime.Errs[2][2]", "TestMultiDimensionalTimeTime.Errs[2][2]", "Errs[2][2]", "Errs[2][2]", "required")
+
+ type TestMultiDimensionalTimeTime2 struct {
+ Errs [][]*time.Time `validate:"gt=0,dive,dive,required"`
+ }
+
+ var errTimeArray [][]*time.Time
+
+ t1 = time.Now().UTC()
+ t2 = time.Now().UTC()
+ t3 = time.Now().UTC().Add(time.Hour * 24)
+
+ errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, &t3})
+ errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, nil})
+ errTimeArray = append(errTimeArray, []*time.Time{&t1, nil, nil})
+
+ tmtp := &TestMultiDimensionalTimeTime2{
+ Errs: errTimeArray,
+ }
+
+ errs = validate.Struct(tmtp)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 3)
+ AssertError(t, errs, "TestMultiDimensionalTimeTime2.Errs[1][2]", "TestMultiDimensionalTimeTime2.Errs[1][2]", "Errs[1][2]", "Errs[1][2]", "required")
+ AssertError(t, errs, "TestMultiDimensionalTimeTime2.Errs[2][1]", "TestMultiDimensionalTimeTime2.Errs[2][1]", "Errs[2][1]", "Errs[2][1]", "required")
+ AssertError(t, errs, "TestMultiDimensionalTimeTime2.Errs[2][2]", "TestMultiDimensionalTimeTime2.Errs[2][2]", "Errs[2][2]", "Errs[2][2]", "required")
+}
+
+func TestNilStructPointerValidation(t *testing.T) {
+ type Inner struct {
+ Data string
+ }
+
+ type Outer struct {
+ Inner *Inner `validate:"omitempty"`
+ }
+
+ inner := &Inner{
+ Data: "test",
+ }
+
+ outer := &Outer{
+ Inner: inner,
+ }
+
+ validate := New()
+ errs := validate.Struct(outer)
+ Equal(t, errs, nil)
+
+ outer = &Outer{
+ Inner: nil,
+ }
+
+ errs = validate.Struct(outer)
+ Equal(t, errs, nil)
+
+ type Inner2 struct {
+ Data string
+ }
+
+ type Outer2 struct {
+ Inner2 *Inner2 `validate:"required"`
+ }
+
+ inner2 := &Inner2{
+ Data: "test",
+ }
+
+ outer2 := &Outer2{
+ Inner2: inner2,
+ }
+
+ errs = validate.Struct(outer2)
+ Equal(t, errs, nil)
+
+ outer2 = &Outer2{
+ Inner2: nil,
+ }
+
+ errs = validate.Struct(outer2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Outer2.Inner2", "Outer2.Inner2", "Inner2", "Inner2", "required")
+
+ type Inner3 struct {
+ Data string
+ }
+
+ type Outer3 struct {
+ Inner3 *Inner3
+ }
+
+ inner3 := &Inner3{
+ Data: "test",
+ }
+
+ outer3 := &Outer3{
+ Inner3: inner3,
+ }
+
+ errs = validate.Struct(outer3)
+ Equal(t, errs, nil)
+
+ type Inner4 struct {
+ Data string
+ }
+
+ type Outer4 struct {
+ Inner4 *Inner4 `validate:"-"`
+ }
+
+ inner4 := &Inner4{
+ Data: "test",
+ }
+
+ outer4 := &Outer4{
+ Inner4: inner4,
+ }
+
+ errs = validate.Struct(outer4)
+ Equal(t, errs, nil)
+}
+
+func TestSSNValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"00-90-8787", false},
+ {"66690-76", false},
+ {"191 60 2869", true},
+ {"191-60-2869", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "ssn")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d SSN failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d SSN failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "ssn" {
+ t.Fatalf("Index: %d Latitude failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestLongitudeValidation(t *testing.T) {
+ tests := []struct {
+ param interface{}
+ expected bool
+ }{
+ {"", false},
+ {"-180.000", true},
+ {"180.1", false},
+ {"+73.234", true},
+ {"+382.3811", false},
+ {"23.11111111", true},
+ {uint(180), true},
+ {float32(-180.0), true},
+ {-180, true},
+ {180.1, false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "longitude")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Longitude failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Longitude failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "longitude" {
+ t.Fatalf("Index: %d Longitude failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+
+ PanicMatches(t, func() { _ = validate.Var(true, "longitude") }, "Bad field type bool")
+}
+
+func TestLatitudeValidation(t *testing.T) {
+ tests := []struct {
+ param interface{}
+ expected bool
+ }{
+ {"", false},
+ {"-90.000", true},
+ {"+90", true},
+ {"47.1231231", true},
+ {"+99.9", false},
+ {"108", false},
+ {uint(90), true},
+ {float32(-90.0), true},
+ {-90, true},
+ {90.1, false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "latitude")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Latitude failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Latitude failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "latitude" {
+ t.Fatalf("Index: %d Latitude failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+
+ PanicMatches(t, func() { _ = validate.Var(true, "latitude") }, "Bad field type bool")
+}
+
+func TestDataURIValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"data:image/png;base64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=", true},
+ {"data:text/plain;base64,Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==", true},
+ {"image/gif;base64,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false},
+ {"data:image/gif;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" +
+ "UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" +
+ "rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" +
+ "FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" +
+ "QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" +
+ "Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true},
+ {"data:image/png;base64,12345", false},
+ {"", false},
+ {"data:text,:;base85,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false},
+ {"data:image/jpeg;key=value;base64,UEsDBBQAAAAI", true},
+ {"data:image/jpeg;key=value,UEsDBBQAAAAI", true},
+ {"data:;base64;sdfgsdfgsdfasdfa=s,UEsDBBQAAAAI", true},
+ {"data:,UEsDBBQAAAAI", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+ errs := validate.Var(test.param, "datauri")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d DataURI failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d DataURI failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "datauri" {
+ t.Fatalf("Index: %d DataURI failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestMultibyteValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", true},
+ {"abc", false},
+ {"123", false},
+ {"<>@;.-=", false},
+ {"ひらがな・カタカナ、.漢字", true},
+ {"あいうえお foobar", true},
+ {"test@example.com", true},
+ {"test@example.com", true},
+ {"1234abcDExyz", true},
+ {"カタカナ", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "multibyte")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Multibyte failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Multibyte failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "multibyte" {
+ t.Fatalf("Index: %d Multibyte failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestPrintableASCIIValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", true},
+ {"foobar", false},
+ {"xyz098", false},
+ {"123456", false},
+ {"カタカナ", false},
+ {"foobar", true},
+ {"0987654321", true},
+ {"test@example.com", true},
+ {"1234abcDEF", true},
+ {"newline\n", false},
+ {"\x19test\x7F", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "printascii")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "printascii" {
+ t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestASCIIValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", true},
+ {"foobar", false},
+ {"xyz098", false},
+ {"123456", false},
+ {"カタカナ", false},
+ {"foobar", true},
+ {"0987654321", true},
+ {"test@example.com", true},
+ {"1234abcDEF", true},
+ {"", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "ascii")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ASCII failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ASCII failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "ascii" {
+ t.Fatalf("Index: %d ASCII failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUUID5Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+
+ {"", false},
+ {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
+ {"9c858901-8a57-4791-81fe-4c455b099bc9", false},
+ {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
+ {"987fbc97-4bed-5078-af07-9141ba07c9f3", true},
+ {"987fbc97-4bed-5078-9f07-9141ba07c9f3", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "uuid5")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID5 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID5 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "uuid5" {
+ t.Fatalf("Index: %d UUID5 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUUID4Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
+ {"a987fbc9-4bed-5078-af07-9141ba07c9f3", false},
+ {"934859", false},
+ {"57b73598-8764-4ad0-a76a-679bb6640eb1", true},
+ {"625e63f3-58f5-40b7-83a1-a72ad31acffb", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "uuid4")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID4 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID4 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "uuid4" {
+ t.Fatalf("Index: %d UUID4 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUUID3Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"412452646", false},
+ {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
+ {"a987fbc9-4bed-4078-8f07-9141ba07c9f3", false},
+ {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "uuid3")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID3 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID3 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "uuid3" {
+ t.Fatalf("Index: %d UUID3 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUUIDValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
+ {"a987fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false},
+ {"a987fbc94bed3078cf079141ba07c9f3", false},
+ {"934859", false},
+ {"987fbc9-4bed-3078-cf07a-9141ba07c9f3", false},
+ {"aaaaaaaa-1111-1111-aaag-111111111111", false},
+ {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "uuid")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "uuid" {
+ t.Fatalf("Index: %d UUID failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUUID5RFC4122Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+
+ {"", false},
+ {"xxxa987Fbc9-4bed-3078-cf07-9141ba07c9f3", false},
+ {"9c858901-8a57-4791-81Fe-4c455b099bc9", false},
+ {"a987Fbc9-4bed-3078-cf07-9141ba07c9f3", false},
+ {"987Fbc97-4bed-5078-af07-9141ba07c9f3", true},
+ {"987Fbc97-4bed-5078-9f07-9141ba07c9f3", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "uuid5_rfc4122")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID5RFC4122 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID5RFC4122 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "uuid5_rfc4122" {
+ t.Fatalf("Index: %d UUID5RFC4122 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUUID4RFC4122Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9F3", false},
+ {"a987fbc9-4bed-5078-af07-9141ba07c9F3", false},
+ {"934859", false},
+ {"57b73598-8764-4ad0-a76A-679bb6640eb1", true},
+ {"625e63f3-58f5-40b7-83a1-a72ad31acFfb", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "uuid4_rfc4122")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID4RFC4122 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID4RFC4122 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "uuid4_rfc4122" {
+ t.Fatalf("Index: %d UUID4RFC4122 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUUID3RFC4122Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"412452646", false},
+ {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9F3", false},
+ {"a987fbc9-4bed-4078-8f07-9141ba07c9F3", false},
+ {"a987fbc9-4bed-3078-cf07-9141ba07c9F3", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "uuid3_rfc4122")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID3RFC4122 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUID3RFC4122 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "uuid3_rfc4122" {
+ t.Fatalf("Index: %d UUID3RFC4122 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestUUIDRFC4122Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"xxxa987Fbc9-4bed-3078-cf07-9141ba07c9f3", false},
+ {"a987Fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false},
+ {"a987Fbc94bed3078cf079141ba07c9f3", false},
+ {"934859", false},
+ {"987fbc9-4bed-3078-cf07a-9141ba07c9F3", false},
+ {"aaaaaaaa-1111-1111-aaaG-111111111111", false},
+ {"a987Fbc9-4bed-3078-cf07-9141ba07c9f3", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "uuid_rfc4122")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUIDRFC4122 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d UUIDRFC4122 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "uuid_rfc4122" {
+ t.Fatalf("Index: %d UUIDRFC4122 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestISBNValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"foo", false},
+ {"3836221195", true},
+ {"1-61729-085-8", true},
+ {"3 423 21412 0", true},
+ {"3 401 01319 X", true},
+ {"9784873113685", true},
+ {"978-4-87311-368-5", true},
+ {"978 3401013190", true},
+ {"978-3-8362-2119-1", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "isbn")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ISBN failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ISBN failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "isbn" {
+ t.Fatalf("Index: %d ISBN failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestISBN13Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"foo", false},
+ {"3-8362-2119-5", false},
+ {"01234567890ab", false},
+ {"978 3 8362 2119 0", false},
+ {"9784873113685", true},
+ {"978-4-87311-368-5", true},
+ {"978 3401013190", true},
+ {"978-3-8362-2119-1", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "isbn13")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ISBN13 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ISBN13 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "isbn13" {
+ t.Fatalf("Index: %d ISBN13 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestISBN10Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"foo", false},
+ {"3423214121", false},
+ {"978-3836221191", false},
+ {"3-423-21412-1", false},
+ {"3 423 21412 1", false},
+ {"3836221195", true},
+ {"1-61729-085-8", true},
+ {"3 423 21412 0", true},
+ {"3 401 01319 X", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "isbn10")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ISBN10 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d ISBN10 failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "isbn10" {
+ t.Fatalf("Index: %d ISBN10 failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestExcludesRuneValidation(t *testing.T) {
+
+ tests := []struct {
+ Value string `validate:"excludesrune=☻"`
+ Tag string
+ ExpectedNil bool
+ }{
+ {Value: "a☺b☻c☹d", Tag: "excludesrune=☻", ExpectedNil: false},
+ {Value: "abcd", Tag: "excludesrune=☻", ExpectedNil: true},
+ }
+
+ validate := New()
+
+ for i, s := range tests {
+ errs := validate.Var(s.Value, s.Tag)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+
+ errs = validate.Struct(s)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+ }
+}
+
+func TestExcludesAllValidation(t *testing.T) {
+
+ tests := []struct {
+ Value string `validate:"excludesall=@!{}[]"`
+ Tag string
+ ExpectedNil bool
+ }{
+ {Value: "abcd@!jfk", Tag: "excludesall=@!{}[]", ExpectedNil: false},
+ {Value: "abcdefg", Tag: "excludesall=@!{}[]", ExpectedNil: true},
+ }
+
+ validate := New()
+
+ for i, s := range tests {
+ errs := validate.Var(s.Value, s.Tag)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+
+ errs = validate.Struct(s)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+ }
+
+ username := "joeybloggs "
+
+ errs := validate.Var(username, "excludesall=@ ")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "excludesall")
+
+ excluded := ","
+
+ errs = validate.Var(excluded, "excludesall=!@#$%^&*()_+.0x2C?")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "excludesall")
+
+ excluded = "="
+
+ errs = validate.Var(excluded, "excludesall=!@#$%^&*()_+.0x2C=?")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "excludesall")
+}
+
+func TestExcludesValidation(t *testing.T) {
+
+ tests := []struct {
+ Value string `validate:"excludes=@"`
+ Tag string
+ ExpectedNil bool
+ }{
+ {Value: "abcd@!jfk", Tag: "excludes=@", ExpectedNil: false},
+ {Value: "abcdq!jfk", Tag: "excludes=@", ExpectedNil: true},
+ }
+
+ validate := New()
+
+ for i, s := range tests {
+ errs := validate.Var(s.Value, s.Tag)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+
+ errs = validate.Struct(s)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+ }
+}
+
+func TestContainsRuneValidation(t *testing.T) {
+
+ tests := []struct {
+ Value string `validate:"containsrune=☻"`
+ Tag string
+ ExpectedNil bool
+ }{
+ {Value: "a☺b☻c☹d", Tag: "containsrune=☻", ExpectedNil: true},
+ {Value: "abcd", Tag: "containsrune=☻", ExpectedNil: false},
+ }
+
+ validate := New()
+
+ for i, s := range tests {
+ errs := validate.Var(s.Value, s.Tag)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+
+ errs = validate.Struct(s)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+ }
+}
+
+func TestContainsAnyValidation(t *testing.T) {
+
+ tests := []struct {
+ Value string `validate:"containsany=@!{}[]"`
+ Tag string
+ ExpectedNil bool
+ }{
+ {Value: "abcd@!jfk", Tag: "containsany=@!{}[]", ExpectedNil: true},
+ {Value: "abcdefg", Tag: "containsany=@!{}[]", ExpectedNil: false},
+ }
+
+ validate := New()
+
+ for i, s := range tests {
+ errs := validate.Var(s.Value, s.Tag)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+
+ errs = validate.Struct(s)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+ }
+}
+
+func TestContainsValidation(t *testing.T) {
+
+ tests := []struct {
+ Value string `validate:"contains=@"`
+ Tag string
+ ExpectedNil bool
+ }{
+ {Value: "abcd@!jfk", Tag: "contains=@", ExpectedNil: true},
+ {Value: "abcdq!jfk", Tag: "contains=@", ExpectedNil: false},
+ }
+
+ validate := New()
+
+ for i, s := range tests {
+ errs := validate.Var(s.Value, s.Tag)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+
+ errs = validate.Struct(s)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+ }
+}
+
+func TestIsNeFieldValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ var j uint64
+ var k float64
+ s := "abcd"
+ i := 1
+ j = 1
+ k = 1.543
+ arr := []string{"test"}
+ now := time.Now().UTC()
+
+ var j2 uint64
+ var k2 float64
+ s2 := "abcdef"
+ i2 := 3
+ j2 = 2
+ k2 = 1.5434456
+ arr2 := []string{"test", "test2"}
+ arr3 := []string{"test"}
+ now2 := now
+
+ errs = validate.VarWithValue(s, s2, "nefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(i2, i, "nefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(j2, j, "nefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(k2, k, "nefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(arr2, arr, "nefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(now2, now, "nefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "nefield")
+
+ errs = validate.VarWithValue(arr3, arr, "nefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "nefield")
+
+ type Test struct {
+ Start *time.Time `validate:"nefield=End"`
+ End *time.Time
+ }
+
+ sv := &Test{
+ Start: &now,
+ End: &now,
+ }
+
+ errs = validate.Struct(sv)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.Start", "Test.Start", "Start", "Start", "nefield")
+
+ now3 := time.Now().UTC()
+
+ sv = &Test{
+ Start: &now,
+ End: &now3,
+ }
+
+ errs = validate.Struct(sv)
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(nil, 1, "nefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "nefield")
+
+ errs = validate.VarWithValue(sv, now, "nefield")
+ Equal(t, errs, nil)
+
+ type Test2 struct {
+ Start *time.Time `validate:"nefield=NonExistantField"`
+ End *time.Time
+ }
+
+ sv2 := &Test2{
+ Start: &now,
+ End: &now,
+ }
+
+ errs = validate.Struct(sv2)
+ Equal(t, errs, nil)
+
+ type Other struct {
+ Value string
+ }
+
+ type Test3 struct {
+ Value Other
+ Time time.Time `validate:"nefield=Value"`
+ }
+
+ tst := Test3{
+ Value: Other{Value: "StringVal"},
+ Time: now,
+ }
+
+ errs = validate.Struct(tst)
+ Equal(t, errs, nil)
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "nefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "nefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "nefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "nefield")
+
+ errs = validate.VarWithValue(time.Duration(0), time.Duration(0), "omitempty,nefield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with time.Duration type fields.
+
+ type TimeDurationTest struct {
+ First time.Duration `validate:"nefield=Second"`
+ Second time.Duration
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "nefield")
+
+ type TimeDurationOmitemptyTest struct {
+ First time.Duration `validate:"omitempty,nefield=Second"`
+ Second time.Duration
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestIsNeValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ var j uint64
+ var k float64
+ s := "abcdef"
+ i := 3
+ j = 2
+ k = 1.5434
+ arr := []string{"test"}
+ now := time.Now().UTC()
+
+ errs = validate.Var(s, "ne=abcd")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(i, "ne=1")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(j, "ne=1")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(k, "ne=1.543")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(arr, "ne=2")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(arr, "ne=1")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ne")
+
+ PanicMatches(t, func() { _ = validate.Var(now, "ne=now") }, "Bad field type time.Time")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for a variable of time.Duration type.
+
+ errs = validate.Var(time.Hour-time.Minute, "ne=1h")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour+time.Minute, "ne=1h")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour, "ne=1h")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ne")
+
+ errs = validate.Var(time.Duration(0), "omitempty,ne=0")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with a time.Duration type field.
+
+ type TimeDurationTest struct {
+ Duration time.Duration `validate:"ne=1h"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "ne")
+
+ type TimeDurationOmitemptyTest struct {
+ Duration time.Duration `validate:"omitempty,ne=0"`
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestIsEqFieldValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ var j uint64
+ var k float64
+ s := "abcd"
+ i := 1
+ j = 1
+ k = 1.543
+ arr := []string{"test"}
+ now := time.Now().UTC()
+
+ var j2 uint64
+ var k2 float64
+ s2 := "abcd"
+ i2 := 1
+ j2 = 1
+ k2 = 1.543
+ arr2 := []string{"test"}
+ arr3 := []string{"test", "test2"}
+ now2 := now
+
+ errs = validate.VarWithValue(s, s2, "eqfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(i2, i, "eqfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(j2, j, "eqfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(k2, k, "eqfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(arr2, arr, "eqfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(now2, now, "eqfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(arr3, arr, "eqfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eqfield")
+
+ type Test struct {
+ Start *time.Time `validate:"eqfield=End"`
+ End *time.Time
+ }
+
+ sv := &Test{
+ Start: &now,
+ End: &now,
+ }
+
+ errs = validate.Struct(sv)
+ Equal(t, errs, nil)
+
+ now3 := time.Now().UTC()
+
+ sv = &Test{
+ Start: &now,
+ End: &now3,
+ }
+
+ errs = validate.Struct(sv)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.Start", "Test.Start", "Start", "Start", "eqfield")
+
+ errs = validate.VarWithValue(nil, 1, "eqfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eqfield")
+
+ channel := make(chan string)
+ errs = validate.VarWithValue(5, channel, "eqfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eqfield")
+
+ errs = validate.VarWithValue(5, now, "eqfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eqfield")
+
+ type Test2 struct {
+ Start *time.Time `validate:"eqfield=NonExistantField"`
+ End *time.Time
+ }
+
+ sv2 := &Test2{
+ Start: &now,
+ End: &now,
+ }
+
+ errs = validate.Struct(sv2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test2.Start", "Test2.Start", "Start", "Start", "eqfield")
+
+ type Inner struct {
+ Name string
+ }
+
+ type TStruct struct {
+ Inner *Inner
+ CreatedAt *time.Time `validate:"eqfield=Inner"`
+ }
+
+ inner := &Inner{
+ Name: "NAME",
+ }
+
+ test := &TStruct{
+ Inner: inner,
+ CreatedAt: &now,
+ }
+
+ errs = validate.Struct(test)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TStruct.CreatedAt", "TStruct.CreatedAt", "CreatedAt", "CreatedAt", "eqfield")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "eqfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "eqfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eqfield")
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "eqfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eqfield")
+
+ errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,eqfield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with time.Duration type fields.
+
+ type TimeDurationTest struct {
+ First time.Duration `validate:"eqfield=Second"`
+ Second time.Duration
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "eqfield")
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "eqfield")
+
+ type TimeDurationOmitemptyTest struct {
+ First time.Duration `validate:"omitempty,eqfield=Second"`
+ Second time.Duration
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), time.Hour}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestIsEqValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ var j uint64
+ var k float64
+ s := "abcd"
+ i := 1
+ j = 1
+ k = 1.543
+ arr := []string{"test"}
+ now := time.Now().UTC()
+
+ errs = validate.Var(s, "eq=abcd")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(i, "eq=1")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(j, "eq=1")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(k, "eq=1.543")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(arr, "eq=1")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(arr, "eq=2")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eq")
+
+ PanicMatches(t, func() { _ = validate.Var(now, "eq=now") }, "Bad field type time.Time")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for a variable of time.Duration type.
+
+ errs = validate.Var(time.Hour, "eq=1h")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour-time.Minute, "eq=1h")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eq")
+
+ errs = validate.Var(time.Hour+time.Minute, "eq=1h")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "eq")
+
+ errs = validate.Var(time.Duration(0), "omitempty,eq=1h")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with a time.Duration type field.
+
+ type TimeDurationTest struct {
+ Duration time.Duration `validate:"eq=1h"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "eq")
+
+ timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "eq")
+
+ type TimeDurationOmitemptyTest struct {
+ Duration time.Duration `validate:"omitempty,eq=1h"`
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestOneOfValidation(t *testing.T) {
+ validate := New()
+
+ passSpecs := []struct {
+ f interface{}
+ t string
+ }{
+ {f: "red", t: "oneof=red green"},
+ {f: "green", t: "oneof=red green"},
+ {f: "red green", t: "oneof='red green' blue"},
+ {f: "blue", t: "oneof='red green' blue"},
+ {f: 5, t: "oneof=5 6"},
+ {f: 6, t: "oneof=5 6"},
+ {f: int8(6), t: "oneof=5 6"},
+ {f: int16(6), t: "oneof=5 6"},
+ {f: int32(6), t: "oneof=5 6"},
+ {f: int64(6), t: "oneof=5 6"},
+ {f: uint(6), t: "oneof=5 6"},
+ {f: uint8(6), t: "oneof=5 6"},
+ {f: uint16(6), t: "oneof=5 6"},
+ {f: uint32(6), t: "oneof=5 6"},
+ {f: uint64(6), t: "oneof=5 6"},
+ }
+
+ for _, spec := range passSpecs {
+ t.Logf("%#v", spec)
+ errs := validate.Var(spec.f, spec.t)
+ Equal(t, errs, nil)
+ }
+
+ failSpecs := []struct {
+ f interface{}
+ t string
+ }{
+ {f: "", t: "oneof=red green"},
+ {f: "yellow", t: "oneof=red green"},
+ {f: "green", t: "oneof='red green' blue"},
+ {f: 5, t: "oneof=red green"},
+ {f: 6, t: "oneof=red green"},
+ {f: 6, t: "oneof=7"},
+ {f: uint(6), t: "oneof=7"},
+ {f: int8(5), t: "oneof=red green"},
+ {f: int16(5), t: "oneof=red green"},
+ {f: int32(5), t: "oneof=red green"},
+ {f: int64(5), t: "oneof=red green"},
+ {f: uint(5), t: "oneof=red green"},
+ {f: uint8(5), t: "oneof=red green"},
+ {f: uint16(5), t: "oneof=red green"},
+ {f: uint32(5), t: "oneof=red green"},
+ {f: uint64(5), t: "oneof=red green"},
+ }
+
+ for _, spec := range failSpecs {
+ t.Logf("%#v", spec)
+ errs := validate.Var(spec.f, spec.t)
+ AssertError(t, errs, "", "", "", "", "oneof")
+ }
+
+ PanicMatches(t, func() {
+ _ = validate.Var(3.14, "oneof=red green")
+ }, "Bad field type float64")
+}
+
+func TestBase64Validation(t *testing.T) {
+
+ validate := New()
+
+ s := "dW5pY29ybg=="
+
+ errs := validate.Var(s, "base64")
+ Equal(t, errs, nil)
+
+ s = "dGhpIGlzIGEgdGVzdCBiYXNlNjQ="
+ errs = validate.Var(s, "base64")
+ Equal(t, errs, nil)
+
+ s = ""
+ errs = validate.Var(s, "base64")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "base64")
+
+ s = "dW5pY29ybg== foo bar"
+ errs = validate.Var(s, "base64")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "base64")
+}
+
+func TestBase64URLValidation(t *testing.T) {
+ validate := New()
+
+ testCases := []struct {
+ decoded, encoded string
+ success bool
+ }{
+ // empty string, although a valid base64 string, should fail
+ {"", "", false},
+ // invalid length
+ {"", "a", false},
+ // base64 with padding
+ {"f", "Zg==", true},
+ {"fo", "Zm8=", true},
+ // base64 without padding
+ {"foo", "Zm9v", true},
+ {"", "Zg", false},
+ {"", "Zm8", false},
+ // base64 URL safe encoding with invalid, special characters '+' and '/'
+ {"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+", false},
+ {"\x14\xfb\x9c\x03\xf9\x73", "FPucA/lz", false},
+ // base64 URL safe encoding with valid, special characters '-' and '_'
+ {"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l-", true},
+ {"\x14\xfb\x9c\x03\xf9\x73", "FPucA_lz", true},
+ // non base64 characters
+ {"", "@mc=", false},
+ {"", "Zm 9", false},
+ }
+ for _, tc := range testCases {
+ err := validate.Var(tc.encoded, "base64url")
+ if tc.success {
+ Equal(t, err, nil)
+ // make sure encoded value is decoded back to the expected value
+ d, innerErr := base64.URLEncoding.DecodeString(tc.encoded)
+ Equal(t, innerErr, nil)
+ Equal(t, tc.decoded, string(d))
+ } else {
+ NotEqual(t, err, nil)
+ if len(tc.encoded) > 0 {
+ // make sure that indeed the encoded value was faulty
+ _, err := base64.URLEncoding.DecodeString(tc.encoded)
+ NotEqual(t, err, nil)
+ }
+ }
+ }
+}
+
+func TestFileValidation(t *testing.T) {
+ validate := New()
+
+ tests := []struct {
+ title string
+ param string
+ expected bool
+ }{
+ {"empty path", "", false},
+ {"regular file", filepath.Join("testdata", "a.go"), true},
+ {"missing file", filepath.Join("testdata", "no.go"), false},
+ {"directory, not a file", "testdata", false},
+ }
+
+ for _, test := range tests {
+ errs := validate.Var(test.param, "file")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Test: '%s' failed Error: %s", test.title, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Test: '%s' failed Error: %s", test.title, errs)
+ }
+ }
+ }
+
+ PanicMatches(t, func() {
+ _ = validate.Var(6, "file")
+ }, "Bad field type int")
+}
+
+func TestEthereumAddressValidation(t *testing.T) {
+
+ validate := New()
+
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ // All caps.
+ {"0x52908400098527886E0F7030069857D2E4169EE7", true},
+ {"0x8617E340B3D01FA5F11F306F4090FD50E238070D", true},
+
+ // All lower.
+ {"0xde709f2102306220921060314715629080e2fb77", true},
+ {"0x27b1fdb04752bbc536007a920d24acb045561c26", true},
+ {"0x123f681646d4a755815f9cb19e1acc8565a0c2ac", true},
+
+ // Mixed case: runs checksum validation.
+ {"0x02F9AE5f22EA3fA88F05780B30385bECFacbf130", true},
+ {"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", true},
+ {"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", true},
+ {"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", true},
+ {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true},
+ {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDB", false}, // Invalid checksum.
+
+ // Other.
+ {"", false},
+ {"D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", false}, // Missing "0x" prefix.
+ {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDbc", false}, // More than 40 hex digits.
+ {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aD", false}, // Less than 40 hex digits.
+ {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDw", false}, // Invalid hex digit "w".
+ }
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "eth_addr")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d eth_addr failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d eth_addr failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "eth_addr" {
+ t.Fatalf("Index: %d Latitude failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestBitcoinAddressValidation(t *testing.T) {
+
+ validate := New()
+
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"x", false},
+ {"0x02F9AE5f22EA3fA88F05780B30385bEC", false},
+ {"1A1zP1ePQGefi2DMPTifTL5SLmv7DivfNa", false},
+ {"1P9RQEr2XeE3PEb44ZE35sfZRRW1JH8Uqx", false},
+ {"3P14159I73E4gFr7JterCCQh9QjiTjiZrG", false},
+ {"3P141597f3E4gFr7JterCCQh9QjiTjiZrG", false},
+ {"37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y", false},
+ {"dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv", false},
+ {"MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S", false},
+ {"rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf", false},
+ {"4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq", false},
+ {"7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb", false},
+ {"17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs", false},
+ {"KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3", false},
+ {"7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th", false},
+ {"cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va", false},
+ {"gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk", false},
+ {"emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs", false},
+ {"7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo", false},
+ {"1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso", false},
+ {"31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq", false},
+ {"DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN", false},
+ {"2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i", false},
+ {"7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos", false},
+ {"1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu", false},
+ {"2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb", false},
+ {"8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ", false},
+ {"163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ", false},
+ {"2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu", false},
+ {"461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU", false},
+ {"2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs", false},
+ {"cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn", false},
+ {"gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj", false},
+ {"nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny", false},
+ {"L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc", false},
+ {"7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ ", false},
+ {"2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP", false},
+ {"dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw", false},
+ {"HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX", false},
+ {"4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB", false},
+ {"31uEbMgunupShBVTewXjtqbBv5MndwfXhb", false},
+ {"175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W", false},
+ {"Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ", false},
+ {"Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs", false},
+ {"6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ", false},
+ {"giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4", false},
+ {"cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK", false},
+ {"37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig", false},
+ {"EsYbG4tWWWY45G31nox838qNdzksbPySWc", false},
+ {"nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT", false},
+ {"cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx", false},
+ {"1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde", false},
+ {"2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU", false},
+ {"ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf", false},
+ {"Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd", false},
+ {"2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED", false},
+ {"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", true},
+ {"1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", true},
+ {"1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", true},
+ {"1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", true},
+ {"1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", true},
+ {"19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", true},
+ {"13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", true},
+ {"1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", true},
+ {"3P14159f73E4gFr7JterCCQh9QjiTjiZrG", true},
+ {"3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", true},
+ {"3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", true},
+ {"3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", true},
+ {"33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", true},
+ {"3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", true},
+ {"37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", true},
+ {"3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", true},
+ {"12KYrjTdVGjFMtaxERSk3gphreJ5US8aUP", true},
+ {"12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y", true},
+ {"1oNLrsHnBcR6dpaBpwz3LSwutbUNkNSjs", true},
+ {"1SQHtwR5oJRKLfiWQ2APsAd9miUc4k2ez", true},
+ {"116CGDLddrZhMrTwhCVJXtXQpxygTT1kHd", true},
+ {"3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt", true},
+ }
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "btc_addr")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d btc_addr failed with Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d btc_addr failed with Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "btc_addr" {
+ t.Fatalf("Index: %d Latitude failed with Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestBitcoinBech32AddressValidation(t *testing.T) {
+
+ validate := New()
+
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"bc1rw5uspcuh", false},
+ {"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", false},
+ {"BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", false},
+ {"qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", false},
+ {"bc1rw5uspcuh", false},
+ {"bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", false},
+ {"BC1QW508d6QEJxTDG4y5R3ZArVARY0C5XW7KV8F3T4", false},
+ {"BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", false},
+ {"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", false},
+ {"bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", false},
+ {"bc1pw508d6qejxtdg4y5r3zarqfsj6c3", false},
+ {"bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", false},
+ {"bc1gmk9yu", false},
+ {"bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", false},
+ {"BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", true},
+ {"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", true},
+ {"bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", true},
+ {"BC1SW50QA3JX3S", true},
+ {"bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", true},
+ }
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "btc_addr_bech32")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d btc_addr_bech32 failed with Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d btc_addr_bech32 failed with Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "btc_addr_bech32" {
+ t.Fatalf("Index: %d Latitude failed with Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestNoStructLevelValidation(t *testing.T) {
+
+ type Inner struct {
+ Test string `validate:"len=5"`
+ }
+
+ type Outer struct {
+ InnerStruct *Inner `validate:"required,nostructlevel"`
+ }
+
+ outer := &Outer{
+ InnerStruct: nil,
+ }
+
+ validate := New()
+
+ errs := validate.Struct(outer)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Outer.InnerStruct", "Outer.InnerStruct", "InnerStruct", "InnerStruct", "required")
+
+ inner := &Inner{
+ Test: "1234",
+ }
+
+ outer = &Outer{
+ InnerStruct: inner,
+ }
+
+ errs = validate.Struct(outer)
+ Equal(t, errs, nil)
+}
+
+func TestStructOnlyValidation(t *testing.T) {
+
+ type Inner struct {
+ Test string `validate:"len=5"`
+ }
+
+ type Outer struct {
+ InnerStruct *Inner `validate:"required,structonly"`
+ }
+
+ outer := &Outer{
+ InnerStruct: nil,
+ }
+
+ validate := New()
+
+ errs := validate.Struct(outer)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Outer.InnerStruct", "Outer.InnerStruct", "InnerStruct", "InnerStruct", "required")
+
+ inner := &Inner{
+ Test: "1234",
+ }
+
+ outer = &Outer{
+ InnerStruct: inner,
+ }
+
+ errs = validate.Struct(outer)
+ Equal(t, errs, nil)
+
+ // Address houses a users address information
+ type Address struct {
+ Street string `validate:"required"`
+ City string `validate:"required"`
+ Planet string `validate:"required"`
+ Phone string `validate:"required"`
+ }
+
+ type User struct {
+ FirstName string `json:"fname"`
+ LastName string `json:"lname"`
+ Age uint8 `validate:"gte=0,lte=130"`
+ Number string `validate:"required,e164"`
+ Email string `validate:"required,email"`
+ FavouriteColor string `validate:"hexcolor|rgb|rgba"`
+ Addresses []*Address `validate:"required"` // a person can have a home and cottage...
+ Address Address `validate:"structonly"` // a person can have a home and cottage...
+ }
+
+ address := &Address{
+ Street: "Eavesdown Docks",
+ Planet: "Persphone",
+ Phone: "none",
+ City: "Unknown",
+ }
+
+ user := &User{
+ FirstName: "",
+ LastName: "",
+ Age: 45,
+ Number: "+1123456789",
+ Email: "Badger.Smith@gmail.com",
+ FavouriteColor: "#000",
+ Addresses: []*Address{address},
+ Address: Address{
+ // Street: "Eavesdown Docks",
+ Planet: "Persphone",
+ Phone: "none",
+ City: "Unknown",
+ },
+ }
+
+ errs = validate.Struct(user)
+ Equal(t, errs, nil)
+}
+
+func TestGtField(t *testing.T) {
+ var errs error
+ validate := New()
+
+ type TimeTest struct {
+ Start *time.Time `validate:"required,gt"`
+ End *time.Time `validate:"required,gt,gtfield=Start"`
+ }
+
+ now := time.Now()
+ start := now.Add(time.Hour * 24)
+ end := start.Add(time.Hour * 24)
+
+ timeTest := &TimeTest{
+ Start: &start,
+ End: &end,
+ }
+
+ errs = validate.Struct(timeTest)
+ Equal(t, errs, nil)
+
+ timeTest = &TimeTest{
+ Start: &end,
+ End: &start,
+ }
+
+ errs = validate.Struct(timeTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeTest.End", "TimeTest.End", "End", "End", "gtfield")
+
+ errs = validate.VarWithValue(&end, &start, "gtfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(&start, &end, "gtfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtfield")
+
+ errs = validate.VarWithValue(&end, &start, "gtfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(&timeTest, &end, "gtfield")
+ NotEqual(t, errs, nil)
+
+ errs = validate.VarWithValue("test bigger", "test", "gtfield")
+ Equal(t, errs, nil)
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "gtfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "gtfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtfield")
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "gtfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtfield")
+
+ errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,gtfield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with time.Duration type fields.
+
+ type TimeDurationTest struct {
+ First time.Duration `validate:"gtfield=Second"`
+ Second time.Duration
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "gtfield")
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "gtfield")
+
+ type TimeDurationOmitemptyTest struct {
+ First time.Duration `validate:"omitempty,gtfield=Second"`
+ Second time.Duration
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), time.Hour}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+
+ // Tests for Ints types.
+
+ type IntTest struct {
+ Val1 int `validate:"required"`
+ Val2 int `validate:"required,gtfield=Val1"`
+ }
+
+ intTest := &IntTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(intTest)
+ Equal(t, errs, nil)
+
+ intTest = &IntTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(intTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "IntTest.Val2", "IntTest.Val2", "Val2", "Val2", "gtfield")
+
+ errs = validate.VarWithValue(int(5), int(1), "gtfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(int(1), int(5), "gtfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtfield")
+
+ type UIntTest struct {
+ Val1 uint `validate:"required"`
+ Val2 uint `validate:"required,gtfield=Val1"`
+ }
+
+ uIntTest := &UIntTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(uIntTest)
+ Equal(t, errs, nil)
+
+ uIntTest = &UIntTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(uIntTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "UIntTest.Val2", "UIntTest.Val2", "Val2", "Val2", "gtfield")
+
+ errs = validate.VarWithValue(uint(5), uint(1), "gtfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(uint(1), uint(5), "gtfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtfield")
+
+ type FloatTest struct {
+ Val1 float64 `validate:"required"`
+ Val2 float64 `validate:"required,gtfield=Val1"`
+ }
+
+ floatTest := &FloatTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(floatTest)
+ Equal(t, errs, nil)
+
+ floatTest = &FloatTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(floatTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "FloatTest.Val2", "FloatTest.Val2", "Val2", "Val2", "gtfield")
+
+ errs = validate.VarWithValue(float32(5), float32(1), "gtfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(float32(1), float32(5), "gtfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtfield")
+
+ errs = validate.VarWithValue(nil, 1, "gtfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtfield")
+
+ errs = validate.VarWithValue(5, "T", "gtfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtfield")
+
+ errs = validate.VarWithValue(5, start, "gtfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtfield")
+
+ type TimeTest2 struct {
+ Start *time.Time `validate:"required"`
+ End *time.Time `validate:"required,gtfield=NonExistantField"`
+ }
+
+ timeTest2 := &TimeTest2{
+ Start: &start,
+ End: &end,
+ }
+
+ errs = validate.Struct(timeTest2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeTest2.End", "TimeTest2.End", "End", "End", "gtfield")
+
+ type Other struct {
+ Value string
+ }
+
+ type Test struct {
+ Value Other
+ Time time.Time `validate:"gtfield=Value"`
+ }
+
+ tst := Test{
+ Value: Other{Value: "StringVal"},
+ Time: end,
+ }
+
+ errs = validate.Struct(tst)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.Time", "Test.Time", "Time", "Time", "gtfield")
+}
+
+func TestLtField(t *testing.T) {
+ var errs error
+ validate := New()
+
+ type TimeTest struct {
+ Start *time.Time `validate:"required,lt,ltfield=End"`
+ End *time.Time `validate:"required,lt"`
+ }
+
+ now := time.Now()
+ start := now.Add(time.Hour * 24 * -1 * 2)
+ end := start.Add(time.Hour * 24)
+
+ timeTest := &TimeTest{
+ Start: &start,
+ End: &end,
+ }
+
+ errs = validate.Struct(timeTest)
+ Equal(t, errs, nil)
+
+ timeTest = &TimeTest{
+ Start: &end,
+ End: &start,
+ }
+
+ errs = validate.Struct(timeTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeTest.Start", "TimeTest.Start", "Start", "Start", "ltfield")
+
+ errs = validate.VarWithValue(&start, &end, "ltfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(&end, &start, "ltfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltfield")
+
+ errs = validate.VarWithValue(&end, timeTest, "ltfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltfield")
+
+ errs = validate.VarWithValue("tes", "test", "ltfield")
+ Equal(t, errs, nil)
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "ltfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "ltfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltfield")
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "ltfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltfield")
+
+ errs = validate.VarWithValue(time.Duration(0), -time.Minute, "omitempty,ltfield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with time.Duration type fields.
+
+ type TimeDurationTest struct {
+ First time.Duration `validate:"ltfield=Second"`
+ Second time.Duration
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "ltfield")
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "ltfield")
+
+ type TimeDurationOmitemptyTest struct {
+ First time.Duration `validate:"omitempty,ltfield=Second"`
+ Second time.Duration
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), -time.Minute}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+
+ // Tests for Ints types.
+
+ type IntTest struct {
+ Val1 int `validate:"required"`
+ Val2 int `validate:"required,ltfield=Val1"`
+ }
+
+ intTest := &IntTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(intTest)
+ Equal(t, errs, nil)
+
+ intTest = &IntTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(intTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "IntTest.Val2", "IntTest.Val2", "Val2", "Val2", "ltfield")
+
+ errs = validate.VarWithValue(int(1), int(5), "ltfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(int(5), int(1), "ltfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltfield")
+
+ type UIntTest struct {
+ Val1 uint `validate:"required"`
+ Val2 uint `validate:"required,ltfield=Val1"`
+ }
+
+ uIntTest := &UIntTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(uIntTest)
+ Equal(t, errs, nil)
+
+ uIntTest = &UIntTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(uIntTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "UIntTest.Val2", "UIntTest.Val2", "Val2", "Val2", "ltfield")
+
+ errs = validate.VarWithValue(uint(1), uint(5), "ltfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(uint(5), uint(1), "ltfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltfield")
+
+ type FloatTest struct {
+ Val1 float64 `validate:"required"`
+ Val2 float64 `validate:"required,ltfield=Val1"`
+ }
+
+ floatTest := &FloatTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(floatTest)
+ Equal(t, errs, nil)
+
+ floatTest = &FloatTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(floatTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "FloatTest.Val2", "FloatTest.Val2", "Val2", "Val2", "ltfield")
+
+ errs = validate.VarWithValue(float32(1), float32(5), "ltfield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(float32(5), float32(1), "ltfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltfield")
+
+ errs = validate.VarWithValue(nil, 5, "ltfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltfield")
+
+ errs = validate.VarWithValue(1, "T", "ltfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltfield")
+
+ errs = validate.VarWithValue(1, end, "ltfield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltfield")
+
+ type TimeTest2 struct {
+ Start *time.Time `validate:"required"`
+ End *time.Time `validate:"required,ltfield=NonExistantField"`
+ }
+
+ timeTest2 := &TimeTest2{
+ Start: &end,
+ End: &start,
+ }
+
+ errs = validate.Struct(timeTest2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeTest2.End", "TimeTest2.End", "End", "End", "ltfield")
+}
+
+func TestFieldContains(t *testing.T) {
+ validate := New()
+
+ type StringTest struct {
+ Foo string `validate:"fieldcontains=Bar"`
+ Bar string
+ }
+
+ stringTest := &StringTest{
+ Foo: "foobar",
+ Bar: "bar",
+ }
+
+ errs := validate.Struct(stringTest)
+ Equal(t, errs, nil)
+
+ stringTest = &StringTest{
+ Foo: "foo",
+ Bar: "bar",
+ }
+
+ errs = validate.Struct(stringTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "StringTest.Foo", "StringTest.Foo", "Foo", "Foo", "fieldcontains")
+
+ errs = validate.VarWithValue("foo", "bar", "fieldcontains")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "fieldcontains")
+
+ errs = validate.VarWithValue("bar", "foobarfoo", "fieldcontains")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "fieldcontains")
+
+ errs = validate.VarWithValue("foobarfoo", "bar", "fieldcontains")
+ Equal(t, errs, nil)
+
+ type StringTestMissingField struct {
+ Foo string `validate:"fieldcontains=Bar"`
+ }
+
+ stringTestMissingField := &StringTestMissingField{
+ Foo: "foo",
+ }
+
+ errs = validate.Struct(stringTestMissingField)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "StringTestMissingField.Foo", "StringTestMissingField.Foo", "Foo", "Foo", "fieldcontains")
+}
+
+func TestFieldExcludes(t *testing.T) {
+ validate := New()
+
+ type StringTest struct {
+ Foo string `validate:"fieldexcludes=Bar"`
+ Bar string
+ }
+
+ stringTest := &StringTest{
+ Foo: "foobar",
+ Bar: "bar",
+ }
+
+ errs := validate.Struct(stringTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "StringTest.Foo", "StringTest.Foo", "Foo", "Foo", "fieldexcludes")
+
+ stringTest = &StringTest{
+ Foo: "foo",
+ Bar: "bar",
+ }
+
+ errs = validate.Struct(stringTest)
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue("foo", "bar", "fieldexcludes")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue("bar", "foobarfoo", "fieldexcludes")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue("foobarfoo", "bar", "fieldexcludes")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "fieldexcludes")
+
+ type StringTestMissingField struct {
+ Foo string `validate:"fieldexcludes=Bar"`
+ }
+
+ stringTestMissingField := &StringTestMissingField{
+ Foo: "foo",
+ }
+
+ errs = validate.Struct(stringTestMissingField)
+ Equal(t, errs, nil)
+}
+
+func TestContainsAndExcludes(t *testing.T) {
+ validate := New()
+
+ type ImpossibleStringTest struct {
+ Foo string `validate:"fieldcontains=Bar"`
+ Bar string `validate:"fieldexcludes=Foo"`
+ }
+
+ impossibleStringTest := &ImpossibleStringTest{
+ Foo: "foo",
+ Bar: "bar",
+ }
+
+ errs := validate.Struct(impossibleStringTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "ImpossibleStringTest.Foo", "ImpossibleStringTest.Foo", "Foo", "Foo", "fieldcontains")
+
+ impossibleStringTest = &ImpossibleStringTest{
+ Foo: "bar",
+ Bar: "foo",
+ }
+
+ errs = validate.Struct(impossibleStringTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "ImpossibleStringTest.Foo", "ImpossibleStringTest.Foo", "Foo", "Foo", "fieldcontains")
+}
+
+func TestLteField(t *testing.T) {
+ var errs error
+ validate := New()
+
+ type TimeTest struct {
+ Start *time.Time `validate:"required,lte,ltefield=End"`
+ End *time.Time `validate:"required,lte"`
+ }
+
+ now := time.Now()
+ start := now.Add(time.Hour * 24 * -1 * 2)
+ end := start.Add(time.Hour * 24)
+
+ timeTest := &TimeTest{
+ Start: &start,
+ End: &end,
+ }
+
+ errs = validate.Struct(timeTest)
+ Equal(t, errs, nil)
+
+ timeTest = &TimeTest{
+ Start: &end,
+ End: &start,
+ }
+
+ errs = validate.Struct(timeTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeTest.Start", "TimeTest.Start", "Start", "Start", "ltefield")
+
+ errs = validate.VarWithValue(&start, &end, "ltefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(&end, &start, "ltefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltefield")
+
+ errs = validate.VarWithValue(&end, timeTest, "ltefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltefield")
+
+ errs = validate.VarWithValue("tes", "test", "ltefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue("test", "test", "ltefield")
+ Equal(t, errs, nil)
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "ltefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "ltefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "ltefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltefield")
+
+ errs = validate.VarWithValue(time.Duration(0), -time.Minute, "omitempty,ltefield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with time.Duration type fields.
+
+ type TimeDurationTest struct {
+ First time.Duration `validate:"ltefield=Second"`
+ Second time.Duration
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "ltefield")
+
+ type TimeDurationOmitemptyTest struct {
+ First time.Duration `validate:"omitempty,ltefield=Second"`
+ Second time.Duration
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), -time.Minute}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+
+ // Tests for Ints types.
+
+ type IntTest struct {
+ Val1 int `validate:"required"`
+ Val2 int `validate:"required,ltefield=Val1"`
+ }
+
+ intTest := &IntTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(intTest)
+ Equal(t, errs, nil)
+
+ intTest = &IntTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(intTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "IntTest.Val2", "IntTest.Val2", "Val2", "Val2", "ltefield")
+
+ errs = validate.VarWithValue(int(1), int(5), "ltefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(int(5), int(1), "ltefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltefield")
+
+ type UIntTest struct {
+ Val1 uint `validate:"required"`
+ Val2 uint `validate:"required,ltefield=Val1"`
+ }
+
+ uIntTest := &UIntTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(uIntTest)
+ Equal(t, errs, nil)
+
+ uIntTest = &UIntTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(uIntTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "UIntTest.Val2", "UIntTest.Val2", "Val2", "Val2", "ltefield")
+
+ errs = validate.VarWithValue(uint(1), uint(5), "ltefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(uint(5), uint(1), "ltefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltefield")
+
+ type FloatTest struct {
+ Val1 float64 `validate:"required"`
+ Val2 float64 `validate:"required,ltefield=Val1"`
+ }
+
+ floatTest := &FloatTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(floatTest)
+ Equal(t, errs, nil)
+
+ floatTest = &FloatTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(floatTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "FloatTest.Val2", "FloatTest.Val2", "Val2", "Val2", "ltefield")
+
+ errs = validate.VarWithValue(float32(1), float32(5), "ltefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(float32(5), float32(1), "ltefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltefield")
+
+ errs = validate.VarWithValue(nil, 5, "ltefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltefield")
+
+ errs = validate.VarWithValue(1, "T", "ltefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltefield")
+
+ errs = validate.VarWithValue(1, end, "ltefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "ltefield")
+
+ type TimeTest2 struct {
+ Start *time.Time `validate:"required"`
+ End *time.Time `validate:"required,ltefield=NonExistantField"`
+ }
+
+ timeTest2 := &TimeTest2{
+ Start: &end,
+ End: &start,
+ }
+
+ errs = validate.Struct(timeTest2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeTest2.End", "TimeTest2.End", "End", "End", "ltefield")
+}
+
+func TestGteField(t *testing.T) {
+ var errs error
+ validate := New()
+
+ type TimeTest struct {
+ Start *time.Time `validate:"required,gte"`
+ End *time.Time `validate:"required,gte,gtefield=Start"`
+ }
+
+ now := time.Now()
+ start := now.Add(time.Hour * 24)
+ end := start.Add(time.Hour * 24)
+
+ timeTest := &TimeTest{
+ Start: &start,
+ End: &end,
+ }
+
+ errs = validate.Struct(timeTest)
+ Equal(t, errs, nil)
+
+ timeTest = &TimeTest{
+ Start: &end,
+ End: &start,
+ }
+
+ errs = validate.Struct(timeTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeTest.End", "TimeTest.End", "End", "End", "gtefield")
+
+ errs = validate.VarWithValue(&end, &start, "gtefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(&start, &end, "gtefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtefield")
+
+ errs = validate.VarWithValue(&start, timeTest, "gtefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtefield")
+
+ errs = validate.VarWithValue("test", "test", "gtefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue("test bigger", "test", "gtefield")
+ Equal(t, errs, nil)
+
+ // Tests for time.Duration type.
+
+ // -- Validations for variables of time.Duration type.
+
+ errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "gtefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour, "gtefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "gtefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtefield")
+
+ errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,gtefield")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with time.Duration type fields.
+
+ type TimeDurationTest struct {
+ First time.Duration `validate:"gtefield=Second"`
+ Second time.Duration
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "gtefield")
+
+ type TimeDurationOmitemptyTest struct {
+ First time.Duration `validate:"omitempty,gtefield=Second"`
+ Second time.Duration
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), time.Hour}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+
+ // Tests for Ints types.
+
+ type IntTest struct {
+ Val1 int `validate:"required"`
+ Val2 int `validate:"required,gtefield=Val1"`
+ }
+
+ intTest := &IntTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(intTest)
+ Equal(t, errs, nil)
+
+ intTest = &IntTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(intTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "IntTest.Val2", "IntTest.Val2", "Val2", "Val2", "gtefield")
+
+ errs = validate.VarWithValue(int(5), int(1), "gtefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(int(1), int(5), "gtefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtefield")
+
+ type UIntTest struct {
+ Val1 uint `validate:"required"`
+ Val2 uint `validate:"required,gtefield=Val1"`
+ }
+
+ uIntTest := &UIntTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(uIntTest)
+ Equal(t, errs, nil)
+
+ uIntTest = &UIntTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(uIntTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "UIntTest.Val2", "UIntTest.Val2", "Val2", "Val2", "gtefield")
+
+ errs = validate.VarWithValue(uint(5), uint(1), "gtefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(uint(1), uint(5), "gtefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtefield")
+
+ type FloatTest struct {
+ Val1 float64 `validate:"required"`
+ Val2 float64 `validate:"required,gtefield=Val1"`
+ }
+
+ floatTest := &FloatTest{
+ Val1: 1,
+ Val2: 5,
+ }
+
+ errs = validate.Struct(floatTest)
+ Equal(t, errs, nil)
+
+ floatTest = &FloatTest{
+ Val1: 5,
+ Val2: 1,
+ }
+
+ errs = validate.Struct(floatTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "FloatTest.Val2", "FloatTest.Val2", "Val2", "Val2", "gtefield")
+
+ errs = validate.VarWithValue(float32(5), float32(1), "gtefield")
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(float32(1), float32(5), "gtefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtefield")
+
+ errs = validate.VarWithValue(nil, 1, "gtefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtefield")
+
+ errs = validate.VarWithValue(5, "T", "gtefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtefield")
+
+ errs = validate.VarWithValue(5, start, "gtefield")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gtefield")
+
+ type TimeTest2 struct {
+ Start *time.Time `validate:"required"`
+ End *time.Time `validate:"required,gtefield=NonExistantField"`
+ }
+
+ timeTest2 := &TimeTest2{
+ Start: &start,
+ End: &end,
+ }
+
+ errs = validate.Struct(timeTest2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeTest2.End", "TimeTest2.End", "End", "End", "gtefield")
+}
+
+func TestValidateByTagAndValue(t *testing.T) {
+ validate := New()
+
+ val := "test"
+ field := "test"
+ errs := validate.VarWithValue(val, field, "required")
+ Equal(t, errs, nil)
+
+ fn := func(fl FieldLevel) bool {
+
+ return fl.Parent().String() == fl.Field().String()
+ }
+
+ errs = validate.RegisterValidation("isequaltestfunc", fn)
+ Equal(t, errs, nil)
+
+ errs = validate.VarWithValue(val, field, "isequaltestfunc")
+ Equal(t, errs, nil)
+
+ val = "unequal"
+
+ errs = validate.VarWithValue(val, field, "isequaltestfunc")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "isequaltestfunc")
+}
+
+func TestAddFunctions(t *testing.T) {
+
+ fn := func(fl FieldLevel) bool {
+
+ return true
+ }
+
+ fnCtx := func(ctx context.Context, fl FieldLevel) bool {
+ return true
+ }
+
+ validate := New()
+
+ errs := validate.RegisterValidation("new", fn)
+ Equal(t, errs, nil)
+
+ errs = validate.RegisterValidation("", fn)
+ NotEqual(t, errs, nil)
+
+ errs = validate.RegisterValidation("new", nil)
+ NotEqual(t, errs, nil)
+
+ errs = validate.RegisterValidation("new", fn)
+ Equal(t, errs, nil)
+
+ errs = validate.RegisterValidationCtx("new", fnCtx)
+ Equal(t, errs, nil)
+
+ PanicMatches(t, func() { _ = validate.RegisterValidation("dive", fn) }, "Tag 'dive' either contains restricted characters or is the same as a restricted tag needed for normal operation")
+}
+
+func TestChangeTag(t *testing.T) {
+
+ validate := New()
+ validate.SetTagName("val")
+
+ type Test struct {
+ Name string `val:"len=4"`
+ }
+ s := &Test{
+ Name: "TEST",
+ }
+
+ errs := validate.Struct(s)
+ Equal(t, errs, nil)
+
+ s.Name = ""
+
+ errs = validate.Struct(s)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.Name", "Test.Name", "Name", "Name", "len")
+}
+
+func TestUnexposedStruct(t *testing.T) {
+ validate := New()
+
+ type Test struct {
+ Name string
+ unexposed struct {
+ A string `validate:"required"`
+ }
+ }
+
+ s := &Test{
+ Name: "TEST",
+ }
+ Equal(t, s.unexposed.A, "")
+
+ errs := validate.Struct(s)
+ Equal(t, errs, nil)
+}
+
+func TestBadParams(t *testing.T) {
+ validate := New()
+ i := 1
+ errs := validate.Var(i, "-")
+ Equal(t, errs, nil)
+
+ PanicMatches(t, func() { _ = validate.Var(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax")
+ PanicMatches(t, func() { _ = validate.Var(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax")
+
+ var ui uint = 1
+ PanicMatches(t, func() { _ = validate.Var(ui, "len=a") }, "strconv.ParseUint: parsing \"a\": invalid syntax")
+
+ f := 1.23
+ PanicMatches(t, func() { _ = validate.Var(f, "len=a") }, "strconv.ParseFloat: parsing \"a\": invalid syntax")
+}
+
+func TestLength(t *testing.T) {
+ validate := New()
+ i := true
+ PanicMatches(t, func() { _ = validate.Var(i, "len") }, "Bad field type bool")
+}
+
+func TestIsGt(t *testing.T) {
+ var errs error
+ validate := New()
+
+ myMap := map[string]string{}
+ errs = validate.Var(myMap, "gt=0")
+ NotEqual(t, errs, nil)
+
+ f := 1.23
+ errs = validate.Var(f, "gt=5")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gt")
+
+ var ui uint = 5
+ errs = validate.Var(ui, "gt=10")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gt")
+
+ i := true
+ PanicMatches(t, func() { _ = validate.Var(i, "gt") }, "Bad field type bool")
+
+ tm := time.Now().UTC()
+ tm = tm.Add(time.Hour * 24)
+
+ errs = validate.Var(tm, "gt")
+ Equal(t, errs, nil)
+
+ t2 := time.Now().UTC().Add(-time.Hour)
+
+ errs = validate.Var(t2, "gt")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gt")
+
+ type Test struct {
+ Now *time.Time `validate:"gt"`
+ }
+ s := &Test{
+ Now: &tm,
+ }
+
+ errs = validate.Struct(s)
+ Equal(t, errs, nil)
+
+ s = &Test{
+ Now: &t2,
+ }
+
+ errs = validate.Struct(s)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.Now", "Test.Now", "Now", "Now", "gt")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for a variable of time.Duration type.
+
+ errs = validate.Var(time.Hour, "gt=59m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour-time.Minute, "gt=59m")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gt")
+
+ errs = validate.Var(time.Hour-2*time.Minute, "gt=59m")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gt")
+
+ errs = validate.Var(time.Duration(0), "omitempty,gt=59m")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with a time.Duration type field.
+
+ type TimeDurationTest struct {
+ Duration time.Duration `validate:"gt=59m"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gt")
+
+ timeDurationTest = &TimeDurationTest{time.Hour - 2*time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gt")
+
+ type TimeDurationOmitemptyTest struct {
+ Duration time.Duration `validate:"omitempty,gt=59m"`
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestIsGte(t *testing.T) {
+ var errs error
+ validate := New()
+
+ i := true
+ PanicMatches(t, func() { _ = validate.Var(i, "gte") }, "Bad field type bool")
+
+ t1 := time.Now().UTC()
+ t1 = t1.Add(time.Hour * 24)
+
+ errs = validate.Var(t1, "gte")
+ Equal(t, errs, nil)
+
+ t2 := time.Now().UTC().Add(-time.Hour)
+
+ errs = validate.Var(t2, "gte")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gte")
+
+ type Test struct {
+ Now *time.Time `validate:"gte"`
+ }
+ s := &Test{
+ Now: &t1,
+ }
+
+ errs = validate.Struct(s)
+ Equal(t, errs, nil)
+
+ s = &Test{
+ Now: &t2,
+ }
+
+ errs = validate.Struct(s)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.Now", "Test.Now", "Now", "Now", "gte")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for a variable of time.Duration type.
+
+ errs = validate.Var(time.Hour, "gte=59m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour-time.Minute, "gte=59m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour-2*time.Minute, "gte=59m")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "gte")
+
+ errs = validate.Var(time.Duration(0), "omitempty,gte=59m")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with a time.Duration type field.
+
+ type TimeDurationTest struct {
+ Duration time.Duration `validate:"gte=59m"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour - 2*time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gte")
+
+ type TimeDurationOmitemptyTest struct {
+ Duration time.Duration `validate:"omitempty,gte=59m"`
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestMinValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ // Tests for time.Duration type.
+
+ // -- Validations for a variable of time.Duration type.
+
+ errs = validate.Var(time.Hour, "min=59m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour-time.Minute, "min=59m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour-2*time.Minute, "min=59m")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "min")
+
+ errs = validate.Var(time.Duration(0), "omitempty,min=59m")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with a time.Duration type field.
+
+ type TimeDurationTest struct {
+ Duration time.Duration `validate:"min=59m"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour - 2*time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "min")
+
+ type TimeDurationOmitemptyTest struct {
+ Duration time.Duration `validate:"omitempty,min=59m"`
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestMaxValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ // Tests for time.Duration type.
+
+ // -- Validations for a variable of time.Duration type.
+
+ errs = validate.Var(time.Hour, "max=1h1m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour+time.Minute, "max=1h1m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour+2*time.Minute, "max=1h1m")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "max")
+
+ errs = validate.Var(time.Duration(0), "omitempty,max=-1s")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with a time.Duration type field.
+
+ type TimeDurationTest struct {
+ Duration time.Duration `validate:"max=1h1m"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour + 2*time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "max")
+
+ type TimeDurationOmitemptyTest struct {
+ Duration time.Duration `validate:"omitempty,max=-1s"`
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestMinMaxValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ // Tests for time.Duration type.
+
+ // -- Validations for a variable of time.Duration type.
+
+ errs = validate.Var(time.Hour, "min=59m,max=1h1m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour-time.Minute, "min=59m,max=1h1m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour+time.Minute, "min=59m,max=1h1m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour-2*time.Minute, "min=59m,max=1h1m")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "min")
+
+ errs = validate.Var(time.Hour+2*time.Minute, "min=59m,max=1h1m")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "max")
+
+ errs = validate.Var(time.Duration(0), "omitempty,min=59m,max=1h1m")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with a time.Duration type field.
+
+ type TimeDurationTest struct {
+ Duration time.Duration `validate:"min=59m,max=1h1m"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour - 2*time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "min")
+
+ timeDurationTest = &TimeDurationTest{time.Hour + 2*time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "max")
+
+ type TimeDurationOmitemptyTest struct {
+ Duration time.Duration `validate:"omitempty,min=59m,max=1h1m"`
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestLenValidation(t *testing.T) {
+ var errs error
+ validate := New()
+
+ // Tests for time.Duration type.
+
+ // -- Validations for a variable of time.Duration type.
+
+ errs = validate.Var(time.Hour, "len=1h")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour-time.Minute, "len=1h")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "len")
+
+ errs = validate.Var(time.Hour+time.Minute, "len=1h")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "len")
+
+ errs = validate.Var(time.Duration(0), "omitempty,len=1h")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with a time.Duration type field.
+
+ type TimeDurationTest struct {
+ Duration time.Duration `validate:"len=1h"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "len")
+
+ timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "len")
+
+ type TimeDurationOmitemptyTest struct {
+ Duration time.Duration `validate:"omitempty,len=1h"`
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestIsLt(t *testing.T) {
+ var errs error
+ validate := New()
+
+ myMap := map[string]string{}
+ errs = validate.Var(myMap, "lt=0")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "lt")
+
+ f := 1.23
+ errs = validate.Var(f, "lt=0")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "lt")
+
+ var ui uint = 5
+ errs = validate.Var(ui, "lt=0")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "lt")
+
+ i := true
+ PanicMatches(t, func() { _ = validate.Var(i, "lt") }, "Bad field type bool")
+
+ t1 := time.Now().UTC().Add(-time.Hour)
+
+ errs = validate.Var(t1, "lt")
+ Equal(t, errs, nil)
+
+ t2 := time.Now().UTC()
+ t2 = t2.Add(time.Hour * 24)
+
+ errs = validate.Var(t2, "lt")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "lt")
+
+ type Test struct {
+ Now *time.Time `validate:"lt"`
+ }
+
+ s := &Test{
+ Now: &t1,
+ }
+
+ errs = validate.Struct(s)
+ Equal(t, errs, nil)
+
+ s = &Test{
+ Now: &t2,
+ }
+
+ errs = validate.Struct(s)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.Now", "Test.Now", "Now", "Now", "lt")
+
+ // Tests for time.Duration type.
+
+ // -- Validations for a variable of time.Duration type.
+
+ errs = validate.Var(time.Hour, "lt=1h1m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour+time.Minute, "lt=1h1m")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "lt")
+
+ errs = validate.Var(time.Hour+2*time.Minute, "lt=1h1m")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "lt")
+
+ errs = validate.Var(time.Duration(0), "omitempty,lt=0")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with a time.Duration type field.
+
+ type TimeDurationTest struct {
+ Duration time.Duration `validate:"lt=1h1m"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "lt")
+
+ timeDurationTest = &TimeDurationTest{time.Hour + 2*time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "lt")
+
+ type TimeDurationOmitemptyTest struct {
+ Duration time.Duration `validate:"omitempty,lt=0"`
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestIsLte(t *testing.T) {
+ var errs error
+ validate := New()
+
+ i := true
+ PanicMatches(t, func() { _ = validate.Var(i, "lte") }, "Bad field type bool")
+
+ t1 := time.Now().UTC().Add(-time.Hour)
+
+ errs = validate.Var(t1, "lte")
+ Equal(t, errs, nil)
+
+ t2 := time.Now().UTC()
+ t2 = t2.Add(time.Hour * 24)
+
+ errs = validate.Var(t2, "lte")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "lte")
+
+ type Test struct {
+ Now *time.Time `validate:"lte"`
+ }
+
+ s := &Test{
+ Now: &t1,
+ }
+
+ errs = validate.Struct(s)
+ Equal(t, errs, nil)
+
+ s = &Test{
+ Now: &t2,
+ }
+
+ errs = validate.Struct(s)
+ NotEqual(t, errs, nil)
+
+ // Tests for time.Duration type.
+
+ // -- Validations for a variable of time.Duration type.
+
+ errs = validate.Var(time.Hour, "lte=1h1m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour+time.Minute, "lte=1h1m")
+ Equal(t, errs, nil)
+
+ errs = validate.Var(time.Hour+2*time.Minute, "lte=1h1m")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "lte")
+
+ errs = validate.Var(time.Duration(0), "omitempty,lte=-1s")
+ Equal(t, errs, nil)
+
+ // -- Validations for a struct with a time.Duration type field.
+
+ type TimeDurationTest struct {
+ Duration time.Duration `validate:"lte=1h1m"`
+ }
+ var timeDurationTest *TimeDurationTest
+
+ timeDurationTest = &TimeDurationTest{time.Hour}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ Equal(t, errs, nil)
+
+ timeDurationTest = &TimeDurationTest{time.Hour + 2*time.Minute}
+ errs = validate.Struct(timeDurationTest)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "lte")
+
+ type TimeDurationOmitemptyTest struct {
+ Duration time.Duration `validate:"omitempty,lte=-1s"`
+ }
+
+ timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+ errs = validate.Struct(timeDurationOmitemptyTest)
+ Equal(t, errs, nil)
+}
+
+func TestUrnRFC2141(t *testing.T) {
+
+ var tests = []struct {
+ param string
+ expected bool
+ }{
+ {"urn:a:b", true},
+ {"urn:a::", true},
+ {"urn:a:-", true},
+ {"URN:simple:simple", true},
+ {"urn:urna:simple", true},
+ {"urn:burnout:nss", true},
+ {"urn:burn:nss", true},
+ {"urn:urnurnurn:x", true},
+ {"urn:abcdefghilmnopqrstuvzabcdefghilm:x", true},
+ {"URN:123:x", true},
+ {"URN:abcd-:x", true},
+ {"URN:abcd-abcd:x", true},
+ {"urn:urnx:urn", true},
+ {"urn:ciao:a:b:c", true},
+ {"urn:aaa:x:y:", true},
+ {"urn:ciao:-", true},
+ {"urn:colon:::::nss", true},
+ {"urn:ciao:@!=%2C(xyz)+a,b.*@g=$_'", true},
+ {"URN:hexes:%25", true},
+ {"URN:x:abc%1Dz%2F%3az", true},
+ {"URN:foo:a123,456", true},
+ {"urn:foo:a123,456", true},
+ {"urn:FOO:a123,456", true},
+ {"urn:foo:A123,456", true},
+ {"urn:foo:a123%2C456", true},
+ {"URN:FOO:a123%2c456", true},
+ {"URN:FOO:ABC%FFabc123%2c456", true},
+ {"URN:FOO:ABC%FFabc123%2C456%9A", true},
+ {"urn:ietf:params:scim:schemas:core:2.0:User", true},
+ {"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:meta.lastModified", true},
+ {"URN:-xxx:x", false},
+ {"urn::colon:nss", false},
+ {"urn:abcdefghilmnopqrstuvzabcdefghilmn:specificstring", false},
+ {"URN:a!?:x", false},
+ {"URN:#,:x", false},
+ {"urn:urn:NSS", false},
+ {"urn:URN:NSS", false},
+ {"urn:white space:NSS", false},
+ {"urn:concat:no spaces", false},
+ {"urn:a:%", false},
+ {"urn:", false},
+ }
+
+ tag := "urn_rfc2141"
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, tag)
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d URN failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d URN failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != tag {
+ t.Fatalf("Index: %d URN failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+
+ i := 1
+ PanicMatches(t, func() { _ = validate.Var(i, tag) }, "Bad field type int")
+}
+
+func TestUrl(t *testing.T) {
+
+ var tests = []struct {
+ param string
+ expected bool
+ }{
+ {"http://foo.bar#com", true},
+ {"http://foobar.com", true},
+ {"https://foobar.com", true},
+ {"foobar.com", false},
+ {"http://foobar.coffee/", true},
+ {"http://foobar.中文网/", true},
+ {"http://foobar.org/", true},
+ {"http://foobar.org:8080/", true},
+ {"ftp://foobar.ru/", true},
+ {"http://user:pass@www.foobar.com/", true},
+ {"http://127.0.0.1/", true},
+ {"http://duckduckgo.com/?q=%2F", true},
+ {"http://localhost:3000/", true},
+ {"http://foobar.com/?foo=bar#baz=qux", true},
+ {"http://foobar.com?foo=bar", true},
+ {"http://www.xn--froschgrn-x9a.net/", true},
+ {"", false},
+ {"xyz://foobar.com", true},
+ {"invalid.", false},
+ {".com", false},
+ {"rtmp://foobar.com", true},
+ {"http://www.foo_bar.com/", true},
+ {"http://localhost:3000/", true},
+ {"http://foobar.com/#baz", true},
+ {"http://foobar.com#baz=qux", true},
+ {"http://foobar.com/t$-_.+!*\\'(),", true},
+ {"http://www.foobar.com/~foobar", true},
+ {"http://www.-foobar.com/", true},
+ {"http://www.foo---bar.com/", true},
+ {"mailto:someone@example.com", true},
+ {"irc://irc.server.org/channel", true},
+ {"irc://#channel@network", true},
+ {"/abs/test/dir", false},
+ {"./rel/test/dir", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "url")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d URL failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d URL failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "url" {
+ t.Fatalf("Index: %d URL failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+
+ i := 1
+ PanicMatches(t, func() { _ = validate.Var(i, "url") }, "Bad field type int")
+}
+
+func TestUri(t *testing.T) {
+
+ var tests = []struct {
+ param string
+ expected bool
+ }{
+ {"http://foo.bar#com", true},
+ {"http://foobar.com", true},
+ {"https://foobar.com", true},
+ {"foobar.com", false},
+ {"http://foobar.coffee/", true},
+ {"http://foobar.中文网/", true},
+ {"http://foobar.org/", true},
+ {"http://foobar.org:8080/", true},
+ {"ftp://foobar.ru/", true},
+ {"http://user:pass@www.foobar.com/", true},
+ {"http://127.0.0.1/", true},
+ {"http://duckduckgo.com/?q=%2F", true},
+ {"http://localhost:3000/", true},
+ {"http://foobar.com/?foo=bar#baz=qux", true},
+ {"http://foobar.com?foo=bar", true},
+ {"http://www.xn--froschgrn-x9a.net/", true},
+ {"", false},
+ {"xyz://foobar.com", true},
+ {"invalid.", false},
+ {".com", false},
+ {"rtmp://foobar.com", true},
+ {"http://www.foo_bar.com/", true},
+ {"http://localhost:3000/", true},
+ {"http://foobar.com#baz=qux", true},
+ {"http://foobar.com/t$-_.+!*\\'(),", true},
+ {"http://www.foobar.com/~foobar", true},
+ {"http://www.-foobar.com/", true},
+ {"http://www.foo---bar.com/", true},
+ {"mailto:someone@example.com", true},
+ {"irc://irc.server.org/channel", true},
+ {"irc://#channel@network", true},
+ {"/abs/test/dir", true},
+ {"./rel/test/dir", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "uri")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d URI failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d URI failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "uri" {
+ t.Fatalf("Index: %d URI failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+
+ i := 1
+ PanicMatches(t, func() { _ = validate.Var(i, "uri") }, "Bad field type int")
+}
+
+func TestOrTag(t *testing.T) {
+
+ validate := New()
+
+ s := "rgba(0,31,255,0.5)"
+ errs := validate.Var(s, "rgb|rgba")
+ Equal(t, errs, nil)
+
+ s = "rgba(0,31,255,0.5)"
+ errs = validate.Var(s, "rgb|rgba|len=18")
+ Equal(t, errs, nil)
+
+ s = "this ain't right"
+ errs = validate.Var(s, "rgb|rgba")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgb|rgba")
+
+ s = "this ain't right"
+ errs = validate.Var(s, "rgb|rgba|len=10")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgb|rgba|len=10")
+
+ s = "this is right"
+ errs = validate.Var(s, "rgb|rgba|len=13")
+ Equal(t, errs, nil)
+
+ s = ""
+ errs = validate.Var(s, "omitempty,rgb|rgba")
+ Equal(t, errs, nil)
+
+ s = "green"
+ errs = validate.Var(s, "eq=|eq=blue,rgb|rgba") //should fail on first validation block
+ NotEqual(t, errs, nil)
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 1)
+ Equal(t, ve[0].Tag(), "eq=|eq=blue")
+
+ s = "this is right, but a blank or isn't"
+
+ PanicMatches(t, func() { _ = validate.Var(s, "rgb||len=13") }, "Invalid validation tag on field ''")
+ PanicMatches(t, func() { _ = validate.Var(s, "rgb|rgbaa|len=13") }, "Undefined validation function 'rgbaa' on field ''")
+
+ v2 := New()
+ v2.RegisterTagNameFunc(func(fld reflect.StructField) string {
+
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+
+ type Colors struct {
+ Fav string `validate:"rgb|rgba" json:"fc"`
+ }
+
+ c := Colors{Fav: "this ain't right"}
+
+ err := v2.Struct(c)
+ NotEqual(t, err, nil)
+
+ errs = err.(ValidationErrors)
+ fe := getError(errs, "Colors.fc", "Colors.Fav")
+ NotEqual(t, fe, nil)
+}
+
+func TestHsla(t *testing.T) {
+
+ validate := New()
+
+ s := "hsla(360,100%,100%,1)"
+ errs := validate.Var(s, "hsla")
+ Equal(t, errs, nil)
+
+ s = "hsla(360,100%,100%,0.5)"
+ errs = validate.Var(s, "hsla")
+ Equal(t, errs, nil)
+
+ s = "hsla(0,0%,0%, 0)"
+ errs = validate.Var(s, "hsla")
+ Equal(t, errs, nil)
+
+ s = "hsl(361,100%,50%,1)"
+ errs = validate.Var(s, "hsla")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsla")
+
+ s = "hsl(361,100%,50%)"
+ errs = validate.Var(s, "hsla")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsla")
+
+ s = "hsla(361,100%,50%)"
+ errs = validate.Var(s, "hsla")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsla")
+
+ s = "hsla(360,101%,50%)"
+ errs = validate.Var(s, "hsla")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsla")
+
+ s = "hsla(360,100%,101%)"
+ errs = validate.Var(s, "hsla")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsla")
+
+ i := 1
+ errs = validate.Var(i, "hsla")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsla")
+}
+
+func TestHsl(t *testing.T) {
+
+ validate := New()
+
+ s := "hsl(360,100%,50%)"
+ errs := validate.Var(s, "hsl")
+ Equal(t, errs, nil)
+
+ s = "hsl(0,0%,0%)"
+ errs = validate.Var(s, "hsl")
+ Equal(t, errs, nil)
+
+ s = "hsl(361,100%,50%)"
+ errs = validate.Var(s, "hsl")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsl")
+
+ s = "hsl(361,101%,50%)"
+ errs = validate.Var(s, "hsl")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsl")
+
+ s = "hsl(361,100%,101%)"
+ errs = validate.Var(s, "hsl")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsl")
+
+ s = "hsl(-10,100%,100%)"
+ errs = validate.Var(s, "hsl")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsl")
+
+ i := 1
+ errs = validate.Var(i, "hsl")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hsl")
+}
+
+func TestRgba(t *testing.T) {
+
+ validate := New()
+
+ s := "rgba(0,31,255,0.5)"
+ errs := validate.Var(s, "rgba")
+ Equal(t, errs, nil)
+
+ s = "rgba(0,31,255,0.12)"
+ errs = validate.Var(s, "rgba")
+ Equal(t, errs, nil)
+
+ s = "rgba(12%,55%,100%,0.12)"
+ errs = validate.Var(s, "rgba")
+ Equal(t, errs, nil)
+
+ s = "rgba( 0, 31, 255, 0.5)"
+ errs = validate.Var(s, "rgba")
+ Equal(t, errs, nil)
+
+ s = "rgba(12%,55,100%,0.12)"
+ errs = validate.Var(s, "rgba")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgba")
+
+ s = "rgb(0, 31, 255)"
+ errs = validate.Var(s, "rgba")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgba")
+
+ s = "rgb(1,349,275,0.5)"
+ errs = validate.Var(s, "rgba")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgba")
+
+ s = "rgb(01,31,255,0.5)"
+ errs = validate.Var(s, "rgba")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgba")
+
+ i := 1
+ errs = validate.Var(i, "rgba")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgba")
+}
+
+func TestRgb(t *testing.T) {
+
+ validate := New()
+
+ s := "rgb(0,31,255)"
+ errs := validate.Var(s, "rgb")
+ Equal(t, errs, nil)
+
+ s = "rgb(0, 31, 255)"
+ errs = validate.Var(s, "rgb")
+ Equal(t, errs, nil)
+
+ s = "rgb(10%, 50%, 100%)"
+ errs = validate.Var(s, "rgb")
+ Equal(t, errs, nil)
+
+ s = "rgb(10%, 50%, 55)"
+ errs = validate.Var(s, "rgb")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgb")
+
+ s = "rgb(1,349,275)"
+ errs = validate.Var(s, "rgb")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgb")
+
+ s = "rgb(01,31,255)"
+ errs = validate.Var(s, "rgb")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgb")
+
+ s = "rgba(0,31,255)"
+ errs = validate.Var(s, "rgb")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgb")
+
+ i := 1
+ errs = validate.Var(i, "rgb")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "rgb")
+}
+
+func TestEmail(t *testing.T) {
+
+ validate := New()
+
+ s := "test@mail.com"
+ errs := validate.Var(s, "email")
+ Equal(t, errs, nil)
+
+ s = "Dörte@Sörensen.example.com"
+ errs = validate.Var(s, "email")
+ Equal(t, errs, nil)
+
+ s = "θσερ@εχαμπλε.ψομ"
+ errs = validate.Var(s, "email")
+ Equal(t, errs, nil)
+
+ s = "юзер@екзампл.ком"
+ errs = validate.Var(s, "email")
+ Equal(t, errs, nil)
+
+ s = "उपयोगकर्ता@उदाहरण.कॉम"
+ errs = validate.Var(s, "email")
+ Equal(t, errs, nil)
+
+ s = "用户@例子.广告"
+ errs = validate.Var(s, "email")
+ Equal(t, errs, nil)
+
+ s = "mail@domain_with_underscores.org"
+ errs = validate.Var(s, "email")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "email")
+
+ s = ""
+ errs = validate.Var(s, "email")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "email")
+
+ s = "test@email"
+ errs = validate.Var(s, "email")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "email")
+
+ s = "test@email."
+ errs = validate.Var(s, "email")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "email")
+
+ s = "@email.com"
+ errs = validate.Var(s, "email")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "email")
+
+ s = `"test test"@email.com`
+ errs = validate.Var(s, "email")
+ Equal(t, errs, nil)
+
+ s = `"@email.com`
+ errs = validate.Var(s, "email")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "email")
+
+ i := true
+ errs = validate.Var(i, "email")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "email")
+}
+
+func TestHexColor(t *testing.T) {
+
+ validate := New()
+
+ s := "#fff"
+ errs := validate.Var(s, "hexcolor")
+ Equal(t, errs, nil)
+
+ s = "#c2c2c2"
+ errs = validate.Var(s, "hexcolor")
+ Equal(t, errs, nil)
+
+ s = "fff"
+ errs = validate.Var(s, "hexcolor")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hexcolor")
+
+ s = "fffFF"
+ errs = validate.Var(s, "hexcolor")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hexcolor")
+
+ i := true
+ errs = validate.Var(i, "hexcolor")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hexcolor")
+}
+
+func TestHexadecimal(t *testing.T) {
+
+ validate := New()
+
+ s := "ff0044"
+ errs := validate.Var(s, "hexadecimal")
+ Equal(t, errs, nil)
+
+ s = "0xff0044"
+ errs = validate.Var(s, "hexadecimal")
+ Equal(t, errs, nil)
+
+ s = "0Xff0044"
+ errs = validate.Var(s, "hexadecimal")
+ Equal(t, errs, nil)
+
+ s = "abcdefg"
+ errs = validate.Var(s, "hexadecimal")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hexadecimal")
+
+ i := true
+ errs = validate.Var(i, "hexadecimal")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "hexadecimal")
+}
+
+func TestNumber(t *testing.T) {
+
+ validate := New()
+
+ s := "1"
+ errs := validate.Var(s, "number")
+ Equal(t, errs, nil)
+
+ s = "+1"
+ errs = validate.Var(s, "number")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "number")
+
+ s = "-1"
+ errs = validate.Var(s, "number")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "number")
+
+ s = "1.12"
+ errs = validate.Var(s, "number")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "number")
+
+ s = "+1.12"
+ errs = validate.Var(s, "number")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "number")
+
+ s = "-1.12"
+ errs = validate.Var(s, "number")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "number")
+
+ s = "1."
+ errs = validate.Var(s, "number")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "number")
+
+ s = "1.o"
+ errs = validate.Var(s, "number")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "number")
+
+ i := 1
+ errs = validate.Var(i, "number")
+ Equal(t, errs, nil)
+}
+
+func TestNumeric(t *testing.T) {
+
+ validate := New()
+
+ s := "1"
+ errs := validate.Var(s, "numeric")
+ Equal(t, errs, nil)
+
+ s = "+1"
+ errs = validate.Var(s, "numeric")
+ Equal(t, errs, nil)
+
+ s = "-1"
+ errs = validate.Var(s, "numeric")
+ Equal(t, errs, nil)
+
+ s = "1.12"
+ errs = validate.Var(s, "numeric")
+ Equal(t, errs, nil)
+
+ s = "+1.12"
+ errs = validate.Var(s, "numeric")
+ Equal(t, errs, nil)
+
+ s = "-1.12"
+ errs = validate.Var(s, "numeric")
+ Equal(t, errs, nil)
+
+ s = "1."
+ errs = validate.Var(s, "numeric")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "numeric")
+
+ s = "1.o"
+ errs = validate.Var(s, "numeric")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "numeric")
+
+ i := 1
+ errs = validate.Var(i, "numeric")
+ Equal(t, errs, nil)
+}
+
+func TestAlphaNumeric(t *testing.T) {
+
+ validate := New()
+
+ s := "abcd123"
+ errs := validate.Var(s, "alphanum")
+ Equal(t, errs, nil)
+
+ s = "abc!23"
+ errs = validate.Var(s, "alphanum")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "alphanum")
+
+ errs = validate.Var(1, "alphanum")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "alphanum")
+}
+
+func TestAlpha(t *testing.T) {
+
+ validate := New()
+
+ s := "abcd"
+ errs := validate.Var(s, "alpha")
+ Equal(t, errs, nil)
+
+ s = "abc®"
+ errs = validate.Var(s, "alpha")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "alpha")
+
+ s = "abc÷"
+ errs = validate.Var(s, "alpha")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "alpha")
+
+ s = "abc1"
+ errs = validate.Var(s, "alpha")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "alpha")
+
+ s = "this is a test string"
+ errs = validate.Var(s, "alpha")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "alpha")
+
+ errs = validate.Var(1, "alpha")
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "", "", "", "", "alpha")
+
+}
+
+func TestStructStringValidation(t *testing.T) {
+
+ validate := New()
+
+ tSuccess := &TestString{
+ Required: "Required",
+ Len: "length==10",
+ Min: "min=1",
+ Max: "1234567890",
+ MinMax: "12345",
+ Lt: "012345678",
+ Lte: "0123456789",
+ Gt: "01234567890",
+ Gte: "0123456789",
+ OmitEmpty: "",
+ Sub: &SubTest{
+ Test: "1",
+ },
+ SubIgnore: &SubTest{
+ Test: "",
+ },
+ Anonymous: struct {
+ A string `validate:"required"`
+ }{
+ A: "1",
+ },
+ Iface: &Impl{
+ F: "123",
+ },
+ }
+
+ errs := validate.Struct(tSuccess)
+ Equal(t, errs, nil)
+
+ tFail := &TestString{
+ Required: "",
+ Len: "",
+ Min: "",
+ Max: "12345678901",
+ MinMax: "",
+ Lt: "0123456789",
+ Lte: "01234567890",
+ Gt: "1",
+ Gte: "1",
+ OmitEmpty: "12345678901",
+ Sub: &SubTest{
+ Test: "",
+ },
+ Anonymous: struct {
+ A string `validate:"required"`
+ }{
+ A: "",
+ },
+ Iface: &Impl{
+ F: "12",
+ },
+ }
+
+ errs = validate.Struct(tFail)
+
+ // Assert Top Level
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 13)
+
+ // Assert Fields
+ AssertError(t, errs, "TestString.Required", "TestString.Required", "Required", "Required", "required")
+ AssertError(t, errs, "TestString.Len", "TestString.Len", "Len", "Len", "len")
+ AssertError(t, errs, "TestString.Min", "TestString.Min", "Min", "Min", "min")
+ AssertError(t, errs, "TestString.Max", "TestString.Max", "Max", "Max", "max")
+ AssertError(t, errs, "TestString.MinMax", "TestString.MinMax", "MinMax", "MinMax", "min")
+ AssertError(t, errs, "TestString.Lt", "TestString.Lt", "Lt", "Lt", "lt")
+ AssertError(t, errs, "TestString.Lte", "TestString.Lte", "Lte", "Lte", "lte")
+ AssertError(t, errs, "TestString.Gt", "TestString.Gt", "Gt", "Gt", "gt")
+ AssertError(t, errs, "TestString.Gte", "TestString.Gte", "Gte", "Gte", "gte")
+ AssertError(t, errs, "TestString.OmitEmpty", "TestString.OmitEmpty", "OmitEmpty", "OmitEmpty", "max")
+
+ // Nested Struct Field Errs
+ AssertError(t, errs, "TestString.Anonymous.A", "TestString.Anonymous.A", "A", "A", "required")
+ AssertError(t, errs, "TestString.Sub.Test", "TestString.Sub.Test", "Test", "Test", "required")
+ AssertError(t, errs, "TestString.Iface.F", "TestString.Iface.F", "F", "F", "len")
+}
+
+func TestStructInt32Validation(t *testing.T) {
+
+ type TestInt32 struct {
+ Required int `validate:"required"`
+ Len int `validate:"len=10"`
+ Min int `validate:"min=1"`
+ Max int `validate:"max=10"`
+ MinMax int `validate:"min=1,max=10"`
+ Lt int `validate:"lt=10"`
+ Lte int `validate:"lte=10"`
+ Gt int `validate:"gt=10"`
+ Gte int `validate:"gte=10"`
+ OmitEmpty int `validate:"omitempty,min=1,max=10"`
+ }
+
+ tSuccess := &TestInt32{
+ Required: 1,
+ Len: 10,
+ Min: 1,
+ Max: 10,
+ MinMax: 5,
+ Lt: 9,
+ Lte: 10,
+ Gt: 11,
+ Gte: 10,
+ OmitEmpty: 0,
+ }
+
+ validate := New()
+ errs := validate.Struct(tSuccess)
+ Equal(t, errs, nil)
+
+ tFail := &TestInt32{
+ Required: 0,
+ Len: 11,
+ Min: -1,
+ Max: 11,
+ MinMax: -1,
+ Lt: 10,
+ Lte: 11,
+ Gt: 10,
+ Gte: 9,
+ OmitEmpty: 11,
+ }
+
+ errs = validate.Struct(tFail)
+
+ // Assert Top Level
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 10)
+
+ // Assert Fields
+ AssertError(t, errs, "TestInt32.Required", "TestInt32.Required", "Required", "Required", "required")
+ AssertError(t, errs, "TestInt32.Len", "TestInt32.Len", "Len", "Len", "len")
+ AssertError(t, errs, "TestInt32.Min", "TestInt32.Min", "Min", "Min", "min")
+ AssertError(t, errs, "TestInt32.Max", "TestInt32.Max", "Max", "Max", "max")
+ AssertError(t, errs, "TestInt32.MinMax", "TestInt32.MinMax", "MinMax", "MinMax", "min")
+ AssertError(t, errs, "TestInt32.Lt", "TestInt32.Lt", "Lt", "Lt", "lt")
+ AssertError(t, errs, "TestInt32.Lte", "TestInt32.Lte", "Lte", "Lte", "lte")
+ AssertError(t, errs, "TestInt32.Gt", "TestInt32.Gt", "Gt", "Gt", "gt")
+ AssertError(t, errs, "TestInt32.Gte", "TestInt32.Gte", "Gte", "Gte", "gte")
+ AssertError(t, errs, "TestInt32.OmitEmpty", "TestInt32.OmitEmpty", "OmitEmpty", "OmitEmpty", "max")
+}
+
+func TestStructUint64Validation(t *testing.T) {
+
+ validate := New()
+
+ tSuccess := &TestUint64{
+ Required: 1,
+ Len: 10,
+ Min: 1,
+ Max: 10,
+ MinMax: 5,
+ OmitEmpty: 0,
+ }
+
+ errs := validate.Struct(tSuccess)
+ Equal(t, errs, nil)
+
+ tFail := &TestUint64{
+ Required: 0,
+ Len: 11,
+ Min: 0,
+ Max: 11,
+ MinMax: 0,
+ OmitEmpty: 11,
+ }
+
+ errs = validate.Struct(tFail)
+
+ // Assert Top Level
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 6)
+
+ // Assert Fields
+ AssertError(t, errs, "TestUint64.Required", "TestUint64.Required", "Required", "Required", "required")
+ AssertError(t, errs, "TestUint64.Len", "TestUint64.Len", "Len", "Len", "len")
+ AssertError(t, errs, "TestUint64.Min", "TestUint64.Min", "Min", "Min", "min")
+ AssertError(t, errs, "TestUint64.Max", "TestUint64.Max", "Max", "Max", "max")
+ AssertError(t, errs, "TestUint64.MinMax", "TestUint64.MinMax", "MinMax", "MinMax", "min")
+ AssertError(t, errs, "TestUint64.OmitEmpty", "TestUint64.OmitEmpty", "OmitEmpty", "OmitEmpty", "max")
+}
+
+func TestStructFloat64Validation(t *testing.T) {
+
+ validate := New()
+
+ tSuccess := &TestFloat64{
+ Required: 1,
+ Len: 10,
+ Min: 1,
+ Max: 10,
+ MinMax: 5,
+ OmitEmpty: 0,
+ }
+
+ errs := validate.Struct(tSuccess)
+ Equal(t, errs, nil)
+
+ tFail := &TestFloat64{
+ Required: 0,
+ Len: 11,
+ Min: 0,
+ Max: 11,
+ MinMax: 0,
+ OmitEmpty: 11,
+ }
+
+ errs = validate.Struct(tFail)
+
+ // Assert Top Level
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 6)
+
+ // Assert Fields
+ AssertError(t, errs, "TestFloat64.Required", "TestFloat64.Required", "Required", "Required", "required")
+ AssertError(t, errs, "TestFloat64.Len", "TestFloat64.Len", "Len", "Len", "len")
+ AssertError(t, errs, "TestFloat64.Min", "TestFloat64.Min", "Min", "Min", "min")
+ AssertError(t, errs, "TestFloat64.Max", "TestFloat64.Max", "Max", "Max", "max")
+ AssertError(t, errs, "TestFloat64.MinMax", "TestFloat64.MinMax", "MinMax", "MinMax", "min")
+ AssertError(t, errs, "TestFloat64.OmitEmpty", "TestFloat64.OmitEmpty", "OmitEmpty", "OmitEmpty", "max")
+}
+
+func TestStructSliceValidation(t *testing.T) {
+
+ validate := New()
+
+ tSuccess := &TestSlice{
+ Required: []int{1},
+ Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
+ Min: []int{1, 2},
+ Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
+ MinMax: []int{1, 2, 3, 4, 5},
+ OmitEmpty: nil,
+ }
+
+ errs := validate.Struct(tSuccess)
+ Equal(t, errs, nil)
+
+ tFail := &TestSlice{
+ Required: nil,
+ Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1},
+ Min: []int{},
+ Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1},
+ MinMax: []int{},
+ OmitEmpty: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1},
+ }
+
+ errs = validate.Struct(tFail)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 6)
+
+ // Assert Field Errors
+ AssertError(t, errs, "TestSlice.Required", "TestSlice.Required", "Required", "Required", "required")
+ AssertError(t, errs, "TestSlice.Len", "TestSlice.Len", "Len", "Len", "len")
+ AssertError(t, errs, "TestSlice.Min", "TestSlice.Min", "Min", "Min", "min")
+ AssertError(t, errs, "TestSlice.Max", "TestSlice.Max", "Max", "Max", "max")
+ AssertError(t, errs, "TestSlice.MinMax", "TestSlice.MinMax", "MinMax", "MinMax", "min")
+ AssertError(t, errs, "TestSlice.OmitEmpty", "TestSlice.OmitEmpty", "OmitEmpty", "OmitEmpty", "max")
+
+ fe := getError(errs, "TestSlice.Len", "TestSlice.Len")
+ NotEqual(t, fe, nil)
+ Equal(t, fe.Field(), "Len")
+ Equal(t, fe.StructField(), "Len")
+ Equal(t, fe.Namespace(), "TestSlice.Len")
+ Equal(t, fe.StructNamespace(), "TestSlice.Len")
+ Equal(t, fe.Tag(), "len")
+ Equal(t, fe.ActualTag(), "len")
+ Equal(t, fe.Param(), "10")
+ Equal(t, fe.Kind(), reflect.Slice)
+ Equal(t, fe.Type(), reflect.TypeOf([]int{}))
+
+ _, ok := fe.Value().([]int)
+ Equal(t, ok, true)
+
+}
+
+func TestInvalidStruct(t *testing.T) {
+
+ validate := New()
+
+ s := &SubTest{
+ Test: "1",
+ }
+
+ err := validate.Struct(s.Test)
+ NotEqual(t, err, nil)
+ Equal(t, err.Error(), "validator: (nil string)")
+
+ err = validate.Struct(nil)
+ NotEqual(t, err, nil)
+ Equal(t, err.Error(), "validator: (nil)")
+
+ err = validate.StructPartial(nil, "SubTest.Test")
+ NotEqual(t, err, nil)
+ Equal(t, err.Error(), "validator: (nil)")
+
+ err = validate.StructExcept(nil, "SubTest.Test")
+ NotEqual(t, err, nil)
+ Equal(t, err.Error(), "validator: (nil)")
+}
+
+func TestInvalidValidatorFunction(t *testing.T) {
+
+ validate := New()
+
+ s := &SubTest{
+ Test: "1",
+ }
+
+ PanicMatches(t, func() { _ = validate.Var(s.Test, "zzxxBadFunction") }, "Undefined validation function 'zzxxBadFunction' on field ''")
+}
+
+func TestCustomFieldName(t *testing.T) {
+
+ validate := New()
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("schema"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+
+ type A struct {
+ B string `schema:"b" validate:"required"`
+ C string `schema:"c" validate:"required"`
+ D []bool `schema:"d" validate:"required"`
+ E string `schema:"-" validate:"required"`
+ }
+
+ a := &A{}
+
+ err := validate.Struct(a)
+ NotEqual(t, err, nil)
+
+ errs := err.(ValidationErrors)
+ Equal(t, len(errs), 4)
+ Equal(t, getError(errs, "A.b", "A.B").Field(), "b")
+ Equal(t, getError(errs, "A.c", "A.C").Field(), "c")
+ Equal(t, getError(errs, "A.d", "A.D").Field(), "d")
+ Equal(t, getError(errs, "A.E", "A.E").Field(), "E")
+
+ v2 := New()
+ err = v2.Struct(a)
+ NotEqual(t, err, nil)
+
+ errs = err.(ValidationErrors)
+ Equal(t, len(errs), 4)
+ Equal(t, getError(errs, "A.B", "A.B").Field(), "B")
+ Equal(t, getError(errs, "A.C", "A.C").Field(), "C")
+ Equal(t, getError(errs, "A.D", "A.D").Field(), "D")
+ Equal(t, getError(errs, "A.E", "A.E").Field(), "E")
+}
+
+func TestMutipleRecursiveExtractStructCache(t *testing.T) {
+
+ validate := New()
+
+ type Recursive struct {
+ Field *string `validate:"required,len=5,ne=string"`
+ }
+
+ var test Recursive
+
+ current := reflect.ValueOf(test)
+ name := "Recursive"
+ proceed := make(chan struct{})
+
+ sc := validate.extractStructCache(current, name)
+ ptr := fmt.Sprintf("%p", sc)
+
+ for i := 0; i < 100; i++ {
+
+ go func() {
+ <-proceed
+ sc := validate.extractStructCache(current, name)
+ Equal(t, ptr, fmt.Sprintf("%p", sc))
+ }()
+ }
+
+ close(proceed)
+}
+
+// Thanks @robbrockbank, see https://github.com/go-playground/validator/issues/249
+func TestPointerAndOmitEmpty(t *testing.T) {
+
+ validate := New()
+
+ type Test struct {
+ MyInt *int `validate:"omitempty,gte=2,lte=255"`
+ }
+
+ val1 := 0
+ val2 := 256
+
+ t1 := Test{MyInt: &val1} // This should fail validation on gte because value is 0
+ t2 := Test{MyInt: &val2} // This should fail validate on lte because value is 256
+ t3 := Test{MyInt: nil} // This should succeed validation because pointer is nil
+
+ errs := validate.Struct(t1)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.MyInt", "Test.MyInt", "MyInt", "MyInt", "gte")
+
+ errs = validate.Struct(t2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "Test.MyInt", "Test.MyInt", "MyInt", "MyInt", "lte")
+
+ errs = validate.Struct(t3)
+ Equal(t, errs, nil)
+
+ type TestIface struct {
+ MyInt interface{} `validate:"omitempty,gte=2,lte=255"`
+ }
+
+ ti1 := TestIface{MyInt: &val1} // This should fail validation on gte because value is 0
+ ti2 := TestIface{MyInt: &val2} // This should fail validate on lte because value is 256
+ ti3 := TestIface{MyInt: nil} // This should succeed validation because pointer is nil
+
+ errs = validate.Struct(ti1)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestIface.MyInt", "TestIface.MyInt", "MyInt", "MyInt", "gte")
+
+ errs = validate.Struct(ti2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestIface.MyInt", "TestIface.MyInt", "MyInt", "MyInt", "lte")
+
+ errs = validate.Struct(ti3)
+ Equal(t, errs, nil)
+}
+
+func TestRequired(t *testing.T) {
+
+ validate := New()
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+
+ type Test struct {
+ Value interface{} `validate:"required"`
+ }
+
+ var test Test
+
+ err := validate.Struct(test)
+ NotEqual(t, err, nil)
+ AssertError(t, err.(ValidationErrors), "Test.Value", "Test.Value", "Value", "Value", "required")
+}
+
+func TestBoolEqual(t *testing.T) {
+
+ validate := New()
+
+ type Test struct {
+ Value bool `validate:"eq=true"`
+ }
+
+ var test Test
+
+ err := validate.Struct(test)
+ NotEqual(t, err, nil)
+ AssertError(t, err.(ValidationErrors), "Test.Value", "Test.Value", "Value", "Value", "eq")
+
+ test.Value = true
+ err = validate.Struct(test)
+ Equal(t, err, nil)
+}
+
+func TestTranslations(t *testing.T) {
+ en := en.New()
+ uni := ut.New(en, en, fr.New())
+
+ trans, _ := uni.GetTranslator("en")
+ fr, _ := uni.GetTranslator("fr")
+
+ validate := New()
+ err := validate.RegisterTranslation("required", trans,
+ func(ut ut.Translator) (err error) {
+
+ // using this stype because multiple translation may have to be added for the full translation
+ if err = ut.Add("required", "{0} is a required field", false); err != nil {
+ return
+ }
+
+ return
+
+ }, func(ut ut.Translator, fe FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe.(*fieldError))
+ return fe.(*fieldError).Error()
+ }
+
+ return t
+ })
+ Equal(t, err, nil)
+
+ err = validate.RegisterTranslation("required", fr,
+ func(ut ut.Translator) (err error) {
+
+ // using this stype because multiple translation may have to be added for the full translation
+ if err = ut.Add("required", "{0} est un champ obligatoire", false); err != nil {
+ return
+ }
+
+ return
+
+ }, func(ut ut.Translator, fe FieldError) string {
+
+ t, transErr := ut.T(fe.Tag(), fe.Field())
+ if transErr != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe.(*fieldError))
+ return fe.(*fieldError).Error()
+ }
+
+ return t
+ })
+
+ Equal(t, err, nil)
+
+ type Test struct {
+ Value interface{} `validate:"required"`
+ }
+
+ var test Test
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs := err.(ValidationErrors)
+ Equal(t, len(errs), 1)
+
+ fe := errs[0]
+ Equal(t, fe.Tag(), "required")
+ Equal(t, fe.Namespace(), "Test.Value")
+ Equal(t, fe.Translate(trans), fmt.Sprintf("%s is a required field", fe.Field()))
+ Equal(t, fe.Translate(fr), fmt.Sprintf("%s est un champ obligatoire", fe.Field()))
+
+ nl := nl.New()
+ uni2 := ut.New(nl, nl)
+ trans2, _ := uni2.GetTranslator("nl")
+ Equal(t, fe.Translate(trans2), "Key: 'Test.Value' Error:Field validation for 'Value' failed on the 'required' tag")
+
+ terrs := errs.Translate(trans)
+ Equal(t, len(terrs), 1)
+
+ v, ok := terrs["Test.Value"]
+ Equal(t, ok, true)
+ Equal(t, v, fmt.Sprintf("%s is a required field", fe.Field()))
+
+ terrs = errs.Translate(fr)
+ Equal(t, len(terrs), 1)
+
+ v, ok = terrs["Test.Value"]
+ Equal(t, ok, true)
+ Equal(t, v, fmt.Sprintf("%s est un champ obligatoire", fe.Field()))
+
+ type Test2 struct {
+ Value string `validate:"gt=1"`
+ }
+
+ var t2 Test2
+
+ err = validate.Struct(t2)
+ NotEqual(t, err, nil)
+
+ errs = err.(ValidationErrors)
+ Equal(t, len(errs), 1)
+
+ fe = errs[0]
+ Equal(t, fe.Tag(), "gt")
+ Equal(t, fe.Namespace(), "Test2.Value")
+ Equal(t, fe.Translate(trans), "Key: 'Test2.Value' Error:Field validation for 'Value' failed on the 'gt' tag")
+}
+
+func TestTranslationErrors(t *testing.T) {
+ en := en.New()
+ uni := ut.New(en, en, fr.New())
+
+ trans, _ := uni.GetTranslator("en")
+ err := trans.Add("required", "{0} is a required field", false) // using translator outside of validator also
+ Equal(t, err, nil)
+
+ validate := New()
+ err = validate.RegisterTranslation("required", trans,
+ func(ut ut.Translator) (err error) {
+
+ // using this stype because multiple translation may have to be added for the full translation
+ if err = ut.Add("required", "{0} is a required field", false); err != nil {
+ return
+ }
+
+ return
+
+ }, func(ut ut.Translator, fe FieldError) string {
+
+ t, err := ut.T(fe.Tag(), fe.Field())
+ if err != nil {
+ fmt.Printf("warning: error translating FieldError: %#v", fe.(*fieldError))
+ return fe.(*fieldError).Error()
+ }
+
+ return t
+ })
+
+ NotEqual(t, err, nil)
+ Equal(t, err.Error(), "error: conflicting key 'required' rule 'Unknown' with text '{0} is a required field' for locale 'en', value being ignored")
+}
+
+func TestStructFiltered(t *testing.T) {
+
+ p1 := func(ns []byte) bool {
+ if bytes.HasSuffix(ns, []byte("NoTag")) || bytes.HasSuffix(ns, []byte("Required")) {
+ return false
+ }
+
+ return true
+ }
+
+ p2 := func(ns []byte) bool {
+ if bytes.HasSuffix(ns, []byte("SubSlice[0].Test")) ||
+ bytes.HasSuffix(ns, []byte("SubSlice[0]")) ||
+ bytes.HasSuffix(ns, []byte("SubSlice")) ||
+ bytes.HasSuffix(ns, []byte("Sub")) ||
+ bytes.HasSuffix(ns, []byte("SubIgnore")) ||
+ bytes.HasSuffix(ns, []byte("Anonymous")) ||
+ bytes.HasSuffix(ns, []byte("Anonymous.A")) {
+ return false
+ }
+
+ return true
+ }
+
+ p3 := func(ns []byte) bool {
+ return !bytes.HasSuffix(ns, []byte("SubTest.Test"))
+ }
+
+ // p4 := []string{
+ // "A",
+ // }
+
+ tPartial := &TestPartial{
+ NoTag: "NoTag",
+ Required: "Required",
+
+ SubSlice: []*SubTest{
+ {
+
+ Test: "Required",
+ },
+ {
+
+ Test: "Required",
+ },
+ },
+
+ Sub: &SubTest{
+ Test: "1",
+ },
+ SubIgnore: &SubTest{
+ Test: "",
+ },
+ Anonymous: struct {
+ A string `validate:"required"`
+ ASubSlice []*SubTest `validate:"required,dive"`
+ SubAnonStruct []struct {
+ Test string `validate:"required"`
+ OtherTest string `validate:"required"`
+ } `validate:"required,dive"`
+ }{
+ A: "1",
+ ASubSlice: []*SubTest{
+ {
+ Test: "Required",
+ },
+ {
+ Test: "Required",
+ },
+ },
+
+ SubAnonStruct: []struct {
+ Test string `validate:"required"`
+ OtherTest string `validate:"required"`
+ }{
+ {"Required", "RequiredOther"},
+ {"Required", "RequiredOther"},
+ },
+ },
+ }
+
+ validate := New()
+
+ // the following should all return no errors as everything is valid in
+ // the default state
+ errs := validate.StructFilteredCtx(context.Background(), tPartial, p1)
+ Equal(t, errs, nil)
+
+ errs = validate.StructFiltered(tPartial, p2)
+ Equal(t, errs, nil)
+
+ // this isn't really a robust test, but is ment to illustrate the ANON CASE below
+ errs = validate.StructFiltered(tPartial.SubSlice[0], p3)
+ Equal(t, errs, nil)
+
+ // mod tParial for required feild and re-test making sure invalid fields are NOT required:
+ tPartial.Required = ""
+
+ // inversion and retesting Partial to generate failures:
+ errs = validate.StructFiltered(tPartial, p1)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.Required", "TestPartial.Required", "Required", "Required", "required")
+
+ // reset Required field, and set nested struct
+ tPartial.Required = "Required"
+ tPartial.Anonymous.A = ""
+
+ // will pass as unset feilds is not going to be tested
+ errs = validate.StructFiltered(tPartial, p1)
+ Equal(t, errs, nil)
+
+ // will fail as unset feild is tested
+ errs = validate.StructFiltered(tPartial, p2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.Anonymous.A", "TestPartial.Anonymous.A", "A", "A", "required")
+
+ // reset nested struct and unset struct in slice
+ tPartial.Anonymous.A = "Required"
+ tPartial.SubSlice[0].Test = ""
+
+ // these will pass as unset item is NOT tested
+ errs = validate.StructFiltered(tPartial, p1)
+ Equal(t, errs, nil)
+
+ errs = validate.StructFiltered(tPartial, p2)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestPartial.SubSlice[0].Test", "TestPartial.SubSlice[0].Test", "Test", "Test", "required")
+ Equal(t, len(errs.(ValidationErrors)), 1)
+
+ // Unset second slice member concurrently to test dive behavior:
+ tPartial.SubSlice[1].Test = ""
+
+ errs = validate.StructFiltered(tPartial, p1)
+ Equal(t, errs, nil)
+
+ errs = validate.StructFiltered(tPartial, p2)
+ NotEqual(t, errs, nil)
+ Equal(t, len(errs.(ValidationErrors)), 1)
+ AssertError(t, errs, "TestPartial.SubSlice[0].Test", "TestPartial.SubSlice[0].Test", "Test", "Test", "required")
+
+ // reset struct in slice, and unset struct in slice in unset posistion
+ tPartial.SubSlice[0].Test = "Required"
+
+ // these will pass as the unset item is NOT tested
+ errs = validate.StructFiltered(tPartial, p1)
+ Equal(t, errs, nil)
+
+ errs = validate.StructFiltered(tPartial, p2)
+ Equal(t, errs, nil)
+
+ tPartial.SubSlice[1].Test = "Required"
+ tPartial.Anonymous.SubAnonStruct[0].Test = ""
+
+ // these will pass as the unset item is NOT tested
+ errs = validate.StructFiltered(tPartial, p1)
+ Equal(t, errs, nil)
+
+ errs = validate.StructFiltered(tPartial, p2)
+ Equal(t, errs, nil)
+
+ dt := time.Now()
+ err := validate.StructFiltered(&dt, func(ns []byte) bool { return true })
+ NotEqual(t, err, nil)
+ Equal(t, err.Error(), "validator: (nil *time.Time)")
+}
+
+func TestRequiredPtr(t *testing.T) {
+
+ type Test struct {
+ Bool *bool `validate:"required"`
+ }
+
+ validate := New()
+
+ f := false
+
+ test := Test{
+ Bool: &f,
+ }
+
+ err := validate.Struct(test)
+ Equal(t, err, nil)
+
+ tr := true
+
+ test.Bool = &tr
+
+ err = validate.Struct(test)
+ Equal(t, err, nil)
+
+ test.Bool = nil
+
+ err = validate.Struct(test)
+ NotEqual(t, err, nil)
+
+ errs, ok := err.(ValidationErrors)
+ Equal(t, ok, true)
+ Equal(t, len(errs), 1)
+ AssertError(t, errs, "Test.Bool", "Test.Bool", "Bool", "Bool", "required")
+
+ type Test2 struct {
+ Bool bool `validate:"required"`
+ }
+
+ var test2 Test2
+
+ err = validate.Struct(test2)
+ NotEqual(t, err, nil)
+
+ errs, ok = err.(ValidationErrors)
+ Equal(t, ok, true)
+ Equal(t, len(errs), 1)
+ AssertError(t, errs, "Test2.Bool", "Test2.Bool", "Bool", "Bool", "required")
+
+ test2.Bool = true
+
+ err = validate.Struct(test2)
+ Equal(t, err, nil)
+
+ type Test3 struct {
+ Arr []string `validate:"required"`
+ }
+
+ var test3 Test3
+
+ err = validate.Struct(test3)
+ NotEqual(t, err, nil)
+
+ errs, ok = err.(ValidationErrors)
+ Equal(t, ok, true)
+ Equal(t, len(errs), 1)
+ AssertError(t, errs, "Test3.Arr", "Test3.Arr", "Arr", "Arr", "required")
+
+ test3.Arr = make([]string, 0)
+
+ err = validate.Struct(test3)
+ Equal(t, err, nil)
+
+ type Test4 struct {
+ Arr *[]string `validate:"required"` // I know I know pointer to array, just making sure validation works as expected...
+ }
+
+ var test4 Test4
+
+ err = validate.Struct(test4)
+ NotEqual(t, err, nil)
+
+ errs, ok = err.(ValidationErrors)
+ Equal(t, ok, true)
+ Equal(t, len(errs), 1)
+ AssertError(t, errs, "Test4.Arr", "Test4.Arr", "Arr", "Arr", "required")
+
+ arr := make([]string, 0)
+ test4.Arr = &arr
+
+ err = validate.Struct(test4)
+ Equal(t, err, nil)
+}
+
+func TestAlphaUnicodeValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"abc", true},
+ {"this is a test string", false},
+ {"这是一个测试字符串", true},
+ {"123", false},
+ {"<>@;.-=", false},
+ {"ひらがな・カタカナ、.漢字", false},
+ {"あいうえおfoobar", true},
+ {"test@example.com", false},
+ {"1234abcDE", false},
+ {"カタカナ", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "alphaunicode")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Alpha Unicode failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Alpha Unicode failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "alphaunicode" {
+ t.Fatalf("Index: %d Alpha Unicode failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestAlphanumericUnicodeValidation(t *testing.T) {
+
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", false},
+ {"abc", true},
+ {"this is a test string", false},
+ {"这是一个测试字符串", true},
+ {"\u0031\u0032\u0033", true}, // unicode 5
+ {"123", true},
+ {"<>@;.-=", false},
+ {"ひらがな・カタカナ、.漢字", false},
+ {"あいうえおfoobar", true},
+ {"test@example.com", false},
+ {"1234abcDE", true},
+ {"カタカナ", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "alphanumunicode")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Alphanum Unicode failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d Alphanum Unicode failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "alphanumunicode" {
+ t.Fatalf("Index: %d Alphanum Unicode failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestArrayStructNamespace(t *testing.T) {
+
+ validate := New()
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+
+ type child struct {
+ Name string `json:"name" validate:"required"`
+ }
+ var input struct {
+ Children []child `json:"children" validate:"required,gt=0,dive"`
+ }
+ input.Children = []child{{"ok"}, {""}}
+
+ errs := validate.Struct(input)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 1)
+ AssertError(t, errs, "children[1].name", "Children[1].Name", "name", "Name", "required")
+}
+
+func TestMapStructNamespace(t *testing.T) {
+
+ validate := New()
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+
+ type child struct {
+ Name string `json:"name" validate:"required"`
+ }
+ var input struct {
+ Children map[int]child `json:"children" validate:"required,gt=0,dive"`
+ }
+ input.Children = map[int]child{
+ 0: {Name: "ok"},
+ 1: {Name: ""},
+ }
+
+ errs := validate.Struct(input)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 1)
+ AssertError(t, errs, "children[1].name", "Children[1].Name", "name", "Name", "required")
+}
+
+func TestFieldLevelName(t *testing.T) {
+ type Test struct {
+ String string `validate:"custom1" json:"json1"`
+ Array []string `validate:"dive,custom2" json:"json2"`
+ Map map[string]string `validate:"dive,custom3" json:"json3"`
+ Array2 []string `validate:"custom4" json:"json4"`
+ Map2 map[string]string `validate:"custom5" json:"json5"`
+ }
+
+ var res1, res2, res3, res4, res5, alt1, alt2, alt3, alt4, alt5 string
+ validate := New()
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+
+ return name
+ })
+ err := validate.RegisterValidation("custom1", func(fl FieldLevel) bool {
+ res1 = fl.FieldName()
+ alt1 = fl.StructFieldName()
+ return true
+ })
+ Equal(t, err, nil)
+
+ err = validate.RegisterValidation("custom2", func(fl FieldLevel) bool {
+ res2 = fl.FieldName()
+ alt2 = fl.StructFieldName()
+ return true
+ })
+ Equal(t, err, nil)
+
+ err = validate.RegisterValidation("custom3", func(fl FieldLevel) bool {
+ res3 = fl.FieldName()
+ alt3 = fl.StructFieldName()
+ return true
+ })
+ Equal(t, err, nil)
+
+ err = validate.RegisterValidation("custom4", func(fl FieldLevel) bool {
+ res4 = fl.FieldName()
+ alt4 = fl.StructFieldName()
+ return true
+ })
+ Equal(t, err, nil)
+
+ err = validate.RegisterValidation("custom5", func(fl FieldLevel) bool {
+ res5 = fl.FieldName()
+ alt5 = fl.StructFieldName()
+ return true
+ })
+ Equal(t, err, nil)
+
+ test := Test{
+ String: "test",
+ Array: []string{"1"},
+ Map: map[string]string{"test": "test"},
+ }
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+ Equal(t, res1, "json1")
+ Equal(t, alt1, "String")
+ Equal(t, res2, "json2[0]")
+ Equal(t, alt2, "Array[0]")
+ Equal(t, res3, "json3[test]")
+ Equal(t, alt3, "Map[test]")
+ Equal(t, res4, "json4")
+ Equal(t, alt4, "Array2")
+ Equal(t, res5, "json5")
+ Equal(t, alt5, "Map2")
+}
+
+func TestValidateStructRegisterCtx(t *testing.T) {
+ var ctxVal string
+
+ fnCtx := func(ctx context.Context, fl FieldLevel) bool {
+ ctxVal = ctx.Value(&ctxVal).(string)
+ return true
+ }
+
+ var ctxSlVal string
+ slFn := func(ctx context.Context, sl StructLevel) {
+ ctxSlVal = ctx.Value(&ctxSlVal).(string)
+ }
+
+ type Test struct {
+ Field string `validate:"val"`
+ }
+
+ var tst Test
+
+ validate := New()
+ err := validate.RegisterValidationCtx("val", fnCtx)
+ Equal(t, err, nil)
+
+ validate.RegisterStructValidationCtx(slFn, Test{})
+
+ ctx := context.WithValue(context.Background(), &ctxVal, "testval")
+ ctx = context.WithValue(ctx, &ctxSlVal, "slVal")
+ errs := validate.StructCtx(ctx, tst)
+ Equal(t, errs, nil)
+ Equal(t, ctxVal, "testval")
+ Equal(t, ctxSlVal, "slVal")
+}
+
+func TestHostnameRFC952Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"test.example.com", true},
+ {"example.com", true},
+ {"example24.com", true},
+ {"test.example24.com", true},
+ {"test24.example24.com", true},
+ {"example", true},
+ {"EXAMPLE", true},
+ {"1.foo.com", false},
+ {"test.example.com.", false},
+ {"example.com.", false},
+ {"example24.com.", false},
+ {"test.example24.com.", false},
+ {"test24.example24.com.", false},
+ {"example.", false},
+ {"192.168.0.1", false},
+ {"email@example.com", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652", false},
+ {"2001:cdba:0:0:0:0:3257:9652", false},
+ {"2001:cdba::3257:9652", false},
+ {"example..........com", false},
+ {"1234", false},
+ {"abc1234", true},
+ {"example. com", false},
+ {"ex ample.com", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "hostname")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "hostname" {
+ t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestHostnameRFC1123Validation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"test.example.com", true},
+ {"example.com", true},
+ {"example24.com", true},
+ {"test.example24.com", true},
+ {"test24.example24.com", true},
+ {"example", true},
+ {"1.foo.com", true},
+ {"test.example.com.", false},
+ {"example.com.", false},
+ {"example24.com.", false},
+ {"test.example24.com.", false},
+ {"test24.example24.com.", false},
+ {"example.", false},
+ {"192.168.0.1", true},
+ {"email@example.com", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652", false},
+ {"2001:cdba:0:0:0:0:3257:9652", false},
+ {"2001:cdba::3257:9652", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "hostname_rfc1123")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Hostname: %v failed Error: %v", test, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Hostname: %v failed Error: %v", test, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "hostname_rfc1123" {
+ t.Fatalf("Hostname: %v failed Error: %v", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestHostnameRFC1123AliasValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"test.example.com", true},
+ {"example.com", true},
+ {"example24.com", true},
+ {"test.example24.com", true},
+ {"test24.example24.com", true},
+ {"example", true},
+ {"1.foo.com", true},
+ {"test.example.com.", false},
+ {"example.com.", false},
+ {"example24.com.", false},
+ {"test.example24.com.", false},
+ {"test24.example24.com.", false},
+ {"example.", false},
+ {"192.168.0.1", true},
+ {"email@example.com", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652", false},
+ {"2001:cdba:0:0:0:0:3257:9652", false},
+ {"2001:cdba::3257:9652", false},
+ }
+
+ validate := New()
+ validate.RegisterAlias("hostname", "hostname_rfc1123")
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "hostname")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "hostname" {
+ t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestFQDNValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"test.example.com", true},
+ {"example.com", true},
+ {"example24.com", true},
+ {"test.example24.com", true},
+ {"test24.example24.com", true},
+ {"test.example.com.", true},
+ {"example.com.", true},
+ {"example24.com.", true},
+ {"test.example24.com.", true},
+ {"test24.example24.com.", true},
+ {"24.example24.com", true},
+ {"test.24.example.com", true},
+ {"test24.example24.com..", false},
+ {"example", false},
+ {"192.168.0.1", false},
+ {"email@example.com", false},
+ {"2001:cdba:0000:0000:0000:0000:3257:9652", false},
+ {"2001:cdba:0:0:0:0:3257:9652", false},
+ {"2001:cdba::3257:9652", false},
+ {"", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "fqdn")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d fqdn failed Error: %v", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d fqdn failed Error: %v", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "fqdn" {
+ t.Fatalf("Index: %d fqdn failed Error: %v", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestIsDefault(t *testing.T) {
+ validate := New()
+
+ type Inner struct {
+ String string `validate:"isdefault"`
+ }
+ type Test struct {
+ String string `validate:"isdefault"`
+ Inner *Inner `validate:"isdefault"`
+ }
+
+ var tt Test
+
+ errs := validate.Struct(tt)
+ Equal(t, errs, nil)
+
+ tt.Inner = &Inner{String: ""}
+ errs = validate.Struct(tt)
+ NotEqual(t, errs, nil)
+
+ fe := errs.(ValidationErrors)[0]
+ Equal(t, fe.Field(), "Inner")
+ Equal(t, fe.Namespace(), "Test.Inner")
+ Equal(t, fe.Tag(), "isdefault")
+
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+ if name == "-" {
+ return ""
+ }
+ return name
+ })
+
+ type Inner2 struct {
+ String string `validate:"isdefault"`
+ }
+
+ type Test2 struct {
+ Inner Inner2 `validate:"isdefault" json:"inner"`
+ }
+
+ var t2 Test2
+ errs = validate.Struct(t2)
+ Equal(t, errs, nil)
+
+ t2.Inner.String = "Changed"
+ errs = validate.Struct(t2)
+ NotEqual(t, errs, nil)
+
+ fe = errs.(ValidationErrors)[0]
+ Equal(t, fe.Field(), "inner")
+ Equal(t, fe.Namespace(), "Test2.inner")
+ Equal(t, fe.Tag(), "isdefault")
+}
+
+func TestUniqueValidation(t *testing.T) {
+ tests := []struct {
+ param interface{}
+ expected bool
+ }{
+ // Arrays
+ {[2]string{"a", "b"}, true},
+ {[2]int{1, 2}, true},
+ {[2]float64{1, 2}, true},
+ {[2]interface{}{"a", "b"}, true},
+ {[2]interface{}{"a", 1}, true},
+ {[2]float64{1, 1}, false},
+ {[2]int{1, 1}, false},
+ {[2]string{"a", "a"}, false},
+ {[2]interface{}{"a", "a"}, false},
+ {[4]interface{}{"a", 1, "b", 1}, false},
+ {[2]*string{stringPtr("a"), stringPtr("b")}, true},
+ {[2]*int{intPtr(1), intPtr(2)}, true},
+ {[2]*float64{float64Ptr(1), float64Ptr(2)}, true},
+ {[2]*string{stringPtr("a"), stringPtr("a")}, false},
+ {[2]*float64{float64Ptr(1), float64Ptr(1)}, false},
+ {[2]*int{intPtr(1), intPtr(1)}, false},
+ // Slices
+ {[]string{"a", "b"}, true},
+ {[]int{1, 2}, true},
+ {[]float64{1, 2}, true},
+ {[]interface{}{"a", "b"}, true},
+ {[]interface{}{"a", 1}, true},
+ {[]float64{1, 1}, false},
+ {[]int{1, 1}, false},
+ {[]string{"a", "a"}, false},
+ {[]interface{}{"a", "a"}, false},
+ {[]interface{}{"a", 1, "b", 1}, false},
+ {[]*string{stringPtr("a"), stringPtr("b")}, true},
+ {[]*int{intPtr(1), intPtr(2)}, true},
+ {[]*float64{float64Ptr(1), float64Ptr(2)}, true},
+ {[]*string{stringPtr("a"), stringPtr("a")}, false},
+ {[]*float64{float64Ptr(1), float64Ptr(1)}, false},
+ {[]*int{intPtr(1), intPtr(1)}, false},
+ // Maps
+ {map[string]string{"one": "a", "two": "b"}, true},
+ {map[string]int{"one": 1, "two": 2}, true},
+ {map[string]float64{"one": 1, "two": 2}, true},
+ {map[string]interface{}{"one": "a", "two": "b"}, true},
+ {map[string]interface{}{"one": "a", "two": 1}, true},
+ {map[string]float64{"one": 1, "two": 1}, false},
+ {map[string]int{"one": 1, "two": 1}, false},
+ {map[string]string{"one": "a", "two": "a"}, false},
+ {map[string]interface{}{"one": "a", "two": "a"}, false},
+ {map[string]interface{}{"one": "a", "two": 1, "three": "b", "four": 1}, false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "unique")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d unique failed Error: %v", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d unique failed Error: %v", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "unique" {
+ t.Fatalf("Index: %d unique failed Error: %v", i, errs)
+ }
+ }
+ }
+ }
+ PanicMatches(t, func() { _ = validate.Var(1.0, "unique") }, "Bad field type float64")
+}
+
+func TestUniqueValidationStructSlice(t *testing.T) {
+ testStructs := []struct {
+ A string
+ B string
+ }{
+ {A: "one", B: "two"},
+ {A: "one", B: "three"},
+ }
+
+ tests := []struct {
+ target interface{}
+ param string
+ expected bool
+ }{
+ {testStructs, "unique", true},
+ {testStructs, "unique=A", false},
+ {testStructs, "unique=B", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.target, test.param)
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d unique failed Error: %v", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d unique failed Error: %v", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "unique" {
+ t.Fatalf("Index: %d unique failed Error: %v", i, errs)
+ }
+ }
+ }
+ }
+ PanicMatches(t, func() { _ = validate.Var(testStructs, "unique=C") }, "Bad field name C")
+}
+
+func TestUniqueValidationStructPtrSlice(t *testing.T) {
+ testStructs := []*struct {
+ A *string
+ B *string
+ }{
+ {A: stringPtr("one"), B: stringPtr("two")},
+ {A: stringPtr("one"), B: stringPtr("three")},
+ }
+
+ tests := []struct {
+ target interface{}
+ param string
+ expected bool
+ }{
+ {testStructs, "unique", true},
+ {testStructs, "unique=A", false},
+ {testStructs, "unique=B", true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.target, test.param)
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d unique failed Error: %v", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d unique failed Error: %v", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "unique" {
+ t.Fatalf("Index: %d unique failed Error: %v", i, errs)
+ }
+ }
+ }
+ }
+ PanicMatches(t, func() { _ = validate.Var(testStructs, "unique=C") }, "Bad field name C")
+}
+
+func TestHTMLValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"", true},
+ {"", true},
+ {"/script>", false},
+ {"<123nonsense>", false},
+ {"test", false},
+ {"&example", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "html")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d html failed Error: %v", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d html failed Error: %v", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "html" {
+ t.Fatalf("Index: %d html failed Error: %v", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestHTMLEncodedValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"<", true},
+ {"¯", true},
+ {"", true},
+ {"ð", true},
+ {"<", true},
+ {"¯", true},
+ {"", true},
+ {"ð", true},
+ {"ab", true},
+ {"<", true},
+ {">", true},
+ {""", true},
+ {"&", true},
+ {"#x0a", false},
+ {"&x00", false},
+ {"z", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "html_encoded")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d html_encoded failed Error: %v", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d html_enocded failed Error: %v", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "html_encoded" {
+ t.Fatalf("Index: %d html_encoded failed Error: %v", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestURLEncodedValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {"%20", true},
+ {"%af", true},
+ {"%ff", true},
+ {"<%az", false},
+ {"%test%", false},
+ {"a%b", false},
+ {"1%2", false},
+ {"%%a%%", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "url_encoded")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d url_encoded failed Error: %v", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d url_enocded failed Error: %v", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "url_encoded" {
+ t.Fatalf("Index: %d url_encoded failed Error: %v", i, errs)
+ }
+ }
+ }
+ }
+}
+
+func TestKeys(t *testing.T) {
+
+ type Test struct {
+ Test1 map[string]string `validate:"gt=0,dive,keys,eq=testkey,endkeys,eq=testval" json:"test1"`
+ Test2 map[int]int `validate:"gt=0,dive,keys,eq=3,endkeys,eq=4" json:"test2"`
+ Test3 map[int]int `validate:"gt=0,dive,keys,eq=3,endkeys" json:"test3"`
+ }
+
+ var tst Test
+
+ validate := New()
+ err := validate.Struct(tst)
+ NotEqual(t, err, nil)
+ Equal(t, len(err.(ValidationErrors)), 3)
+ AssertError(t, err.(ValidationErrors), "Test.Test1", "Test.Test1", "Test1", "Test1", "gt")
+ AssertError(t, err.(ValidationErrors), "Test.Test2", "Test.Test2", "Test2", "Test2", "gt")
+ AssertError(t, err.(ValidationErrors), "Test.Test3", "Test.Test3", "Test3", "Test3", "gt")
+
+ tst.Test1 = map[string]string{
+ "testkey": "testval",
+ }
+
+ tst.Test2 = map[int]int{
+ 3: 4,
+ }
+
+ tst.Test3 = map[int]int{
+ 3: 4,
+ }
+
+ err = validate.Struct(tst)
+ Equal(t, err, nil)
+
+ tst.Test1["badtestkey"] = "badtestvalue"
+ tst.Test2[10] = 11
+
+ err = validate.Struct(tst)
+ NotEqual(t, err, nil)
+
+ errs := err.(ValidationErrors)
+
+ Equal(t, len(errs), 4)
+
+ AssertDeepError(t, errs, "Test.Test1[badtestkey]", "Test.Test1[badtestkey]", "Test1[badtestkey]", "Test1[badtestkey]", "eq", "eq")
+ AssertDeepError(t, errs, "Test.Test1[badtestkey]", "Test.Test1[badtestkey]", "Test1[badtestkey]", "Test1[badtestkey]", "eq", "eq")
+ AssertDeepError(t, errs, "Test.Test2[10]", "Test.Test2[10]", "Test2[10]", "Test2[10]", "eq", "eq")
+ AssertDeepError(t, errs, "Test.Test2[10]", "Test.Test2[10]", "Test2[10]", "Test2[10]", "eq", "eq")
+
+ type Test2 struct {
+ NestedKeys map[[1]string]string `validate:"gt=0,dive,keys,dive,eq=innertestkey,endkeys,eq=outertestval"`
+ }
+
+ var tst2 Test2
+
+ err = validate.Struct(tst2)
+ NotEqual(t, err, nil)
+ Equal(t, len(err.(ValidationErrors)), 1)
+ AssertError(t, err.(ValidationErrors), "Test2.NestedKeys", "Test2.NestedKeys", "NestedKeys", "NestedKeys", "gt")
+
+ tst2.NestedKeys = map[[1]string]string{
+ [1]string{"innertestkey"}: "outertestval",
+ }
+
+ err = validate.Struct(tst2)
+ Equal(t, err, nil)
+
+ tst2.NestedKeys[[1]string{"badtestkey"}] = "badtestvalue"
+
+ err = validate.Struct(tst2)
+ NotEqual(t, err, nil)
+
+ errs = err.(ValidationErrors)
+
+ Equal(t, len(errs), 2)
+ AssertDeepError(t, errs, "Test2.NestedKeys[[badtestkey]][0]", "Test2.NestedKeys[[badtestkey]][0]", "NestedKeys[[badtestkey]][0]", "NestedKeys[[badtestkey]][0]", "eq", "eq")
+ AssertDeepError(t, errs, "Test2.NestedKeys[[badtestkey]]", "Test2.NestedKeys[[badtestkey]]", "NestedKeys[[badtestkey]]", "NestedKeys[[badtestkey]]", "eq", "eq")
+
+ // test bad tag definitions
+
+ PanicMatches(t, func() { _ = validate.Var(map[string]string{"key": "val"}, "endkeys,dive,eq=val") }, "'endkeys' tag encountered without a corresponding 'keys' tag")
+ PanicMatches(t, func() { _ = validate.Var(1, "keys,eq=1,endkeys") }, "'keys' tag must be immediately preceded by the 'dive' tag")
+
+ // test custom tag name
+ validate = New()
+ validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
+ name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
+
+ if name == "-" {
+ return ""
+ }
+ return name
+ })
+
+ err = validate.Struct(tst)
+ NotEqual(t, err, nil)
+
+ errs = err.(ValidationErrors)
+
+ Equal(t, len(errs), 4)
+
+ AssertDeepError(t, errs, "Test.test1[badtestkey]", "Test.Test1[badtestkey]", "test1[badtestkey]", "Test1[badtestkey]", "eq", "eq")
+ AssertDeepError(t, errs, "Test.test1[badtestkey]", "Test.Test1[badtestkey]", "test1[badtestkey]", "Test1[badtestkey]", "eq", "eq")
+ AssertDeepError(t, errs, "Test.test2[10]", "Test.Test2[10]", "test2[10]", "Test2[10]", "eq", "eq")
+ AssertDeepError(t, errs, "Test.test2[10]", "Test.Test2[10]", "test2[10]", "Test2[10]", "eq", "eq")
+}
+
+// Thanks @adrian-sgn specific test for your specific scenario
+func TestKeysCustomValidation(t *testing.T) {
+ type LangCode string
+ type Label map[LangCode]string
+
+ type TestMapStructPtr struct {
+ Label Label `validate:"dive,keys,lang_code,endkeys,required"`
+ }
+
+ validate := New()
+ err := validate.RegisterValidation("lang_code", func(fl FieldLevel) bool {
+ validLangCodes := map[LangCode]struct{}{
+ "en": {},
+ "es": {},
+ "pt": {},
+ }
+
+ _, ok := validLangCodes[fl.Field().Interface().(LangCode)]
+ return ok
+ })
+ Equal(t, err, nil)
+
+ label := Label{
+ "en": "Good morning!",
+ "pt": "",
+ "es": "¡Buenos días!",
+ "xx": "Bad key",
+ "xxx": "",
+ }
+
+ err = validate.Struct(TestMapStructPtr{label})
+ NotEqual(t, err, nil)
+
+ errs := err.(ValidationErrors)
+ Equal(t, len(errs), 4)
+
+ AssertDeepError(t, errs, "TestMapStructPtr.Label[xx]", "TestMapStructPtr.Label[xx]", "Label[xx]", "Label[xx]", "lang_code", "lang_code")
+ AssertDeepError(t, errs, "TestMapStructPtr.Label[pt]", "TestMapStructPtr.Label[pt]", "Label[pt]", "Label[pt]", "required", "required")
+ AssertDeepError(t, errs, "TestMapStructPtr.Label[xxx]", "TestMapStructPtr.Label[xxx]", "Label[xxx]", "Label[xxx]", "lang_code", "lang_code")
+ AssertDeepError(t, errs, "TestMapStructPtr.Label[xxx]", "TestMapStructPtr.Label[xxx]", "Label[xxx]", "Label[xxx]", "required", "required")
+
+ // find specific error
+
+ var e FieldError
+ for _, e = range errs {
+ if e.Namespace() == "TestMapStructPtr.Label[xxx]" {
+ break
+ }
+ }
+
+ Equal(t, e.Param(), "")
+ Equal(t, e.Value().(LangCode), LangCode("xxx"))
+
+ for _, e = range errs {
+ if e.Namespace() == "TestMapStructPtr.Label[xxx]" && e.Tag() == "required" {
+ break
+ }
+ }
+
+ Equal(t, e.Param(), "")
+ Equal(t, e.Value().(string), "")
+}
+
+func TestKeyOrs(t *testing.T) {
+
+ type Test struct {
+ Test1 map[string]string `validate:"gt=0,dive,keys,eq=testkey|eq=testkeyok,endkeys,eq=testval" json:"test1"`
+ }
+
+ var tst Test
+
+ validate := New()
+ err := validate.Struct(tst)
+ NotEqual(t, err, nil)
+ Equal(t, len(err.(ValidationErrors)), 1)
+ AssertError(t, err.(ValidationErrors), "Test.Test1", "Test.Test1", "Test1", "Test1", "gt")
+
+ tst.Test1 = map[string]string{
+ "testkey": "testval",
+ }
+
+ err = validate.Struct(tst)
+ Equal(t, err, nil)
+
+ tst.Test1["badtestkey"] = "badtestval"
+
+ err = validate.Struct(tst)
+ NotEqual(t, err, nil)
+
+ errs := err.(ValidationErrors)
+
+ Equal(t, len(errs), 2)
+
+ AssertDeepError(t, errs, "Test.Test1[badtestkey]", "Test.Test1[badtestkey]", "Test1[badtestkey]", "Test1[badtestkey]", "eq=testkey|eq=testkeyok", "eq=testkey|eq=testkeyok")
+ AssertDeepError(t, errs, "Test.Test1[badtestkey]", "Test.Test1[badtestkey]", "Test1[badtestkey]", "Test1[badtestkey]", "eq", "eq")
+
+ validate.RegisterAlias("okkey", "eq=testkey|eq=testkeyok")
+
+ type Test2 struct {
+ Test1 map[string]string `validate:"gt=0,dive,keys,okkey,endkeys,eq=testval" json:"test1"`
+ }
+
+ var tst2 Test2
+
+ err = validate.Struct(tst2)
+ NotEqual(t, err, nil)
+ Equal(t, len(err.(ValidationErrors)), 1)
+ AssertError(t, err.(ValidationErrors), "Test2.Test1", "Test2.Test1", "Test1", "Test1", "gt")
+
+ tst2.Test1 = map[string]string{
+ "testkey": "testval",
+ }
+
+ err = validate.Struct(tst2)
+ Equal(t, err, nil)
+
+ tst2.Test1["badtestkey"] = "badtestval"
+
+ err = validate.Struct(tst2)
+ NotEqual(t, err, nil)
+
+ errs = err.(ValidationErrors)
+
+ Equal(t, len(errs), 2)
+
+ AssertDeepError(t, errs, "Test2.Test1[badtestkey]", "Test2.Test1[badtestkey]", "Test1[badtestkey]", "Test1[badtestkey]", "okkey", "eq=testkey|eq=testkeyok")
+ AssertDeepError(t, errs, "Test2.Test1[badtestkey]", "Test2.Test1[badtestkey]", "Test1[badtestkey]", "Test1[badtestkey]", "eq", "eq")
+}
+
+func TestStructLevelValidationsPointerPassing(t *testing.T) {
+ v1 := New()
+ v1.RegisterStructValidation(StructValidationTestStruct, &TestStruct{})
+
+ tst := &TestStruct{
+ String: "good value",
+ }
+
+ errs := v1.Struct(tst)
+ NotEqual(t, errs, nil)
+ AssertError(t, errs, "TestStruct.StringVal", "TestStruct.String", "StringVal", "String", "badvalueteststruct")
+}
+
+func TestDirValidation(t *testing.T) {
+ validate := New()
+
+ tests := []struct {
+ title string
+ param string
+ expected bool
+ }{
+ {"existing dir", "testdata", true},
+ {"existing self dir", ".", true},
+ {"existing parent dir", "..", true},
+ {"empty dir", "", false},
+ {"missing dir", "non_existing_testdata", false},
+ {"a file not a directory", filepath.Join("testdata", "a.go"), false},
+ }
+
+ for _, test := range tests {
+ errs := validate.Var(test.param, "dir")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Test: '%s' failed Error: %s", test.title, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Test: '%s' failed Error: %s", test.title, errs)
+ }
+ }
+ }
+
+ PanicMatches(t, func() {
+ _ = validate.Var(2, "dir")
+ }, "Bad field type int")
+}
+
+func TestStartsWithValidation(t *testing.T) {
+ tests := []struct {
+ Value string `validate:"startswith=(/^ヮ^)/*:・゚✧"`
+ Tag string
+ ExpectedNil bool
+ }{
+ {Value: "(/^ヮ^)/*:・゚✧ glitter", Tag: "startswith=(/^ヮ^)/*:・゚✧", ExpectedNil: true},
+ {Value: "abcd", Tag: "startswith=(/^ヮ^)/*:・゚✧", ExpectedNil: false},
+ }
+
+ validate := New()
+
+ for i, s := range tests {
+ errs := validate.Var(s.Value, s.Tag)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+
+ errs = validate.Struct(s)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+ }
+}
+
+func TestEndsWithValidation(t *testing.T) {
+ tests := []struct {
+ Value string `validate:"endswith=(/^ヮ^)/*:・゚✧"`
+ Tag string
+ ExpectedNil bool
+ }{
+ {Value: "glitter (/^ヮ^)/*:・゚✧", Tag: "endswith=(/^ヮ^)/*:・゚✧", ExpectedNil: true},
+ {Value: "(/^ヮ^)/*:・゚✧ glitter", Tag: "endswith=(/^ヮ^)/*:・゚✧", ExpectedNil: false},
+ }
+
+ validate := New()
+
+ for i, s := range tests {
+ errs := validate.Var(s.Value, s.Tag)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+
+ errs = validate.Struct(s)
+
+ if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) {
+ t.Fatalf("Index: %d failed Error: %s", i, errs)
+ }
+ }
+}
+
+func TestRequiredIf(t *testing.T) {
+ type Inner struct {
+ Field *string
+ }
+
+ fieldVal := "test"
+ test := struct {
+ Inner *Inner
+ FieldE string `validate:"omitempty" json:"field_e"`
+ FieldER string `validate:"required_if=FieldE test" json:"field_er"`
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_if=Field1 test" json:"field_2"`
+ Field3 map[string]string `validate:"required_if=Field2 test" json:"field_3"`
+ Field4 interface{} `validate:"required_if=Field3 1" json:"field_4"`
+ Field5 int `validate:"required_if=Inner.Field test" json:"field_5"`
+ Field6 uint `validate:"required_if=Field5 1" json:"field_6"`
+ Field7 float32 `validate:"required_if=Field6 1" json:"field_7"`
+ Field8 float64 `validate:"required_if=Field7 1.0" json:"field_8"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: 2,
+ }
+
+ validate := New()
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test2 := struct {
+ Inner *Inner
+ Inner2 *Inner
+ FieldE string `validate:"omitempty" json:"field_e"`
+ FieldER string `validate:"required_if=FieldE test" json:"field_er"`
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_if=Field1 test" json:"field_2"`
+ Field3 map[string]string `validate:"required_if=Field2 test" json:"field_3"`
+ Field4 interface{} `validate:"required_if=Field2 test" json:"field_4"`
+ Field5 string `validate:"required_if=Field3 1" json:"field_5"`
+ Field6 string `validate:"required_if=Inner.Field test" json:"field_6"`
+ Field7 string `validate:"required_if=Inner2.Field test" json:"field_7"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field2: &fieldVal,
+ }
+
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 3)
+ AssertError(t, errs, "Field3", "Field3", "Field3", "Field3", "required_if")
+ AssertError(t, errs, "Field4", "Field4", "Field4", "Field4", "required_if")
+ AssertError(t, errs, "Field6", "Field6", "Field6", "Field6", "required_if")
+
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("test3 should have panicked!")
+ }
+ }()
+
+ test3 := struct {
+ Inner *Inner
+ Field1 string `validate:"required_if=Inner.Field" json:"field_1"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ }
+ _ = validate.Struct(test3)
+}
+
+func TestRequiredUnless(t *testing.T) {
+ type Inner struct {
+ Field *string
+ }
+
+ fieldVal := "test"
+ test := struct {
+ Inner *Inner
+ FieldE string `validate:"omitempty" json:"field_e"`
+ FieldER string `validate:"required_unless=FieldE test" json:"field_er"`
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_unless=Field1 test" json:"field_2"`
+ Field3 map[string]string `validate:"required_unless=Field2 test" json:"field_3"`
+ Field4 interface{} `validate:"required_unless=Field3 1" json:"field_4"`
+ Field5 int `validate:"required_unless=Inner.Field test" json:"field_5"`
+ Field6 uint `validate:"required_unless=Field5 2" json:"field_6"`
+ Field7 float32 `validate:"required_unless=Field6 0" json:"field_7"`
+ Field8 float64 `validate:"required_unless=Field7 0.0" json:"field_8"`
+ }{
+ FieldE: "test",
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: 2,
+ }
+
+ validate := New()
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test2 := struct {
+ Inner *Inner
+ Inner2 *Inner
+ FieldE string `validate:"omitempty" json:"field_e"`
+ FieldER string `validate:"required_unless=FieldE test" json:"field_er"`
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_unless=Field1 test" json:"field_2"`
+ Field3 map[string]string `validate:"required_unless=Field2 test" json:"field_3"`
+ Field4 interface{} `validate:"required_unless=Field2 test" json:"field_4"`
+ Field5 string `validate:"required_unless=Field3 0" json:"field_5"`
+ Field6 string `validate:"required_unless=Inner.Field test" json:"field_6"`
+ Field7 string `validate:"required_unless=Inner2.Field test" json:"field_7"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ FieldE: "test",
+ Field1: "test",
+ }
+
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 3)
+ AssertError(t, errs, "Field3", "Field3", "Field3", "Field3", "required_unless")
+ AssertError(t, errs, "Field4", "Field4", "Field4", "Field4", "required_unless")
+ AssertError(t, errs, "Field7", "Field7", "Field7", "Field7", "required_unless")
+
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("test3 should have panicked!")
+ }
+ }()
+
+ test3 := struct {
+ Inner *Inner
+ Field1 string `validate:"required_unless=Inner.Field" json:"field_1"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ }
+ _ = validate.Struct(test3)
+}
+
+func TestRequiredWith(t *testing.T) {
+ type Inner struct {
+ Field *string
+ }
+
+ fieldVal := "test"
+ test := struct {
+ Inner *Inner
+ FieldE string `validate:"omitempty" json:"field_e"`
+ FieldER string `validate:"required_with=FieldE" json:"field_er"`
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_with=Field1" json:"field_2"`
+ Field3 map[string]string `validate:"required_with=Field2" json:"field_3"`
+ Field4 interface{} `validate:"required_with=Field3" json:"field_4"`
+ Field5 string `validate:"required_with=Inner.Field" json:"field_5"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ }
+
+ validate := New()
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test2 := struct {
+ Inner *Inner
+ Inner2 *Inner
+ FieldE string `validate:"omitempty" json:"field_e"`
+ FieldER string `validate:"required_with=FieldE" json:"field_er"`
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_with=Field1" json:"field_2"`
+ Field3 map[string]string `validate:"required_with=Field2" json:"field_3"`
+ Field4 interface{} `validate:"required_with=Field2" json:"field_4"`
+ Field5 string `validate:"required_with=Field3" json:"field_5"`
+ Field6 string `validate:"required_with=Inner.Field" json:"field_6"`
+ Field7 string `validate:"required_with=Inner2.Field" json:"field_7"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field2: &fieldVal,
+ }
+
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 3)
+ AssertError(t, errs, "Field3", "Field3", "Field3", "Field3", "required_with")
+ AssertError(t, errs, "Field4", "Field4", "Field4", "Field4", "required_with")
+ AssertError(t, errs, "Field6", "Field6", "Field6", "Field6", "required_with")
+}
+
+func TestExcludedWith(t *testing.T) {
+ type Inner struct {
+ FieldE string
+ Field *string
+ }
+
+ fieldVal := "test"
+ test := struct {
+ Inner *Inner
+ Inner2 *Inner
+ Field string `validate:"omitempty" json:"field"`
+ FieldE string `validate:"omitempty" json:"field_e"`
+ Field1 string `validate:"excluded_with=FieldE" json:"field_1"`
+ Field2 *string `validate:"excluded_with=FieldE" json:"field_2"`
+ Field3 map[string]string `validate:"excluded_with=FieldE" json:"field_3"`
+ Field4 interface{} `validate:"excluded_with=FieldE" json:"field_4"`
+ Field5 string `validate:"excluded_with=Inner.FieldE" json:"field_5"`
+ Field6 string `validate:"excluded_with=Inner2.FieldE" json:"field_6"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field1: fieldVal,
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ Field6: "test",
+ }
+
+ validate := New()
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test2 := struct {
+ Inner *Inner
+ Inner2 *Inner
+ Field string `validate:"omitempty" json:"field"`
+ FieldE string `validate:"omitempty" json:"field_e"`
+ Field1 string `validate:"excluded_with=Field" json:"field_1"`
+ Field2 *string `validate:"excluded_with=Field" json:"field_2"`
+ Field3 map[string]string `validate:"excluded_with=Field" json:"field_3"`
+ Field4 interface{} `validate:"excluded_with=Field" json:"field_4"`
+ Field5 string `validate:"excluded_with=Inner.Field" json:"field_5"`
+ Field6 string `validate:"excluded_with=Inner2.Field" json:"field_6"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field: "populated",
+ Field1: fieldVal,
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ Field6: "test",
+ }
+
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 5)
+ for i := 1; i <= 5; i++ {
+ name := fmt.Sprintf("Field%d", i)
+ AssertError(t, errs, name, name, name, name, "excluded_with")
+ }
+}
+
+func TestExcludedWithout(t *testing.T) {
+ type Inner struct {
+ FieldE string
+ Field *string
+ }
+
+ fieldVal := "test"
+ test := struct {
+ Inner *Inner
+ Inner2 *Inner
+ Field string `validate:"omitempty" json:"field"`
+ FieldE string `validate:"omitempty" json:"field_e"`
+ Field1 string `validate:"excluded_without=Field" json:"field_1"`
+ Field2 *string `validate:"excluded_without=Field" json:"field_2"`
+ Field3 map[string]string `validate:"excluded_without=Field" json:"field_3"`
+ Field4 interface{} `validate:"excluded_without=Field" json:"field_4"`
+ Field5 string `validate:"excluded_without=Inner.Field" json:"field_5"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field: "populated",
+ Field1: fieldVal,
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ }
+
+ validate := New()
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test2 := struct {
+ Inner *Inner
+ Inner2 *Inner
+ Field string `validate:"omitempty" json:"field"`
+ FieldE string `validate:"omitempty" json:"field_e"`
+ Field1 string `validate:"excluded_without=FieldE" json:"field_1"`
+ Field2 *string `validate:"excluded_without=FieldE" json:"field_2"`
+ Field3 map[string]string `validate:"excluded_without=FieldE" json:"field_3"`
+ Field4 interface{} `validate:"excluded_without=FieldE" json:"field_4"`
+ Field5 string `validate:"excluded_without=Inner.FieldE" json:"field_5"`
+ Field6 string `validate:"excluded_without=Inner2.FieldE" json:"field_6"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field1: fieldVal,
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ Field6: "test",
+ }
+
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 6)
+ for i := 1; i <= 6; i++ {
+ name := fmt.Sprintf("Field%d", i)
+ AssertError(t, errs, name, name, name, name, "excluded_without")
+ }
+}
+
+func TestExcludedWithAll(t *testing.T) {
+ type Inner struct {
+ FieldE string
+ Field *string
+ }
+
+ fieldVal := "test"
+ test := struct {
+ Inner *Inner
+ Inner2 *Inner
+ Field string `validate:"omitempty" json:"field"`
+ FieldE string `validate:"omitempty" json:"field_e"`
+ Field1 string `validate:"excluded_with_all=FieldE Field" json:"field_1"`
+ Field2 *string `validate:"excluded_with_all=FieldE Field" json:"field_2"`
+ Field3 map[string]string `validate:"excluded_with_all=FieldE Field" json:"field_3"`
+ Field4 interface{} `validate:"excluded_with_all=FieldE Field" json:"field_4"`
+ Field5 string `validate:"excluded_with_all=Inner.FieldE" json:"field_5"`
+ Field6 string `validate:"excluded_with_all=Inner2.FieldE" json:"field_6"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field: fieldVal,
+ Field1: fieldVal,
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ Field6: "test",
+ }
+
+ validate := New()
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test2 := struct {
+ Inner *Inner
+ Inner2 *Inner
+ Field string `validate:"omitempty" json:"field"`
+ FieldE string `validate:"omitempty" json:"field_e"`
+ Field1 string `validate:"excluded_with_all=Field FieldE" json:"field_1"`
+ Field2 *string `validate:"excluded_with_all=Field FieldE" json:"field_2"`
+ Field3 map[string]string `validate:"excluded_with_all=Field FieldE" json:"field_3"`
+ Field4 interface{} `validate:"excluded_with_all=Field FieldE" json:"field_4"`
+ Field5 string `validate:"excluded_with_all=Inner.Field" json:"field_5"`
+ Field6 string `validate:"excluded_with_all=Inner2.Field" json:"field_6"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field: "populated",
+ FieldE: "populated",
+ Field1: fieldVal,
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ Field6: "test",
+ }
+
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 5)
+ for i := 1; i <= 5; i++ {
+ name := fmt.Sprintf("Field%d", i)
+ AssertError(t, errs, name, name, name, name, "excluded_with_all")
+ }
+}
+
+func TestExcludedWithoutAll(t *testing.T) {
+ type Inner struct {
+ FieldE string
+ Field *string
+ }
+
+ fieldVal := "test"
+ test := struct {
+ Inner *Inner
+ Inner2 *Inner
+ Field string `validate:"omitempty" json:"field"`
+ FieldE string `validate:"omitempty" json:"field_e"`
+ Field1 string `validate:"excluded_without_all=Field FieldE" json:"field_1"`
+ Field2 *string `validate:"excluded_without_all=Field FieldE" json:"field_2"`
+ Field3 map[string]string `validate:"excluded_without_all=Field FieldE" json:"field_3"`
+ Field4 interface{} `validate:"excluded_without_all=Field FieldE" json:"field_4"`
+ Field5 string `validate:"excluded_without_all=Inner.Field Inner.Field2" json:"field_5"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field: "populated",
+ Field1: fieldVal,
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ }
+
+ validate := New()
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test2 := struct {
+ Inner *Inner
+ Inner2 *Inner
+ Field string `validate:"omitempty" json:"field"`
+ FieldE string `validate:"omitempty" json:"field_e"`
+ Field1 string `validate:"excluded_without_all=FieldE Field" json:"field_1"`
+ Field2 *string `validate:"excluded_without_all=FieldE Field" json:"field_2"`
+ Field3 map[string]string `validate:"excluded_without_all=FieldE Field" json:"field_3"`
+ Field4 interface{} `validate:"excluded_without_all=FieldE Field" json:"field_4"`
+ Field5 string `validate:"excluded_without_all=Inner.FieldE" json:"field_5"`
+ Field6 string `validate:"excluded_without_all=Inner2.FieldE" json:"field_6"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field1: fieldVal,
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ Field6: "test",
+ }
+
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 6)
+ for i := 1; i <= 6; i++ {
+ name := fmt.Sprintf("Field%d", i)
+ AssertError(t, errs, name, name, name, name, "excluded_without_all")
+ }
+}
+
+func TestRequiredWithAll(t *testing.T) {
+ type Inner struct {
+ Field *string
+ }
+
+ fieldVal := "test"
+ test := struct {
+ Inner *Inner
+ FieldE string `validate:"omitempty" json:"field_e"`
+ FieldER string `validate:"required_with_all=FieldE" json:"field_er"`
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_with_all=Field1" json:"field_2"`
+ Field3 map[string]string `validate:"required_with_all=Field2" json:"field_3"`
+ Field4 interface{} `validate:"required_with_all=Field3" json:"field_4"`
+ Field5 string `validate:"required_with_all=Inner.Field" json:"field_5"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field1: "test_field1",
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ }
+
+ validate := New()
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test2 := struct {
+ Inner *Inner
+ Inner2 *Inner
+ FieldE string `validate:"omitempty" json:"field_e"`
+ FieldER string `validate:"required_with_all=FieldE" json:"field_er"`
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_with_all=Field1" json:"field_2"`
+ Field3 map[string]string `validate:"required_with_all=Field2" json:"field_3"`
+ Field4 interface{} `validate:"required_with_all=Field1 FieldE" json:"field_4"`
+ Field5 string `validate:"required_with_all=Inner.Field Field2" json:"field_5"`
+ Field6 string `validate:"required_with_all=Inner2.Field Field2" json:"field_6"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field2: &fieldVal,
+ }
+
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 2)
+ AssertError(t, errs, "Field3", "Field3", "Field3", "Field3", "required_with_all")
+ AssertError(t, errs, "Field5", "Field5", "Field5", "Field5", "required_with_all")
+}
+
+func TestRequiredWithout(t *testing.T) {
+
+ type Inner struct {
+ Field *string
+ }
+
+ fieldVal := "test"
+ test := struct {
+ Inner *Inner
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_without=Field1" json:"field_2"`
+ Field3 map[string]string `validate:"required_without=Field2" json:"field_3"`
+ Field4 interface{} `validate:"required_without=Field3" json:"field_4"`
+ Field5 string `validate:"required_without=Field3" json:"field_5"`
+ }{
+ Inner: &Inner{Field: &fieldVal},
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ }
+
+ validate := New()
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test2 := struct {
+ Inner *Inner
+ Inner2 *Inner
+ Field1 string `json:"field_1"`
+ Field2 *string `validate:"required_without=Field1" json:"field_2"`
+ Field3 map[string]string `validate:"required_without=Field2" json:"field_3"`
+ Field4 interface{} `validate:"required_without=Field3" json:"field_4"`
+ Field5 string `validate:"required_without=Field3" json:"field_5"`
+ Field6 string `validate:"required_without=Field1" json:"field_6"`
+ Field7 string `validate:"required_without=Inner.Field" json:"field_7"`
+ Field8 string `validate:"required_without=Inner.Field" json:"field_8"`
+ }{
+ Inner: &Inner{},
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ }
+
+ errs = validate.Struct(&test2)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 4)
+ AssertError(t, errs, "Field2", "Field2", "Field2", "Field2", "required_without")
+ AssertError(t, errs, "Field6", "Field6", "Field6", "Field6", "required_without")
+ AssertError(t, errs, "Field7", "Field7", "Field7", "Field7", "required_without")
+ AssertError(t, errs, "Field8", "Field8", "Field8", "Field8", "required_without")
+
+ test3 := struct {
+ Field1 *string `validate:"required_without=Field2,omitempty,min=1" json:"field_1"`
+ Field2 *string `validate:"required_without=Field1,omitempty,min=1" json:"field_2"`
+ }{
+ Field1: &fieldVal,
+ }
+
+ errs = validate.Struct(&test3)
+ Equal(t, errs, nil)
+}
+
+func TestRequiredWithoutAll(t *testing.T) {
+
+ fieldVal := "test"
+ test := struct {
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_without_all=Field1" json:"field_2"`
+ Field3 map[string]string `validate:"required_without_all=Field2" json:"field_3"`
+ Field4 interface{} `validate:"required_without_all=Field3" json:"field_4"`
+ Field5 string `validate:"required_without_all=Field3" json:"field_5"`
+ }{
+ Field1: "",
+ Field2: &fieldVal,
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ }
+
+ validate := New()
+
+ errs := validate.Struct(test)
+ Equal(t, errs, nil)
+
+ test2 := struct {
+ Field1 string `validate:"omitempty" json:"field_1"`
+ Field2 *string `validate:"required_without_all=Field1" json:"field_2"`
+ Field3 map[string]string `validate:"required_without_all=Field2" json:"field_3"`
+ Field4 interface{} `validate:"required_without_all=Field3" json:"field_4"`
+ Field5 string `validate:"required_without_all=Field3" json:"field_5"`
+ Field6 string `validate:"required_without_all=Field1 Field3" json:"field_6"`
+ }{
+ Field3: map[string]string{"key": "val"},
+ Field4: "test",
+ Field5: "test",
+ }
+
+ errs = validate.Struct(test2)
+ NotEqual(t, errs, nil)
+
+ ve := errs.(ValidationErrors)
+ Equal(t, len(ve), 1)
+ AssertError(t, errs, "Field2", "Field2", "Field2", "Field2", "required_without_all")
+}
+
+func TestLookup(t *testing.T) {
+ type Lookup struct {
+ FieldA *string `json:"fieldA,omitempty" validate:"required_without=FieldB"`
+ FieldB *string `json:"fieldB,omitempty" validate:"required_without=FieldA"`
+ }
+
+ fieldAValue := "1232"
+ lookup := Lookup{
+ FieldA: &fieldAValue,
+ FieldB: nil,
+ }
+ Equal(t, New().Struct(lookup), nil)
+}
+
+func TestAbilityToValidateNils(t *testing.T) {
+
+ type TestStruct struct {
+ Test *string `validate:"nil"`
+ }
+
+ ts := TestStruct{}
+ val := New()
+ fn := func(fl FieldLevel) bool {
+ return fl.Field().Kind() == reflect.Ptr && fl.Field().IsNil()
+ }
+
+ err := val.RegisterValidation("nil", fn, true)
+ Equal(t, err, nil)
+
+ errs := val.Struct(ts)
+ Equal(t, errs, nil)
+
+ str := "string"
+ ts.Test = &str
+
+ errs = val.Struct(ts)
+ NotEqual(t, errs, nil)
+}
+
+func TestRequiredWithoutPointers(t *testing.T) {
+ type Lookup struct {
+ FieldA *bool `json:"fieldA,omitempty" validate:"required_without=FieldB"`
+ FieldB *bool `json:"fieldB,omitempty" validate:"required_without=FieldA"`
+ }
+
+ b := true
+ lookup := Lookup{
+ FieldA: &b,
+ FieldB: nil,
+ }
+
+ val := New()
+ errs := val.Struct(lookup)
+ Equal(t, errs, nil)
+
+ b = false
+ lookup = Lookup{
+ FieldA: &b,
+ FieldB: nil,
+ }
+ errs = val.Struct(lookup)
+ Equal(t, errs, nil)
+}
+
+func TestRequiredWithoutAllPointers(t *testing.T) {
+ type Lookup struct {
+ FieldA *bool `json:"fieldA,omitempty" validate:"required_without_all=FieldB"`
+ FieldB *bool `json:"fieldB,omitempty" validate:"required_without_all=FieldA"`
+ }
+
+ b := true
+ lookup := Lookup{
+ FieldA: &b,
+ FieldB: nil,
+ }
+
+ val := New()
+ errs := val.Struct(lookup)
+ Equal(t, errs, nil)
+
+ b = false
+ lookup = Lookup{
+ FieldA: &b,
+ FieldB: nil,
+ }
+ errs = val.Struct(lookup)
+ Equal(t, errs, nil)
+}
+
+func TestGetTag(t *testing.T) {
+ var tag string
+
+ type Test struct {
+ String string `validate:"mytag"`
+ }
+
+ val := New()
+ _ = val.RegisterValidation("mytag", func(fl FieldLevel) bool {
+ tag = fl.GetTag()
+ return true
+ })
+
+ var test Test
+ errs := val.Struct(test)
+ Equal(t, errs, nil)
+ Equal(t, tag, "mytag")
+}
+
+func TestJSONValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {`foo`, false},
+ {`}{`, false},
+ {`{]`, false},
+ {`{}`, true},
+ {`{"foo":"bar"}`, true},
+ {`{"foo":"bar","bar":{"baz":["qux"]}}`, true},
+ {`{"foo": 3 "bar": 4}`, false},
+ {`{"foo": 3 ,"bar": 4`, false},
+ {`{foo": 3, "bar": 4}`, false},
+ {`foo`, false},
+ {`1`, true},
+ {`true`, true},
+ {`null`, true},
+ {`"null"`, true},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "json")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d json failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d json failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "json" {
+ t.Fatalf("Index: %d json failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+
+ PanicMatches(t, func() {
+ _ = validate.Var(2, "json")
+ }, "Bad field type int")
+}
+
+func Test_hostnameport_validator(t *testing.T) {
+
+ type Host struct {
+ Addr string `validate:"hostname_port"`
+ }
+
+ type testInput struct {
+ data string
+ expected bool
+ }
+ testData := []testInput{
+ {"bad..domain.name:234", false},
+ {"extra.dot.com.", false},
+ {"localhost:1234", true},
+ {"192.168.1.1:1234", true},
+ {":1234", true},
+ {"domain.com:1334", true},
+ {"this.domain.com:234", true},
+ {"domain:75000", false},
+ {"missing.port", false},
+ }
+ for _, td := range testData {
+ h := Host{Addr: td.data}
+ v := New()
+ err := v.Struct(h)
+ if td.expected != (err == nil) {
+ t.Fatalf("Test failed for data: %v Error: %v", td.data, err)
+ }
+ }
+}
+
+func TestLowercaseValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {`abcdefg`, true},
+ {`Abcdefg`, false},
+ {"", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "lowercase")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d lowercase failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d lowercase failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "lowercase" {
+ t.Fatalf("Index: %d lowercase failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+
+ PanicMatches(t, func() {
+ _ = validate.Var(2, "lowercase")
+ }, "Bad field type int")
+}
+
+func TestUppercaseValidation(t *testing.T) {
+ tests := []struct {
+ param string
+ expected bool
+ }{
+ {`ABCDEFG`, true},
+ {`aBCDEFG`, false},
+ {"", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.param, "uppercase")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d uppercase failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d uppercase failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "uppercase" {
+ t.Fatalf("Index: %d uppercase failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+
+ PanicMatches(t, func() {
+ _ = validate.Var(2, "uppercase")
+ }, "Bad field type int")
+
+}
+
+func TestDatetimeValidation(t *testing.T) {
+ tests := []struct {
+ value string `validate:"datetime=2006-01-02"`
+ tag string
+ expected bool
+ }{
+ {"2008-02-01", `datetime=2006-01-02`, true},
+ {"2008-Feb-01", `datetime=2006-01-02`, false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.value, test.tag)
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d datetime failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d datetime failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "datetime" {
+ t.Fatalf("Index: %d datetime failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+
+ PanicMatches(t, func() {
+ _ = validate.Var(2, "datetime")
+ }, "Bad field type int")
+}
+
+func TestIsIso3166Alpha2Validation(t *testing.T) {
+ tests := []struct {
+ value string `validate:"iso3166_1_alpha2"`
+ expected bool
+ }{
+ {"PL", true},
+ {"POL", false},
+ {"AA", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.value, "iso3166_1_alpha2")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d iso3166_1_alpha2 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d iso3166_1_alpha2 failed Error: %s", i, errs)
+ }
+ }
+ }
+}
+
+func TestIsIso3166Alpha3Validation(t *testing.T) {
+ tests := []struct {
+ value string `validate:"iso3166_1_alpha3"`
+ expected bool
+ }{
+ {"POL", true},
+ {"PL", false},
+ {"AAA", false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.value, "iso3166_1_alpha3")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d iso3166_1_alpha3 failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d iso3166_1_alpha3 failed Error: %s", i, errs)
+ }
+ }
+ }
+}
+
+func TestIsIso3166AlphaNumericValidation(t *testing.T) {
+ tests := []struct {
+ value int
+ expected bool
+ }{
+ {248, true},
+ {0, false},
+ {1, false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.value, "iso3166_1_alpha_numeric")
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d iso3166_1_alpha_numeric failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d iso3166_1_alpha_numeric failed Error: %s", i, errs)
+ }
+ }
+ }
+
+ PanicMatches(t, func() {
+ _ = validate.Var("1", "iso3166_1_alpha_numeric")
+ }, "Bad field type string")
+}
+
+func TestTimeZoneValidation(t *testing.T) {
+ tests := []struct {
+ value string `validate:"timezone"`
+ tag string
+ expected bool
+ }{
+ // systems may have different time zone database, some systems time zone are case insensitive
+ {"America/New_York", `timezone`, true},
+ {"UTC", `timezone`, true},
+ {"", `timezone`, false},
+ {"Local", `timezone`, false},
+ {"Unknown", `timezone`, false},
+ }
+
+ validate := New()
+
+ for i, test := range tests {
+
+ errs := validate.Var(test.value, test.tag)
+
+ if test.expected {
+ if !IsEqual(errs, nil) {
+ t.Fatalf("Index: %d time zone failed Error: %s", i, errs)
+ }
+ } else {
+ if IsEqual(errs, nil) {
+ t.Fatalf("Index: %d time zone failed Error: %s", i, errs)
+ } else {
+ val := getError(errs, "", "")
+ if val.Tag() != "timezone" {
+ t.Fatalf("Index: %d time zone failed Error: %s", i, errs)
+ }
+ }
+ }
+ }
+
+ PanicMatches(t, func() {
+ _ = validate.Var(2, "timezone")
+ }, "Bad field type int")
+}
+
+func TestDurationType(t *testing.T) {
+ tests := []struct {
+ name string
+ s interface{} // struct
+ success bool
+ }{
+ {
+ name: "valid duration string pass",
+ s: struct {
+ Value time.Duration `validate:"gte=500ns"`
+ }{
+ Value: time.Second,
+ },
+ success: true,
+ },
+ {
+ name: "valid duration int pass",
+ s: struct {
+ Value time.Duration `validate:"gte=500"`
+ }{
+ Value: time.Second,
+ },
+ success: true,
+ },
+ }
+
+ validate := New()
+
+ for _, tc := range tests {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ errs := validate.Struct(tc.s)
+ if tc.success {
+ Equal(t, errs, nil)
+ return
+ }
+ NotEqual(t, errs, nil)
+ })
+ }
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..c32083b
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,15 @@
+module gin-valid
+
+go 1.13
+
+require (
+ github.com/go-playground/assert/v2 v2.0.1 // indirect
+ github.com/go-playground/universal-translator v0.17.0
+ github.com/golang/protobuf v1.4.3
+ github.com/json-iterator/go v1.1.10
+ github.com/leodido/go-urn v1.2.0
+ github.com/stretchr/testify v1.6.1
+ github.com/ugorji/go/codec v1.2.0
+ golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392
+ gopkg.in/yaml.v2 v2.4.0
+)
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..e0b87a2
--- /dev/null
+++ b/main.go
@@ -0,0 +1,5 @@
+package main
+
+func main() {
+ return
+}