Repository: go-playground/validator
Pull request #890 (2022-01-25 15:17:59)

Fix support for aliased time.Time types
## Fixes detection of aliased time.Type types

Allows to validate fields with type:
```go
type CustomTime time.Time
```

**Make sure that you've checked the boxes below before you submit PR:**
- [x] Tests exist or have been written that cover this particular change.

@go-playground/validator-maintainers

coveralls: 
[![Coverage Status](https://coveralls.io/builds/45927978/badge)](https://coveralls.io/builds/45927978)

Coverage decreased (-0.03%) to 75.102% when pulling **f3fa470b434fa3f115ac244db1a51de3259975e0 on lafriks-fork:fix/time_convertable** into **88976f39f600bb3979de1ae5efdfd9cff2cf10f1 on go-playground:master**.


Patch:
diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index d1ca1e8..d6bfd55 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -8,7 +8,7 @@ jobs:
   test:
     strategy:
       matrix:
-        go-version: [1.15.x, 1.16.x]
+        go-version: [1.17.x, 1.18.x]
         os: [ubuntu-latest, macos-latest, windows-latest]
     runs-on: ${{ matrix.os }}
     steps:
@@ -32,7 +32,7 @@ jobs:
         run: go test -race -covermode=atomic -coverprofile="profile.cov" ./...
 
       - name: Send Coverage
-        if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.16.x'
+        if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.18.x'
         uses: shogo82148/actions-goveralls@v1
         with:
           path-to-profile: profile.cov
@@ -41,8 +41,11 @@ jobs:
     name: lint
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/setup-go@v3
+        with:
+          go-version: 1.16
+      - uses: actions/checkout@v3
       - name: golangci-lint
-        uses: golangci/golangci-lint-action@v2
+        uses: golangci/golangci-lint-action@v3
         with:
-          version: v1.41.1
+          version: v1.45.2
diff --git a/README.md b/README.md
index 5c42ee4..1c89266 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 Package validator
 =================
 <img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![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.10.0-green.svg)
+![Project status](https://img.shields.io/badge/version-10.10.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)
@@ -153,6 +153,7 @@ Baked-in Validations
 | bcp47_language_tag | Language tag (BCP 47) |
 | btc_addr | Bitcoin Address |
 | btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
+| credit_card | Credit Card Number |
 | datetime | Datetime |
 | e164 | e164 formatted phone number |
 | email | E-mail String
@@ -219,6 +220,8 @@ Baked-in Validations
 | required_with_all | Required With All |
 | required_without | Required Without |
 | required_without_all | Required Without All |
+| excluded_if | Excluded If |
+| excluded_unless | Excluded Unless |
 | excluded_with | Excluded With |
 | excluded_with_all | Excluded With All |
 | excluded_without | Excluded Without |
diff --git a/baked_in.go b/baked_in.go
index 7868b66..bc638f2 100644
--- a/baked_in.go
+++ b/baked_in.go
@@ -75,6 +75,8 @@ var (
 		"required_with_all":             requiredWithAll,
 		"required_without":              requiredWithout,
 		"required_without_all":          requiredWithoutAll,
+		"excluded_if":                   excludedIf,
+		"excluded_unless":               excludedUnless,
 		"excluded_with":                 excludedWith,
 		"excluded_with_all":             excludedWithAll,
 		"excluded_without":              excludedWithout,
@@ -201,6 +203,7 @@ var (
 		"bic":                           isIsoBicFormat,
 		"semver":                        isSemverFormat,
 		"dns_rfc1035_label":             isDnsRFC1035LabelFormat,
+		"credit_card":                   isCreditCard,
 	}
 )
 
@@ -816,12 +819,7 @@ func isNeField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != currentField.Type() {
-			return true
-		}
-
-		if fieldType == timeType {
+		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
 
 			t := currentField.Interface().(time.Time)
 			fieldTime := field.Interface().(time.Time)
@@ -829,6 +827,10 @@ func isNeField(fl FieldLevel) bool {
 			return !fieldTime.Equal(t)
 		}
 
+		// Not Same underlying type i.e. struct and time
+		if fieldType != currentField.Type() {
+			return true
+		}
 	}
 
 	// default reflect.String:
@@ -869,18 +871,18 @@ func isLteCrossStructField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != topField.Type() {
-			return false
-		}
-
-		if fieldType == timeType {
+		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
 
-			fieldTime := field.Interface().(time.Time)
-			topTime := topField.Interface().(time.Time)
+			fieldTime := field.Convert(timeType).Interface().(time.Time)
+			topTime := topField.Convert(timeType).Interface().(time.Time)
 
 			return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
 		}
+
+		// Not Same underlying type i.e. struct and time
+		if fieldType != topField.Type() {
+			return false
+		}
 	}
 
 	// default reflect.String:
@@ -917,18 +919,18 @@ func isLtCrossStructField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != topField.Type() {
-			return false
-		}
-
-		if fieldType == timeType {
+		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
 
-			fieldTime := field.Interface().(time.Time)
-			topTime := topField.Interface().(time.Time)
+			fieldTime := field.Convert(timeType).Interface().(time.Time)
+			topTime := topField.Convert(timeType).Interface().(time.Time)
 
 			return fieldTime.Before(topTime)
 		}
+
+		// Not Same underlying type i.e. struct and time
+		if fieldType != topField.Type() {
+			return false
+		}
 	}
 
 	// default reflect.String:
@@ -964,18 +966,18 @@ func isGteCrossStructField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != topField.Type() {
-			return false
-		}
+		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
 
-		if fieldType == timeType {
-
-			fieldTime := field.Interface().(time.Time)
-			topTime := topField.Interface().(time.Time)
+			fieldTime := field.Convert(timeType).Interface().(time.Time)
+			topTime := topField.Convert(timeType).Interface().(time.Time)
 
 			return fieldTime.After(topTime) || fieldTime.Equal(topTime)
 		}
+
+		// Not Same underlying type i.e. struct and time
+		if fieldType != topField.Type() {
+			return false
+		}
 	}
 
 	// default reflect.String:
@@ -1011,18 +1013,18 @@ func isGtCrossStructField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != topField.Type() {
-			return false
-		}
-
-		if fieldType == timeType {
+		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
 
-			fieldTime := field.Interface().(time.Time)
-			topTime := topField.Interface().(time.Time)
+			fieldTime := field.Convert(timeType).Interface().(time.Time)
+			topTime := topField.Convert(timeType).Interface().(time.Time)
 
 			return fieldTime.After(topTime)
 		}
+
+		// Not Same underlying type i.e. struct and time
+		if fieldType != topField.Type() {
+			return false
+		}
 	}
 
 	// default reflect.String:
@@ -1061,18 +1063,18 @@ func isNeCrossStructField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != topField.Type() {
-			return true
-		}
-
-		if fieldType == timeType {
+		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
 
-			t := field.Interface().(time.Time)
-			fieldTime := topField.Interface().(time.Time)
+			t := field.Convert(timeType).Interface().(time.Time)
+			fieldTime := topField.Convert(timeType).Interface().(time.Time)
 
 			return !fieldTime.Equal(t)
 		}
+
+		// Not Same underlying type i.e. struct and time
+		if fieldType != topField.Type() {
+			return true
+		}
 	}
 
 	// default reflect.String:
@@ -1111,18 +1113,18 @@ func isEqCrossStructField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != topField.Type() {
-			return false
-		}
-
-		if fieldType == timeType {
+		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
 
-			t := field.Interface().(time.Time)
-			fieldTime := topField.Interface().(time.Time)
+			t := field.Convert(timeType).Interface().(time.Time)
+			fieldTime := topField.Convert(timeType).Interface().(time.Time)
 
 			return fieldTime.Equal(t)
 		}
+
+		// Not Same underlying type i.e. struct and time
+		if fieldType != topField.Type() {
+			return false
+		}
 	}
 
 	// default reflect.String:
@@ -1161,19 +1163,18 @@ func isEqField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != currentField.Type() {
-			return false
-		}
+		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
 
