From c0195b2b40b26d8d7f435f109bf827d52ec145e3 Mon Sep 17 00:00:00 2001 From: Jacob Hochstetler Date: Sun, 1 May 2022 09:59:09 -0500 Subject: [PATCH 01/18] added excluded_if/excluded_unless tags + tests (#847) --- README.md | 2 + baked_in.go | 33 ++++++++++ doc.go | 34 ++++++++++ validator_instance.go | 4 +- validator_test.go | 141 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 212 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6712e95a..f25649a2 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,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 7868b66f..36a410b6 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, @@ -1542,6 +1544,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 +1576,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 { diff --git a/doc.go b/doc.go index b284c379..6cf62089 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 diff --git a/validator_instance.go b/validator_instance.go index 973964fc..6d660600 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 diff --git a/validator_test.go b/validator_test.go index 3730fb9d..4da0de6a 100644 --- a/validator_test.go +++ b/validator_test.go @@ -10675,6 +10675,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 +11599,7 @@ func TestSemverFormatValidation(t *testing.T) { } } } - + func TestRFC1035LabelFormatValidation(t *testing.T) { tests := []struct { value string `validate:"dns_rfc1035_label"` From bb30072b4887f1e059af1573445847b3abd6db4d Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 1 May 2022 08:09:26 -0700 Subject: [PATCH 02/18] update ci config --- .github/workflows/workflow.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index d1ca1e8f..8e82f438 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 @@ -45,4 +45,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: - version: v1.41.1 + version: v1.45.2 From dd2857a4cb6c53af5bf6944cc15fd04c865d28ae Mon Sep 17 00:00:00 2001 From: alessmar Date: Sun, 1 May 2022 17:17:59 +0200 Subject: [PATCH 03/18] Credit card validation (#924) --- .github/workflows/workflow.yml | 7 ++++-- README.md | 1 + baked_in.go | 39 ++++++++++++++++++++++++++++++++++ doc.go | 6 ++++++ validator_test.go | 38 +++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 2 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 8e82f438..d6bfd55d 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -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.45.2 diff --git a/README.md b/README.md index f25649a2..1c892662 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/baked_in.go b/baked_in.go index 36a410b6..7833f24b 100644 --- a/baked_in.go +++ b/baked_in.go @@ -203,6 +203,7 @@ var ( "bic": isIsoBicFormat, "semver": isSemverFormat, "dns_rfc1035_label": isDnsRFC1035LabelFormat, + "credit_card": isCreditCard, } ) @@ -2469,3 +2470,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 6cf62089..7341c67d 100644 --- a/doc.go +++ b/doc.go @@ -1317,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/validator_test.go b/validator_test.go index 4da0de6a..a5b1fa28 100644 --- a/validator_test.go +++ b/validator_test.go @@ -11750,3 +11750,41 @@ 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) + } + } + } + } +} From bc9f9dd2ebe8fd783fe5e8ad1ab694137f48ea4b Mon Sep 17 00:00:00 2001 From: NgeKaworu Date: Sun, 1 May 2022 23:23:51 +0800 Subject: [PATCH 04/18] Update zh.go (#856) --- translations/zh/zh.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/translations/zh/zh.go b/translations/zh/zh.go index 3dc73566..80165d0c 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) { From 24b5175a342e0953049e1c6fe13c98dca9533fe0 Mon Sep 17 00:00:00 2001 From: Sol <39742391+SolKuczala@users.noreply.github.com> Date: Sun, 1 May 2022 16:26:35 +0100 Subject: [PATCH 05/18] Fix typo (#891) --- validator_instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator_instance.go b/validator_instance.go index 6d660600..8917d8c2 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -171,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) } From aea8782168f5ea921dfab6569268a4d6b4274f73 Mon Sep 17 00:00:00 2001 From: "hehe.bu" Date: Sun, 1 May 2022 23:27:37 +0800 Subject: [PATCH 06/18] fix ja typos (#898) --- translations/ja/ja.go | 8 ++++---- translations/ja/ja_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/translations/ja/ja.go b/translations/ja/ja.go index 4502e2db..1cc67a4f 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/ja/ja_test.go b/translations/ja/ja_test.go index 393bcb3e..7950251c 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", From f2d3ff7f980910456594bee6f282c1d4fe6213dc Mon Sep 17 00:00:00 2001 From: Stefan Dillenburg Date: Sun, 1 May 2022 17:32:50 +0200 Subject: [PATCH 07/18] fix: Remove underscore from RFC-1123 based regex (#912) RFC-1123 is based on RFC-952, which doesn't allow underscores. RFC-1123 must be therefore implemented with the same constraint to disallow underscores in host names. --- regexes.go | 12 ++++++------ validator_test.go | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/regexes.go b/regexes.go index 48e51d57..a8668f8b 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/validator_test.go b/validator_test.go index a5b1fa28..6aee5596 100644 --- a/validator_test.go +++ b/validator_test.go @@ -9151,6 +9151,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 +9200,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}, From af72f63d39e2f2ce4176386412f5dd8f6d3e4ecf Mon Sep 17 00:00:00 2001 From: XIE Long Date: Sun, 1 May 2022 23:40:00 +0800 Subject: [PATCH 08/18] Result is wrong while there are multiple group of OR operators #910 (#911) --- validator.go | 9 +++++++++ validator_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/validator.go b/validator.go index 2a4fad02..c2e03583 100644 --- a/validator.go +++ b/validator.go @@ -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_test.go b/validator_test.go index 6aee5596..b7bcec25 100644 --- a/validator_test.go +++ b/validator_test.go @@ -11790,3 +11790,27 @@ func TestCreditCardFormatValidation(t *testing.T) { } } } + +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) + } + } + } + } From 090afeb8aca552e3a3f37ca640e3fb85161a3622 Mon Sep 17 00:00:00 2001 From: Massimo Costa Date: Sun, 1 May 2022 17:44:10 +0200 Subject: [PATCH 09/18] enhancement: add italian translation (#914) --- translations/it/it.go | 1244 ++++++++++++++++++++++++++++++++++++ translations/it/it_test.go | 724 +++++++++++++++++++++ 2 files changed, 1968 insertions(+) create mode 100644 translations/it/it.go create mode 100644 translations/it/it_test.go diff --git a/translations/it/it.go b/translations/it/it.go new file mode 100644 index 00000000..8abd67f5 --- /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/it/it_test.go b/translations/it/it_test.go new file mode 100644 index 00000000..301e8c1c --- /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)) + } +} From f09500fca7d3aa986ebfbea8087b4e477b5a4103 Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Sun, 1 May 2022 18:44:56 +0300 Subject: [PATCH 10/18] Fix support for aliased time.Time types (#890) --- baked_in.go | 202 +++++++++++++++++++++--------------------- util.go | 2 +- validator.go | 2 +- validator_instance.go | 8 +- validator_test.go | 22 +++++ 5 files changed, 128 insertions(+), 108 deletions(-) diff --git a/baked_in.go b/baked_in.go index 7833f24b..bc638f2d 100644 --- a/baked_in.go +++ b/baked_in.go @@ -819,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) @@ -832,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: @@ -872,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.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.Before(topTime) || fieldTime.Equal(topTime) } + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } } // default reflect.String: @@ -920,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: @@ -967,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 == 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) || fieldTime.Equal(topTime) } + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } } // default reflect.String: @@ -1014,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.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) } + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } } // default reflect.String: @@ -1064,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: @@ -1114,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: @@ -1164,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: @@ -1711,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 == 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) || fieldTime.Equal(t) } + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } } // default reflect.String @@ -1758,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 @@ -1811,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) } @@ -1857,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()) } } @@ -1937,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 @@ -1984,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 @@ -2037,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) } @@ -2084,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()) } } diff --git a/util.go b/util.go index 56420f43..36da8551 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 c2e03583..80da095a 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 { diff --git a/validator_instance.go b/validator_instance.go index 8917d8c2..316ffb08 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -333,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)} } @@ -378,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)} } @@ -426,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)} } @@ -516,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)} } diff --git a/validator_test.go b/validator_test.go index b7bcec25..8abe0ce7 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() From 0a26ee57e4acca0fedd7936b5570bd28ff92e532 Mon Sep 17 00:00:00 2001 From: Ciprian Date: Sun, 1 May 2022 17:48:44 +0200 Subject: [PATCH 11/18] Updated `endsnotwith` description in Readme (#824) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c892662..0201880a 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ Baked-in Validations | contains | Contains | | containsany | Contains Any | | containsrune | Contains Rune | -| endsnotwith | Ends With | +| endsnotwith | Ends Not With | | endswith | Ends With | | excludes | Excludes | | excludesall | Excludes All | From d37da5e53c4b42b70227b383814692133a5f738f Mon Sep 17 00:00:00 2001 From: Renato Alves Torres Date: Sun, 1 May 2022 16:50:01 +0100 Subject: [PATCH 12/18] fix: add en translation for required_if (#884) --- translations/en/en.go | 5 +++++ translations/en/en_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/translations/en/en.go b/translations/en/en.go index 5ed76aea..5784d1f6 100644 --- a/translations/en/en.go +++ b/translations/en/en.go @@ -28,6 +28,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} is a required field", override: false, }, + { + tag: "required_if", + translation: "{0} is a required field", + override: false, + }, { tag: "len", customRegisFunc: func(ut ut.Translator) (err error) { diff --git a/translations/en/en_test.go b/translations/en/en_test.go index 146c475e..4c1d605a 100644 --- a/translations/en/en_test.go +++ b/translations/en/en_test.go @@ -27,6 +27,7 @@ func TestTranslations(t *testing.T) { GteCSFieldString string LtCSFieldString string LteCSFieldString string + RequiredIf string } type Test struct { @@ -34,6 +35,7 @@ func TestTranslations(t *testing.T) { RequiredString string `validate:"required"` RequiredNumber int `validate:"required"` RequiredMultiple []string `validate:"required"` + RequiredIf string `validate:"required_if=Inner.RequiredIf abcd"` LenString string `validate:"len=1"` LenNumber float64 `validate:"len=1113.00"` LenMultiple []string `validate:"len=7"` @@ -202,6 +204,8 @@ func TestTranslations(t *testing.T) { test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"} test.Datetime = "2008-Feb-01" + test.Inner.RequiredIf = "abcd" + err = validate.Struct(test) NotEqual(t, err, nil) @@ -592,6 +596,10 @@ func TestTranslations(t *testing.T) { ns: "Test.RequiredString", expected: "RequiredString is a required field", }, + { + ns: "Test.RequiredIf", + expected: "RequiredIf is a required field", + }, { ns: "Test.RequiredNumber", expected: "RequiredNumber is a required field", From e3f29bf0888e6fbcfa5c8ae9a2b0508c73858b6d Mon Sep 17 00:00:00 2001 From: Eduardo Mello Date: Sun, 1 May 2022 12:51:09 -0300 Subject: [PATCH 13/18] Boolean translation (#930) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ add en boolean translation * ✨ add pt-BR boolean translation --- translations/en/en.go | 5 +++++ translations/en/en_test.go | 6 ++++++ translations/pt_BR/pt_BR.go | 5 +++++ translations/pt_BR/pt_BR_test.go | 6 ++++++ 4 files changed, 22 insertions(+) diff --git a/translations/en/en.go b/translations/en/en.go index 5784d1f6..ee05f915 100644 --- a/translations/en/en.go +++ b/translations/en/en.go @@ -1351,6 +1351,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "boolean", + translation: "{0} must be a valid boolean value", + override: false, + }, } for _, t := range translations { diff --git a/translations/en/en_test.go b/translations/en/en_test.go index 4c1d605a..9cb6deb5 100644 --- a/translations/en/en_test.go +++ b/translations/en/en_test.go @@ -151,6 +151,7 @@ func TestTranslations(t *testing.T) { PostCode string `validate:"postcode_iso3166_alpha2=SG"` PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + BooleanString string `validate:"boolean"` } var test Test @@ -203,6 +204,7 @@ func TestTranslations(t *testing.T) { test.UniqueSlice = []string{"1234", "1234"} test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"} test.Datetime = "2008-Feb-01" + test.BooleanString = "A" test.Inner.RequiredIf = "abcd" @@ -684,6 +686,10 @@ func TestTranslations(t *testing.T) { ns: "Test.PostCodeByField", expected: "PostCodeByField does not match postcode format of country in PostCodeCountry field", }, + { + ns: "Test.BooleanString", + expected: "BooleanString must be a valid boolean value", + }, } for _, tt := range tests { diff --git a/translations/pt_BR/pt_BR.go b/translations/pt_BR/pt_BR.go index ef044963..d6883aa9 100644 --- a/translations/pt_BR/pt_BR.go +++ b/translations/pt_BR/pt_BR.go @@ -1316,6 +1316,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "boolean", + translation: "{0} deve ser um valor booleano válido", + override: false, + }, } for _, t := range translations { diff --git a/translations/pt_BR/pt_BR_test.go b/translations/pt_BR/pt_BR_test.go index c8a64826..426f2468 100644 --- a/translations/pt_BR/pt_BR_test.go +++ b/translations/pt_BR/pt_BR_test.go @@ -139,6 +139,7 @@ func TestTranslations(t *testing.T) { StrPtrGte *string `validate:"gte=10"` OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` + BooleanString string `validate:"boolean"` } var test Test @@ -171,6 +172,7 @@ func TestTranslations(t *testing.T) { test.AlphanumString = "abc3!" test.NumericString = "12E.00" test.NumberString = "12E" + test.BooleanString = "A" test.Excludes = "este é um texto de teste" test.ExcludesAll = "Isso é Ótimo!" @@ -619,6 +621,10 @@ func TestTranslations(t *testing.T) { ns: "Test.OneOfInt", expected: "OneOfInt deve ser um de [5 63]", }, + { + ns: "Test.BooleanString", + expected: "BooleanString deve ser um valor booleano válido", + }, } for _, tt := range tests { From 21a103f428bb5891eef736364dbbb0a165e9ca68 Mon Sep 17 00:00:00 2001 From: Ayoob Mohammed <37803924+AyoobMH@users.noreply.github.com> Date: Sun, 1 May 2022 19:00:36 +0300 Subject: [PATCH 14/18] Add Arabic translations (#825) * Add arabic translations * Add ULID translation --- translations/ar/ar.go | 1389 ++++++++++++++++++++++++++++++++++++ translations/ar/ar_test.go | 695 ++++++++++++++++++ 2 files changed, 2084 insertions(+) create mode 100644 translations/ar/ar.go create mode 100644 translations/ar/ar_test.go diff --git a/translations/ar/ar.go b/translations/ar/ar.go new file mode 100644 index 00000000..3886120d --- /dev/null +++ b/translations/ar/ar.go @@ -0,0 +1,1389 @@ +package ar + +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} مطلوب", + override: false, + }, + { + tag: "len", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("len-string", "يجب أن يكون طول {0} مساويا ل {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("len-string-character", "{0} حرف", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("len-string-character", "{0} أحرف", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("len-number", "يجب أن يكون {0} مساويا ل {1}", false); err != nil { + return + } + + if err = ut.Add("len-items", "يجب أن يحتوي {0} على {1}", false); err != nil { + return + } + if err = ut.AddCardinal("len-items-item", "{0} عنصر", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("len-items-item", "{0} عناصر", locales.PluralRuleOther, false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + + var digits uint64 + var kind reflect.Kind + + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err := strconv.ParseFloat(fe.Param(), 64) + if err != nil { + goto END + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("len-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("len-items", fe.Field(), c) + + default: + t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "min", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("min-string", "{0} يجب أن يكون {1} أو اقل", false); err != nil { + return + } + + if err = ut.AddCardinal("min-string-character", "{0} حرف", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("min-string-character", "{0} أحرف", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("min-number", "{0} يجب أن يكون {1} أو اقل", false); err != nil { + return + } + + if err = ut.Add("min-items", "يجب أن يحتوي {0} على {1} على الأقل", false); err != nil { + return + } + if err = ut.AddCardinal("min-items-item", "{0} عنصر", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("min-items-item", "{0} عناصر", locales.PluralRuleOther, false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + + var digits uint64 + var kind reflect.Kind + + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err := strconv.ParseFloat(fe.Param(), 64) + if err != nil { + goto END + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("min-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("min-items", fe.Field(), c) + + default: + t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "max", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("max-string", "يجب أن يكون طول {0} بحد أقصى {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("max-string-character", "{0} حرف", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("max-string-character", "{0} أحرف", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("max-number", "{0} يجب أن يكون {1} أو اقل", false); err != nil { + return + } + + if err = ut.Add("max-items", "يجب أن يحتوي {0} على {1} كحد أقصى", false); err != nil { + return + } + if err = ut.AddCardinal("max-items-item", "{0} عنصر", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("max-items-item", "{0} عناصر", locales.PluralRuleOther, false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + + var digits uint64 + var kind reflect.Kind + + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err := strconv.ParseFloat(fe.Param(), 64) + if err != nil { + goto END + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("max-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("max-items", fe.Field(), c) + + default: + t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "eq", + translation: "{0} لا يساوي {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + fmt.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ne", + translation: "{0} يجب ألا يساوي {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + fmt.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "lt", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("lt-string", "يجب أن يكون طول {0} أقل من {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("lt-string-character", "{0} حرف", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("lt-string-character", "{0} أحرف", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lt-number", "يجب أن يكون {0} أقل من {1}", false); err != nil { + return + } + + if err = ut.Add("lt-items", "يجب أن يحتوي {0} على أقل من {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("lt-items-item", "{0} عنصر", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("lt-items-item", "{0} عناصر", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lt-datetime", "يجب أن يكون {0} أقل من التاريخ والوقت الحاليين", false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + var f64 float64 + var digits uint64 + var kind reflect.Kind + + fn := func() (err error) { + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err = strconv.ParseFloat(fe.Param(), 64) + + return + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("lt-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("lt-items", fe.Field(), c) + + case reflect.Struct: + if fe.Type() != reflect.TypeOf(time.Time{}) { + err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag()) + goto END + } + + t, err = ut.T("lt-datetime", fe.Field()) + + default: + err = fn() + if err != nil { + goto END + } + + t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "lte", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("lte-string", "يجب أن يكون طول {0} كحد أقصى {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("lte-string-character", "{0} حرف", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("lte-string-character", "{0} أحرف", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lte-number", "{0} يجب أن يكون {1} أو اقل", false); err != nil { + return + } + + if err = ut.Add("lte-items", "يجب أن يحتوي {0} على {1} كحد أقصى", false); err != nil { + return + } + + if err = ut.AddCardinal("lte-items-item", "{0} عنصر", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("lte-items-item", "{0} عناصر", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lte-datetime", "يجب أن يكون {0} أقل من أو يساوي التاريخ والوقت الحاليين", false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + var f64 float64 + var digits uint64 + var kind reflect.Kind + + fn := func() (err error) { + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err = strconv.ParseFloat(fe.Param(), 64) + + return + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("lte-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("lte-items", fe.Field(), c) + + case reflect.Struct: + if fe.Type() != reflect.TypeOf(time.Time{}) { + err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag()) + goto END + } + + t, err = ut.T("lte-datetime", fe.Field()) + + default: + err = fn() + if err != nil { + goto END + } + + t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gt", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("gt-string", "يجب أن يكون طول {0} أكبر من {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("gt-string-character", "{0} حرف", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("gt-string-character", "{0} أحرف", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gt-number", "يجب أن يكون {0} أكبر من {1}", false); err != nil { + return + } + + if err = ut.Add("gt-items", "يجب أن يحتوي {0} على أكثر من {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("gt-items-item", "{0}عنصر", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("gt-items-item", "{0} عناصر", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gt-datetime", "يجب أن يكون {0} أكبر من التاريخ والوقت الحاليين", false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + var f64 float64 + var digits uint64 + var kind reflect.Kind + + fn := func() (err error) { + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err = strconv.ParseFloat(fe.Param(), 64) + + return + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("gt-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("gt-items", fe.Field(), c) + + case reflect.Struct: + if fe.Type() != reflect.TypeOf(time.Time{}) { + err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag()) + goto END + } + + t, err = ut.T("gt-datetime", fe.Field()) + + default: + err = fn() + if err != nil { + goto END + } + + t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gte", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("gte-string", "يجب أن يكون طول {0} على الأقل {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("gte-string-character", "{0} حرف", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("gte-string-character", "{0} أحرف", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gte-number", "{0} يجب أن يكون {1} أو أكبر", false); err != nil { + return + } + + if err = ut.Add("gte-items", "يجب أن يحتوي {0} على {1} على الأقل", false); err != nil { + return + } + + if err = ut.AddCardinal("gte-items-item", "{0} عنصر", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("gte-items-item", "{0} عناصر", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gte-datetime", "يجب أن يكون {0} أكبر من أو يساوي التاريخ والوقت الحاليين", false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + var f64 float64 + var digits uint64 + var kind reflect.Kind + + fn := func() (err error) { + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err = strconv.ParseFloat(fe.Param(), 64) + + return + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("gte-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("gte-items", fe.Field(), c) + + case reflect.Struct: + if fe.Type() != reflect.TypeOf(time.Time{}) { + err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag()) + goto END + } + + t, err = ut.T("gte-datetime", fe.Field()) + + default: + err = fn() + if err != nil { + goto END + } + + t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "eqfield", + translation: "يجب أن يكون {0} مساويا ل {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "eqcsfield", + translation: "يجب أن يكون {0} مساويا ل {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "necsfield", + translation: "{0} لا يمكن أن يساوي {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtcsfield", + translation: "يجب أن يكون {0} أكبر من {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtecsfield", + translation: "يجب أن يكون {0} أكبر من أو يساوي {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltcsfield", + translation: "يجب أن يكون {0} أصغر من {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltecsfield", + translation: "يجب أن يكون {0} أصغر من أو يساوي {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "nefield", + translation: "{0} لا يمكن أن يساوي {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtfield", + translation: "يجب أن يكون {0} أكبر من {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtefield", + translation: "يجب أن يكون {0} أكبر من أو يساوي {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltfield", + translation: "يجب أن يكون {0} أصغر من {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltefield", + translation: "يجب أن يكون {0} أصغر من أو يساوي {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "alpha", + translation: "يمكن أن يحتوي {0} على أحرف أبجدية فقط", + override: false, + }, + { + tag: "alphanum", + translation: "يمكن أن يحتوي {0} على أحرف أبجدية رقمية فقط", + override: false, + }, + { + tag: "numeric", + translation: "يجب أن يكون {0} قيمة رقمية صالحة", + override: false, + }, + { + tag: "number", + translation: "يجب أن يكون {0} رقم صالح", + override: false, + }, + { + tag: "hexadecimal", + translation: "يجب أن يكون {0} عددًا سداسيًا عشريًا صالحاً", + override: false, + }, + { + tag: "hexcolor", + translation: "يجب أن يكون {0} لون HEX صالح", + override: false, + }, + { + tag: "rgb", + translation: "يجب أن يكون {0} لون RGB صالح", + override: false, + }, + { + tag: "rgba", + translation: "يجب أن يكون {0} لون RGBA صالح", + override: false, + }, + { + tag: "hsl", + translation: "يجب أن يكون {0} لون HSL صالح", + override: false, + }, + { + tag: "hsla", + translation: "يجب أن يكون {0} لون HSLA صالح", + override: false, + }, + { + tag: "e164", + translation: "يجب أن يكون {0} رقم هاتف صالح بتنسيق E.164", + override: false, + }, + { + tag: "email", + translation: "يجب أن يكون {0} عنوان بريد إلكتروني صالح", + override: false, + }, + { + tag: "url", + translation: "يجب أن يكون {0} رابط إنترنت صالح", + override: false, + }, + { + tag: "uri", + translation: "يجب أن يكون {0} URI صالح", + override: false, + }, + { + tag: "base64", + translation: "يجب أن يكون {0} سلسلة Base64 صالحة", + override: false, + }, + { + tag: "contains", + translation: "يجب أن يحتوي {0} على النص '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "containsany", + translation: "يجب أن يحتوي {0} على حرف واحد على الأقل من الأحرف التالية '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "excludes", + translation: "لا يمكن أن يحتوي {0} على النص '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "excludesall", + translation: "لا يمكن أن يحتوي {0} على أي من الأحرف التالية '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "excludesrune", + translation: "لا يمكن أن يحتوي {0} على التالي '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "isbn", + translation: "يجب أن يكون {0} رقم ISBN صالح", + override: false, + }, + { + tag: "isbn10", + translation: "يجب أن يكون {0} رقم ISBN-10 صالح", + override: false, + }, + { + tag: "isbn13", + translation: "يجب أن يكون {0} رقم ISBN-13 صالح", + override: false, + }, + { + tag: "uuid", + translation: "يجب أن يكون {0} UUID صالح", + override: false, + }, + { + tag: "uuid3", + translation: "يجب أن يكون {0} UUID صالح من النسخة 3", + override: false, + }, + { + tag: "uuid4", + translation: "يجب أن يكون {0} UUID صالح من النسخة 4", + override: false, + }, + { + tag: "uuid5", + translation: "يجب أن يكون {0} UUID صالح من النسخة 5", + override: false, + }, + { + tag: "ulid", + translation: "يجب أن يكون {0} ULID صالح من نسخة", + override: false, + }, + { + tag: "ascii", + translation: "يجب أن يحتوي {0} على أحرف ascii فقط", + override: false, + }, + { + tag: "printascii", + translation: "يجب أن يحتوي {0} على أحرف ascii قابلة للطباعة فقط", + override: false, + }, + { + tag: "multibyte", + translation: "يجب أن يحتوي {0} على أحرف متعددة البايت", + override: false, + }, + { + tag: "datauri", + translation: "يجب أن يحتوي {0} على URI صالح للبيانات", + override: false, + }, + { + tag: "latitude", + translation: "يجب أن يحتوي {0} على إحداثيات خط عرض صالحة", + override: false, + }, + { + tag: "longitude", + translation: "يجب أن يحتوي {0} على إحداثيات خط طول صالحة", + override: false, + }, + { + tag: "ssn", + translation: "يجب أن يكون {0} رقم SSN صالح", + override: false, + }, + { + tag: "ipv4", + translation: "يجب أن يكون {0} عنوان IPv4 صالح", + override: false, + }, + { + tag: "ipv6", + translation: "يجب أن يكون {0} عنوان IPv6 صالح", + override: false, + }, + { + tag: "ip", + translation: "يجب أن يكون {0} عنوان IP صالح", + override: false, + }, + { + tag: "cidr", + translation: "يجب أن يحتوي {0} على علامة CIDR صالحة", + override: false, + }, + { + tag: "cidrv4", + translation: "يجب أن يحتوي {0} على علامة CIDR صالحة لعنوان IPv4", + override: false, + }, + { + tag: "cidrv6", + translation: "يجب أن يحتوي {0} على علامة CIDR صالحة لعنوان IPv6", + override: false, + }, + { + tag: "tcp_addr", + translation: "يجب أن يكون {0} عنوان TCP صالح", + override: false, + }, + { + tag: "tcp4_addr", + translation: "يجب أن يكون {0} عنوان IPv4 TCP صالح", + override: false, + }, + { + tag: "tcp6_addr", + translation: "يجب أن يكون {0} عنوان IPv6 TCP صالح", + override: false, + }, + { + tag: "udp_addr", + translation: "يجب أن يكون {0} عنوان UDP صالح", + override: false, + }, + { + tag: "udp4_addr", + translation: "يجب أن يكون {0} عنوان IPv4 UDP صالح", + override: false, + }, + { + tag: "udp6_addr", + translation: "يجب أن يكون {0} عنوان IPv6 UDP صالح", + override: false, + }, + { + tag: "ip_addr", + translation: "يجب أن يكون {0} عنوان IP قابل للحل", + override: false, + }, + { + tag: "ip4_addr", + translation: "يجب أن يكون {0} عنوان IP قابل للحل", + override: false, + }, + { + tag: "ip6_addr", + translation: "يجب أن يكون {0} عنوان IPv6 قابل للحل", + override: false, + }, + { + tag: "unix_addr", + translation: "يجب أن يكون {0} عنوان UNIX قابل للحل", + override: false, + }, + { + tag: "mac", + translation: "يجب أن يحتوي {0} على عنوان MAC صالح", + override: false, + }, + { + tag: "unique", + translation: "يجب أن يحتوي {0} على قيم فريدة", + override: false, + }, + { + tag: "iscolor", + translation: "يجب أن يكون {0} لون صالح", + override: false, + }, + { + tag: "oneof", + translation: "يجب أن يكون {0} واحدا من [{1}]", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + s, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + return s + }, + }, + { + tag: "json", + translation: "يجب أن يكون {0} نص json صالح", + override: false, + }, + { + tag: "jwt", + translation: "يجب أن يكون {0} نص jwt صالح", + override: false, + }, + { + tag: "lowercase", + translation: "يجب أن يكون {0} نص حروف صغيرة", + override: false, + }, + { + tag: "uppercase", + translation: "يجب أن يكون {0} نص حروف كبيرة", + override: false, + }, + { + tag: "datetime", + translation: "لا يتطابق {0} مع تنسيق {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "postcode_iso3166_alpha2", + translation: "لا يتطابق {0} مع تنسيق الرمز البريدي للبلد {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "postcode_iso3166_alpha2_field", + translation: "لا يتطابق {0} مع تنسيق الرمز البريدي للبلد في حقل {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + } + + for _, t := range translations { + + if t.customTransFunc != nil && t.customRegisFunc != nil { + err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc) + } else if t.customTransFunc != nil && t.customRegisFunc == nil { + err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc) + } else if t.customTransFunc == nil && t.customRegisFunc != nil { + err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc) + } else { + err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc) + } + + if err != nil { + return + } + } + + return +} + +func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc { + return func(ut ut.Translator) (err error) { + if err = ut.Add(tag, translation, override); err != nil { + return + } + + return + } +} + +func translateFunc(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t +} diff --git a/translations/ar/ar_test.go b/translations/ar/ar_test.go new file mode 100644 index 00000000..93adfe9e --- /dev/null +++ b/translations/ar/ar_test.go @@ -0,0 +1,695 @@ +package ar + +import ( + "testing" + "time" + + . "github.com/go-playground/assert/v2" + english "github.com/go-playground/locales/en" + ut "github.com/go-playground/universal-translator" + "github.com/go-playground/validator/v10" +) + +func TestTranslations(t *testing.T) { + eng := english.New() + uni := ut.New(eng, eng) + trans, _ := uni.GetTranslator("en") + + validate := validator.New() + + err := RegisterDefaultTranslations(validate, trans) + Equal(t, err, nil) + + type Inner struct { + EqCSFieldString string + NeCSFieldString string + GtCSFieldString string + GteCSFieldString string + LtCSFieldString string + LteCSFieldString string + } + + type Test struct { + Inner Inner + RequiredString string `validate:"required"` + RequiredNumber int `validate:"required"` + RequiredMultiple []string `validate:"required"` + LenString string `validate:"len=1"` + LenNumber float64 `validate:"len=1113.00"` + LenMultiple []string `validate:"len=7"` + MinString string `validate:"min=1"` + MinNumber float64 `validate:"min=1113.00"` + MinMultiple []string `validate:"min=7"` + MaxString string `validate:"max=3"` + MaxNumber float64 `validate:"max=1113.00"` + MaxMultiple []string `validate:"max=7"` + EqString string `validate:"eq=3"` + EqNumber float64 `validate:"eq=2.33"` + EqMultiple []string `validate:"eq=7"` + NeString string `validate:"ne="` + NeNumber float64 `validate:"ne=0.00"` + NeMultiple []string `validate:"ne=0"` + LtString string `validate:"lt=3"` + LtNumber float64 `validate:"lt=5.56"` + LtMultiple []string `validate:"lt=2"` + LtTime time.Time `validate:"lt"` + LteString string `validate:"lte=3"` + LteNumber float64 `validate:"lte=5.56"` + LteMultiple []string `validate:"lte=2"` + LteTime time.Time `validate:"lte"` + GtString string `validate:"gt=3"` + GtNumber float64 `validate:"gt=5.56"` + GtMultiple []string `validate:"gt=2"` + GtTime time.Time `validate:"gt"` + GteString string `validate:"gte=3"` + GteNumber float64 `validate:"gte=5.56"` + GteMultiple []string `validate:"gte=2"` + GteTime time.Time `validate:"gte"` + EqFieldString string `validate:"eqfield=MaxString"` + EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"` + NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"` + GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"` + GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"` + LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"` + LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"` + NeFieldString string `validate:"nefield=EqFieldString"` + GtFieldString string `validate:"gtfield=MaxString"` + GteFieldString string `validate:"gtefield=MaxString"` + LtFieldString string `validate:"ltfield=MaxString"` + LteFieldString string `validate:"ltefield=MaxString"` + AlphaString string `validate:"alpha"` + AlphanumString string `validate:"alphanum"` + NumericString string `validate:"numeric"` + NumberString string `validate:"number"` + HexadecimalString string `validate:"hexadecimal"` + HexColorString string `validate:"hexcolor"` + RGBColorString string `validate:"rgb"` + RGBAColorString string `validate:"rgba"` + HSLColorString string `validate:"hsl"` + HSLAColorString string `validate:"hsla"` + Email string `validate:"email"` + URL string `validate:"url"` + URI string `validate:"uri"` + Base64 string `validate:"base64"` + Contains string `validate:"contains=purpose"` + ContainsAny string `validate:"containsany=!@#$"` + Excludes string `validate:"excludes=text"` + ExcludesAll string `validate:"excludesall=!@#$"` + ExcludesRune string `validate:"excludesrune=☻"` + ISBN string `validate:"isbn"` + ISBN10 string `validate:"isbn10"` + ISBN13 string `validate:"isbn13"` + UUID string `validate:"uuid"` + UUID3 string `validate:"uuid3"` + UUID4 string `validate:"uuid4"` + UUID5 string `validate:"uuid5"` + 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"` + JSONString string `validate:"json"` + JWTString string `validate:"jwt"` + LowercaseString string `validate:"lowercase"` + UppercaseString string `validate:"uppercase"` + 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" + + 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 لون صالح", + }, + { + ns: "Test.MAC", + expected: "يجب أن يحتوي MAC على عنوان MAC صالح", + }, + { + ns: "Test.IPAddr", + expected: "يجب أن يكون IPAddr عنوان IP قابل للحل", + }, + { + ns: "Test.IPAddrv4", + expected: "يجب أن يكون IPAddrv4 عنوان IP قابل للحل", + }, + { + ns: "Test.IPAddrv6", + expected: "يجب أن يكون IPAddrv6 عنوان IPv6 قابل للحل", + }, + { + ns: "Test.UDPAddr", + expected: "يجب أن يكون UDPAddr عنوان UDP صالح", + }, + { + ns: "Test.UDPAddrv4", + expected: "يجب أن يكون UDPAddrv4 عنوان IPv4 UDP صالح", + }, + { + ns: "Test.UDPAddrv6", + expected: "يجب أن يكون UDPAddrv6 عنوان IPv6 UDP صالح", + }, + { + ns: "Test.TCPAddr", + expected: "يجب أن يكون TCPAddr عنوان TCP صالح", + }, + { + ns: "Test.TCPAddrv4", + expected: "يجب أن يكون TCPAddrv4 عنوان IPv4 TCP صالح", + }, + { + ns: "Test.TCPAddrv6", + expected: "يجب أن يكون TCPAddrv6 عنوان IPv6 TCP صالح", + }, + { + ns: "Test.CIDR", + expected: "يجب أن يحتوي CIDR على علامة CIDR صالحة", + }, + { + ns: "Test.CIDRv4", + expected: "يجب أن يحتوي CIDRv4 على علامة CIDR صالحة لعنوان IPv4", + }, + { + ns: "Test.CIDRv6", + expected: "يجب أن يحتوي CIDRv6 على علامة CIDR صالحة لعنوان IPv6", + }, + { + ns: "Test.SSN", + expected: "يجب أن يكون SSN رقم SSN صالح", + }, + { + ns: "Test.IP", + expected: "يجب أن يكون IP عنوان IP صالح", + }, + { + ns: "Test.IPv4", + expected: "يجب أن يكون IPv4 عنوان IPv4 صالح", + }, + { + ns: "Test.IPv6", + expected: "يجب أن يكون IPv6 عنوان IPv6 صالح", + }, + { + ns: "Test.DataURI", + expected: "يجب أن يحتوي DataURI على URI صالح للبيانات", + }, + { + ns: "Test.Latitude", + expected: "يجب أن يحتوي Latitude على إحداثيات خط عرض صالحة", + }, + { + ns: "Test.Longitude", + expected: "يجب أن يحتوي Longitude على إحداثيات خط طول صالحة", + }, + { + ns: "Test.MultiByte", + expected: "يجب أن يحتوي MultiByte على أحرف متعددة البايت", + }, + { + ns: "Test.ASCII", + expected: "يجب أن يحتوي ASCII على أحرف ascii فقط", + }, + { + ns: "Test.PrintableASCII", + expected: "يجب أن يحتوي PrintableASCII على أحرف ascii قابلة للطباعة فقط", + }, + { + ns: "Test.UUID", + expected: "يجب أن يكون UUID UUID صالح", + }, + { + ns: "Test.UUID3", + expected: "يجب أن يكون UUID3 UUID صالح من النسخة 3", + }, + { + ns: "Test.UUID4", + expected: "يجب أن يكون UUID4 UUID صالح من النسخة 4", + }, + { + ns: "Test.UUID5", + expected: "يجب أن يكون UUID5 UUID صالح من النسخة 5", + }, + { + ns: "Test.ULID", + expected: "يجب أن يكون ULID ULID صالح من نسخة", + }, + { + ns: "Test.ISBN", + expected: "يجب أن يكون ISBN رقم ISBN صالح", + }, + { + ns: "Test.ISBN10", + expected: "يجب أن يكون ISBN10 رقم ISBN-10 صالح", + }, + { + ns: "Test.ISBN13", + expected: "يجب أن يكون ISBN13 رقم ISBN-13 صالح", + }, + { + ns: "Test.Excludes", + expected: "لا يمكن أن يحتوي Excludes على النص 'text'", + }, + { + ns: "Test.ExcludesAll", + expected: "لا يمكن أن يحتوي ExcludesAll على أي من الأحرف التالية '!@#$'", + }, + { + ns: "Test.ExcludesRune", + expected: "لا يمكن أن يحتوي ExcludesRune على التالي '☻'", + }, + { + ns: "Test.ContainsAny", + expected: "يجب أن يحتوي ContainsAny على حرف واحد على الأقل من الأحرف التالية '!@#$'", + }, + { + ns: "Test.Contains", + expected: "يجب أن يحتوي Contains على النص 'purpose'", + }, + { + ns: "Test.Base64", + expected: "يجب أن يكون Base64 سلسلة Base64 صالحة", + }, + { + ns: "Test.Email", + expected: "يجب أن يكون Email عنوان بريد إلكتروني صالح", + }, + { + ns: "Test.URL", + expected: "يجب أن يكون URL رابط إنترنت صالح", + }, + { + ns: "Test.URI", + expected: "يجب أن يكون URI URI صالح", + }, + { + ns: "Test.RGBColorString", + expected: "يجب أن يكون RGBColorString لون RGB صالح", + }, + { + ns: "Test.RGBAColorString", + expected: "يجب أن يكون RGBAColorString لون RGBA صالح", + }, + { + ns: "Test.HSLColorString", + expected: "يجب أن يكون HSLColorString لون HSL صالح", + }, + { + ns: "Test.HSLAColorString", + expected: "يجب أن يكون HSLAColorString لون HSLA صالح", + }, + { + ns: "Test.HexadecimalString", + expected: "يجب أن يكون HexadecimalString عددًا سداسيًا عشريًا صالحاً", + }, + { + ns: "Test.HexColorString", + expected: "يجب أن يكون HexColorString لون HEX صالح", + }, + { + ns: "Test.NumberString", + expected: "يجب أن يكون NumberString رقم صالح", + }, + { + ns: "Test.NumericString", + expected: "يجب أن يكون NumericString قيمة رقمية صالحة", + }, + { + ns: "Test.AlphanumString", + expected: "يمكن أن يحتوي AlphanumString على أحرف أبجدية رقمية فقط", + }, + { + ns: "Test.AlphaString", + expected: "يمكن أن يحتوي AlphaString على أحرف أبجدية فقط", + }, + { + ns: "Test.LtFieldString", + expected: "يجب أن يكون LtFieldString أصغر من MaxString", + }, + { + ns: "Test.LteFieldString", + expected: "يجب أن يكون LteFieldString أصغر من أو يساوي MaxString", + }, + { + ns: "Test.GtFieldString", + expected: "يجب أن يكون GtFieldString أكبر من MaxString", + }, + { + ns: "Test.GteFieldString", + expected: "يجب أن يكون GteFieldString أكبر من أو يساوي MaxString", + }, + { + ns: "Test.NeFieldString", + expected: "NeFieldString لا يمكن أن يساوي EqFieldString", + }, + { + ns: "Test.LtCSFieldString", + expected: "يجب أن يكون LtCSFieldString أصغر من Inner.LtCSFieldString", + }, + { + ns: "Test.LteCSFieldString", + expected: "يجب أن يكون LteCSFieldString أصغر من أو يساوي Inner.LteCSFieldString", + }, + { + ns: "Test.GtCSFieldString", + expected: "يجب أن يكون GtCSFieldString أكبر من Inner.GtCSFieldString", + }, + { + ns: "Test.GteCSFieldString", + expected: "يجب أن يكون GteCSFieldString أكبر من أو يساوي Inner.GteCSFieldString", + }, + { + ns: "Test.NeCSFieldString", + expected: "NeCSFieldString لا يمكن أن يساوي Inner.NeCSFieldString", + }, + { + ns: "Test.EqCSFieldString", + expected: "يجب أن يكون EqCSFieldString مساويا ل Inner.EqCSFieldString", + }, + { + ns: "Test.EqFieldString", + expected: "يجب أن يكون EqFieldString مساويا ل MaxString", + }, + { + ns: "Test.GteString", + expected: "يجب أن يكون طول GteString على الأقل 3 أحرف", + }, + { + ns: "Test.GteNumber", + expected: "GteNumber يجب أن يكون 5.56 أو أكبر", + }, + { + ns: "Test.GteMultiple", + expected: "يجب أن يحتوي GteMultiple على 2 عناصر على الأقل", + }, + { + ns: "Test.GteTime", + expected: "يجب أن يكون GteTime أكبر من أو يساوي التاريخ والوقت الحاليين", + }, + { + ns: "Test.GtString", + expected: "يجب أن يكون طول GtString أكبر من 3 أحرف", + }, + { + ns: "Test.GtNumber", + expected: "يجب أن يكون GtNumber أكبر من 5.56", + }, + { + ns: "Test.GtMultiple", + expected: "يجب أن يحتوي GtMultiple على أكثر من 2 عناصر", + }, + { + ns: "Test.GtTime", + expected: "يجب أن يكون GtTime أكبر من التاريخ والوقت الحاليين", + }, + { + ns: "Test.LteString", + expected: "يجب أن يكون طول LteString كحد أقصى 3 أحرف", + }, + { + ns: "Test.LteNumber", + expected: "LteNumber يجب أن يكون 5.56 أو اقل", + }, + { + ns: "Test.LteMultiple", + expected: "يجب أن يحتوي LteMultiple على 2 عناصر كحد أقصى", + }, + { + ns: "Test.LteTime", + expected: "يجب أن يكون LteTime أقل من أو يساوي التاريخ والوقت الحاليين", + }, + { + ns: "Test.LtString", + expected: "يجب أن يكون طول LtString أقل من 3 أحرف", + }, + { + ns: "Test.LtNumber", + expected: "يجب أن يكون LtNumber أقل من 5.56", + }, + { + ns: "Test.LtMultiple", + expected: "يجب أن يحتوي LtMultiple على أقل من 2 عناصر", + }, + { + ns: "Test.LtTime", + expected: "يجب أن يكون LtTime أقل من التاريخ والوقت الحاليين", + }, + { + ns: "Test.NeString", + expected: "NeString يجب ألا يساوي ", + }, + { + ns: "Test.NeNumber", + expected: "NeNumber يجب ألا يساوي 0.00", + }, + { + ns: "Test.NeMultiple", + expected: "NeMultiple يجب ألا يساوي 0", + }, + { + ns: "Test.EqString", + expected: "EqString لا يساوي 3", + }, + { + ns: "Test.EqNumber", + expected: "EqNumber لا يساوي 2.33", + }, + { + ns: "Test.EqMultiple", + expected: "EqMultiple لا يساوي 7", + }, + { + ns: "Test.MaxString", + expected: "يجب أن يكون طول MaxString بحد أقصى 3 أحرف", + }, + { + ns: "Test.MaxNumber", + expected: "MaxNumber يجب أن يكون 1,113.00 أو اقل", + }, + { + ns: "Test.MaxMultiple", + expected: "يجب أن يحتوي MaxMultiple على 7 عناصر كحد أقصى", + }, + { + ns: "Test.MinString", + expected: "MinString يجب أن يكون 1 حرف أو اقل", + }, + { + ns: "Test.MinNumber", + expected: "MinNumber يجب أن يكون 1,113.00 أو اقل", + }, + { + ns: "Test.MinMultiple", + expected: "يجب أن يحتوي MinMultiple على 7 عناصر على الأقل", + }, + { + ns: "Test.LenString", + expected: "يجب أن يكون طول LenString مساويا ل 1 حرف", + }, + { + ns: "Test.LenNumber", + expected: "يجب أن يكون LenNumber مساويا ل 1,113.00", + }, + { + ns: "Test.LenMultiple", + expected: "يجب أن يحتوي LenMultiple على 7 عناصر", + }, + { + ns: "Test.RequiredString", + expected: "حقل RequiredString مطلوب", + }, + { + ns: "Test.RequiredNumber", + expected: "حقل RequiredNumber مطلوب", + }, + { + ns: "Test.RequiredMultiple", + expected: "حقل RequiredMultiple مطلوب", + }, + { + ns: "Test.StrPtrMinLen", + expected: "StrPtrMinLen يجب أن يكون 10 أحرف أو اقل", + }, + { + ns: "Test.StrPtrMaxLen", + expected: "يجب أن يكون طول StrPtrMaxLen بحد أقصى 1 حرف", + }, + { + ns: "Test.StrPtrLen", + expected: "يجب أن يكون طول StrPtrLen مساويا ل 2 أحرف", + }, + { + ns: "Test.StrPtrLt", + expected: "يجب أن يكون طول StrPtrLt أقل من 1 حرف", + }, + { + ns: "Test.StrPtrLte", + expected: "يجب أن يكون طول StrPtrLte كحد أقصى 1 حرف", + }, + { + ns: "Test.StrPtrGt", + expected: "يجب أن يكون طول StrPtrGt أكبر من 10 أحرف", + }, + { + ns: "Test.StrPtrGte", + expected: "يجب أن يكون طول StrPtrGte على الأقل 10 أحرف", + }, + { + ns: "Test.OneOfString", + expected: "يجب أن يكون OneOfString واحدا من [red green]", + }, + { + ns: "Test.OneOfInt", + expected: "يجب أن يكون OneOfInt واحدا من [5 63]", + }, + { + ns: "Test.UniqueSlice", + expected: "يجب أن يحتوي UniqueSlice على قيم فريدة", + }, + { + ns: "Test.UniqueArray", + expected: "يجب أن يحتوي UniqueArray على قيم فريدة", + }, + { + ns: "Test.UniqueMap", + expected: "يجب أن يحتوي UniqueMap على قيم فريدة", + }, + { + ns: "Test.JSONString", + expected: "يجب أن يكون JSONString نص json صالح", + }, + { + ns: "Test.JWTString", + expected: "يجب أن يكون JWTString نص jwt صالح", + }, + { + ns: "Test.LowercaseString", + expected: "يجب أن يكون LowercaseString نص حروف صغيرة", + }, + { + ns: "Test.UppercaseString", + expected: "يجب أن يكون UppercaseString نص حروف كبيرة", + }, + { + ns: "Test.Datetime", + expected: "لا يتطابق Datetime مع تنسيق 2006-01-02", + }, + { + ns: "Test.PostCode", + expected: "لا يتطابق PostCode مع تنسيق الرمز البريدي للبلد SG", + }, + { + ns: "Test.PostCodeByField", + expected: "لا يتطابق PostCodeByField مع تنسيق الرمز البريدي للبلد في حقل 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)) + } +} From 7fa836dc0ac526c954a47710e3dc34a557ea41a0 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Sun, 1 May 2022 23:11:05 +0700 Subject: [PATCH 15/18] Add Vietnamese translations (#820) --- baked_in.go | 57 +- translations/vi/vi.go | 1384 ++++++++++++++++++++++++++++++++++++ translations/vi/vi_test.go | 690 ++++++++++++++++++ 3 files changed, 2079 insertions(+), 52 deletions(-) create mode 100644 translations/vi/vi.go create mode 100644 translations/vi/vi_test.go diff --git a/baked_in.go b/baked_in.go index bc638f2d..22344797 100644 --- a/baked_in.go +++ b/baked_in.go @@ -207,8 +207,10 @@ var ( } ) -var oneofValsCache = map[string][]string{} -var oneofValsCacheRWLock = sync.RWMutex{} +var ( + oneofValsCache = map[string][]string{} + oneofValsCacheRWLock = sync.RWMutex{} +) func parseOneOfParam2(s string) []string { oneofValsCacheRWLock.RLock() @@ -264,7 +266,6 @@ func isOneOf(fl FieldLevel) bool { // isUnique is the validation function for validating if each array|slice|map value is unique func isUnique(fl FieldLevel) bool { - field := fl.Field() param := fl.Param() v := reflect.ValueOf(struct{}{}) @@ -314,7 +315,6 @@ func isUnique(fl FieldLevel) bool { // isMAC is the validation function for validating if the field's value is a valid MAC address. func isMAC(fl FieldLevel) bool { - _, err := net.ParseMAC(fl.Field().String()) return err == nil @@ -322,7 +322,6 @@ func isMAC(fl FieldLevel) bool { // isCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address. func isCIDRv4(fl FieldLevel) bool { - ip, _, err := net.ParseCIDR(fl.Field().String()) return err == nil && ip.To4() != nil @@ -330,7 +329,6 @@ func isCIDRv4(fl FieldLevel) bool { // isCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address. func isCIDRv6(fl FieldLevel) bool { - ip, _, err := net.ParseCIDR(fl.Field().String()) return err == nil && ip.To4() == nil @@ -338,7 +336,6 @@ func isCIDRv6(fl FieldLevel) bool { // isCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address. func isCIDR(fl FieldLevel) bool { - _, _, err := net.ParseCIDR(fl.Field().String()) return err == nil @@ -346,7 +343,6 @@ func isCIDR(fl FieldLevel) bool { // isIPv4 is the validation function for validating if a value is a valid v4 IP address. func isIPv4(fl FieldLevel) bool { - ip := net.ParseIP(fl.Field().String()) return ip != nil && ip.To4() != nil @@ -354,7 +350,6 @@ func isIPv4(fl FieldLevel) bool { // isIPv6 is the validation function for validating if the field's value is a valid v6 IP address. func isIPv6(fl FieldLevel) bool { - ip := net.ParseIP(fl.Field().String()) return ip != nil && ip.To4() == nil @@ -362,7 +357,6 @@ func isIPv6(fl FieldLevel) bool { // isIP is the validation function for validating if the field's value is a valid v4 or v6 IP address. func isIP(fl FieldLevel) bool { - ip := net.ParseIP(fl.Field().String()) return ip != nil @@ -370,7 +364,6 @@ func isIP(fl FieldLevel) bool { // isSSN is the validation function for validating if the field's value is a valid SSN. func isSSN(fl FieldLevel) bool { - field := fl.Field() if field.Len() != 11 { @@ -428,7 +421,6 @@ func isLatitude(fl FieldLevel) bool { // isDataURI is the validation function for validating if the field's value is a valid data URI. func isDataURI(fl FieldLevel) bool { - uri := strings.SplitN(fl.Field().String(), ",", 2) if len(uri) != 2 { @@ -444,7 +436,6 @@ func isDataURI(fl FieldLevel) bool { // hasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character. func hasMultiByteCharacter(fl FieldLevel) bool { - field := fl.Field() if field.Len() == 0 { @@ -516,7 +507,6 @@ func isISBN(fl FieldLevel) bool { // isISBN13 is the validation function for validating if the field's value is a valid v13 ISBN. func isISBN13(fl FieldLevel) bool { - s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 4), " ", "", 4) if !iSBN13Regex.MatchString(s) { @@ -537,7 +527,6 @@ func isISBN13(fl FieldLevel) bool { // isISBN10 is the validation function for validating if the field's value is a valid v10 ISBN. func isISBN10(fl FieldLevel) bool { - s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 3), " ", "", 3) if !iSBN10Regex.MatchString(s) { @@ -725,7 +714,6 @@ func excludes(fl FieldLevel) bool { // containsRune is the validation function for validating that the field's value contains the rune specified within the param. func containsRune(fl FieldLevel) bool { - r, _ := utf8.DecodeRuneInString(fl.Param()) return strings.ContainsRune(fl.Field().String(), r) @@ -788,7 +776,6 @@ func fieldExcludes(fl FieldLevel) bool { // isNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value. func isNeField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -844,7 +831,6 @@ func isNe(fl FieldLevel) bool { // isLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value. func isLteCrossStructField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -892,7 +878,6 @@ func isLteCrossStructField(fl FieldLevel) bool { // isLtCrossStructField is the validation function for validating if the current field's value is less than the field, within a separate struct, specified by the param's value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func isLtCrossStructField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -939,7 +924,6 @@ func isLtCrossStructField(fl FieldLevel) bool { // isGteCrossStructField is the validation function for validating if the current field's value is greater than or equal to the field, within a separate struct, specified by the param's value. func isGteCrossStructField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -986,7 +970,6 @@ func isGteCrossStructField(fl FieldLevel) bool { // isGtCrossStructField is the validation function for validating if the current field's value is greater than the field, within a separate struct, specified by the param's value. func isGtCrossStructField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -1033,7 +1016,6 @@ func isGtCrossStructField(fl FieldLevel) bool { // isNeCrossStructField is the validation function for validating that the current field's value is not equal to the field, within a separate struct, specified by the param's value. func isNeCrossStructField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -1083,7 +1065,6 @@ func isNeCrossStructField(fl FieldLevel) bool { // isEqCrossStructField is the validation function for validating that the current field's value is equal to the field, within a separate struct, specified by the param's value. func isEqCrossStructField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -1133,7 +1114,6 @@ func isEqCrossStructField(fl FieldLevel) bool { // isEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value. func isEqField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -1183,7 +1163,6 @@ func isEqField(fl FieldLevel) bool { // isEq is the validation function for validating if the current field's value is equal to the param's value. func isEq(fl FieldLevel) bool { - field := fl.Field() param := fl.Param() @@ -1235,7 +1214,7 @@ func isPostcodeByIso3166Alpha2(fl FieldLevel) bool { return reg.MatchString(field.String()) } -// isPostcodeByIso3166Alpha2 validates by field which represents for a value of country code in iso 3166 alpha 2 +// isPostcodeByIso3166Alpha2Field validates by field which represents for a value of country code in iso 3166 alpha 2 // example: `postcode_iso3166_alpha2_field=CountryCode` func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool { field := fl.Field() @@ -1274,11 +1253,9 @@ func isBase64URL(fl FieldLevel) bool { // isURI is the validation function for validating if the current field's value is a valid URI. func isURI(fl FieldLevel) bool { - field := fl.Field() switch field.Kind() { - case reflect.String: s := field.String() @@ -1303,11 +1280,9 @@ func isURI(fl FieldLevel) bool { // isURL is the validation function for validating if the current field's value is a valid URL. func isURL(fl FieldLevel) bool { - field := fl.Field() switch field.Kind() { - case reflect.String: var i int @@ -1340,7 +1315,6 @@ func isUrnRFC2141(fl FieldLevel) bool { field := fl.Field() switch field.Kind() { - case reflect.String: str := field.String() @@ -1682,7 +1656,6 @@ func requiredWithoutAll(fl FieldLevel) bool { // isGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value. func isGteField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -1729,7 +1702,6 @@ func isGteField(fl FieldLevel) bool { // isGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value. func isGtField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -1776,7 +1748,6 @@ func isGtField(fl FieldLevel) bool { // isGte is the validation function for validating if the current field's value is greater than or equal to the param's value. func isGte(fl FieldLevel) bool { - field := fl.Field() param := fl.Param() @@ -1823,7 +1794,6 @@ func isGte(fl FieldLevel) bool { // isGt is the validation function for validating if the current field's value is greater than the param's value. func isGt(fl FieldLevel) bool { - field := fl.Field() param := fl.Param() @@ -1866,7 +1836,6 @@ func isGt(fl FieldLevel) bool { // hasLengthOf is the validation function for validating if the current field's value is equal to the param's value. func hasLengthOf(fl FieldLevel) bool { - field := fl.Field() param := fl.Param() @@ -1908,7 +1877,6 @@ func hasMinOf(fl FieldLevel) bool { // isLteField is the validation function for validating if the current field's value is less than or equal to the field specified by the param's value. func isLteField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -1955,7 +1923,6 @@ func isLteField(fl FieldLevel) bool { // isLtField is the validation function for validating if the current field's value is less than the field specified by the param's value. func isLtField(fl FieldLevel) bool { - field := fl.Field() kind := field.Kind() @@ -2002,7 +1969,6 @@ func isLtField(fl FieldLevel) bool { // isLte is the validation function for validating if the current field's value is less than or equal to the param's value. func isLte(fl FieldLevel) bool { - field := fl.Field() param := fl.Param() @@ -2049,7 +2015,6 @@ func isLte(fl FieldLevel) bool { // isLt is the validation function for validating if the current field's value is less than the param's value. func isLt(fl FieldLevel) bool { - field := fl.Field() param := fl.Param() @@ -2098,7 +2063,6 @@ func hasMaxOf(fl FieldLevel) bool { // isTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address. func isTCP4AddrResolvable(fl FieldLevel) bool { - if !isIP4Addr(fl) { return false } @@ -2109,7 +2073,6 @@ func isTCP4AddrResolvable(fl FieldLevel) bool { // isTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address. func isTCP6AddrResolvable(fl FieldLevel) bool { - if !isIP6Addr(fl) { return false } @@ -2121,7 +2084,6 @@ func isTCP6AddrResolvable(fl FieldLevel) bool { // isTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address. func isTCPAddrResolvable(fl FieldLevel) bool { - if !isIP4Addr(fl) && !isIP6Addr(fl) { return false } @@ -2133,7 +2095,6 @@ func isTCPAddrResolvable(fl FieldLevel) bool { // isUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address. func isUDP4AddrResolvable(fl FieldLevel) bool { - if !isIP4Addr(fl) { return false } @@ -2145,7 +2106,6 @@ func isUDP4AddrResolvable(fl FieldLevel) bool { // isUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address. func isUDP6AddrResolvable(fl FieldLevel) bool { - if !isIP6Addr(fl) { return false } @@ -2157,7 +2117,6 @@ func isUDP6AddrResolvable(fl FieldLevel) bool { // isUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address. func isUDPAddrResolvable(fl FieldLevel) bool { - if !isIP4Addr(fl) && !isIP6Addr(fl) { return false } @@ -2169,7 +2128,6 @@ func isUDPAddrResolvable(fl FieldLevel) bool { // isIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address. func isIP4AddrResolvable(fl FieldLevel) bool { - if !isIPv4(fl) { return false } @@ -2181,7 +2139,6 @@ func isIP4AddrResolvable(fl FieldLevel) bool { // isIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address. func isIP6AddrResolvable(fl FieldLevel) bool { - if !isIPv6(fl) { return false } @@ -2193,7 +2150,6 @@ func isIP6AddrResolvable(fl FieldLevel) bool { // isIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address. func isIPAddrResolvable(fl FieldLevel) bool { - if !isIP(fl) { return false } @@ -2205,14 +2161,12 @@ func isIPAddrResolvable(fl FieldLevel) bool { // isUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address. func isUnixAddrResolvable(fl FieldLevel) bool { - _, err := net.ResolveUnixAddr("unix", fl.Field().String()) return err == nil } func isIP4Addr(fl FieldLevel) bool { - val := fl.Field().String() if idx := strings.LastIndex(val, ":"); idx != -1 { @@ -2225,7 +2179,6 @@ func isIP4Addr(fl FieldLevel) bool { } func isIP6Addr(fl FieldLevel) bool { - val := fl.Field().String() if idx := strings.LastIndex(val, ":"); idx != -1 { diff --git a/translations/vi/vi.go b/translations/vi/vi.go new file mode 100644 index 00000000..009ba4cc --- /dev/null +++ b/translations/vi/vi.go @@ -0,0 +1,1384 @@ +package vi + +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} không được bỏ trống", + override: false, + }, + { + tag: "len", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("len-string", "{0} phải có độ dài là {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("len-string-character", "{0} ký tự", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("len-string-character", "{0} ký tự", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("len-number", "{0} phải bằng {1}", false); err != nil { + return + } + + if err = ut.Add("len-items", "{0} phải chứa {1}", false); err != nil { + return + } + // if err = ut.AddCardinal("len-items-item", "{0} phần tử", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("len-items-item", "{0} phần tử", 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("cảnh báo: lỗi chuyển ngữ 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} phải chứa ít nhất {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("min-string-character", "{0} ký tự", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("min-string-character", "{0} ký tự", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("min-number", "{0} phải bằng {1} hoặc lớn hơn", false); err != nil { + return + } + + if err = ut.Add("min-items", "{0} phải chứa ít nhất {1}", false); err != nil { + return + } + // if err = ut.AddCardinal("min-items-item", "{0} phần tử", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("min-items-item", "{0} phần tử", 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("cảnh báo: lỗi chuyển ngữ 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} chỉ được chứa tối đa {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("max-string-character", "{0} ký tự", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("max-string-character", "{0} ký tự", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("max-number", "{0} phải là {1} hoặc nhỏ hơn", false); err != nil { + return + } + + if err = ut.Add("max-items", "{0} chỉ được chứa tối đa {1}", false); err != nil { + return + } + // if err = ut.AddCardinal("max-items-item", "{0} phần tử", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("max-items-item", "{0} phần tử", 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("cảnh báo: lỗi chuyển ngữ FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "eq", + translation: "{0} không bằng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + fmt.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ne", + translation: "{0} không được bằng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + fmt.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "lt", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("lt-string", "{0} phải có độ dài nhỏ hơn {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("lt-string-character", "{0} ký tự", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("lt-string-character", "{0} ký tự", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lt-number", "{0} phải nhỏ hơn {1}", false); err != nil { + return + } + + if err = ut.Add("lt-items", "{0} chỉ được chứa ít hơn {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("lt-items-item", "{0} phần tử", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("lt-items-item", "{0} phần tử", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lt-datetime", "{0} phải nhỏ hơn Ngày & Giờ hiện tại", 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' không thể dùng trên kiểu struct", 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("cảnh báo: lỗi chuyển ngữ 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} chỉ được có độ dài tối đa là {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("lte-string-character", "{0} ký tự", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("lte-string-character", "{0} ký tự", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lte-number", "{0} phải là {1} hoặc nhỏ hơn", false); err != nil { + return + } + + if err = ut.Add("lte-items", "{0} chỉ được chứa nhiều nhất {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("lte-items-item", "{0} phần tử", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("lte-items-item", "{0} phần tử", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lte-datetime", "{0} chỉ được nhỏ hơn hoặc bằng Ngày & Giờ hiện tại", 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' không thể dùng trên kiểu struct", 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("cảnh báo: lỗi chuyển ngữ 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} phải có độ dài lớn hơn {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("gt-string-character", "{0} ký tự", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("gt-string-character", "{0} ký tự", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gt-number", "{0} phải lớn hơn {1}", false); err != nil { + return + } + + if err = ut.Add("gt-items", "{0} phải chứa nhiều hơn {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("gt-items-item", "{0} phần tử", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("gt-items-item", "{0} phần tử", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gt-datetime", "{0} phải lớn hơn Ngày & Giờ hiện tại", 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' không thể dùng trên kiểu struct", 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("cảnh báo: lỗi chuyển ngữ 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} phải có độ dài ít nhất {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("gte-string-character", "{0} ký tự", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("gte-string-character", "{0} ký tự", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gte-number", "{0} phải là {1} hoặc lớn hơn", false); err != nil { + return + } + + if err = ut.Add("gte-items", "{0} phải chứa ít nhất {1}", false); err != nil { + return + } + + // if err = ut.AddCardinal("gte-items-item", "{0} phần tử", locales.PluralRuleOne, false); err != nil { + // return + // } + + if err = ut.AddCardinal("gte-items-item", "{0} phần tử", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gte-datetime", "{0} phải lớn hơn hoặc bằng Ngày & Giờ hiện tại", 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' không thể dùng trên kiểu struct", 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("cảnh báo: lỗi chuyển ngữ FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "eqfield", + translation: "{0} phải bằng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "eqcsfield", + translation: "{0} phải bằng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "necsfield", + translation: "{0} không được phép bằng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtcsfield", + translation: "{0} phải lớn hơn {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtecsfield", + translation: "{0} phải lớn hơn hoặc bằng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltcsfield", + translation: "{0} chỉ được nhỏ hơn {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltecsfield", + translation: "{0} chỉ được nhỏ hơn hoặc bằng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "nefield", + translation: "{0} không được phép bằng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtfield", + translation: "{0} phải lớn hơn {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtefield", + translation: "{0} phải lớn hơn hoặc bằng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltfield", + translation: "{0} chỉ được nhỏ hơn {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltefield", + translation: "{0} chỉ được nhỏ hơn hoặc bằng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "alpha", + translation: "{0} chỉ được chứa ký tự dạng alphabetic", + override: false, + }, + { + tag: "alphanum", + translation: "{0} chỉ được chứa ký tự dạng alphanumeric", + override: false, + }, + { + tag: "numeric", + translation: "{0} chỉ được chứa giá trị số hoặc số dưới dạng chữ", + override: false, + }, + { + tag: "number", + translation: "{0} chỉ được chứa giá trị số", + override: false, + }, + { + tag: "hexadecimal", + translation: "{0} phải là giá trị hexadecimal", + override: false, + }, + { + tag: "hexcolor", + translation: "{0} phải là giá trị HEX color", + override: false, + }, + { + tag: "rgb", + translation: "{0} phải là giá trị RGB color", + override: false, + }, + { + tag: "rgba", + translation: "{0} phải là giá trị RGBA color", + override: false, + }, + { + tag: "hsl", + translation: "{0} phải là giá trị HSL color", + override: false, + }, + { + tag: "hsla", + translation: "{0} phải là giá trị HSLA color", + override: false, + }, + { + tag: "e164", + translation: "{0} phải là giá trị số điện thoại theo định dạng E.164", + override: false, + }, + { + tag: "email", + translation: "{0} phải là giá trị email address", + override: false, + }, + { + tag: "url", + translation: "{0} phải là giá trị URL", + override: false, + }, + { + tag: "uri", + translation: "{0} phải là giá trị URI", + override: false, + }, + { + tag: "base64", + translation: "{0} phải là giá trị chuỗi Base64", + override: false, + }, + { + tag: "contains", + translation: "{0} phải chứa chuỗi '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "containsany", + translation: "{0} phải chứa ít nhất 1 trong cách ký tự sau '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "excludes", + translation: "{0} không được chứa chuỗi '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "excludesall", + translation: "{0} không được chứa bất kỳ ký tự nào trong nhóm ký tự '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "excludesrune", + translation: "{0} không được chứa '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "isbn", + translation: "{0} phải là số ISBN", + override: false, + }, + { + tag: "isbn10", + translation: "{0} phải là số ISBN-10", + override: false, + }, + { + tag: "isbn13", + translation: "{0} phải là số ISBN-13", + override: false, + }, + { + tag: "uuid", + translation: "{0} phải là giá trị UUID", + override: false, + }, + { + tag: "uuid3", + translation: "{0} phải là giá trị UUID phiên bản 3", + override: false, + }, + { + tag: "uuid4", + translation: "{0} phải là giá trị UUID phiên bản 4", + override: false, + }, + { + tag: "uuid5", + translation: "{0} phải là giá trị UUID phiên bản 5", + override: false, + }, + { + tag: "ascii", + translation: "{0} chỉ được chứa ký tự ASCII", + override: false, + }, + { + tag: "printascii", + translation: "{0} chỉ được chứa ký tự ASCII có thể in ấn", + override: false, + }, + { + tag: "multibyte", + translation: "{0} chỉ được chứa ký tự multibyte", + override: false, + }, + { + tag: "datauri", + translation: "{0} chỉ được chứa Data URI", + override: false, + }, + { + tag: "latitude", + translation: "{0} chỉ được chứa latitude (vỹ độ)", + override: false, + }, + { + tag: "longitude", + translation: "{0} chỉ được chứa longitude (kinh độ)", + override: false, + }, + { + tag: "ssn", + translation: "{0} phải là SSN number", + override: false, + }, + { + tag: "ipv4", + translation: "{0} phải là địa chỉ IPv4", + override: false, + }, + { + tag: "ipv6", + translation: "{0} phải là địa chỉ IPv6", + override: false, + }, + { + tag: "ip", + translation: "{0} phải là địa chỉ IP", + override: false, + }, + { + tag: "cidr", + translation: "{0} chỉ được chứa CIDR notation", + override: false, + }, + { + tag: "cidrv4", + translation: "{0} chỉ được chứa CIDR notation của một địa chỉ IPv4", + override: false, + }, + { + tag: "cidrv6", + translation: "{0} chỉ được chứa CIDR notation của một địa chỉ IPv6", + override: false, + }, + { + tag: "tcp_addr", + translation: "{0} phải là địa chỉ TCP", + override: false, + }, + { + tag: "tcp4_addr", + translation: "{0} phải là địa chỉ IPv4 TCP", + override: false, + }, + { + tag: "tcp6_addr", + translation: "{0} phải là địa chỉ IPv6 TCP", + override: false, + }, + { + tag: "udp_addr", + translation: "{0} phải là địa chỉ UDP", + override: false, + }, + { + tag: "udp4_addr", + translation: "{0} phải là địa chỉ IPv4 UDP", + override: false, + }, + { + tag: "udp6_addr", + translation: "{0} phải là địa chỉ IPv6 UDP", + override: false, + }, + { + tag: "ip_addr", + translation: "{0} phải là địa chỉ IP có thể phân giải", + override: false, + }, + { + tag: "ip4_addr", + translation: "{0} phải là địa chỉ IPv4 có thể phân giải", + override: false, + }, + { + tag: "ip6_addr", + translation: "{0} phải là địa chỉ IPv6 có thể phân giải", + override: false, + }, + { + tag: "unix_addr", + translation: "{0} phải là địa chỉ UNIX có thể phân giải", + override: false, + }, + { + tag: "mac", + translation: "{0} chỉ được chứa địa chỉ MAC", + override: false, + }, + { + tag: "unique", + translation: "{0} chỉ được chứa những giá trị không trùng lặp", + override: false, + }, + { + tag: "iscolor", + translation: "{0} phải là màu sắc hợp lệ", + override: false, + }, + { + tag: "oneof", + translation: "{0} phải là trong những giá trị [{1}]", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + s, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + return s + }, + }, + { + tag: "json", + translation: "{0} phải là một chuỗi json hợp lệ", + override: false, + }, + { + tag: "jwt", + translation: "{0} phải là một chuỗi jwt hợp lệ", + override: false, + }, + { + tag: "lowercase", + translation: "{0} phải được viết thường", + override: false, + }, + { + tag: "uppercase", + translation: "{0} phải được viết hoa", + override: false, + }, + { + tag: "datetime", + translation: "{0} không trùng định dạng ngày tháng {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "postcode_iso3166_alpha2", + translation: "{0} sai định dạng postcode của quốc gia {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "postcode_iso3166_alpha2_field", + translation: "{0} sai định dạng postcode của quốc gia tương ứng thuộc trường {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + } + + for _, t := range translations { + + if t.customTransFunc != nil && t.customRegisFunc != nil { + err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc) + } else if t.customTransFunc != nil && t.customRegisFunc == nil { + err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc) + } else if t.customTransFunc == nil && t.customRegisFunc != nil { + err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc) + } else { + err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc) + } + + if err != nil { + return + } + } + + return +} + +func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc { + return func(ut ut.Translator) (err error) { + if err = ut.Add(tag, translation, override); err != nil { + return + } + + return + } +} + +func translateFunc(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field()) + if err != nil { + log.Printf("cảnh báo: lỗi chuyển ngữ FieldError: %#v", fe) + return fe.(error).Error() + } + + return t +} diff --git a/translations/vi/vi_test.go b/translations/vi/vi_test.go new file mode 100644 index 00000000..6e128664 --- /dev/null +++ b/translations/vi/vi_test.go @@ -0,0 +1,690 @@ +package vi + +import ( + "testing" + "time" + + . "github.com/go-playground/assert/v2" + vietnamese "github.com/go-playground/locales/vi" + ut "github.com/go-playground/universal-translator" + "github.com/go-playground/validator/v10" +) + +func TestTranslations(t *testing.T) { + vie := vietnamese.New() + uni := ut.New(vie, vie) + trans, _ := uni.GetTranslator("vi") + + validate := validator.New() + + err := RegisterDefaultTranslations(validate, trans) + Equal(t, err, nil) + + type Inner struct { + EqCSFieldString string + NeCSFieldString string + GtCSFieldString string + GteCSFieldString string + LtCSFieldString string + LteCSFieldString string + } + + type Test struct { + Inner Inner + RequiredString string `validate:"required"` + RequiredNumber int `validate:"required"` + RequiredMultiple []string `validate:"required"` + LenString string `validate:"len=1"` + LenNumber float64 `validate:"len=1113.00"` + LenMultiple []string `validate:"len=7"` + MinString string `validate:"min=1"` + MinNumber float64 `validate:"min=1113.00"` + MinMultiple []string `validate:"min=7"` + MaxString string `validate:"max=3"` + MaxNumber float64 `validate:"max=1113.00"` + MaxMultiple []string `validate:"max=7"` + EqString string `validate:"eq=3"` + EqNumber float64 `validate:"eq=2.33"` + EqMultiple []string `validate:"eq=7"` + NeString string `validate:"ne="` + NeNumber float64 `validate:"ne=0.00"` + NeMultiple []string `validate:"ne=0"` + LtString string `validate:"lt=3"` + LtNumber float64 `validate:"lt=5.56"` + LtMultiple []string `validate:"lt=2"` + LtTime time.Time `validate:"lt"` + LteString string `validate:"lte=3"` + LteNumber float64 `validate:"lte=5.56"` + LteMultiple []string `validate:"lte=2"` + LteTime time.Time `validate:"lte"` + GtString string `validate:"gt=3"` + GtNumber float64 `validate:"gt=5.56"` + GtMultiple []string `validate:"gt=2"` + GtTime time.Time `validate:"gt"` + GteString string `validate:"gte=3"` + GteNumber float64 `validate:"gte=5.56"` + GteMultiple []string `validate:"gte=2"` + GteTime time.Time `validate:"gte"` + EqFieldString string `validate:"eqfield=MaxString"` + EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"` + NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"` + GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"` + GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"` + LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"` + LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"` + NeFieldString string `validate:"nefield=EqFieldString"` + GtFieldString string `validate:"gtfield=MaxString"` + GteFieldString string `validate:"gtefield=MaxString"` + LtFieldString string `validate:"ltfield=MaxString"` + LteFieldString string `validate:"ltefield=MaxString"` + AlphaString string `validate:"alpha"` + AlphanumString string `validate:"alphanum"` + NumericString string `validate:"numeric"` + NumberString string `validate:"number"` + HexadecimalString string `validate:"hexadecimal"` + HexColorString string `validate:"hexcolor"` + RGBColorString string `validate:"rgb"` + RGBAColorString string `validate:"rgba"` + HSLColorString string `validate:"hsl"` + HSLAColorString string `validate:"hsla"` + Email string `validate:"email"` + URL string `validate:"url"` + URI string `validate:"uri"` + Base64 string `validate:"base64"` + Contains string `validate:"contains=purpose"` + ContainsAny string `validate:"containsany=!@#$"` + Excludes string `validate:"excludes=text"` + ExcludesAll string `validate:"excludesall=!@#$"` + ExcludesRune string `validate:"excludesrune=☻"` + ISBN string `validate:"isbn"` + ISBN10 string `validate:"isbn10"` + ISBN13 string `validate:"isbn13"` + UUID string `validate:"uuid"` + UUID3 string `validate:"uuid3"` + UUID4 string `validate:"uuid4"` + UUID5 string `validate:"uuid5"` + ASCII string `validate:"ascii"` + PrintableASCII string `validate:"printascii"` + MultiByte string `validate:"multibyte"` + DataURI string `validate:"datauri"` + Latitude string `validate:"latitude"` + Longitude string `validate:"longitude"` + SSN string `validate:"ssn"` + IP string `validate:"ip"` + IPv4 string `validate:"ipv4"` + IPv6 string `validate:"ipv6"` + CIDR string `validate:"cidr"` + CIDRv4 string `validate:"cidrv4"` + CIDRv6 string `validate:"cidrv6"` + TCPAddr string `validate:"tcp_addr"` + TCPAddrv4 string `validate:"tcp4_addr"` + TCPAddrv6 string `validate:"tcp6_addr"` + UDPAddr string `validate:"udp_addr"` + UDPAddrv4 string `validate:"udp4_addr"` + UDPAddrv6 string `validate:"udp6_addr"` + IPAddr string `validate:"ip_addr"` + IPAddrv4 string `validate:"ip4_addr"` + IPAddrv6 string `validate:"ip6_addr"` + UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future + MAC string `validate:"mac"` + IsColor string `validate:"iscolor"` + StrPtrMinLen *string `validate:"min=10"` + StrPtrMaxLen *string `validate:"max=1"` + StrPtrLen *string `validate:"len=2"` + StrPtrLt *string `validate:"lt=1"` + StrPtrLte *string `validate:"lte=1"` + StrPtrGt *string `validate:"gt=10"` + StrPtrGte *string `validate:"gte=10"` + OneOfString string `validate:"oneof=red green"` + OneOfInt int `validate:"oneof=5 63"` + UniqueSlice []string `validate:"unique"` + UniqueArray [3]string `validate:"unique"` + UniqueMap map[string]string `validate:"unique"` + JSONString string `validate:"json"` + JWTString string `validate:"jwt"` + LowercaseString string `validate:"lowercase"` + UppercaseString string `validate:"uppercase"` + 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" + + 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 phải là màu sắc hợp lệ", + }, + { + ns: "Test.MAC", + expected: "MAC chỉ được chứa địa chỉ MAC", + }, + { + ns: "Test.IPAddr", + expected: "IPAddr phải là địa chỉ IP có thể phân giải", + }, + { + ns: "Test.IPAddrv4", + expected: "IPAddrv4 phải là địa chỉ IPv4 có thể phân giải", + }, + { + ns: "Test.IPAddrv6", + expected: "IPAddrv6 phải là địa chỉ IPv6 có thể phân giải", + }, + { + ns: "Test.UDPAddr", + expected: "UDPAddr phải là địa chỉ UDP", + }, + { + ns: "Test.UDPAddrv4", + expected: "UDPAddrv4 phải là địa chỉ IPv4 UDP", + }, + { + ns: "Test.UDPAddrv6", + expected: "UDPAddrv6 phải là địa chỉ IPv6 UDP", + }, + { + ns: "Test.TCPAddr", + expected: "TCPAddr phải là địa chỉ TCP", + }, + { + ns: "Test.TCPAddrv4", + expected: "TCPAddrv4 phải là địa chỉ IPv4 TCP", + }, + { + ns: "Test.TCPAddrv6", + expected: "TCPAddrv6 phải là địa chỉ IPv6 TCP", + }, + { + ns: "Test.CIDR", + expected: "CIDR chỉ được chứa CIDR notation", + }, + { + ns: "Test.CIDRv4", + expected: "CIDRv4 chỉ được chứa CIDR notation của một địa chỉ IPv4", + }, + { + ns: "Test.CIDRv6", + expected: "CIDRv6 chỉ được chứa CIDR notation của một địa chỉ IPv6", + }, + { + ns: "Test.SSN", + expected: "SSN phải là SSN number", + }, + { + ns: "Test.IP", + expected: "IP phải là địa chỉ IP", + }, + { + ns: "Test.IPv4", + expected: "IPv4 phải là địa chỉ IPv4", + }, + { + ns: "Test.IPv6", + expected: "IPv6 phải là địa chỉ IPv6", + }, + { + ns: "Test.DataURI", + expected: "DataURI chỉ được chứa Data URI", + }, + { + ns: "Test.Latitude", + expected: "Latitude chỉ được chứa latitude (vỹ độ)", + }, + { + ns: "Test.Longitude", + expected: "Longitude chỉ được chứa longitude (kinh độ)", + }, + { + ns: "Test.MultiByte", + expected: "MultiByte chỉ được chứa ký tự multibyte", + }, + { + ns: "Test.ASCII", + expected: "ASCII chỉ được chứa ký tự ASCII", + }, + { + ns: "Test.PrintableASCII", + expected: "PrintableASCII chỉ được chứa ký tự ASCII có thể in ấn", + }, + { + ns: "Test.UUID", + expected: "UUID phải là giá trị UUID", + }, + { + ns: "Test.UUID3", + expected: "UUID3 phải là giá trị UUID phiên bản 3", + }, + { + ns: "Test.UUID4", + expected: "UUID4 phải là giá trị UUID phiên bản 4", + }, + { + ns: "Test.UUID5", + expected: "UUID5 phải là giá trị UUID phiên bản 5", + }, + { + ns: "Test.ISBN", + expected: "ISBN phải là số ISBN", + }, + { + ns: "Test.ISBN10", + expected: "ISBN10 phải là số ISBN-10", + }, + { + ns: "Test.ISBN13", + expected: "ISBN13 phải là số ISBN-13", + }, + { + ns: "Test.Excludes", + expected: "Excludes không được chứa chuỗi 'text'", + }, + { + ns: "Test.ExcludesAll", + expected: "ExcludesAll không được chứa bất kỳ ký tự nào trong nhóm ký tự '!@#$'", + }, + { + ns: "Test.ExcludesRune", + expected: "ExcludesRune không được chứa '☻'", + }, + { + ns: "Test.ContainsAny", + expected: "ContainsAny phải chứa ít nhất 1 trong cách ký tự sau '!@#$'", + }, + { + ns: "Test.Contains", + expected: "Contains phải chứa chuỗi 'purpose'", + }, + { + ns: "Test.Base64", + expected: "Base64 phải là giá trị chuỗi Base64", + }, + { + ns: "Test.Email", + expected: "Email phải là giá trị email address", + }, + { + ns: "Test.URL", + expected: "URL phải là giá trị URL", + }, + { + ns: "Test.URI", + expected: "URI phải là giá trị URI", + }, + { + ns: "Test.RGBColorString", + expected: "RGBColorString phải là giá trị RGB color", + }, + { + ns: "Test.RGBAColorString", + expected: "RGBAColorString phải là giá trị RGBA color", + }, + { + ns: "Test.HSLColorString", + expected: "HSLColorString phải là giá trị HSL color", + }, + { + ns: "Test.HSLAColorString", + expected: "HSLAColorString phải là giá trị HSLA color", + }, + { + ns: "Test.HexadecimalString", + expected: "HexadecimalString phải là giá trị hexadecimal", + }, + { + ns: "Test.HexColorString", + expected: "HexColorString phải là giá trị HEX color", + }, + { + ns: "Test.NumberString", + expected: "NumberString chỉ được chứa giá trị số", + }, + { + ns: "Test.NumericString", + expected: "NumericString chỉ được chứa giá trị số hoặc số dưới dạng chữ", + }, + { + ns: "Test.AlphanumString", + expected: "AlphanumString chỉ được chứa ký tự dạng alphanumeric", + }, + { + ns: "Test.AlphaString", + expected: "AlphaString chỉ được chứa ký tự dạng alphabetic", + }, + { + ns: "Test.LtFieldString", + expected: "LtFieldString chỉ được nhỏ hơn MaxString", + }, + { + ns: "Test.LteFieldString", + expected: "LteFieldString chỉ được nhỏ hơn hoặc bằng MaxString", + }, + { + ns: "Test.GtFieldString", + expected: "GtFieldString phải lớn hơn MaxString", + }, + { + ns: "Test.GteFieldString", + expected: "GteFieldString phải lớn hơn hoặc bằng MaxString", + }, + { + ns: "Test.NeFieldString", + expected: "NeFieldString không được phép bằng EqFieldString", + }, + { + ns: "Test.LtCSFieldString", + expected: "LtCSFieldString chỉ được nhỏ hơn Inner.LtCSFieldString", + }, + { + ns: "Test.LteCSFieldString", + expected: "LteCSFieldString chỉ được nhỏ hơn hoặc bằng Inner.LteCSFieldString", + }, + { + ns: "Test.GtCSFieldString", + expected: "GtCSFieldString phải lớn hơn Inner.GtCSFieldString", + }, + { + ns: "Test.GteCSFieldString", + expected: "GteCSFieldString phải lớn hơn hoặc bằng Inner.GteCSFieldString", + }, + { + ns: "Test.NeCSFieldString", + expected: "NeCSFieldString không được phép bằng Inner.NeCSFieldString", + }, + { + ns: "Test.EqCSFieldString", + expected: "EqCSFieldString phải bằng Inner.EqCSFieldString", + }, + { + ns: "Test.EqFieldString", + expected: "EqFieldString phải bằng MaxString", + }, + { + ns: "Test.GteString", + expected: "GteString phải có độ dài ít nhất 3 ký tự", + }, + { + ns: "Test.GteNumber", + expected: "GteNumber phải là 5,56 hoặc lớn hơn", + }, + { + ns: "Test.GteMultiple", + expected: "GteMultiple phải chứa ít nhất 2 phần tử", + }, + { + ns: "Test.GteTime", + expected: "GteTime phải lớn hơn hoặc bằng Ngày & Giờ hiện tại", + }, + { + ns: "Test.GtString", + expected: "GtString phải có độ dài lớn hơn 3 ký tự", + }, + { + ns: "Test.GtNumber", + expected: "GtNumber phải lớn hơn 5,56", + }, + { + ns: "Test.GtMultiple", + expected: "GtMultiple phải chứa nhiều hơn 2 phần tử", + }, + { + ns: "Test.GtTime", + expected: "GtTime phải lớn hơn Ngày & Giờ hiện tại", + }, + { + ns: "Test.LteString", + expected: "LteString chỉ được có độ dài tối đa là 3 ký tự", + }, + { + ns: "Test.LteNumber", + expected: "LteNumber phải là 5,56 hoặc nhỏ hơn", + }, + { + ns: "Test.LteMultiple", + expected: "LteMultiple chỉ được chứa nhiều nhất 2 phần tử", + }, + { + ns: "Test.LteTime", + expected: "LteTime chỉ được nhỏ hơn hoặc bằng Ngày & Giờ hiện tại", + }, + { + ns: "Test.LtString", + expected: "LtString phải có độ dài nhỏ hơn 3 ký tự", + }, + { + ns: "Test.LtNumber", + expected: "LtNumber phải nhỏ hơn 5,56", + }, + { + ns: "Test.LtMultiple", + expected: "LtMultiple chỉ được chứa ít hơn 2 phần tử", + }, + { + ns: "Test.LtTime", + expected: "LtTime phải nhỏ hơn Ngày & Giờ hiện tại", + }, + { + ns: "Test.NeString", + expected: "NeString không được bằng ", + }, + { + ns: "Test.NeNumber", + expected: "NeNumber không được bằng 0.00", + }, + { + ns: "Test.NeMultiple", + expected: "NeMultiple không được bằng 0", + }, + { + ns: "Test.EqString", + expected: "EqString không bằng 3", + }, + { + ns: "Test.EqNumber", + expected: "EqNumber không bằng 2.33", + }, + { + ns: "Test.EqMultiple", + expected: "EqMultiple không bằng 7", + }, + { + ns: "Test.MaxString", + expected: "MaxString chỉ được chứa tối đa 3 ký tự", + }, + { + ns: "Test.MaxNumber", + expected: "MaxNumber phải là 1.113,00 hoặc nhỏ hơn", + }, + { + ns: "Test.MaxMultiple", + expected: "MaxMultiple chỉ được chứa tối đa 7 phần tử", + }, + { + ns: "Test.MinString", + expected: "MinString phải chứa ít nhất 1 ký tự", + }, + { + ns: "Test.MinNumber", + expected: "MinNumber phải bằng 1.113,00 hoặc lớn hơn", + }, + { + ns: "Test.MinMultiple", + expected: "MinMultiple phải chứa ít nhất 7 phần tử", + }, + { + ns: "Test.LenString", + expected: "LenString phải có độ dài là 1 ký tự", + }, + { + ns: "Test.LenNumber", + expected: "LenNumber phải bằng 1.113,00", + }, + { + ns: "Test.LenMultiple", + expected: "LenMultiple phải chứa 7 phần tử", + }, + { + ns: "Test.RequiredString", + expected: "RequiredString không được bỏ trống", + }, + { + ns: "Test.RequiredNumber", + expected: "RequiredNumber không được bỏ trống", + }, + { + ns: "Test.RequiredMultiple", + expected: "RequiredMultiple không được bỏ trống", + }, + { + ns: "Test.StrPtrMinLen", + expected: "StrPtrMinLen phải chứa ít nhất 10 ký tự", + }, + { + ns: "Test.StrPtrMaxLen", + expected: "StrPtrMaxLen chỉ được chứa tối đa 1 ký tự", + }, + { + ns: "Test.StrPtrLen", + expected: "StrPtrLen phải có độ dài là 2 ký tự", + }, + { + ns: "Test.StrPtrLt", + expected: "StrPtrLt phải có độ dài nhỏ hơn 1 ký tự", + }, + { + ns: "Test.StrPtrLte", + expected: "StrPtrLte chỉ được có độ dài tối đa là 1 ký tự", + }, + { + ns: "Test.StrPtrGt", + expected: "StrPtrGt phải có độ dài lớn hơn 10 ký tự", + }, + { + ns: "Test.StrPtrGte", + expected: "StrPtrGte phải có độ dài ít nhất 10 ký tự", + }, + { + ns: "Test.OneOfString", + expected: "OneOfString phải là trong những giá trị [red green]", + }, + { + ns: "Test.OneOfInt", + expected: "OneOfInt phải là trong những giá trị [5 63]", + }, + { + ns: "Test.UniqueSlice", + expected: "UniqueSlice chỉ được chứa những giá trị không trùng lặp", + }, + { + ns: "Test.UniqueArray", + expected: "UniqueArray chỉ được chứa những giá trị không trùng lặp", + }, + { + ns: "Test.UniqueMap", + expected: "UniqueMap chỉ được chứa những giá trị không trùng lặp", + }, + { + ns: "Test.JSONString", + expected: "JSONString phải là một chuỗi json hợp lệ", + }, + { + ns: "Test.JWTString", + expected: "JWTString phải là một chuỗi jwt hợp lệ", + }, + { + ns: "Test.LowercaseString", + expected: "LowercaseString phải được viết thường", + }, + { + ns: "Test.UppercaseString", + expected: "UppercaseString phải được viết hoa", + }, + { + ns: "Test.Datetime", + expected: "Datetime không trùng định dạng ngày tháng 2006-01-02", + }, + { + ns: "Test.PostCode", + expected: "PostCode sai định dạng postcode của quốc gia SG", + }, + { + ns: "Test.PostCodeByField", + expected: "PostCodeByField sai định dạng postcode của quốc gia tương ứng thuộc trường 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)) + } +} From 39aa2e3a272ab7e03849fc511dc5cb7bfd538140 Mon Sep 17 00:00:00 2001 From: mrkongo <20625823+mrkongo@users.noreply.github.com> Date: Sun, 1 May 2022 18:36:02 +0200 Subject: [PATCH 16/18] add it_IT translation (#694) --- translations/it/it.go | 16 ++++++++++++++-- translations/it/it_test.go | 9 +++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/translations/it/it.go b/translations/it/it.go index 8abd67f5..0b46fc43 100644 --- a/translations/it/it.go +++ b/translations/it/it.go @@ -1,4 +1,4 @@ -package en +package it import ( "fmt" @@ -28,6 +28,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} è un campo obbligatorio", override: false, }, + { + tag: "required_without", + translation: "{0} è un campo obbligatorio", + override: false, + }, { tag: "len", customRegisFunc: func(ut ut.Translator) (err error) { @@ -119,6 +124,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { 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 } @@ -150,6 +156,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er }, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { var err error + var t string var digits uint64 @@ -320,6 +327,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er } if err = ut.Add("lt-number", "{0} deve essere minore di {1}", false); err != nil { + return } @@ -424,6 +432,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { 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 } @@ -459,6 +468,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return }, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error var t string var f64 float64 @@ -541,6 +551,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { 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 } @@ -576,6 +587,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return }, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error var t string var f64 float64 @@ -693,6 +705,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return }, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error var t string var f64 float64 @@ -1195,7 +1208,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er } 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 { diff --git a/translations/it/it_test.go b/translations/it/it_test.go index 301e8c1c..98d2ab57 100644 --- a/translations/it/it_test.go +++ b/translations/it/it_test.go @@ -1,4 +1,4 @@ -package en +package it import ( "testing" @@ -11,9 +11,9 @@ import ( ) func TestTranslations(t *testing.T) { - it := italian.New() - uni := ut.New(it, it) - trans, _ := uni.GetTranslator("en") + ita := italian.New() + uni := ut.New(ita, ita) + trans, _ := uni.GetTranslator("it") validate := validator.New() @@ -198,6 +198,7 @@ func TestTranslations(t *testing.T) { test.LowercaseString = "ABCDEFG" test.UppercaseString = "abcdefg" + test.StartsWithString = "hello" test.StartsNotWithString = "foo-hello" test.EndsWithString = "hello" From 4f55647bd7da42675cc24bfd16229a4ed3025f8c Mon Sep 17 00:00:00 2001 From: Sec Cake Date: Mon, 2 May 2022 00:40:16 +0800 Subject: [PATCH 17/18] Added some hash validation (#875) --- README.md | 10 ++ baked_in.go | 60 ++++++++ regexes.go | 20 +++ validator_test.go | 359 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 449 insertions(+) diff --git a/README.md b/README.md index 0201880a..2294cce0 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,16 @@ Baked-in Validations | uuid5 | Universally Unique Identifier UUID v5 | | uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 | | uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 | +| md4 | MD4 hash | +| md5 | MD5 hash | +| sha256 | SHA256 hash | +| sha384 | SHA384 hash | +| sha512 | SHA512 hash | +| ripemd128 | RIPEMD-128 hash | +| ripemd128 | RIPEMD-160 hash | +| tiger128 | TIGER128 hash | +| tiger160 | TIGER160 hash | +| tiger192 | TIGER192 hash | | semver | Semantic Versioning 2.0.0 | | ulid | Universally Unique Lexicographically Sortable Identifier ULID | diff --git a/baked_in.go b/baked_in.go index 22344797..f2f0939c 100644 --- a/baked_in.go +++ b/baked_in.go @@ -151,6 +151,16 @@ var ( "uuid4_rfc4122": isUUID4RFC4122, "uuid5_rfc4122": isUUID5RFC4122, "ulid": isULID, + "md4": isMD4, + "md5": isMD5, + "sha256": isSHA256, + "sha384": isSHA384, + "sha512": isSHA512, + "ripemd128": isRIPEMD128, + "ripemd160": isRIPEMD160, + "tiger128": isTIGER128, + "tiger160": isTIGER160, + "tiger192": isTIGER192, "ascii": isASCII, "printascii": isPrintableASCII, "multibyte": hasMultiByteCharacter, @@ -500,6 +510,56 @@ func isULID(fl FieldLevel) bool { return uLIDRegex.MatchString(fl.Field().String()) } +// isMD4 is the validation function for validating if the field's value is a valid MD4. +func isMD4(fl FieldLevel) bool { + return md4Regex.MatchString(fl.Field().String()) +} + +// isMD5 is the validation function for validating if the field's value is a valid MD5. +func isMD5(fl FieldLevel) bool { + return md5Regex.MatchString(fl.Field().String()) +} + +// isSHA256 is the validation function for validating if the field's value is a valid SHA256. +func isSHA256(fl FieldLevel) bool { + return sha256Regex.MatchString(fl.Field().String()) +} + +// isSHA384 is the validation function for validating if the field's value is a valid SHA384. +func isSHA384(fl FieldLevel) bool { + return sha384Regex.MatchString(fl.Field().String()) +} + +// isSHA512 is the validation function for validating if the field's value is a valid SHA512. +func isSHA512(fl FieldLevel) bool { + return sha512Regex.MatchString(fl.Field().String()) +} + +// isRIPEMD128 is the validation function for validating if the field's value is a valid PIPEMD128. +func isRIPEMD128(fl FieldLevel) bool { + return ripemd128Regex.MatchString(fl.Field().String()) +} + +// isRIPEMD160 is the validation function for validating if the field's value is a valid PIPEMD160. +func isRIPEMD160(fl FieldLevel) bool { + return ripemd160Regex.MatchString(fl.Field().String()) +} + +// isTIGER128 is the validation function for validating if the field's value is a valid TIGER128. +func isTIGER128(fl FieldLevel) bool { + return tiger128Regex.MatchString(fl.Field().String()) +} + +// isTIGER160 is the validation function for validating if the field's value is a valid TIGER160. +func isTIGER160(fl FieldLevel) bool { + return tiger160Regex.MatchString(fl.Field().String()) +} + +// isTIGER192 is the validation function for validating if the field's value is a valid isTIGER192. +func isTIGER192(fl FieldLevel) bool { + return tiger192Regex.MatchString(fl.Field().String()) +} + // isISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN. func isISBN(fl FieldLevel) bool { return isISBN10(fl) || isISBN13(fl) diff --git a/regexes.go b/regexes.go index a8668f8b..9c1c6342 100644 --- a/regexes.go +++ b/regexes.go @@ -30,6 +30,16 @@ const ( uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" uUIDRFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" uLIDRegexString = "^[A-HJKMNP-TV-Z0-9]{26}$" + md4RegexString = "^[0-9a-f]{32}$" + md5RegexString = "^[0-9a-f]{32}$" + sha256RegexString = "^[0-9a-f]{64}$" + sha384RegexString = "^[0-9a-f]{96}$" + sha512RegexString = "^[0-9a-f]{128}$" + ripemd128RegexString = "^[0-9a-f]{32}$" + ripemd160RegexString = "^[0-9a-f]{40}$" + tiger128RegexString = "^[0-9a-f]{32}$" + tiger160RegexString = "^[0-9a-f]{40}$" + tiger192RegexString = "^[0-9a-f]{48}$" aSCIIRegexString = "^[\x00-\x7F]*$" printableASCIIRegexString = "^[\x20-\x7E]*$" multibyteRegexString = "[^\x00-\x7F]" @@ -84,6 +94,16 @@ var ( uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString) uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString) uLIDRegex = regexp.MustCompile(uLIDRegexString) + md4Regex = regexp.MustCompile(md4RegexString) + md5Regex = regexp.MustCompile(md5RegexString) + sha256Regex = regexp.MustCompile(sha256RegexString) + sha384Regex = regexp.MustCompile(sha384RegexString) + sha512Regex = regexp.MustCompile(sha512RegexString) + ripemd128Regex = regexp.MustCompile(ripemd128RegexString) + ripemd160Regex = regexp.MustCompile(ripemd160RegexString) + tiger128Regex = regexp.MustCompile(tiger128RegexString) + tiger160Regex = regexp.MustCompile(tiger160RegexString) + tiger192Regex = regexp.MustCompile(tiger192RegexString) aSCIIRegex = regexp.MustCompile(aSCIIRegexString) printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString) multibyteRegex = regexp.MustCompile(multibyteRegexString) diff --git a/validator_test.go b/validator_test.go index 8abe0ce7..933626fb 100644 --- a/validator_test.go +++ b/validator_test.go @@ -4309,6 +4309,365 @@ func TestULIDValidation(t *testing.T) { } } +func TestMD4Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"6f5902ac237024bdd0c176cb93063dc4", true}, + {"6f5902ac237024bdd0c176cb93063dc-", false}, + {"6f5902ac237024bdd0c176cb93063dc41", false}, + {"6f5902ac237024bdd0c176cb93063dcC", false}, + {"6f5902ac237024bdd0c176cb93063dc", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "md4") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d MD4 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d MD4 failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "md4" { + t.Fatalf("Index: %d MD4 failed Error: %s", i, errs) + } + } + } + } +} + +func TestMD5Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"6f5902ac237024bdd0c176cb93063dc4", true}, + {"6f5902ac237024bdd0c176cb93063dc-", false}, + {"6f5902ac237024bdd0c176cb93063dc41", false}, + {"6f5902ac237024bdd0c176cb93063dcC", false}, + {"6f5902ac237024bdd0c176cb93063dc", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "md5") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d MD5 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d MD5 failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "md5" { + t.Fatalf("Index: %d MD5 failed Error: %s", i, errs) + } + } + } + } +} + +func TestSHA256Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc4", true}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc-", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc41", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dcC", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "sha256") + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d SHA256 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d SHA256 failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "sha256" { + t.Fatalf("Index: %d SHA256 failed Error: %s", i, errs) + } + } + } + } +} + +func TestSHA384Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc4", true}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc-", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc41", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dcC", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "sha384") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d SHA384 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d SHA384 failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "sha384" { + t.Fatalf("Index: %d SHA384 failed Error: %s", i, errs) + } + } + } + } +} + +func TestSHA512Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc4", true}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc-", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc41", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dcC", false}, + {"6f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc46f5902ac237024bdd0c176cb93063dc", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "sha512") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d SHA512 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d SHA512 failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "sha512" { + t.Fatalf("Index: %d SHA512 failed Error: %s", i, errs) + } + } + } + } +} + +func TestRIPEMD128Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"6f5902ac237024bdd0c176cb93063dc4", true}, + {"6f5902ac237024bdd0c176cb93063dc-", false}, + {"6f5902ac237024bdd0c176cb93063dc41", false}, + {"6f5902ac237024bdd0c176cb93063dcC", false}, + {"6f5902ac237024bdd0c176cb93063dc", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "ripemd128") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d RIPEMD128 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d RIPEMD128 failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "ripemd128" { + t.Fatalf("Index: %d RIPEMD128 failed Error: %s", i, errs) + } + } + } + } +} + +func TestRIPEMD160Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"6f5902ac6f5902ac237024bdd0c176cb93063dc4", true}, + {"6f5902ac6f5902ac237024bdd0c176cb93063dc-", false}, + {"6f5902ac6f5902ac237024bdd0c176cb93063dc41", false}, + {"6f5902ac6f5902ac237024bdd0c176cb93063dcC", false}, + {"6f5902ac6f5902ac237024bdd0c176cb93063dc", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "ripemd160") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d RIPEMD160 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d RIPEMD160 failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "ripemd160" { + t.Fatalf("Index: %d RIPEMD160 failed Error: %s", i, errs) + } + } + } + } +} + +func TestTIGER128Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"6f5902ac237024bdd0c176cb93063dc4", true}, + {"6f5902ac237024bdd0c176cb93063dc-", false}, + {"6f5902ac237024bdd0c176cb93063dc41", false}, + {"6f5902ac237024bdd0c176cb93063dcC", false}, + {"6f5902ac237024bdd0c176cb93063dc", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "tiger128") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d TIGER128 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d TIGER128 failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "tiger128" { + t.Fatalf("Index: %d TIGER128 failed Error: %s", i, errs) + } + } + } + } +} + +func TestTIGER160Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"6f5902ac6f5902ac237024bdd0c176cb93063dc4", true}, + {"6f5902ac6f5902ac237024bdd0c176cb93063dc-", false}, + {"6f5902ac6f5902ac237024bdd0c176cb93063dc41", false}, + {"6f5902ac6f5902ac237024bdd0c176cb93063dcC", false}, + {"6f5902ac6f5902ac237024bdd0c176cb93063dc", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "tiger160") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d TIGER160 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d TIGER160 failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "tiger160" { + t.Fatalf("Index: %d TIGER160 failed Error: %s", i, errs) + } + } + } + } +} + +func TestTIGER192Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"6f5902ac237024bd6f5902ac237024bdd0c176cb93063dc4", true}, + {"6f5902ac237024bd6f5902ac237024bdd0c176cb93063dc-", false}, + {"6f5902ac237024bd6f5902ac237024bdd0c176cb93063dc41", false}, + {"6f5902ac237024bd6f5902ac237024bdd0c176cb93063dcC", false}, + {"6f5902ac237024bd6f5902ac237024bdd0c176cb93063dc", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "tiger192") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d TIGER192 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d TIGER192 failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "tiger192" { + t.Fatalf("Index: %d TIGER192 failed Error: %s", i, errs) + } + } + } + } +} + func TestISBNValidation(t *testing.T) { tests := []struct { param string From 99922fccc732cdc51d1ff1eff78a8a9b0e2249dc Mon Sep 17 00:00:00 2001 From: gosua Date: Sun, 1 May 2022 18:46:42 +0200 Subject: [PATCH 18/18] Enhance example in function docu (#858) --- validator_instance.go | 1 + 1 file changed, 1 insertion(+) diff --git a/validator_instance.go b/validator_instance.go index 316ffb08..6b675488 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -182,6 +182,7 @@ func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]int // // validate.RegisterTagNameFunc(func(fld reflect.StructField) string { // name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] +// // skip if tag key says it should be ignored // if name == "-" { // return "" // }