From 8ce3a82748a51593d61dd1e4eb0e7b8c1b755436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D1=B5=C2=B5=CF=83=C9=B3=C9=A0?= <3168632+vuon9@users.noreply.github.com> Date: Wed, 11 Aug 2021 18:07:11 +0000 Subject: [PATCH 1/2] WIP: add vi translations --- baked_in.go | 59 +- translations/vi/vi.go | 1384 ++++++++++++++++++++++++++++++++++++ translations/vi/vi_test.go | 690 ++++++++++++++++++ 3 files changed, 2079 insertions(+), 54 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 f5fd2391..3e6f1730 100644 --- a/baked_in.go +++ b/baked_in.go @@ -201,8 +201,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() @@ -258,7 +260,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{}{}) @@ -308,7 +309,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 @@ -316,7 +316,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 @@ -324,7 +323,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 @@ -332,7 +330,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 @@ -340,7 +337,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 @@ -348,7 +344,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 @@ -356,7 +351,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 @@ -364,7 +358,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 { @@ -422,7 +415,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 { @@ -438,7 +430,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 { @@ -505,7 +496,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) { @@ -526,7 +516,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) { @@ -714,7 +703,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) @@ -777,7 +765,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() @@ -834,7 +821,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() @@ -882,7 +868,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() @@ -929,7 +914,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() @@ -976,7 +960,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() @@ -1023,7 +1006,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() @@ -1073,7 +1055,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() @@ -1123,7 +1104,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() @@ -1174,7 +1154,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() @@ -1226,7 +1205,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() @@ -1265,11 +1244,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() @@ -1294,11 +1271,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 @@ -1331,7 +1306,6 @@ func isUrnRFC2141(fl FieldLevel) bool { field := fl.Field() switch field.Kind() { - case reflect.String: str := field.String() @@ -1642,7 +1616,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() @@ -1689,7 +1662,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() @@ -1736,7 +1708,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() @@ -1783,7 +1754,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() @@ -1816,7 +1786,6 @@ func isGt(fl FieldLevel) bool { case reflect.Struct: if field.Type() == timeType { - return field.Interface().(time.Time).After(time.Now().UTC()) } } @@ -1826,7 +1795,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() @@ -1868,7 +1836,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() @@ -1915,7 +1882,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() @@ -1962,7 +1928,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() @@ -2009,7 +1974,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() @@ -2043,7 +2007,6 @@ func isLt(fl FieldLevel) bool { case reflect.Struct: if field.Type() == timeType { - return field.Interface().(time.Time).Before(time.Now().UTC()) } } @@ -2058,7 +2021,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 } @@ -2069,7 +2031,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 } @@ -2081,7 +2042,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 } @@ -2093,7 +2053,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 } @@ -2105,7 +2064,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 } @@ -2117,7 +2075,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 } @@ -2129,7 +2086,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 } @@ -2141,7 +2097,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 } @@ -2153,7 +2108,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 } @@ -2165,14 +2119,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 { @@ -2185,7 +2137,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..ce80710c --- /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 chể 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..ca9242a2 --- /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 must contain valid latitude coordinates", + }, + { + ns: "Test.Longitude", + expected: "Longitude must contain a valid longitude coordinates", + }, + { + ns: "Test.MultiByte", + expected: "MultiByte must contain multibyte characters", + }, + { + ns: "Test.ASCII", + expected: "ASCII must contain only ascii characters", + }, + { + ns: "Test.PrintableASCII", + expected: "PrintableASCII must contain only printable ascii characters", + }, + { + ns: "Test.UUID", + expected: "UUID must be a valid UUID", + }, + { + ns: "Test.UUID3", + expected: "UUID3 must be a valid version 3 UUID", + }, + { + ns: "Test.UUID4", + expected: "UUID4 must be a valid version 4 UUID", + }, + { + ns: "Test.UUID5", + expected: "UUID5 must be a valid version 5 UUID", + }, + { + ns: "Test.ISBN", + expected: "ISBN must be a valid ISBN number", + }, + { + ns: "Test.ISBN10", + expected: "ISBN10 must be a valid ISBN-10 number", + }, + { + ns: "Test.ISBN13", + expected: "ISBN13 must be a valid ISBN-13 number", + }, + { + ns: "Test.Excludes", + expected: "Excludes cannot contain the text 'text'", + }, + { + ns: "Test.ExcludesAll", + expected: "ExcludesAll cannot contain any of the following characters '!@#$'", + }, + { + ns: "Test.ExcludesRune", + expected: "ExcludesRune cannot contain the following '☻'", + }, + { + ns: "Test.ContainsAny", + expected: "ContainsAny must contain at least one of the following characters '!@#$'", + }, + { + ns: "Test.Contains", + expected: "Contains must contain the text 'purpose'", + }, + { + ns: "Test.Base64", + expected: "Base64 must be a valid Base64 string", + }, + { + ns: "Test.Email", + expected: "Email must be a valid email address", + }, + { + ns: "Test.URL", + expected: "URL must be a valid URL", + }, + { + ns: "Test.URI", + expected: "URI must be a valid URI", + }, + { + ns: "Test.RGBColorString", + expected: "RGBColorString must be a valid RGB color", + }, + { + ns: "Test.RGBAColorString", + expected: "RGBAColorString must be a valid RGBA color", + }, + { + ns: "Test.HSLColorString", + expected: "HSLColorString must be a valid HSL color", + }, + { + ns: "Test.HSLAColorString", + expected: "HSLAColorString must be a valid HSLA color", + }, + { + ns: "Test.HexadecimalString", + expected: "HexadecimalString must be a valid hexadecimal", + }, + { + ns: "Test.HexColorString", + expected: "HexColorString must be a valid HEX color", + }, + { + ns: "Test.NumberString", + expected: "NumberString must be a valid number", + }, + { + ns: "Test.NumericString", + expected: "NumericString must be a valid numeric value", + }, + { + ns: "Test.AlphanumString", + expected: "AlphanumString can only contain alphanumeric characters", + }, + { + ns: "Test.AlphaString", + expected: "AlphaString can only contain alphabetic characters", + }, + { + ns: "Test.LtFieldString", + expected: "LtFieldString must be less than MaxString", + }, + { + ns: "Test.LteFieldString", + expected: "LteFieldString must be less than or equal to MaxString", + }, + { + ns: "Test.GtFieldString", + expected: "GtFieldString must be greater than MaxString", + }, + { + ns: "Test.GteFieldString", + expected: "GteFieldString must be greater than or equal to MaxString", + }, + { + ns: "Test.NeFieldString", + expected: "NeFieldString cannot be equal to EqFieldString", + }, + { + ns: "Test.LtCSFieldString", + expected: "LtCSFieldString must be less than Inner.LtCSFieldString", + }, + { + ns: "Test.LteCSFieldString", + expected: "LteCSFieldString must be less than or equal to Inner.LteCSFieldString", + }, + { + ns: "Test.GtCSFieldString", + expected: "GtCSFieldString must be greater than Inner.GtCSFieldString", + }, + { + ns: "Test.GteCSFieldString", + expected: "GteCSFieldString must be greater than or equal to Inner.GteCSFieldString", + }, + { + ns: "Test.NeCSFieldString", + expected: "NeCSFieldString cannot be equal to Inner.NeCSFieldString", + }, + { + ns: "Test.EqCSFieldString", + expected: "EqCSFieldString must be equal to Inner.EqCSFieldString", + }, + { + ns: "Test.EqFieldString", + expected: "EqFieldString must be equal to MaxString", + }, + { + ns: "Test.GteString", + expected: "GteString must be at least 3 characters in length", + }, + { + ns: "Test.GteNumber", + expected: "GteNumber must be 5.56 or greater", + }, + { + ns: "Test.GteMultiple", + expected: "GteMultiple must contain at least 2 items", + }, + { + ns: "Test.GteTime", + expected: "GteTime must be greater than or equal to the current Date & Time", + }, + { + ns: "Test.GtString", + expected: "GtString must be greater than 3 characters in length", + }, + { + ns: "Test.GtNumber", + expected: "GtNumber must be greater than 5.56", + }, + { + ns: "Test.GtMultiple", + expected: "GtMultiple must contain more than 2 items", + }, + { + ns: "Test.GtTime", + expected: "GtTime must be greater than the current Date & Time", + }, + { + ns: "Test.LteString", + expected: "LteString must be at maximum 3 characters in length", + }, + { + ns: "Test.LteNumber", + expected: "LteNumber must be 5.56 or less", + }, + { + ns: "Test.LteMultiple", + expected: "LteMultiple must contain at maximum 2 items", + }, + { + ns: "Test.LteTime", + expected: "LteTime must be less than or equal to the current Date & Time", + }, + { + ns: "Test.LtString", + expected: "LtString must be less than 3 characters in length", + }, + { + ns: "Test.LtNumber", + expected: "LtNumber must be less than 5.56", + }, + { + ns: "Test.LtMultiple", + expected: "LtMultiple must contain less than 2 items", + }, + { + ns: "Test.LtTime", + expected: "LtTime must be less than the current Date & Time", + }, + { + ns: "Test.NeString", + expected: "NeString should not be equal to ", + }, + { + ns: "Test.NeNumber", + expected: "NeNumber should not be equal to 0.00", + }, + { + ns: "Test.NeMultiple", + expected: "NeMultiple should not be equal to 0", + }, + { + ns: "Test.EqString", + expected: "EqString is not equal to 3", + }, + { + ns: "Test.EqNumber", + expected: "EqNumber is not equal to 2.33", + }, + { + ns: "Test.EqMultiple", + expected: "EqMultiple is not equal to 7", + }, + { + ns: "Test.MaxString", + expected: "MaxString must be a maximum of 3 characters in length", + }, + { + ns: "Test.MaxNumber", + expected: "MaxNumber must be 1,113.00 or less", + }, + { + ns: "Test.MaxMultiple", + expected: "MaxMultiple must contain at maximum 7 items", + }, + { + ns: "Test.MinString", + expected: "MinString must be at least 1 character in length", + }, + { + ns: "Test.MinNumber", + expected: "MinNumber must be 1,113.00 or greater", + }, + { + ns: "Test.MinMultiple", + expected: "MinMultiple must contain at least 7 items", + }, + { + ns: "Test.LenString", + expected: "LenString must be 1 character in length", + }, + { + ns: "Test.LenNumber", + expected: "LenNumber must be equal to 1,113.00", + }, + { + ns: "Test.LenMultiple", + expected: "LenMultiple must contain 7 items", + }, + { + ns: "Test.RequiredString", + expected: "RequiredString is a required field", + }, + { + ns: "Test.RequiredNumber", + expected: "RequiredNumber is a required field", + }, + { + ns: "Test.RequiredMultiple", + expected: "RequiredMultiple is a required field", + }, + { + ns: "Test.StrPtrMinLen", + expected: "StrPtrMinLen must be at least 10 characters in length", + }, + { + ns: "Test.StrPtrMaxLen", + expected: "StrPtrMaxLen must be a maximum of 1 character in length", + }, + { + ns: "Test.StrPtrLen", + expected: "StrPtrLen must be 2 characters in length", + }, + { + ns: "Test.StrPtrLt", + expected: "StrPtrLt must be less than 1 character in length", + }, + { + ns: "Test.StrPtrLte", + expected: "StrPtrLte must be at maximum 1 character in length", + }, + { + ns: "Test.StrPtrGt", + expected: "StrPtrGt must be greater than 10 characters in length", + }, + { + ns: "Test.StrPtrGte", + expected: "StrPtrGte must be at least 10 characters in length", + }, + { + ns: "Test.OneOfString", + expected: "OneOfString must be one of [red green]", + }, + { + ns: "Test.OneOfInt", + expected: "OneOfInt must be one of [5 63]", + }, + { + ns: "Test.UniqueSlice", + expected: "UniqueSlice must contain unique values", + }, + { + ns: "Test.UniqueArray", + expected: "UniqueArray must contain unique values", + }, + { + ns: "Test.UniqueMap", + expected: "UniqueMap must contain unique values", + }, + { + ns: "Test.JSONString", + expected: "JSONString must be a valid json string", + }, + { + ns: "Test.JWTString", + expected: "JWTString must be a valid jwt string", + }, + { + ns: "Test.LowercaseString", + expected: "LowercaseString must be a lowercase string", + }, + { + ns: "Test.UppercaseString", + expected: "UppercaseString must be an uppercase string", + }, + { + ns: "Test.Datetime", + expected: "Datetime does not match the 2006-01-02 format", + }, + { + ns: "Test.PostCode", + expected: "PostCode does not match postcode format of SG country", + }, + { + ns: "Test.PostCodeByField", + expected: "PostCodeByField does not match postcode format of country in PostCodeCountry field", + }, + } + + for _, tt := range tests { + + var fe validator.FieldError + + for _, e := range errs { + if tt.ns == e.Namespace() { + fe = e + break + } + } + + NotEqual(t, fe, nil) + Equal(t, tt.expected, fe.Translate(trans)) + } +} From ff2bb05c8bbb84a18ff2643d28beee0af231145c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D1=B5=C2=B5=CF=83=C9=B3=C9=A0?= <3168632+vuon9@users.noreply.github.com> Date: Wed, 11 Aug 2021 18:07:11 +0000 Subject: [PATCH 2/2] Add vi translations --- baked_in.go | 59 +- translations/vi/vi.go | 1384 ++++++++++++++++++++++++++++++++++++ translations/vi/vi_test.go | 690 ++++++++++++++++++ 3 files changed, 2079 insertions(+), 54 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 f5fd2391..3e6f1730 100644 --- a/baked_in.go +++ b/baked_in.go @@ -201,8 +201,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() @@ -258,7 +260,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{}{}) @@ -308,7 +309,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 @@ -316,7 +316,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 @@ -324,7 +323,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 @@ -332,7 +330,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 @@ -340,7 +337,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 @@ -348,7 +344,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 @@ -356,7 +351,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 @@ -364,7 +358,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 { @@ -422,7 +415,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 { @@ -438,7 +430,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 { @@ -505,7 +496,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) { @@ -526,7 +516,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) { @@ -714,7 +703,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) @@ -777,7 +765,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() @@ -834,7 +821,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() @@ -882,7 +868,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() @@ -929,7 +914,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() @@ -976,7 +960,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() @@ -1023,7 +1006,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() @@ -1073,7 +1055,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() @@ -1123,7 +1104,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() @@ -1174,7 +1154,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() @@ -1226,7 +1205,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() @@ -1265,11 +1244,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() @@ -1294,11 +1271,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 @@ -1331,7 +1306,6 @@ func isUrnRFC2141(fl FieldLevel) bool { field := fl.Field() switch field.Kind() { - case reflect.String: str := field.String() @@ -1642,7 +1616,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() @@ -1689,7 +1662,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() @@ -1736,7 +1708,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() @@ -1783,7 +1754,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() @@ -1816,7 +1786,6 @@ func isGt(fl FieldLevel) bool { case reflect.Struct: if field.Type() == timeType { - return field.Interface().(time.Time).After(time.Now().UTC()) } } @@ -1826,7 +1795,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() @@ -1868,7 +1836,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() @@ -1915,7 +1882,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() @@ -1962,7 +1928,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() @@ -2009,7 +1974,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() @@ -2043,7 +2007,6 @@ func isLt(fl FieldLevel) bool { case reflect.Struct: if field.Type() == timeType { - return field.Interface().(time.Time).Before(time.Now().UTC()) } } @@ -2058,7 +2021,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 } @@ -2069,7 +2031,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 } @@ -2081,7 +2042,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 } @@ -2093,7 +2053,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 } @@ -2105,7 +2064,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 } @@ -2117,7 +2075,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 } @@ -2129,7 +2086,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 } @@ -2141,7 +2097,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 } @@ -2153,7 +2108,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 } @@ -2165,14 +2119,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 { @@ -2185,7 +2137,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)) + } +}