-		if fieldType == timeType {
-
-			t := currentField.Interface().(time.Time)
-			fieldTime := field.Interface().(time.Time)
+			t := currentField.Convert(timeType).Interface().(time.Time)
+			fieldTime := field.Convert(timeType).Interface().(time.Time)
 
 			return fieldTime.Equal(t)
 		}
 
+		// Not Same underlying type i.e. struct and time
+		if fieldType != currentField.Type() {
+			return false
+		}
 	}
 
 	// default reflect.String:
@@ -1542,6 +1543,22 @@ func requiredIf(fl FieldLevel) bool {
 	return hasValue(fl)
 }
 
+// excludedIf is the validation function
+// The field under validation must not be present or is empty only if all the other specified fields are equal to the value following with the specified field.
+func excludedIf(fl FieldLevel) bool {
+	params := parseOneOfParam2(fl.Param())
+	if len(params)%2 != 0 {
+		panic(fmt.Sprintf("Bad param number for excluded_if %s", fl.FieldName()))
+	}
+
+	for i := 0; i < len(params); i += 2 {
+		if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
+			return false
+		}
+	}
+	return true
+}
+
 // 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 {
@@ -1558,6 +1575,21 @@ func requiredUnless(fl FieldLevel) bool {
 	return hasValue(fl)
 }
 
