diff --git a/README.md b/README.md
index a38b4ec..948ecec 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@ 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-9.0.0-green.svg)
+![Project status](https://img.shields.io/badge/version-9.1.0-green.svg)
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github)](https://coveralls.io/github/go-playground/validator?branch=v9)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
@@ -333,54 +333,54 @@ Benchmarks
------
###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go version go1.7 darwin/amd64
```go
-BenchmarkFieldSuccess-8 20000000 108 ns/op 0 B/op 0 allocs/op
-BenchmarkFieldSuccessParallel-8 50000000 35.7 ns/op 0 B/op 0 allocs/op
-BenchmarkFieldFailure-8 5000000 320 ns/op 192 B/op 4 allocs/op
-BenchmarkFieldFailureParallel-8 20000000 113 ns/op 192 B/op 4 allocs/op
-BenchmarkFieldDiveSuccess-8 2000000 726 ns/op 201 B/op 11 allocs/op
-BenchmarkFieldDiveSuccessParallel-8 10000000 263 ns/op 201 B/op 11 allocs/op
-BenchmarkFieldDiveFailure-8 2000000 939 ns/op 396 B/op 16 allocs/op
-BenchmarkFieldDiveFailureParallel-8 5000000 382 ns/op 397 B/op 16 allocs/op
-BenchmarkFieldCustomTypeSuccess-8 5000000 268 ns/op 32 B/op 2 allocs/op
-BenchmarkFieldCustomTypeSuccessParallel-8 20000000 87.8 ns/op 32 B/op 2 allocs/op
-BenchmarkFieldCustomTypeFailure-8 5000000 310 ns/op 192 B/op 4 allocs/op
-BenchmarkFieldCustomTypeFailureParallel-8 20000000 131 ns/op 192 B/op 4 allocs/op
-BenchmarkFieldOrTagSuccess-8 2000000 889 ns/op 16 B/op 1 allocs/op
-BenchmarkFieldOrTagSuccessParallel-8 5000000 418 ns/op 16 B/op 1 allocs/op
-BenchmarkFieldOrTagFailure-8 3000000 546 ns/op 208 B/op 5 allocs/op
-BenchmarkFieldOrTagFailureParallel-8 3000000 450 ns/op 208 B/op 5 allocs/op
-BenchmarkStructLevelValidationSuccess-8 5000000 336 ns/op 32 B/op 2 allocs/op
-BenchmarkStructLevelValidationSuccessParallel-8 20000000 123 ns/op 32 B/op 2 allocs/op
-BenchmarkStructLevelValidationFailure-8 2000000 611 ns/op 288 B/op 8 allocs/op
-BenchmarkStructLevelValidationFailureParallel-8 5000000 298 ns/op 288 B/op 8 allocs/op
-BenchmarkStructSimpleCustomTypeSuccess-8 2000000 555 ns/op 32 B/op 2 allocs/op
-BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 197 ns/op 32 B/op 2 allocs/op
-BenchmarkStructSimpleCustomTypeFailure-8 2000000 811 ns/op 392 B/op 9 allocs/op
-BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 370 ns/op 408 B/op 10 allocs/op
-BenchmarkStructPartialSuccess-8 2000000 676 ns/op 256 B/op 6 allocs/op
-BenchmarkStructPartialSuccessParallel-8 5000000 301 ns/op 256 B/op 6 allocs/op
-BenchmarkStructPartialFailure-8 1000000 1001 ns/op 464 B/op 11 allocs/op
-BenchmarkStructPartialFailureParallel-8 3000000 436 ns/op 464 B/op 11 allocs/op
-BenchmarkStructExceptSuccess-8 1000000 1038 ns/op 480 B/op 12 allocs/op
-BenchmarkStructExceptSuccessParallel-8 10000000 281 ns/op 240 B/op 5 allocs/op
-BenchmarkStructExceptFailure-8 2000000 863 ns/op 448 B/op 10 allocs/op
-BenchmarkStructExceptFailureParallel-8 3000000 379 ns/op 448 B/op 10 allocs/op
-BenchmarkStructSimpleCrossFieldSuccess-8 3000000 549 ns/op 72 B/op 3 allocs/op
-BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 192 ns/op 72 B/op 3 allocs/op
-BenchmarkStructSimpleCrossFieldFailure-8 2000000 783 ns/op 288 B/op 8 allocs/op
-BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 296 ns/op 288 B/op 8 allocs/op
-BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 837 ns/op 80 B/op 4 allocs/op
-BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 5000000 284 ns/op 80 B/op 4 allocs/op
-BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 1110 ns/op 304 B/op 9 allocs/op
-BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 3000000 416 ns/op 304 B/op 9 allocs/op
-BenchmarkStructSimpleSuccess-8 5000000 380 ns/op 0 B/op 0 allocs/op
-BenchmarkStructSimpleSuccessParallel-8 20000000 114 ns/op 0 B/op 0 allocs/op
-BenchmarkStructSimpleFailure-8 2000000 760 ns/op 392 B/op 9 allocs/op
-BenchmarkStructSimpleFailureParallel-8 5000000 353 ns/op 392 B/op 9 allocs/op
-BenchmarkStructComplexSuccess-8 1000000 2100 ns/op 128 B/op 8 allocs/op
-BenchmarkStructComplexSuccessParallel-8 2000000 662 ns/op 128 B/op 8 allocs/op
-BenchmarkStructComplexFailure-8 200000 5080 ns/op 2833 B/op 53 allocs/op
-BenchmarkStructComplexFailureParallel-8 1000000 2159 ns/op 2833 B/op 53 allocs/op
+BenchmarkFieldSuccess-8 20000000 105 ns/op 0 B/op 0 allocs/op
+BenchmarkFieldSuccessParallel-8 50000000 35.1 ns/op 0 B/op 0 allocs/op
+BenchmarkFieldFailure-8 5000000 337 ns/op 208 B/op 4 allocs/op
+BenchmarkFieldFailureParallel-8 20000000 120 ns/op 208 B/op 4 allocs/op
+BenchmarkFieldDiveSuccess-8 2000000 716 ns/op 201 B/op 11 allocs/op
+BenchmarkFieldDiveSuccessParallel-8 10000000 253 ns/op 201 B/op 11 allocs/op
+BenchmarkFieldDiveFailure-8 1000000 1060 ns/op 412 B/op 16 allocs/op
+BenchmarkFieldDiveFailureParallel-8 5000000 360 ns/op 413 B/op 16 allocs/op
+BenchmarkFieldCustomTypeSuccess-8 5000000 299 ns/op 32 B/op 2 allocs/op
+BenchmarkFieldCustomTypeSuccessParallel-8 20000000 86.0 ns/op 32 B/op 2 allocs/op
+BenchmarkFieldCustomTypeFailure-8 5000000 341 ns/op 208 B/op 4 allocs/op
+BenchmarkFieldCustomTypeFailureParallel-8 20000000 140 ns/op 208 B/op 4 allocs/op
+BenchmarkFieldOrTagSuccess-8 2000000 893 ns/op 16 B/op 1 allocs/op
+BenchmarkFieldOrTagSuccessParallel-8 5000000 431 ns/op 16 B/op 1 allocs/op
+BenchmarkFieldOrTagFailure-8 2000000 563 ns/op 224 B/op 5 allocs/op
+BenchmarkFieldOrTagFailureParallel-8 5000000 417 ns/op 224 B/op 5 allocs/op
+BenchmarkStructLevelValidationSuccess-8 5000000 339 ns/op 32 B/op 2 allocs/op
+BenchmarkStructLevelValidationSuccessParallel-8 20000000 114 ns/op 32 B/op 2 allocs/op
+BenchmarkStructLevelValidationFailure-8 2000000 630 ns/op 304 B/op 8 allocs/op
+BenchmarkStructLevelValidationFailureParallel-8 5000000 291 ns/op 304 B/op 8 allocs/op
+BenchmarkStructSimpleCustomTypeSuccess-8 3000000 540 ns/op 32 B/op 2 allocs/op
+BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 176 ns/op 32 B/op 2 allocs/op
+BenchmarkStructSimpleCustomTypeFailure-8 2000000 821 ns/op 424 B/op 9 allocs/op
+BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 336 ns/op 440 B/op 10 allocs/op
+BenchmarkStructPartialSuccess-8 2000000 686 ns/op 256 B/op 6 allocs/op
+BenchmarkStructPartialSuccessParallel-8 5000000 282 ns/op 256 B/op 6 allocs/op
+BenchmarkStructPartialFailure-8 2000000 931 ns/op 480 B/op 11 allocs/op
+BenchmarkStructPartialFailureParallel-8 5000000 394 ns/op 480 B/op 11 allocs/op
+BenchmarkStructExceptSuccess-8 1000000 1017 ns/op 496 B/op 12 allocs/op
+BenchmarkStructExceptSuccessParallel-8 10000000 233 ns/op 240 B/op 5 allocs/op
+BenchmarkStructExceptFailure-8 2000000 864 ns/op 464 B/op 10 allocs/op
+BenchmarkStructExceptFailureParallel-8 5000000 393 ns/op 464 B/op 10 allocs/op
+BenchmarkStructSimpleCrossFieldSuccess-8 3000000 552 ns/op 72 B/op 3 allocs/op
+BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 202 ns/op 72 B/op 3 allocs/op
+BenchmarkStructSimpleCrossFieldFailure-8 2000000 798 ns/op 304 B/op 8 allocs/op
+BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 356 ns/op 304 B/op 8 allocs/op
+BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 825 ns/op 80 B/op 4 allocs/op
+BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 5000000 300 ns/op 80 B/op 4 allocs/op
+BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 1103 ns/op 320 B/op 9 allocs/op
+BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 3000000 433 ns/op 320 B/op 9 allocs/op
+BenchmarkStructSimpleSuccess-8 5000000 360 ns/op 0 B/op 0 allocs/op
+BenchmarkStructSimpleSuccessParallel-8 20000000 110 ns/op 0 B/op 0 allocs/op
+BenchmarkStructSimpleFailure-8 2000000 783 ns/op 424 B/op 9 allocs/op
+BenchmarkStructSimpleFailureParallel-8 5000000 358 ns/op 424 B/op 9 allocs/op
+BenchmarkStructComplexSuccess-8 1000000 2120 ns/op 128 B/op 8 allocs/op
+BenchmarkStructComplexSuccessParallel-8 2000000 659 ns/op 128 B/op 8 allocs/op
+BenchmarkStructComplexFailure-8 300000 5126 ns/op 3041 B/op 53 allocs/op
+BenchmarkStructComplexFailureParallel-8 1000000 2261 ns/op 3041 B/op 53 allocs/op
```
Complimentary Software
diff --git a/errors.go b/errors.go
index 5b13259..7b6a556 100644
--- a/errors.go
+++ b/errors.go
@@ -63,6 +63,14 @@ func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslati
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)
}
@@ -192,8 +200,18 @@ func (fe *fieldError) StructNamespace() string {
// Field returns the fields name with the tag name taking precedence over the
// fields actual name.
func (fe *fieldError) Field() string {
- // return fe.field
+
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 fields actual name from the struct, when able to determine.
@@ -236,7 +254,7 @@ func (fe *fieldError) Error() string {
// as calling fe.Error()
func (fe *fieldError) Translate(ut ut.Translator) string {
- m, ok := fe.v.transTagFunc[ut.Locale()]
+ m, ok := fe.v.transTagFunc[ut]
if !ok {
return fe.Error()
}
diff --git a/examples/translations/main.go b/examples/translations/main.go
new file mode 100644
index 0000000..9e3d106
--- /dev/null
+++ b/examples/translations/main.go
@@ -0,0 +1,101 @@
+package main
+
+import (
+ "fmt"
+
+ "gopkg.in/go-playground/validator.v9"
+)
+
+// 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 ( map[string]*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()) // can differ when a custom TagNameFunc is registered or
+ fmt.Println(err.StructField()) // by passing alt name to ReportError like below
+ 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/translations/en/en.go b/translations/en/en.go
new file mode 100644
index 0000000..bc6b3ac
--- /dev/null
+++ b/translations/en/en.go
@@ -0,0 +1,1301 @@
+package en
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/go-playground/locales"
+ "github.com/go-playground/universal-translator"
+ "gopkg.in/go-playground/validator.v9"
+)
+
+// 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
+
+ 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
+ }
+
+ switch fe.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
+
+ 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
+ }
+
+ switch fe.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
+
+ 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
+ }
+
+ switch fe.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
+
+ 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
+ }
+
+ switch fe.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())
+ }
+
+ 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
+
+ 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
+ }
+
+ switch fe.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())
+ }
+
+ 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
+
+ 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
+ }
+
+ switch fe.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())
+ }
+
+ 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
+
+ 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
+ }
+
+ switch fe.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())
+ }
+
+ 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: "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,
+ },
+ }
+
+ 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/translations/en/en_test.go b/translations/en/en_test.go
new file mode 100644
index 0000000..05453fe
--- /dev/null
+++ b/translations/en/en_test.go
@@ -0,0 +1,580 @@
+package en
+
+import (
+ "testing"
+ "time"
+
+ english "github.com/go-playground/locales/en"
+ "github.com/go-playground/universal-translator"
+ . "gopkg.in/go-playground/assert.v1"
+ "gopkg.in/go-playground/validator.v9"
+)
+
+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"`
+ }
+
+ 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"
+
+ 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.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",
+ },
+ }
+
+ 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/util.go b/util.go
index ee9874e..7711428 100644
--- a/util.go
+++ b/util.go
@@ -6,22 +6,6 @@ import (
"strings"
)
-// import (
-// "reflect"
-// "strconv"
-// "strings"
-// )
-
-// const (
-// blank = ""
-// 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"
-// )
-
// 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.
diff --git a/validator.go b/validator.go
index 960bd7b..6622b98 100644
--- a/validator.go
+++ b/validator.go
@@ -37,7 +37,7 @@ func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, t
cs = v.v.extractStructCache(current, typ.Name())
}
- if len(ns) == 0 {
+ if len(ns) == 0 && len(cs.name) != 0 {
ns = append(ns, cs.name...)
ns = append(ns, '.')
@@ -310,7 +310,7 @@ OUTER:
v.misc = append(v.misc, '|')
v.misc = append(v.misc, ct.tag...)
- if ct.next == nil {
+ if ct.next == nil || ct.next.typeof != typeOr { // ct.typeof != typeOr
// if we get here, no valid 'or' value and no more tags
v.str1 = string(append(ns, cf.altName...))
diff --git a/validator_instance.go b/validator_instance.go
index fd380b2..5d7ba3e 100644
--- a/validator_instance.go
+++ b/validator_instance.go
@@ -55,7 +55,7 @@ type Validate struct {
customFuncs map[reflect.Type]CustomTypeFunc
aliases map[string]string
validations map[string]Func
- transTagFunc map[string]map[string]TranslationFunc // map[]map[]TranslationFunc
+ transTagFunc map[ut.Translator]map[string]TranslationFunc // map[]map[]TranslationFunc
tagCache *tagCache
structCache *structCache
}
@@ -192,20 +192,20 @@ func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{
v.hasCustomFuncs = true
}
-func (v *Validate) RegisterTranslation(tag string, ut ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
+func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
if v.transTagFunc == nil {
- v.transTagFunc = make(map[string]map[string]TranslationFunc)
+ v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
}
- if err = registerFn(ut); err != nil {
+ if err = registerFn(trans); err != nil {
return
}
- m, ok := v.transTagFunc[ut.Locale()]
+ m, ok := v.transTagFunc[trans]
if !ok {
m = make(map[string]TranslationFunc)
- v.transTagFunc[ut.Locale()] = m
+ v.transTagFunc[trans] = m
}
m[tag] = translationFn
@@ -357,8 +357,13 @@ func (v *Validate) StructExcept(s interface{}, fields ...string) (err error) {
for _, key := range fields {
- vd.misc = append(vd.misc[0:0], name...)
- vd.misc = append(vd.misc, '.')
+ 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{}{}
}