+// excludedUnless is the validation function
+// The field under validation must not be present or is empty unless all the other specified fields are equal to the value following with the specified field.
+func excludedUnless(fl FieldLevel) bool {
+	params := parseOneOfParam2(fl.Param())
+	if len(params)%2 != 0 {
+		panic(fmt.Sprintf("Bad param number for excluded_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 {
@@ -1677,18 +1709,18 @@ func isGteField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != currentField.Type() {
-			return false
-		}
+		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
 
-		if fieldType == timeType {
-
-			t := currentField.Interface().(time.Time)
-			fieldTime := field.Interface().(time.Time)
+			t := currentField.Convert(timeType).Interface().(time.Time)
+			fieldTime := field.Convert(timeType).Interface().(time.Time)
 
 			return fieldTime.After(t) || fieldTime.Equal(t)
 		}
+
+		// Not Same underlying type i.e. struct and time
+		if fieldType != currentField.Type() {
+			return false
+		}
 	}
 
 	// default reflect.String
@@ -1724,18 +1756,18 @@ func isGtField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != currentField.Type() {
-			return false
-		}
-
-		if fieldType == timeType {
+		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
 
-			t := currentField.Interface().(time.Time)
-			fieldTime := field.Interface().(time.Time)
+			t := currentField.Convert(timeType).Interface().(time.Time)
+			fieldTime := field.Convert(timeType).Interface().(time.Time)
 
 			return fieldTime.After(t)
 		}
+
+		// Not Same underlying type i.e. struct and time
+		if fieldType != currentField.Type() {
+			return false
+		}
 	}
 
 	// default reflect.String
@@ -1777,10 +1809,10 @@ func isGte(fl FieldLevel) bool {
 
 	case reflect.Struct:
 
-		if field.Type() == timeType {
+		if field.Type().ConvertibleTo(timeType) {
 
 			now := time.Now().UTC()
-			t := field.Interface().(time.Time)
+			t := field.Convert(timeType).Interface().(time.Time)
 
 			return t.After(now) || t.Equal(now)
 		}
@@ -1823,9 +1855,9 @@ func isGt(fl FieldLevel) bool {
 		return field.Float() > p
 	case reflect.Struct:
 
-		if field.Type() == timeType {
+		if field.Type().ConvertibleTo(timeType) {
 
-			return field.Interface().(time.Time).After(time.Now().UTC())
+			return field.Convert(timeType).Interface().(time.Time).After(time.Now().UTC())
 		}
 	}
 
@@ -1903,18 +1935,18 @@ func isLteField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != currentField.Type() {
-			return false
-		}
+		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
 
-		if fieldType == timeType {
-
-			t := currentField.Interface().(time.Time)
-			fieldTime := field.Interface().(time.Time)
+			t := currentField.Convert(timeType).Interface().(time.Time)
+			fieldTime := field.Convert(timeType).Interface().(time.Time)
 
 			return fieldTime.Before(t) || fieldTime.Equal(t)
 		}
+
+		// Not Same underlying type i.e. struct and time
+		if fieldType != currentField.Type() {
+			return false
+		}
 	}
 
 	// default reflect.String
@@ -1950,18 +1982,18 @@ func isLtField(fl FieldLevel) bool {
 
 		fieldType := field.Type()
 
-		// Not Same underlying type i.e. struct and time
-		if fieldType != currentField.Type() {
-			return false
-		}
-
-		if fieldType == timeType {
+		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
 
-			t := currentField.Interface().(time.Time)
-			fieldTime := field.Interface().(time.Time)
+			t := currentField.Convert(timeType).Interface().(time.Time)
+			fieldTime := field.Convert(timeType).Interface().(time.Time)
 
 			return fieldTime.Before(t)
 		}
+
+		// Not Same underlying type i.e. struct and time
+		if fieldType != currentField.Type() {
+			return false
+		}
 	}
 
 	// default reflect.String
@@ -2003,10 +2035,10 @@ func isLte(fl FieldLevel) bool {
 
 	case reflect.Struct:
 
-		if field.Type() == timeType {
+		if field.Type().ConvertibleTo(timeType) {
 
 			now := time.Now().UTC()
-			t := field.Interface().(time.Time)
+			t := field.Convert(timeType).Interface().(time.Time)
 
 			return t.Before(now) || t.Equal(now)
 		}
@@ -2050,9 +2082,9 @@ func isLt(fl FieldLevel) bool {
 
 	case reflect.Struct:
 
-		if field.Type() == timeType {
+		if field.Type().ConvertibleTo(timeType) {
 
-			return field.Interface().(time.Time).Before(time.Now().UTC())
+			return field.Convert(timeType).Interface().(time.Time).Before(time.Now().UTC())
 		}
 	}
 
@@ -2436,3 +2468,41 @@ func isDnsRFC1035LabelFormat(fl FieldLevel) bool {
 	val := fl.Field().String()
 	return dnsRegexRFC1035Label.MatchString(val)
 }
+
+// isCreditCard is the validation function for validating if the current field's value is a valid credit card number
+func isCreditCard(fl FieldLevel) bool {
+	val := fl.Field().String()
+	var creditCard bytes.Buffer
+	segments := strings.Split(val, " ")
+	for _, segment := range segments {
+		if len(segment) < 3 {
+			return false
+		}
+		creditCard.WriteString(segment)
+	}
+
+	ccDigits := strings.Split(creditCard.String(), "")
+	size := len(ccDigits)
+	if size < 12 || size > 19 {
+		return false
+	}
+
+	sum := 0
+	for i, digit := range ccDigits {
+		value, err := strconv.Atoi(digit)
+		if err != nil {
+			return false
+		}
+		if size%2 == 0 && i%2 == 0 || size%2 == 1 && i%2 == 1 {
+			v := value * 2
+			if v >= 10 {
+				sum += 1 + (v % 10)
+			} else {
+				sum += v
+			}
+		} else {
+			sum += value
+		}
+	}
+	return (sum % 10) == 0
+}
diff --git a/doc.go b/doc.go
index b284c37..7341c67 100644
--- a/doc.go
+++ b/doc.go
@@ -349,6 +349,40 @@ Example:
 	// require the field if the Field1 and Field2 is not present:
 	Usage: required_without_all=Field1 Field2
 
+Excluded If
+
+The field under validation must not be present or 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: excluded_if
+
+Examples:
+
+	// exclude the field if the Field1 is equal to the parameter given:
+	Usage: excluded_if=Field1 foobar
+
+	// exclude the field if the Field1 and Field2 is equal to the value respectively:
+	Usage: excluded_if=Field1 foo Field2 bar
+
+Excluded Unless
+
+The field under validation must not be present or 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: excluded_unless
+
+Examples:
+
+	// exclude the field unless the Field1 is equal to the parameter given:
+	Usage: excluded_unless=Field1 foobar
+
+	// exclude the field unless the Field1 and Field2 is equal to the value respectively:
+	Usage: excluded_unless=Field1 foo Field2 bar
+
 Is Default
 
 This validates that the value is the default value and is almost the
@@ -1283,6 +1317,12 @@ More information on https://semver.org/
 
 	Usage: semver
 
+Credit Card
+
+This validates that a string value contains a valid credit card number using Luhn algoritm.
+
+	Usage: credit_card
+
 Alias Validators and Tags
 
 NOTE: When returning an error, the tag returned in "FieldError" will be
diff --git a/go.mod b/go.mod
index ddecff6..e281ec0 100644
--- a/go.mod
+++ b/go.mod
@@ -11,9 +11,9 @@ require (
 	github.com/leodido/go-urn v1.2.1
 	github.com/rogpeppe/go-internal v1.8.0 // indirect
 	github.com/stretchr/testify v1.7.0 // indirect
-	golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
+	golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
 	golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
-	golang.org/x/text v0.3.6
+	golang.org/x/text v0.3.7
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
 )
diff --git a/go.sum b/go.sum
index 5750093..a1c43c3 100644
--- a/go.sum
+++ b/go.sum
@@ -28,17 +28,19 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
-golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/regexes.go b/regexes.go
index 48e51d5..a8668f8 100644
--- a/regexes.go
+++ b/regexes.go
@@ -37,12 +37,12 @@ const (
 	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
+	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}$`
diff --git a/translations/it/it.go b/translations/it/it.go
new file mode 100644
index 0000000..8abd67f
--- /dev/null
+++ b/translations/it/it.go
@@ -0,0 +1,1244 @@
+package en
+
+import (
+	"fmt"
+	"log"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/go-playground/locales"
+	ut "github.com/go-playground/universal-translator"
+	"github.com/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} è un campo obbligatorio",
+			override:    false,
+		},
+		{
+			tag: "len",
+			customRegisFunc: func(ut ut.Translator) (err error) {
+				if err = ut.Add("len-string", "{0} deve essere lungo {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("len-string-character", "{0} carattere", locales.PluralRuleOne, false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("len-string-character", "{0} caratteri", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("len-number", "{0} deve essere uguale a {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.Add("len-items", "{0} deve contenere {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} elementi", 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 essere lungo almeno {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("min-string-character", "{0} carattere", locales.PluralRuleOne, false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("min-string-character", "{0} caratteri", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("min-number", "{0} deve essere maggiore o uguale a {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.Add("min-items", "{0} deve contenere almeno {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} elementi", 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 essere lungo al massimo {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("max-string-character", "{0} carattere", locales.PluralRuleOne, false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("max-string-character", "{0} caratteri", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("max-number", "{0} deve essere minore o uguale a {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.Add("max-items", "{0} deve contenere al massimo {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} elementi", 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} non è uguale a {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "ne",
+			translation:     "{0} deve essere diverso da {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag: "lt",
+			customRegisFunc: func(ut ut.Translator) (err error) {
+				if err = ut.Add("lt-string", "{0} deve essere lungo meno di {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("lt-string-character", "{0} carattere", locales.PluralRuleOne, false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("lt-string-character", "{0} caratteri", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("lt-number", "{0} deve essere minore di {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.Add("lt-items", "{0} deve contenere meno di {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} elementi", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("lt-datetime", "{0} deve essere precedente alla Data/Ora corrente", 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 essere lungo al massimo {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("lte-string-character", "{0} carattere", locales.PluralRuleOne, false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("lte-string-character", "{0} caratteri", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("lte-number", "{0} deve essere minore o uguale a {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.Add("lte-items", "{0} deve contenere al massimo {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} elementi", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("lte-datetime", "{0} deve essere uguale o precedente alla Data/Ora corrente", 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 essere lungo più di {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("gt-string-character", "{0} carattere", locales.PluralRuleOne, false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("gt-string-character", "{0} caratteri", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("gt-number", "{0} deve essere maggiore di {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.Add("gt-items", "{0} deve contenere più di {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} elementi", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("gt-datetime", "{0} deve essere successivo alla Data/Ora corrente", 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 essere lungo almeno {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("gte-string-character", "{0} carattere", locales.PluralRuleOne, false); err != nil {
+					return
+				}
+
+				if err = ut.AddCardinal("gte-string-character", "{0} caratteri", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("gte-number", "{0} deve essere maggiore o uguale a {1}", false); err != nil {
+					return
+				}
+
+				if err = ut.Add("gte-items", "{0} deve contenere almeno {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} elementi", locales.PluralRuleOther, false); err != nil {
+					return
+				}
+
+				if err = ut.Add("gte-datetime", "{0} deve essere uguale o successivo alla Data/Ora corrente", 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 essere uguale a {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "eqcsfield",
+			translation:     "{0} deve essere uguale a {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "necsfield",
+			translation:     "{0} deve essere diverso da {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "gtcsfield",
+			translation:     "{0} deve essere maggiore di {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "gtecsfield",
+			translation:     "{0} deve essere maggiore o uguale a {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "ltcsfield",
+			translation:     "{0} deve essere minore di {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "ltecsfield",
+			translation:     "{0} deve essere minore o uguale a {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "nefield",
+			translation:     "{0} deve essere diverso da {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "gtfield",
+			translation:     "{0} deve essere maggiore di {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "gtefield",
+			translation:     "{0} deve essere maggiore o uguale a {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "ltfield",
+			translation:     "{0} deve essere minore di {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "ltefield",
+			translation:     "{0} deve essere minore o uguale a {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:         "alpha",
+			translation: "{0} può contenere solo caratteri alfabetici",
+			override:    false,
+		},
+		{
+			tag:         "alphanum",
+			translation: "{0} può contenere solo caratteri alfanumerici",
+			override:    false,
+		},
+		{
+			tag:         "numeric",
+			translation: "{0} deve essere un valore numerico valido",
+			override:    false,
+		},
+		{
+			tag:         "number",
+			translation: "{0} deve essere un numero valido",
+			override:    false,
+		},
+		{
+			tag:         "hexadecimal",
+			translation: "{0} deve essere un esadecimale valido",
+			override:    false,
+		},
+		{
+			tag:         "hexcolor",
+			translation: "{0} deve essere un colore HEX valido",
+			override:    false,
+		},
+		{
+			tag:         "rgb",
+			translation: "{0} deve essere un colore RGB valido",
+			override:    false,
+		},
+		{
+			tag:         "rgba",
+			translation: "{0} deve essere un colore RGBA valido",
+			override:    false,
+		},
+		{
+			tag:         "hsl",
+			translation: "{0} deve essere un colore HSL valido",
+			override:    false,
+		},
+		{
+			tag:         "hsla",
+			translation: "{0} deve essere un colore HSLA valido",
+			override:    false,
+		},
+		{
+			tag:         "e164",
+			translation: "{0} deve essere un numero telefonico in formato E.164 valido",
+			override:    false,
+		},
+		{
+			tag:         "email",
+			translation: "{0} deve essere un indirizzo email valido",
+			override:    false,
+		},
+		{
+			tag:         "url",
+			translation: "{0} deve essere un URL valido",
+			override:    false,
+		},
+		{
+			tag:         "uri",
+			translation: "{0} deve essere un URI valido",
+			override:    false,
+		},
+		{
+			tag:         "base64",
+			translation: "{0} deve essere una stringa Base64 valida",
+			override:    false,
+		},
+		{
+			tag:             "contains",
+			translation:     "{0} deve contenere il testo '{1}'",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "containsany",
+			translation:     "{0} deve contenere almeno uno dei seguenti caratteri '{1}'",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "excludes",
+			translation:     "{0} non deve contenere il testo '{1}'",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "excludesall",
+			translation:     "{0} non deve contenere alcuno dei seguenti caratteri '{1}'",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "excludesrune",
+			translation:     "{0} non deve contenere '{1}'",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:         "isbn",
+			translation: "{0} deve essere un numero ISBN valido",
+			override:    false,
+		},
+		{
+			tag:         "isbn10",
+			translation: "{0} deve essere un numero ISBN-10 valido",
+			override:    false,
+		},
+		{
+			tag:         "isbn13",
+			translation: "{0} deve essere un numero ISBN-13 valido",
+			override:    false,
+		},
+		{
+			tag:         "uuid",
+			translation: "{0} deve essere un UUID valido",
+			override:    false,
+		},
+		{
+			tag:         "uuid3",
+			translation: "{0} deve essere un UUID versione 3 valido",
+			override:    false,
+		},
+		{
+			tag:         "uuid4",
+			translation: "{0} deve essere un UUID versione 4 valido",
+			override:    false,
+		},
+		{
+			tag:         "uuid5",
+			translation: "{0} deve essere un UUID versione 5 valido",
+			override:    false,
+		},
+		{
+			tag:         "ulid",
+			translation: "{0} deve essere un ULID valido",
+			override:    false,
+		},
+		{
+			tag:         "ascii",
+			translation: "{0} deve contenere solo caratteri ascii",
+			override:    false,
+		},
+		{
+			tag:         "printascii",
+			translation: "{0} deve contenere solo caratteri ascii stampabili",
+			override:    false,
+		},
+		{
+			tag:         "multibyte",
+			translation: "{0} deve contenere caratteri multibyte",
+			override:    false,
+		},
+		{
+			tag:         "datauri",
+			translation: "{0} deve contenere un Data URI valido",
+			override:    false,
+		},
+		{
+			tag:         "latitude",
+			translation: "{0} deve contenere una latitudine valida",
+			override:    false,
+		},
+		{
+			tag:         "longitude",
+			translation: "{0} deve contenere una longitudine valida",
+			override:    false,
+		},
+		{
+			tag:         "ssn",
+			translation: "{0} deve essere un numero SSN valido",
+			override:    false,
+		},
+		{
+			tag:         "ipv4",
+			translation: "{0} deve essere un indirizzo IPv4 valido",
+			override:    false,
+		},
+		{
+			tag:         "ipv6",
+			translation: "{0} deve essere un indirizzo IPv6 valido",
+			override:    false,
+		},
+		{
+			tag:         "ip",
+			translation: "{0} deve essere un indirizzo IP valido",
+			override:    false,
+		},
+		{
+			tag:         "cidr",
+			translation: "{0} deve contenere una notazione CIDR valida",
+			override:    false,
+		},
+		{
+			tag:         "cidrv4",
+			translation: "{0} deve contenere una notazione CIDR per un indirizzo IPv4 valida",
+			override:    false,
+		},
+		{
+			tag:         "cidrv6",
+			translation: "{0} deve contenere una notazione CIDR per un indirizzo IPv6 valida",
+			override:    false,
+		},
+		{
+			tag:         "tcp_addr",
+			translation: "{0} deve essere un indirizzo TCP valido",
+			override:    false,
+		},
+		{
+			tag:         "tcp4_addr",
+			translation: "{0} deve essere un indirizzo IPv4 TCP valido",
+			override:    false,
+		},
+		{
+			tag:         "tcp6_addr",
+			translation: "{0} deve essere un indirizzo IPv6 TCP valido",
+			override:    false,
+		},
+		{
+			tag:         "udp_addr",
+			translation: "{0} deve essere un indirizzo UDP valido",
+			override:    false,
+		},
+		{
+			tag:         "udp4_addr",
+			translation: "{0} deve essere un indirizzo IPv4 UDP valido",
+			override:    false,
+		},
+		{
+			tag:         "udp6_addr",
+			translation: "{0} deve essere un indirizzo IPv6 UDP valido",
+			override:    false,
+		},
+		{
+			tag:         "ip_addr",
+			translation: "{0} deve essere un indirizzo IP risolvibile",
+			override:    false,
+		},
+		{
+			tag:         "ip4_addr",
+			translation: "{0} deve essere un indirizzo IPv4 risolvibile",
+			override:    false,
+		},
+		{
+			tag:         "ip6_addr",
+			translation: "{0} deve essere un indirizzo IPv6 risolvibile",
+			override:    false,
+		},
+		{
+			tag:         "unix_addr",
+			translation: "{0} deve essere un indirizzo UNIX risolvibile",
+			override:    false,
+		},
+		{
+			tag:         "mac",
+			translation: "{0} deve contenere un indirizzo MAC valido",
+			override:    false,
+		},
+		{
+			tag:         "unique",
+			translation: "{0} deve contenere valori unici",
+			override:    false,
+		},
+		{
+			tag:         "iscolor",
+			translation: "{0} deve essere un colore valido",
+			override:    false,
+		},
+		{
+			tag:             "oneof",
+			translation:     "{0} deve essere uno di [{1}]",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:         "json",
+			translation: "{0} deve essere una stringa json valida",
+			override:    false,
+		},
+		{
+			tag:         "jwt",
+			translation: "{0} deve essere una stringa jwt valida",
+			override:    false,
+		},
+		{
+			tag:         "lowercase",
+			translation: "{0} deve essere una stringa minuscola",
+			override:    false,
+		},
+		{
+			tag:         "boolean",
+			translation: "{0} deve rappresentare un valore booleano",
+			override:    false,
+		},
+		{
+			tag:         "uppercase",
+			translation: "{0} deve essere una stringa maiuscola",
+			override:    false,
+		},
+		{
+			tag:             "startswith",
+			translation:     "{0} deve iniziare con {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "startsnotwith",
+			translation:     "{0} non deve iniziare con {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "endswith",
+			translation:     "{0} deve terminare con {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "endsnotwith",
+			translation:     "{0} non deve terminare con {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "datetime",
+			translation:     "{0} non corrisponde al formato {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "postcode_iso3166_alpha2",
+			translation:     "{0} non corrisponde al formato del codice postale dello stato {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+		{
+			tag:             "postcode_iso3166_alpha2_field",
+			translation:     "{0} non corrisponde al formato del codice postale dello stato nel campo {1}",
+			override:        false,
+			customTransFunc: customTransFuncV1,
+		},
+	}
+
+	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
+}
+
+func customTransFuncV1(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
+}
diff --git a/translations/ja/ja.go b/translations/ja/ja.go
index 4502e2d..1cc67a4 100644
--- a/translations/ja/ja.go
+++ b/translations/ja/ja.go
@@ -136,7 +136,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 					return
 				}
 
-				if err = ut.Add("min-number", "{0}は{1}かより大きくなければなりません", false); err != nil {
+				if err = ut.Add("min-number", "{0}は{1}より大きくなければなりません", false); err != nil {
 					return
 				}
 
@@ -227,7 +227,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 					return
 				}
 
-				if err = ut.Add("max-number", "{0}は{1}かより小さくなければなりません", false); err != nil {
+				if err = ut.Add("max-number", "{0}は{1}より小さくなければなりません", false); err != nil {
 					return
 				}
 
@@ -525,7 +525,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 					return
 				}
 
-				if err = ut.Add("lte-number", "{0}は{1}かより小さくなければなりません", false); err != nil {
+				if err = ut.Add("lte-number", "{0}は{1}より小さくなければなりません", false); err != nil {
 					return
 				}
 
@@ -765,7 +765,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 					return
 				}
 
-				if err = ut.Add("gte-number", "{0}は{1}かより大きくなければなりません", false); err != nil {
+				if err = ut.Add("gte-number", "{0}は{1}より大きくなければなりません", false); err != nil {
 					return
 				}
 
diff --git a/translations/zh/zh.go b/translations/zh/zh.go
index 3dc7356..80165d0 100644
--- a/translations/zh/zh.go
+++ b/translations/zh/zh.go
@@ -29,6 +29,36 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0}为必填字段",
 			override:    false,
 		},
+		{
+			tag:         "required_if",
+			translation: "{0}为必填字段",
+			override:    false,
+		},
+		{
+			tag:         "required_unless",
+			translation: "{0}为必填字段",
+			override:    false,
+		},
+		{
+			tag:         "required_with",
+			translation: "{0}为必填字段",
+			override:    false,
+		},
+		{
+			tag:         "required_with_all",
+			translation: "{0}为必填字段",
+			override:    false,
+		},
+		{
+			tag:         "required_without",
+			translation: "{0}为必填字段",
+			override:    false,
+		},
+		{
+			tag:         "required_without_all",
+			translation: "{0}为必填字段",
+			override:    false,
+		},
 		{
 			tag: "len",
 			customRegisFunc: func(ut ut.Translator) (err error) {
diff --git a/util.go b/util.go
index 56420f4..36da855 100644
--- a/util.go
+++ b/util.go
@@ -82,7 +82,7 @@ BEGIN:
 		fld := namespace
 		var ns string
 
-		if typ != timeType {
+		if !typ.ConvertibleTo(timeType) {
 
 			idx := strings.Index(namespace, namespaceSeparator)
 
diff --git a/validator.go b/validator.go
index 2a4fad0..80da095 100644
--- a/validator.go
+++ b/validator.go
@@ -164,7 +164,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
 
 		typ = current.Type()
 
-		if typ != timeType {
+		if !typ.ConvertibleTo(timeType) {
 
 			if ct != nil {
 
@@ -355,6 +355,10 @@ OUTER:
 				v.ct = ct
 
 				if ct.fn(ctx, v) {
+					if ct.isBlockEnd {
+						ct = ct.next
+						continue OUTER
+					}
 
 					// drain rest of the 'or' values, then continue or leave
 					for {
@@ -368,6 +372,11 @@ OUTER:
 						if ct.typeof != typeOr {
 							continue OUTER
 						}
+
+						if ct.isBlockEnd {
+							ct = ct.next
+							continue OUTER
+						}
 					}
 				}
 
diff --git a/validator_instance.go b/validator_instance.go
index 973964f..316ffb0 100644
--- a/validator_instance.go
+++ b/validator_instance.go
@@ -33,6 +33,8 @@ const (
 	excludedWithoutTag    = "excluded_without"
 	excludedWithTag       = "excluded_with"
 	excludedWithAllTag    = "excluded_with_all"
+	excludedIfTag         = "excluded_if"
+	excludedUnlessTag     = "excluded_unless"
 	skipValidationTag     = "-"
 	diveTag               = "dive"
 	keysTag               = "keys"
@@ -120,7 +122,7 @@ func New() *Validate {
 		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,
-			excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag:
+			excludedIfTag, excludedUnlessTag, excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag:
 			_ = v.registerValidation(k, wrapFunc(val), true, true)
 		default:
 			// no need to error check here, baked in will always be valid
@@ -169,7 +171,7 @@ func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{
 	return errs
 }
 
-// ValidateMap validates map data form a map of tags
+// ValidateMap validates map data from a map of tags
 func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
 	return v.ValidateMapCtx(context.Background(), data, rules)
 }
@@ -331,7 +333,7 @@ func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
 		val = val.Elem()
 	}
 
-	if val.Kind() != reflect.Struct || val.Type() == timeType {
+	if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
 		return &InvalidValidationError{Type: reflect.TypeOf(s)}
 	}
 
@@ -376,7 +378,7 @@ func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn Filt
 		val = val.Elem()
 	}
 
-	if val.Kind() != reflect.Struct || val.Type() == timeType {
+	if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
 		return &InvalidValidationError{Type: reflect.TypeOf(s)}
 	}
 
@@ -424,7 +426,7 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
 		val = val.Elem()
 	}
 
-	if val.Kind() != reflect.Struct || val.Type() == timeType {
+	if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
 		return &InvalidValidationError{Type: reflect.TypeOf(s)}
 	}
 
@@ -514,7 +516,7 @@ func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ..
 		val = val.Elem()
 	}
 
-	if val.Kind() != reflect.Struct || val.Type() == timeType {
+	if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
 		return &InvalidValidationError{Type: reflect.TypeOf(s)}
 	}
 


Test patch:
diff --git a/translations/it/it_test.go b/translations/it/it_test.go
new file mode 100644
index 0000000..301e8c1
--- /dev/null
+++ b/translations/it/it_test.go
@@ -0,0 +1,724 @@
+package en
+
+import (
+	"testing"
+	"time"
+
+	. "github.com/go-playground/assert/v2"
+	italian "github.com/go-playground/locales/it"
+	ut "github.com/go-playground/universal-translator"
+	"github.com/go-playground/validator/v10"
+)
+
+func TestTranslations(t *testing.T) {
+	it := italian.New()
+	uni := ut.New(it, it)
+	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"`
+		ULID                string            `validate:"ulid"`
+		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"`
+		BooleanString       string            `validate:"boolean"`
+		JSONString          string            `validate:"json"`
+		JWTString           string            `validate:"jwt"`
+		LowercaseString     string            `validate:"lowercase"`
+		UppercaseString     string            `validate:"uppercase"`
+		StartsWithString    string            `validate:"startswith=foo"`
+		StartsNotWithString string            `validate:"startsnotwith=foo"`
+		EndsWithString      string            `validate:"endswith=foo"`
+		EndsNotWithString   string            `validate:"endsnotwith=foo"`
+		Datetime            string            `validate:"datetime=2006-01-02"`
+		PostCode            string            `validate:"postcode_iso3166_alpha2=SG"`
+		PostCodeCountry     string
+		PostCodeByField     string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"`
+	}
+
+	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"
+	test.StartsWithString = "hello"
+	test.StartsNotWithString = "foo-hello"
+	test.EndsWithString = "hello"
+	test.EndsNotWithString = "hello-foo"
+
+	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 essere un colore valido",
+		},
+		{
+			ns:       "Test.MAC",
+			expected: "MAC deve contenere un indirizzo MAC valido",
+		},
+		{
+			ns:       "Test.IPAddr",
+			expected: "IPAddr deve essere un indirizzo IP risolvibile",
+		},
+		{
+			ns:       "Test.IPAddrv4",
+			expected: "IPAddrv4 deve essere un indirizzo IPv4 risolvibile",
+		},
+		{
+			ns:       "Test.IPAddrv6",
+			expected: "IPAddrv6 deve essere un indirizzo IPv6 risolvibile",
+		},
+		{
+			ns:       "Test.UDPAddr",
+			expected: "UDPAddr deve essere un indirizzo UDP valido",
+		},
+		{
+			ns:       "Test.UDPAddrv4",
+			expected: "UDPAddrv4 deve essere un indirizzo IPv4 UDP valido",
+		},
+		{
+			ns:       "Test.UDPAddrv6",
+			expected: "UDPAddrv6 deve essere un indirizzo IPv6 UDP valido",
+		},
+		{
+			ns:       "Test.TCPAddr",
+			expected: "TCPAddr deve essere un indirizzo TCP valido",
+		},
+		{
+			ns:       "Test.TCPAddrv4",
+			expected: "TCPAddrv4 deve essere un indirizzo IPv4 TCP valido",
+		},
+		{
+			ns:       "Test.TCPAddrv6",
+			expected: "TCPAddrv6 deve essere un indirizzo IPv6 TCP valido",
+		},
+		{
+			ns:       "Test.CIDR",
+			expected: "CIDR deve contenere una notazione CIDR valida",
+		},
+		{
+			ns:       "Test.CIDRv4",
+			expected: "CIDRv4 deve contenere una notazione CIDR per un indirizzo IPv4 valida",
+		},
+		{
+			ns:       "Test.CIDRv6",
+			expected: "CIDRv6 deve contenere una notazione CIDR per un indirizzo IPv6 valida",
+		},
+		{
+			ns:       "Test.SSN",
+			expected: "SSN deve essere un numero SSN valido",
+		},
+		{
+			ns:       "Test.IP",
+			expected: "IP deve essere un indirizzo IP valido",
+		},
+		{
+			ns:       "Test.IPv4",
+			expected: "IPv4 deve essere un indirizzo IPv4 valido",
+		},
+		{
+			ns:       "Test.IPv6",
+			expected: "IPv6 deve essere un indirizzo IPv6 valido",
+		},
+		{
+			ns:       "Test.DataURI",
+			expected: "DataURI deve contenere un Data URI valido",
+		},
+		{
+			ns:       "Test.Latitude",
+			expected: "Latitude deve contenere una latitudine valida",
+		},
+		{
+			ns:       "Test.Longitude",
+			expected: "Longitude deve contenere una longitudine valida",
+		},
+		{
+			ns:       "Test.MultiByte",
+			expected: "MultiByte deve contenere caratteri multibyte",
+		},
+		{
+			ns:       "Test.ASCII",
+			expected: "ASCII deve contenere solo caratteri ascii",
+		},
+		{
+			ns:       "Test.PrintableASCII",
+			expected: "PrintableASCII deve contenere solo caratteri ascii stampabili",
+		},
+		{
+			ns:       "Test.UUID",
+			expected: "UUID deve essere un UUID valido",
+		},
+		{
+			ns:       "Test.UUID3",
+			expected: "UUID3 deve essere un UUID versione 3 valido",
+		},
+		{
+			ns:       "Test.UUID4",
+			expected: "UUID4 deve essere un UUID versione 4 valido",
+		},
+		{
+			ns:       "Test.UUID5",
+			expected: "UUID5 deve essere un UUID versione 5 valido",
+		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID deve essere un ULID valido",
+		},
+		{
+			ns:       "Test.ISBN",
+			expected: "ISBN deve essere un numero ISBN valido",
+		},
+		{
+			ns:       "Test.ISBN10",
+			expected: "ISBN10 deve essere un numero ISBN-10 valido",
+		},
+		{
+			ns:       "Test.ISBN13",
+			expected: "ISBN13 deve essere un numero ISBN-13 valido",
+		},
+		{
+			ns:       "Test.Excludes",
+			expected: "Excludes non deve contenere il testo 'text'",
+		},
+		{
+			ns:       "Test.ExcludesAll",
+			expected: "ExcludesAll non deve contenere alcuno dei seguenti caratteri '!@#$'",
+		},
+		{
+			ns:       "Test.ExcludesRune",
+			expected: "ExcludesRune non deve contenere '☻'",
+		},
+		{
+			ns:       "Test.ContainsAny",
+			expected: "ContainsAny deve contenere almeno uno dei seguenti caratteri '!@#$'",
+		},
+		{
+			ns:       "Test.Contains",
+			expected: "Contains deve contenere il testo 'purpose'",
+		},
+		{
+			ns:       "Test.Base64",
+			expected: "Base64 deve essere una stringa Base64 valida",
+		},
+		{
+			ns:       "Test.Email",
+			expected: "Email deve essere un indirizzo email valido",
+		},
+		{
+			ns:       "Test.URL",
+			expected: "URL deve essere un URL valido",
+		},
+		{
+			ns:       "Test.URI",
+			expected: "URI deve essere un URI valido",
+		},
+		{
+			ns:       "Test.RGBColorString",
+			expected: "RGBColorString deve essere un colore RGB valido",
+		},
+		{
+			ns:       "Test.RGBAColorString",
+			expected: "RGBAColorString deve essere un colore RGBA valido",
+		},
+		{
+			ns:       "Test.HSLColorString",
+			expected: "HSLColorString deve essere un colore HSL valido",
+		},
+		{
+			ns:       "Test.HSLAColorString",
+			expected: "HSLAColorString deve essere un colore HSLA valido",
+		},
+		{
+			ns:       "Test.HexadecimalString",
+			expected: "HexadecimalString deve essere un esadecimale valido",
+		},
+		{
+			ns:       "Test.HexColorString",
+			expected: "HexColorString deve essere un colore HEX valido",
+		},
+		{
+			ns:       "Test.NumberString",
+			expected: "NumberString deve essere un numero valido",
+		},
+		{
+			ns:       "Test.NumericString",
+			expected: "NumericString deve essere un valore numerico valido",
+		},
+		{
+			ns:       "Test.AlphanumString",
+			expected: "AlphanumString può contenere solo caratteri alfanumerici",
+		},
+		{
+			ns:       "Test.AlphaString",
+			expected: "AlphaString può contenere solo caratteri alfabetici",
+		},
+		{
+			ns:       "Test.LtFieldString",
+			expected: "LtFieldString deve essere minore di MaxString",
+		},
+		{
+			ns:       "Test.LteFieldString",
+			expected: "LteFieldString deve essere minore o uguale a MaxString",
+		},
+		{
+			ns:       "Test.GtFieldString",
+			expected: "GtFieldString deve essere maggiore di MaxString",
+		},
+		{
+			ns:       "Test.GteFieldString",
+			expected: "GteFieldString deve essere maggiore o uguale a MaxString",
+		},
+		{
+			ns:       "Test.NeFieldString",
+			expected: "NeFieldString deve essere diverso da EqFieldString",
+		},
+		{
+			ns:       "Test.LtCSFieldString",
+			expected: "LtCSFieldString deve essere minore di Inner.LtCSFieldString",
+		},
+		{
+			ns:       "Test.LteCSFieldString",
+			expected: "LteCSFieldString deve essere minore o uguale a Inner.LteCSFieldString",
+		},
+		{
+			ns:       "Test.GtCSFieldString",
+			expected: "GtCSFieldString deve essere maggiore di Inner.GtCSFieldString",
+		},
+		{
+			ns:       "Test.GteCSFieldString",
+			expected: "GteCSFieldString deve essere maggiore o uguale a Inner.GteCSFieldString",
+		},
+		{
+			ns:       "Test.NeCSFieldString",
+			expected: "NeCSFieldString deve essere diverso da Inner.NeCSFieldString",
+		},
+		{
+			ns:       "Test.EqCSFieldString",
+			expected: "EqCSFieldString deve essere uguale a Inner.EqCSFieldString",
+		},
+		{
+			ns:       "Test.EqFieldString",
+			expected: "EqFieldString deve essere uguale a MaxString",
+		},
+		{
+			ns:       "Test.GteString",
+			expected: "GteString deve essere lungo almeno 3 caratteri",
+		},
+		{
+			ns:       "Test.GteNumber",
+			expected: "GteNumber deve essere maggiore o uguale a 5,56",
+		},
+		{
+			ns:       "Test.GteMultiple",
+			expected: "GteMultiple deve contenere almeno 2 elementi",
+		},
+		{
+			ns:       "Test.GteTime",
+			expected: "GteTime deve essere uguale o successivo alla Data/Ora corrente",
+		},
+		{
+			ns:       "Test.GtString",
+			expected: "GtString deve essere lungo più di 3 caratteri",
+		},
+		{
+			ns:       "Test.GtNumber",
+			expected: "GtNumber deve essere maggiore di 5,56",
+		},
+		{
+			ns:       "Test.GtMultiple",
+			expected: "GtMultiple deve contenere più di 2 elementi",
+		},
+		{
+			ns:       "Test.GtTime",
+			expected: "GtTime deve essere successivo alla Data/Ora corrente",
+		},
+		{
+			ns:       "Test.LteString",
+			expected: "LteString deve essere lungo al massimo 3 caratteri",
+		},
+		{
+			ns:       "Test.LteNumber",
+			expected: "LteNumber deve essere minore o uguale a 5,56",
+		},
+		{
+			ns:       "Test.LteMultiple",
+			expected: "LteMultiple deve contenere al massimo 2 elementi",
+		},
+		{
+			ns:       "Test.LteTime",
+			expected: "LteTime deve essere uguale o precedente alla Data/Ora corrente",
+		},
+		{
+			ns:       "Test.LtString",
+			expected: "LtString deve essere lungo meno di 3 caratteri",
+		},
+		{
+			ns:       "Test.LtNumber",
+			expected: "LtNumber deve essere minore di 5,56",
+		},
+		{
+			ns:       "Test.LtMultiple",
+			expected: "LtMultiple deve contenere meno di 2 elementi",
+		},
+		{
+			ns:       "Test.LtTime",
+			expected: "LtTime deve essere precedente alla Data/Ora corrente",
+		},
+		{
+			ns:       "Test.NeString",
+			expected: "NeString deve essere diverso da ",
+		},
+		{
+			ns:       "Test.NeNumber",
+			expected: "NeNumber deve essere diverso da 0.00",
+		},
+		{
+			ns:       "Test.NeMultiple",
+			expected: "NeMultiple deve essere diverso da 0",
+		},
+		{
+			ns:       "Test.EqString",
+			expected: "EqString non è uguale a 3",
+		},
+		{
+			ns:       "Test.EqNumber",
+			expected: "EqNumber non è uguale a 2.33",
+		},
+		{
+			ns:       "Test.EqMultiple",
+			expected: "EqMultiple non è uguale a 7",
+		},
+		{
+			ns:       "Test.MaxString",
+			expected: "MaxString deve essere lungo al massimo 3 caratteri",
+		},
+		{
+			ns:       "Test.MaxNumber",
+			expected: "MaxNumber deve essere minore o uguale a 1.113,00",
+		},
+		{
+			ns:       "Test.MaxMultiple",
+			expected: "MaxMultiple deve contenere al massimo 7 elementi",
+		},
+		{
+			ns:       "Test.MinString",
+			expected: "MinString deve essere lungo almeno 1 carattere",
+		},
+		{
+			ns:       "Test.MinNumber",
+			expected: "MinNumber deve essere maggiore o uguale a 1.113,00",
+		},
+		{
+			ns:       "Test.MinMultiple",
+			expected: "MinMultiple deve contenere almeno 7 elementi",
+		},
+		{
+			ns:       "Test.LenString",
+			expected: "LenString deve essere lungo 1 carattere",
+		},
+		{
+			ns:       "Test.LenNumber",
+			expected: "LenNumber deve essere uguale a 1.113,00",
+		},
+		{
+			ns:       "Test.LenMultiple",
+			expected: "LenMultiple deve contenere 7 elementi",
+		},
+		{
+			ns:       "Test.RequiredString",
+			expected: "RequiredString è un campo obbligatorio",
+		},
+		{
+			ns:       "Test.RequiredNumber",
+			expected: "RequiredNumber è un campo obbligatorio",
+		},
+		{
+			ns:       "Test.RequiredMultiple",
+			expected: "RequiredMultiple è un campo obbligatorio",
+		},
+		{
+			ns:       "Test.StrPtrMinLen",
+			expected: "StrPtrMinLen deve essere lungo almeno 10 caratteri",
+		},
+		{
+			ns:       "Test.StrPtrMaxLen",
+			expected: "StrPtrMaxLen deve essere lungo al massimo 1 carattere",
+		},
+		{
+			ns:       "Test.StrPtrLen",
+			expected: "StrPtrLen deve essere lungo 2 caratteri",
+		},
+		{
+			ns:       "Test.StrPtrLt",
+			expected: "StrPtrLt deve essere lungo meno di 1 carattere",
+		},
+		{
+			ns:       "Test.StrPtrLte",
+			expected: "StrPtrLte deve essere lungo al massimo 1 carattere",
+		},
+		{
+			ns:       "Test.StrPtrGt",
+			expected: "StrPtrGt deve essere lungo più di 10 caratteri",
+		},
+		{
+			ns:       "Test.StrPtrGte",
+			expected: "StrPtrGte deve essere lungo almeno 10 caratteri",
+		},
+		{
+			ns:       "Test.OneOfString",
+			expected: "OneOfString deve essere uno di [red green]",
+		},
+		{
+			ns:       "Test.OneOfInt",
+			expected: "OneOfInt deve essere uno di [5 63]",
+		},
+		{
+			ns:       "Test.UniqueSlice",
+			expected: "UniqueSlice deve contenere valori unici",
+		},
+		{
+			ns:       "Test.UniqueArray",
+			expected: "UniqueArray deve contenere valori unici",
+		},
+		{
+			ns:       "Test.UniqueMap",
+			expected: "UniqueMap deve contenere valori unici",
+		},
+		{
+			ns:       "Test.BooleanString",
+			expected: "BooleanString deve rappresentare un valore booleano",
+		},
+		{
+			ns:       "Test.JSONString",
+			expected: "JSONString deve essere una stringa json valida",
+		},
+		{
+			ns:       "Test.JWTString",
+			expected: "JWTString deve essere una stringa jwt valida",
+		},
+		{
+			ns:       "Test.LowercaseString",
+			expected: "LowercaseString deve essere una stringa minuscola",
+		},
+		{
+			ns:       "Test.UppercaseString",
+			expected: "UppercaseString deve essere una stringa maiuscola",
+		},
+		{
+			ns:       "Test.StartsWithString",
+			expected: "StartsWithString deve iniziare con foo",
+		},
+		{
+			ns:       "Test.StartsNotWithString",
+			expected: "StartsNotWithString non deve iniziare con foo",
+		},
+		{
+			ns:       "Test.EndsWithString",
+			expected: "EndsWithString deve terminare con foo",
+		},
+		{
+			ns:       "Test.EndsNotWithString",
+			expected: "EndsNotWithString non deve terminare con foo",
+		},
+		{
+			ns:       "Test.Datetime",
+			expected: "Datetime non corrisponde al formato 2006-01-02",
+		},
+		{
+			ns:       "Test.PostCode",
+			expected: "PostCode non corrisponde al formato del codice postale dello stato SG",
+		},
+		{
+			ns:       "Test.PostCodeByField",
+			expected: "PostCodeByField non corrisponde al formato del codice postale dello stato nel campo PostCodeCountry",
+		},
+	}
+
+	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/translations/ja/ja_test.go b/translations/ja/ja_test.go
index 393bcb3..7950251 100644
--- a/translations/ja/ja_test.go
+++ b/translations/ja/ja_test.go
@@ -453,7 +453,7 @@ func TestTranslations(t *testing.T) {
 		},
 		{
 			ns:       "Test.GteNumber",
-			expected: "GteNumberは5.56かより大きくなければなりません",
+			expected: "GteNumberは5.56より大きくなければなりません",
 		},
 		{
 			ns:       "Test.GteMultiple",
@@ -485,7 +485,7 @@ func TestTranslations(t *testing.T) {
 		},
 		{
 			ns:       "Test.LteNumber",
-			expected: "LteNumberは5.56かより小さくなければなりません",
+			expected: "LteNumberは5.56より小さくなければなりません",
 		},
 		{
 			ns:       "Test.LteMultiple",
@@ -541,7 +541,7 @@ func TestTranslations(t *testing.T) {
 		},
 		{
 			ns:       "Test.MaxNumber",
-			expected: "MaxNumberは1,113.00かより小さくなければなりません",
+			expected: "MaxNumberは1,113.00より小さくなければなりません",
 		},
 		{
 			ns:       "Test.MaxMultiple",
@@ -553,7 +553,7 @@ func TestTranslations(t *testing.T) {
 		},
 		{
 			ns:       "Test.MinNumber",
-			expected: "MinNumberは1,113.00かより大きくなければなりません",
+			expected: "MinNumberは1,113.00より大きくなければなりません",
 		},
 		{
 			ns:       "Test.MinMultiple",
diff --git a/validator_test.go b/validator_test.go
index 3730fb9..8abe0ce 100644
--- a/validator_test.go
+++ b/validator_test.go
@@ -5021,6 +5021,28 @@ func TestIsEqFieldValidation(t *testing.T) {
 	Equal(t, errs, nil)
 }
 
+func TestIsEqFieldValidationWithAliasTime(t *testing.T) {
+	var errs error
+	validate := New()
+
+	type CustomTime time.Time
+
+	type Test struct {
+		Start CustomTime `validate:"eqfield=End"`
+		End   *time.Time
+	}
+
+	now := time.Now().UTC()
+
+	sv := &Test{
+		Start: CustomTime(now),
+		End:   &now,
+	}
+
+	errs = validate.Struct(sv)
+	Equal(t, errs, nil)
+}
+
 func TestIsEqValidation(t *testing.T) {
 	var errs error
 	validate := New()
@@ -9151,6 +9173,7 @@ func TestHostnameRFC1123Validation(t *testing.T) {
 		{"test.example24.com.", false},
 		{"test24.example24.com.", false},
 		{"example.", false},
+		{"test_example", false},
 		{"192.168.0.1", true},
 		{"email@example.com", false},
 		{"2001:cdba:0000:0000:0000:0000:3257:9652", false},
@@ -9199,6 +9222,7 @@ func TestHostnameRFC1123AliasValidation(t *testing.T) {
 		{"test.example24.com.", false},
 		{"test24.example24.com.", false},
 		{"example.", false},
+		{"test_example", false},
 		{"192.168.0.1", true},
 		{"email@example.com", false},
 		{"2001:cdba:0000:0000:0000:0000:3257:9652", false},
@@ -10675,6 +10699,145 @@ func TestRequiredWithoutAll(t *testing.T) {
 	AssertError(t, errs, "Field2", "Field2", "Field2", "Field2", "required_without_all")
 }
 
+func TestExcludedIf(t *testing.T) {
+	validate := New()
+	type Inner struct {
+		Field *string
+	}
+
+	test1 := struct {
+		FieldE  string  `validate:"omitempty" json:"field_e"`
+		FieldER *string `validate:"excluded_if=FieldE test" json:"field_er"`
+	}{
+		FieldE: "test",
+	}
+	errs := validate.Struct(test1)
+	Equal(t, errs, nil)
+
+	test2 := struct {
+		FieldE  string `validate:"omitempty" json:"field_e"`
+		FieldER string `validate:"excluded_if=FieldE test" json:"field_er"`
+	}{
+		FieldE: "notest",
+	}
+	errs = validate.Struct(test2)
+	NotEqual(t, errs, nil)
+	ve := errs.(ValidationErrors)
+	Equal(t, len(ve), 1)
+	AssertError(t, errs, "FieldER", "FieldER", "FieldER", "FieldER", "excluded_if")
+
+	shouldError := "shouldError"
+	test3 := struct {
+		Inner  *Inner
+		FieldE string `validate:"omitempty" json:"field_e"`
+		Field1 int    `validate:"excluded_if=Inner.Field test" json:"field_1"`
+	}{
+		Inner: &Inner{Field: &shouldError},
+	}
+	errs = validate.Struct(test3)
+	NotEqual(t, errs, nil)
+	ve = errs.(ValidationErrors)
+	Equal(t, len(ve), 1)
+	AssertError(t, errs, "Field1", "Field1", "Field1", "Field1", "excluded_if")
+
+	shouldPass := "test"
+	test4 := struct {
+		Inner  *Inner
+		FieldE string `validate:"omitempty" json:"field_e"`
+		Field1 int    `validate:"excluded_if=Inner.Field test" json:"field_1"`
+	}{
+		Inner: &Inner{Field: &shouldPass},
+	}
+	errs = validate.Struct(test4)
+	Equal(t, errs, nil)
+
+	// Checks number of params in struct tag is correct
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("panicTest should have panicked!")
+		}
+	}()
+	fieldVal := "panicTest"
+	panicTest := struct {
+		Inner  *Inner
+		Field1 string `validate:"excluded_if=Inner.Field" json:"field_1"`
+	}{
+		Inner: &Inner{Field: &fieldVal},
+	}
+	_ = validate.Struct(panicTest)
+}
+
+func TestExcludedUnless(t *testing.T) {
+	validate := New()
+	type Inner struct {
+		Field *string
+	}
+
+	fieldVal := "test"
+	test := struct {
+		FieldE  string `validate:"omitempty" json:"field_e"`
+		FieldER string `validate:"excluded_unless=FieldE test" json:"field_er"`
+	}{
+		FieldE:  "notest",
+		FieldER: "filled",
+	}
+	errs := validate.Struct(test)
+	Equal(t, errs, nil)
+
+	test2 := struct {
+		FieldE  string `validate:"omitempty" json:"field_e"`
+		FieldER string `validate:"excluded_unless=FieldE test" json:"field_er"`
+	}{
+		FieldE:  "test",
+		FieldER: "filled",
+	}
+	errs = validate.Struct(test2)
+	NotEqual(t, errs, nil)
+	ve := errs.(ValidationErrors)
+	Equal(t, len(ve), 1)
+	AssertError(t, errs, "FieldER", "FieldER", "FieldER", "FieldER", "excluded_unless")
+
+	shouldError := "test"
+	test3 := struct {
+		Inner  *Inner
+		Field1 string `validate:"excluded_unless=Inner.Field test" json:"field_1"`
+	}{
+		Inner:  &Inner{Field: &shouldError},
+		Field1: "filled",
+	}
+	errs = validate.Struct(test3)
+	NotEqual(t, errs, nil)
+	ve = errs.(ValidationErrors)
+	Equal(t, len(ve), 1)
+	AssertError(t, errs, "Field1", "Field1", "Field1", "Field1", "excluded_unless")
+
+	shouldPass := "shouldPass"
+	test4 := struct {
+		Inner  *Inner
+		FieldE string `validate:"omitempty" json:"field_e"`
+		Field1 string `validate:"excluded_unless=Inner.Field test" json:"field_1"`
+	}{
+		Inner:  &Inner{Field: &shouldPass},
+		Field1: "filled",
+	}
+	errs = validate.Struct(test4)
+	Equal(t, errs, nil)
+
+	// Checks number of params in struct tag is correct
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("panicTest should have panicked!")
+		}
+	}()
+	panicTest := struct {
+		Inner  *Inner
+		Field1 string `validate:"excluded_unless=Inner.Field" json:"field_1"`
+	}{
+		Inner: &Inner{Field: &fieldVal},
+	}
+	_ = validate.Struct(panicTest)
+}
+
 func TestLookup(t *testing.T) {
 	type Lookup struct {
 		FieldA *string `json:"fieldA,omitempty" validate:"required_without=FieldB"`
@@ -11460,7 +11623,7 @@ func TestSemverFormatValidation(t *testing.T) {
 		}
 	}
 }
-  
+
 func TestRFC1035LabelFormatValidation(t *testing.T) {
 	tests := []struct {
 		value    string `validate:"dns_rfc1035_label"`
@@ -11611,3 +11774,65 @@ func TestPostCodeByIso3166Alpha2Field_InvalidKind(t *testing.T) {
 	_ = New().Struct(test{"ABC", 123, false})
 	t.Errorf("Didn't panic as expected")
 }
+
+func TestCreditCardFormatValidation(t *testing.T) {
+	tests := []struct {
+		value    string `validate:"credit_card"`
+		tag      string
+		expected bool
+	}{
+		{"586824160825533338", "credit_card", true},
+		{"586824160825533328", "credit_card", false},
+		{"4624748233249780", "credit_card", true},
+		{"4624748233349780", "credit_card", false},
+		{"378282246310005", "credit_card", true},
+		{"378282146310005", "credit_card", false},
+		{"4624 7482 3324 9780", "credit_card", true},
+		{"4624 7482 3324  9780", "credit_card", 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 credit_card failed Error: %s", i, errs)
+			}
+		} else {
+			if IsEqual(errs, nil) {
+				t.Fatalf("Index: %d credit_card failed Error: %s", i, errs)
+			} else {
+				val := getError(errs, "", "")
+				if val.Tag() != "credit_card" {
+					t.Fatalf("Index: %d credit_card failed Error: %s", i, errs)
+				}
+			}
+		}
+	}
+}
+
+func TestMultiOrOperatorGroup(t *testing.T) {
+ 	tests := []struct {
+ 		Value    int `validate:"eq=1|gte=5,eq=1|lt=7"`
+ 		expected bool
+ 	}{
+ 		{1, true}, {2, false}, {5, true}, {6, true}, {8, false},
+ 	}
+
+ 	validate := New()
+
+ 	for i, test := range tests {
+ 		errs := validate.Struct(test)
+ 		if test.expected {
+ 			if !IsEqual(errs, nil) {
+ 				t.Fatalf("Index: %d multi_group_of_OR_operators failed Error: %s", i, errs)
+ 			}
+ 		} else {
+ 			if IsEqual(errs, nil) {
+ 				t.Fatalf("Index: %d multi_group_of_OR_operators should have errs", i)
+ 			}
+ 		}
+ 	}
+ }