修改包路径

This commit is contained in:
gamife 2020-11-27 23:07:41 +08:00
parent 142c4d88bc
commit 2e2ba084bf
107 changed files with 48530 additions and 0 deletions

62
gin/b/b.go Normal file
View File

@ -0,0 +1,62 @@
package b
import (
"gin-valid/gin/binding"
"gin-valid/go-playground/validator/v10"
"mime"
"net/http"
)
type ValidError struct {
ErrString string
}
func (e *ValidError) Error() string {
return e.ErrString
}
func ShouldBind(req *http.Request, obj interface{}) error {
content, err := contentType(req)
if err != nil {
return err
}
b := binding.Default(req.Method, content)
err = ShouldBindWith(req, obj, b)
errs, ok := err.(validator.ValidationErrors)
if !ok {
// 非validator.ValidationErrors类型错误直接返回
return err
}
return errs.Translate(binding.ValidTrans)
}
func ShouldBindWith(req *http.Request, obj interface{}, b binding.Binding) error {
return b.Bind(req, obj)
}
func ShouldBindJSON(req *http.Request, obj interface{}) error {
return ShouldBindWith(req, obj, binding.JSON)
}
func ShouldBindHeader(req *http.Request, obj interface{}) error {
return ShouldBindWith(req, obj, binding.Header)
}
func ShouldBindQuery(req *http.Request, obj interface{}) error {
return ShouldBindWith(req, obj, binding.Query)
}
func contentType(r *http.Request) (string, error) {
ct := r.Header.Get("Content-Type")
if ct == "" {
ct = "application/octet-stream"
}
ct, _, err := mime.ParseMediaType(ct)
return ct, err
}
func filterFlags(content string) string {
for i, char := range content {
if char == ' ' || char == ';' {
return content[:i]
}
}
return content
}

116
gin/binding/binding.go Normal file
View File

@ -0,0 +1,116 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// +build !nomsgpack
package binding
import "net/http"
// Content-Type MIME of the most common data formats.
const (
MIMEJSON = "application/json"
MIMEHTML = "text/html"
MIMEXML = "application/xml"
MIMEXML2 = "text/xml"
MIMEPlain = "text/plain"
MIMEPOSTForm = "application/x-www-form-urlencoded"
MIMEMultipartPOSTForm = "multipart/form-data"
MIMEPROTOBUF = "application/x-protobuf"
MIMEMSGPACK = "application/x-msgpack"
MIMEMSGPACK2 = "application/msgpack"
MIMEYAML = "application/x-yaml"
)
// Binding describes the interface which needs to be implemented for binding the
// data present in the request such as JSON request body, query parameters or
// the form POST.
type Binding interface {
Name() string
Bind(*http.Request, interface{}) error
}
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
// but it reads the body from supplied bytes instead of req.Body.
type BindingBody interface {
Binding
BindBody([]byte, interface{}) error
}
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
// but it read the Params.
type BindingUri interface {
Name() string
BindUri(map[string][]string, interface{}) error
}
// StructValidator is the minimal interface which needs to be implemented in
// order for it to be used as the validator engine for ensuring the correctness
// of the request. Gin provides a default implementation for this using
// https://github.com/go-playground/validator/tree/v8.18.2.
type StructValidator interface {
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
// If the received type is not a struct, any validation should be skipped and nil must be returned.
// If the received type is a struct or pointer to a struct, the validation should be performed.
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
// Otherwise nil must be returned.
ValidateStruct(interface{}) error
// Engine returns the underlying validator engine which powers the
// StructValidator implementation.
Engine() interface{}
}
// Validator is the default validator which implements the StructValidator
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
// under the hood.
var Validator StructValidator = &defaultValidator{}
// These implement the Binding interface and can be used to bind the data
// present in the request to struct instances.
var (
JSON = jsonBinding{}
XML = xmlBinding{}
Form = formBinding{}
Query = queryBinding{}
FormPost = formPostBinding{}
FormMultipart = formMultipartBinding{}
ProtoBuf = protobufBinding{}
MsgPack = msgpackBinding{}
YAML = yamlBinding{}
Uri = uriBinding{}
Header = headerBinding{}
)
// Default returns the appropriate Binding instance based on the HTTP method
// and the content type.
func Default(method, contentType string) Binding {
if method == http.MethodGet {
return Form
}
switch contentType {
case MIMEJSON:
return JSON
case MIMEXML, MIMEXML2:
return XML
case MIMEPROTOBUF:
return ProtoBuf
case MIMEMSGPACK, MIMEMSGPACK2:
return MsgPack
case MIMEYAML:
return YAML
case MIMEMultipartPOSTForm:
return FormMultipart
default: // case MIMEPOSTForm:
return Form
}
}
func validate(obj interface{}) error {
if Validator == nil {
return nil
}
return Validator.ValidateStruct(obj)
}

View File

@ -0,0 +1,111 @@
// Copyright 2020 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// +build nomsgpack
package binding
import "net/http"
// Content-Type MIME of the most common data formats.
const (
MIMEJSON = "application/json"
MIMEHTML = "text/html"
MIMEXML = "application/xml"
MIMEXML2 = "text/xml"
MIMEPlain = "text/plain"
MIMEPOSTForm = "application/x-www-form-urlencoded"
MIMEMultipartPOSTForm = "multipart/form-data"
MIMEPROTOBUF = "application/x-protobuf"
MIMEYAML = "application/x-yaml"
)
// Binding describes the interface which needs to be implemented for binding the
// data present in the request such as JSON request body, query parameters or
// the form POST.
type Binding interface {
Name() string
Bind(*http.Request, interface{}) error
}
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
// but it reads the body from supplied bytes instead of req.Body.
type BindingBody interface {
Binding
BindBody([]byte, interface{}) error
}
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
// but it read the Params.
type BindingUri interface {
Name() string
BindUri(map[string][]string, interface{}) error
}
// StructValidator is the minimal interface which needs to be implemented in
// order for it to be used as the validator engine for ensuring the correctness
// of the request. Gin provides a default implementation for this using
// https://github.com/go-playground/validator/tree/v8.18.2.
type StructValidator interface {
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
// If the received type is not a struct, any validation should be skipped and nil must be returned.
// If the received type is a struct or pointer to a struct, the validation should be performed.
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
// Otherwise nil must be returned.
ValidateStruct(interface{}) error
// Engine returns the underlying validator engine which powers the
// StructValidator implementation.
Engine() interface{}
}
// Validator is the default validator which implements the StructValidator
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
// under the hood.
var Validator StructValidator = &defaultValidator{}
// These implement the Binding interface and can be used to bind the data
// present in the request to struct instances.
var (
JSON = jsonBinding{}
XML = xmlBinding{}
Form = formBinding{}
Query = queryBinding{}
FormPost = formPostBinding{}
FormMultipart = formMultipartBinding{}
ProtoBuf = protobufBinding{}
YAML = yamlBinding{}
Uri = uriBinding{}
Header = headerBinding{}
)
// Default returns the appropriate Binding instance based on the HTTP method
// and the content type.
func Default(method, contentType string) Binding {
if method == "GET" {
return Form
}
switch contentType {
case MIMEJSON:
return JSON
case MIMEXML, MIMEXML2:
return XML
case MIMEPROTOBUF:
return ProtoBuf
case MIMEYAML:
return YAML
case MIMEMultipartPOSTForm:
return FormMultipart
default: // case MIMEPOSTForm:
return Form
}
}
func validate(obj interface{}) error {
if Validator == nil {
return nil
}
return Validator.ValidateStruct(obj)
}

View File

@ -0,0 +1,74 @@
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"gin-valid/go-playground/validator/v10"
ut "github.com/go-playground/universal-translator"
zhTrans "go-playground/validator/v10/translations/zh"
"go-playground/locales/zh"
"reflect"
"strings"
"sync"
"go-playground/validator/v10"
)
type defaultValidator struct {
once sync.Once
validate *validator.Validate
}
var _ StructValidator = &defaultValidator{} //这是啥情况?yang
// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
value := reflect.ValueOf(obj)
valueType := value.Kind()
if valueType == reflect.Ptr {
valueType = value.Elem().Kind()
}
if valueType == reflect.Struct {
v.lazyinit()
if err := v.validate.Struct(obj); err != nil {
return err
}
}
return nil
}
// Engine returns the underlying validator engine which powers the default
// Validator instance. This is useful if you want to register custom validations
// or struct level validations. See validator GoDoc for more info -
// https://godoc.org/gopkg.in/go-playground/validator.v8
func (v *defaultValidator) Engine() interface{} {
v.lazyinit()
return v.validate
}
var ValidTrans ut.Translator
func (v *defaultValidator) lazyinit() {
v.once.Do(func() {
v.validate = validator.New()
zh := zh.New()
uni := ut.New(zh, zh)
// this is usually know or extracted from http 'Accept-Language' header
// also see uni.FindTranslator(...)
ValidTrans, _ = uni.GetTranslator("zh")
zhTrans.RegisterDefaultTranslations(v.validate, ValidTrans) // 为gin的校验 注册翻译
v.validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("description"), ",", 2)[0]
//if name == "-" {
// return ""
//}
return name
})
// 设置 tag 的名字
v.validate.SetTagName("binding")
})
}

63
gin/binding/form.go Normal file
View File

@ -0,0 +1,63 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"net/http"
)
const defaultMemory = 32 << 20
type formBinding struct{}
type formPostBinding struct{}
type formMultipartBinding struct{}
func (formBinding) Name() string {
return "form"
}
func (formBinding) Bind(req *http.Request, obj interface{}) error {
if err := req.ParseForm(); err != nil {
return err
}
if err := req.ParseMultipartForm(defaultMemory); err != nil {
if err != http.ErrNotMultipart {
return err
}
}
if err := mapForm(obj, req.Form); err != nil {
return err
}
return validate(obj)
}
func (formPostBinding) Name() string {
return "form-urlencoded"
}
func (formPostBinding) Bind(req *http.Request, obj interface{}) error {
if err := req.ParseForm(); err != nil {
return err
}
if err := mapForm(obj, req.PostForm); err != nil {
return err
}
return validate(obj)
}
func (formMultipartBinding) Name() string {
return "multipart/form-data"
}
func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
if err := req.ParseMultipartForm(defaultMemory); err != nil {
return err
}
if err := mappingByPtr(obj, (*multipartRequest)(req), "form"); err != nil {
return err
}
return validate(obj)
}

392
gin/binding/form_mapping.go Normal file
View File

@ -0,0 +1,392 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"time"
"gin-valid/gin/internal/bytesconv"
"gin-valid/gin/internal/json"
)
var errUnknownType = errors.New("unknown type")
func mapUri(ptr interface{}, m map[string][]string) error {
return mapFormByTag(ptr, m, "uri")
}
func mapForm(ptr interface{}, form map[string][]string) error {
return mapFormByTag(ptr, form, "form")
}
var emptyField = reflect.StructField{}
func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
// Check if ptr is a map
ptrVal := reflect.ValueOf(ptr)
var pointed interface{}
if ptrVal.Kind() == reflect.Ptr {
ptrVal = ptrVal.Elem()
pointed = ptrVal.Interface()
}
if ptrVal.Kind() == reflect.Map &&
ptrVal.Type().Key().Kind() == reflect.String {
if pointed != nil {
ptr = pointed
}
return setFormMap(ptr, form)
}
return mappingByPtr(ptr, formSource(form), tag)
}
// setter tries to set value on a walking by fields of a struct
type setter interface {
TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error)
}
type formSource map[string][]string
var _ setter = formSource(nil)
// TrySet tries to set a value by request's form source (like map[string][]string)
func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
return setByForm(value, field, form, tagValue, opt)
}
func mappingByPtr(ptr interface{}, setter setter, tag string) error {
_, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag)
return err
}
func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
if field.Tag.Get(tag) == "-" { // just ignoring this field
return false, nil
}
var vKind = value.Kind()
if vKind == reflect.Ptr {
var isNew bool
vPtr := value
if value.IsNil() {
isNew = true
vPtr = reflect.New(value.Type().Elem())
}
isSetted, err := mapping(vPtr.Elem(), field, setter, tag)
if err != nil {
return false, err
}
if isNew && isSetted {
value.Set(vPtr)
}
return isSetted, nil
}
if vKind != reflect.Struct || !field.Anonymous {
ok, err := tryToSetValue(value, field, setter, tag)
if err != nil {
return false, err
}
if ok {
return true, nil
}
}
if vKind == reflect.Struct {
tValue := value.Type()
var isSetted bool
for i := 0; i < value.NumField(); i++ {
sf := tValue.Field(i)
if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue
}
ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag)
if err != nil {
return false, err
}
isSetted = isSetted || ok
}
return isSetted, nil
}
return false, nil
}
type setOptions struct {
isDefaultExists bool
defaultValue string
}
func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
var tagValue string
var setOpt setOptions
tagValue = field.Tag.Get(tag)
tagValue, opts := head(tagValue, ",")
if tagValue == "" { // default value is FieldName
tagValue = field.Name
}
if tagValue == "" { // when field is "emptyField" variable
return false, nil
}
var opt string
for len(opts) > 0 {
opt, opts = head(opts, ",")
if k, v := head(opt, "="); k == "default" {
setOpt.isDefaultExists = true
setOpt.defaultValue = v
}
}
return setter.TrySet(value, field, tagValue, setOpt)
}
func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSetted bool, err error) {
vs, ok := form[tagValue]
if !ok && !opt.isDefaultExists {
return false, nil
}
switch value.Kind() {
case reflect.Slice:
if !ok {
vs = []string{opt.defaultValue}
}
return true, setSlice(vs, value, field)
case reflect.Array:
if !ok {
vs = []string{opt.defaultValue}
}
if len(vs) != value.Len() {
return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
}
return true, setArray(vs, value, field)
default:
var val string
if !ok {
val = opt.defaultValue
}
if len(vs) > 0 {
val = vs[0]
}
return true, setWithProperType(val, value, field)
}
}
func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
switch value.Kind() {
case reflect.Int:
return setIntField(val, 0, value)
case reflect.Int8:
return setIntField(val, 8, value)
case reflect.Int16:
return setIntField(val, 16, value)
case reflect.Int32:
return setIntField(val, 32, value)
case reflect.Int64:
switch value.Interface().(type) {
case time.Duration:
return setTimeDuration(val, value, field)
}
return setIntField(val, 64, value)
case reflect.Uint:
return setUintField(val, 0, value)
case reflect.Uint8:
return setUintField(val, 8, value)
case reflect.Uint16:
return setUintField(val, 16, value)
case reflect.Uint32:
return setUintField(val, 32, value)
case reflect.Uint64:
return setUintField(val, 64, value)
case reflect.Bool:
return setBoolField(val, value)
case reflect.Float32:
return setFloatField(val, 32, value)
case reflect.Float64:
return setFloatField(val, 64, value)
case reflect.String:
value.SetString(val)
case reflect.Struct:
switch value.Interface().(type) {
case time.Time:
return setTimeField(val, field, value)
}
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
case reflect.Map:
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
default:
return errUnknownType
}
return nil
}
func setIntField(val string, bitSize int, field reflect.Value) error {
if val == "" {
val = "0"
}
intVal, err := strconv.ParseInt(val, 10, bitSize)
if err == nil {
field.SetInt(intVal)
}
return err
}
func setUintField(val string, bitSize int, field reflect.Value) error {
if val == "" {
val = "0"
}
uintVal, err := strconv.ParseUint(val, 10, bitSize)
if err == nil {
field.SetUint(uintVal)
}
return err
}
func setBoolField(val string, field reflect.Value) error {
if val == "" {
val = "false"
}
boolVal, err := strconv.ParseBool(val)
if err == nil {
field.SetBool(boolVal)
}
return err
}
func setFloatField(val string, bitSize int, field reflect.Value) error {
if val == "" {
val = "0.0"
}
floatVal, err := strconv.ParseFloat(val, bitSize)
if err == nil {
field.SetFloat(floatVal)
}
return err
}
func setTimeField(val string, structField reflect.StructField, value reflect.Value) error {
timeFormat := structField.Tag.Get("time_format")
if timeFormat == "" {
timeFormat = time.RFC3339
}
switch tf := strings.ToLower(timeFormat); tf {
case "unix", "unixnano":
tv, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return err
}
d := time.Duration(1)
if tf == "unixnano" {
d = time.Second
}
t := time.Unix(tv/int64(d), tv%int64(d))
value.Set(reflect.ValueOf(t))
return nil
}
if val == "" {
value.Set(reflect.ValueOf(time.Time{}))
return nil
}
l := time.Local
if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
l = time.UTC
}
if locTag := structField.Tag.Get("time_location"); locTag != "" {
loc, err := time.LoadLocation(locTag)
if err != nil {
return err
}
l = loc
}
t, err := time.ParseInLocation(timeFormat, val, l)
if err != nil {
return err
}
value.Set(reflect.ValueOf(t))
return nil
}
func setArray(vals []string, value reflect.Value, field reflect.StructField) error {
for i, s := range vals {
err := setWithProperType(s, value.Index(i), field)
if err != nil {
return err
}
}
return nil
}
func setSlice(vals []string, value reflect.Value, field reflect.StructField) error {
slice := reflect.MakeSlice(value.Type(), len(vals), len(vals))
err := setArray(vals, slice, field)
if err != nil {
return err
}
value.Set(slice)
return nil
}
func setTimeDuration(val string, value reflect.Value, field reflect.StructField) error {
d, err := time.ParseDuration(val)
if err != nil {
return err
}
value.Set(reflect.ValueOf(d))
return nil
}
func head(str, sep string) (head string, tail string) {
idx := strings.Index(str, sep)
if idx < 0 {
return str, ""
}
return str[:idx], str[idx+len(sep):]
}
func setFormMap(ptr interface{}, form map[string][]string) error {
el := reflect.TypeOf(ptr).Elem()
if el.Kind() == reflect.Slice {
ptrMap, ok := ptr.(map[string][]string)
if !ok {
return errors.New("cannot convert to map slices of strings")
}
for k, v := range form {
ptrMap[k] = v
}
return nil
}
ptrMap, ok := ptr.(map[string]string)
if !ok {
return errors.New("cannot convert to map of strings")
}
for k, v := range form {
ptrMap[k] = v[len(v)-1] // pick last
}
return nil
}

View File

@ -0,0 +1,67 @@
// Copyright 2019 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
var form = map[string][]string{
"name": {"mike"},
"friends": {"anna", "nicole"},
"id_number": {"12345678"},
"id_date": {"2018-01-20"},
}
type structFull struct {
Name string `form:"name"`
Age int `form:"age,default=25"`
Friends []string `form:"friends"`
ID *struct {
Number string `form:"id_number"`
DateOfIssue time.Time `form:"id_date" time_format:"2006-01-02" time_utc:"true"`
}
Nationality *string `form:"nationality"`
}
func BenchmarkMapFormFull(b *testing.B) {
var s structFull
for i := 0; i < b.N; i++ {
err := mapForm(&s, form)
if err != nil {
b.Fatalf("Error on a form mapping")
}
}
b.StopTimer()
t := b
assert.Equal(t, "mike", s.Name)
assert.Equal(t, 25, s.Age)
assert.Equal(t, []string{"anna", "nicole"}, s.Friends)
assert.Equal(t, "12345678", s.ID.Number)
assert.Equal(t, time.Date(2018, 1, 20, 0, 0, 0, 0, time.UTC), s.ID.DateOfIssue)
assert.Nil(t, s.Nationality)
}
type structName struct {
Name string `form:"name"`
}
func BenchmarkMapFormName(b *testing.B) {
var s structName
for i := 0; i < b.N; i++ {
err := mapForm(&s, form)
if err != nil {
b.Fatalf("Error on a form mapping")
}
}
b.StopTimer()
t := b
assert.Equal(t, "mike", s.Name)
}

View File

@ -0,0 +1,281 @@
// Copyright 2019 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestMappingBaseTypes(t *testing.T) {
intPtr := func(i int) *int {
return &i
}
for _, tt := range []struct {
name string
value interface{}
form string
expect interface{}
}{
{"base type", struct{ F int }{}, "9", int(9)},
{"base type", struct{ F int8 }{}, "9", int8(9)},
{"base type", struct{ F int16 }{}, "9", int16(9)},
{"base type", struct{ F int32 }{}, "9", int32(9)},
{"base type", struct{ F int64 }{}, "9", int64(9)},
{"base type", struct{ F uint }{}, "9", uint(9)},
{"base type", struct{ F uint8 }{}, "9", uint8(9)},
{"base type", struct{ F uint16 }{}, "9", uint16(9)},
{"base type", struct{ F uint32 }{}, "9", uint32(9)},
{"base type", struct{ F uint64 }{}, "9", uint64(9)},
{"base type", struct{ F bool }{}, "True", true},
{"base type", struct{ F float32 }{}, "9.1", float32(9.1)},
{"base type", struct{ F float64 }{}, "9.1", float64(9.1)},
{"base type", struct{ F string }{}, "test", string("test")},
{"base type", struct{ F *int }{}, "9", intPtr(9)},
// zero values
{"zero value", struct{ F int }{}, "", int(0)},
{"zero value", struct{ F uint }{}, "", uint(0)},
{"zero value", struct{ F bool }{}, "", false},
{"zero value", struct{ F float32 }{}, "", float32(0)},
} {
tp := reflect.TypeOf(tt.value)
testName := tt.name + ":" + tp.Field(0).Type.String()
val := reflect.New(reflect.TypeOf(tt.value))
val.Elem().Set(reflect.ValueOf(tt.value))
field := val.Elem().Type().Field(0)
_, err := mapping(val, emptyField, formSource{field.Name: {tt.form}}, "form")
assert.NoError(t, err, testName)
actual := val.Elem().Field(0).Interface()
assert.Equal(t, tt.expect, actual, testName)
}
}
func TestMappingDefault(t *testing.T) {
var s struct {
Int int `form:",default=9"`
Slice []int `form:",default=9"`
Array [1]int `form:",default=9"`
}
err := mappingByPtr(&s, formSource{}, "form")
assert.NoError(t, err)
assert.Equal(t, 9, s.Int)
assert.Equal(t, []int{9}, s.Slice)
assert.Equal(t, [1]int{9}, s.Array)
}
func TestMappingSkipField(t *testing.T) {
var s struct {
A int
}
err := mappingByPtr(&s, formSource{}, "form")
assert.NoError(t, err)
assert.Equal(t, 0, s.A)
}
func TestMappingIgnoreField(t *testing.T) {
var s struct {
A int `form:"A"`
B int `form:"-"`
}
err := mappingByPtr(&s, formSource{"A": {"9"}, "B": {"9"}}, "form")
assert.NoError(t, err)
assert.Equal(t, 9, s.A)
assert.Equal(t, 0, s.B)
}
func TestMappingUnexportedField(t *testing.T) {
var s struct {
A int `form:"a"`
b int `form:"b"`
}
err := mappingByPtr(&s, formSource{"a": {"9"}, "b": {"9"}}, "form")
assert.NoError(t, err)
assert.Equal(t, 9, s.A)
assert.Equal(t, 0, s.b)
}
func TestMappingPrivateField(t *testing.T) {
var s struct {
f int `form:"field"`
}
err := mappingByPtr(&s, formSource{"field": {"6"}}, "form")
assert.NoError(t, err)
assert.Equal(t, int(0), s.f)
}
func TestMappingUnknownFieldType(t *testing.T) {
var s struct {
U uintptr
}
err := mappingByPtr(&s, formSource{"U": {"unknown"}}, "form")
assert.Error(t, err)
assert.Equal(t, errUnknownType, err)
}
func TestMappingURI(t *testing.T) {
var s struct {
F int `uri:"field"`
}
err := mapUri(&s, map[string][]string{"field": {"6"}})
assert.NoError(t, err)
assert.Equal(t, int(6), s.F)
}
func TestMappingForm(t *testing.T) {
var s struct {
F int `form:"field"`
}
err := mapForm(&s, map[string][]string{"field": {"6"}})
assert.NoError(t, err)
assert.Equal(t, int(6), s.F)
}
func TestMappingTime(t *testing.T) {
var s struct {
Time time.Time
LocalTime time.Time `time_format:"2006-01-02"`
ZeroValue time.Time
CSTTime time.Time `time_format:"2006-01-02" time_location:"Asia/Shanghai"`
UTCTime time.Time `time_format:"2006-01-02" time_utc:"1"`
}
var err error
time.Local, err = time.LoadLocation("Europe/Berlin")
assert.NoError(t, err)
err = mapForm(&s, map[string][]string{
"Time": {"2019-01-20T16:02:58Z"},
"LocalTime": {"2019-01-20"},
"ZeroValue": {},
"CSTTime": {"2019-01-20"},
"UTCTime": {"2019-01-20"},
})
assert.NoError(t, err)
assert.Equal(t, "2019-01-20 16:02:58 +0000 UTC", s.Time.String())
assert.Equal(t, "2019-01-20 00:00:00 +0100 CET", s.LocalTime.String())
assert.Equal(t, "2019-01-19 23:00:00 +0000 UTC", s.LocalTime.UTC().String())
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", s.ZeroValue.String())
assert.Equal(t, "2019-01-20 00:00:00 +0800 CST", s.CSTTime.String())
assert.Equal(t, "2019-01-19 16:00:00 +0000 UTC", s.CSTTime.UTC().String())
assert.Equal(t, "2019-01-20 00:00:00 +0000 UTC", s.UTCTime.String())
// wrong location
var wrongLoc struct {
Time time.Time `time_location:"wrong"`
}
err = mapForm(&wrongLoc, map[string][]string{"Time": {"2019-01-20T16:02:58Z"}})
assert.Error(t, err)
// wrong time value
var wrongTime struct {
Time time.Time
}
err = mapForm(&wrongTime, map[string][]string{"Time": {"wrong"}})
assert.Error(t, err)
}
func TestMappingTimeDuration(t *testing.T) {
var s struct {
D time.Duration
}
// ok
err := mappingByPtr(&s, formSource{"D": {"5s"}}, "form")
assert.NoError(t, err)
assert.Equal(t, 5*time.Second, s.D)
// error
err = mappingByPtr(&s, formSource{"D": {"wrong"}}, "form")
assert.Error(t, err)
}
func TestMappingSlice(t *testing.T) {
var s struct {
Slice []int `form:"slice,default=9"`
}
// default value
err := mappingByPtr(&s, formSource{}, "form")
assert.NoError(t, err)
assert.Equal(t, []int{9}, s.Slice)
// ok
err = mappingByPtr(&s, formSource{"slice": {"3", "4"}}, "form")
assert.NoError(t, err)
assert.Equal(t, []int{3, 4}, s.Slice)
// error
err = mappingByPtr(&s, formSource{"slice": {"wrong"}}, "form")
assert.Error(t, err)
}
func TestMappingArray(t *testing.T) {
var s struct {
Array [2]int `form:"array,default=9"`
}
// wrong default
err := mappingByPtr(&s, formSource{}, "form")
assert.Error(t, err)
// ok
err = mappingByPtr(&s, formSource{"array": {"3", "4"}}, "form")
assert.NoError(t, err)
assert.Equal(t, [2]int{3, 4}, s.Array)
// error - not enough vals
err = mappingByPtr(&s, formSource{"array": {"3"}}, "form")
assert.Error(t, err)
// error - wrong value
err = mappingByPtr(&s, formSource{"array": {"wrong"}}, "form")
assert.Error(t, err)
}
func TestMappingStructField(t *testing.T) {
var s struct {
J struct {
I int
}
}
err := mappingByPtr(&s, formSource{"J": {`{"I": 9}`}}, "form")
assert.NoError(t, err)
assert.Equal(t, 9, s.J.I)
}
func TestMappingMapField(t *testing.T) {
var s struct {
M map[string]int
}
err := mappingByPtr(&s, formSource{"M": {`{"one": 1}`}}, "form")
assert.NoError(t, err)
assert.Equal(t, map[string]int{"one": 1}, s.M)
}
func TestMappingIgnoredCircularRef(t *testing.T) {
type S struct {
S *S `form:"-"`
}
var s S
err := mappingByPtr(&s, formSource{}, "form")
assert.NoError(t, err)
}

34
gin/binding/header.go Normal file
View File

@ -0,0 +1,34 @@
package binding
import (
"net/http"
"net/textproto"
"reflect"
)
type headerBinding struct{}
func (headerBinding) Name() string {
return "header"
}
func (headerBinding) Bind(req *http.Request, obj interface{}) error {
if err := mapHeader(obj, req.Header); err != nil {
return err
}
return validate(obj)
}
func mapHeader(ptr interface{}, h map[string][]string) error {
return mappingByPtr(ptr, headerSource(h), "header")
}
type headerSource map[string][]string
var _ setter = headerSource(nil)
func (hs headerSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
return setByForm(value, field, hs, textproto.CanonicalMIMEHeaderKey(tagValue), opt)
}

56
gin/binding/json.go Normal file
View File

@ -0,0 +1,56 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"bytes"
"fmt"
"io"
"net/http"
"gin-valid/gin/internal/json"
)
// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
// interface{} as a Number instead of as a float64.
var EnableDecoderUseNumber = false
// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method
// on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to
// return an error when the destination is a struct and the input contains object
// keys which do not match any non-ignored, exported fields in the destination.
var EnableDecoderDisallowUnknownFields = false
type jsonBinding struct{}
func (jsonBinding) Name() string {
return "json"
}
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
if req == nil || req.Body == nil {
return fmt.Errorf("invalid request")
}
return decodeJSON(req.Body, obj)
}
func (jsonBinding) BindBody(body []byte, obj interface{}) error {
return decodeJSON(bytes.NewReader(body), obj)
}
func decodeJSON(r io.Reader, obj interface{}) error {
decoder := json.NewDecoder(r)
if EnableDecoderUseNumber {
decoder.UseNumber()
}
if EnableDecoderDisallowUnknownFields {
decoder.DisallowUnknownFields()
}
if err := decoder.Decode(obj); err != nil {
return err
}
return validate(obj)
}

30
gin/binding/json_test.go Normal file
View File

@ -0,0 +1,30 @@
/ Copyright 2019 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestJSONBindingBindBody(t *testing.T) {
var s struct {
Foo string `json:"foo"`
}
err := jsonBinding{}.BindBody([]byte(`{"foo": "FOO"}`), &s)
require.NoError(t, err)
assert.Equal(t, "FOO", s.Foo)
}
func TestJSONBindingBindBodyMap(t *testing.T) {
s := make(map[string]string)
err := jsonBinding{}.BindBody([]byte(`{"foo": "FOO","hello":"world"}`), &s)
require.NoError(t, err)
assert.Len(t, s, 2)
assert.Equal(t, "FOO", s["foo"])
assert.Equal(t, "world", s["hello"])
}

37
gin/binding/msgpack.go Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// +build !nomsgpack
package binding
import (
"bytes"
"io"
"net/http"
"github.com/ugorji/go/codec"
)
type msgpackBinding struct{}
func (msgpackBinding) Name() string {
return "msgpack"
}
func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {
return decodeMsgPack(req.Body, obj)
}
func (msgpackBinding) BindBody(body []byte, obj interface{}) error {
return decodeMsgPack(bytes.NewReader(body), obj)
}
func decodeMsgPack(r io.Reader, obj interface{}) error {
cdc := new(codec.MsgpackHandle)
if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil {
return err
}
return validate(obj)
}

View File

@ -0,0 +1,34 @@
// Copyright 2019 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// +build !nomsgpack
package binding
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ugorji/go/codec"
)
func TestMsgpackBindingBindBody(t *testing.T) {
type teststruct struct {
Foo string `msgpack:"foo"`
}
var s teststruct
err := msgpackBinding{}.BindBody(msgpackBody(t, teststruct{"FOO"}), &s)
require.NoError(t, err)
assert.Equal(t, "FOO", s.Foo)
}
func msgpackBody(t *testing.T, obj interface{}) []byte {
var bs bytes.Buffer
h := &codec.MsgpackHandle{}
err := codec.NewEncoder(&bs, h).Encode(obj)
require.NoError(t, err)
return bs.Bytes()
}

View File

@ -0,0 +1,66 @@
// Copyright 2019 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"errors"
"mime/multipart"
"net/http"
"reflect"
)
type multipartRequest http.Request
var _ setter = (*multipartRequest)(nil)
// TrySet tries to set a value by the multipart request with the binding a form file
func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error) {
if files := r.MultipartForm.File[key]; len(files) != 0 {
return setByMultipartFormFile(value, field, files)
}
return setByForm(value, field, r.MultipartForm.Value, key, opt)
}
func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
switch value.Kind() {
case reflect.Ptr:
switch value.Interface().(type) {
case *multipart.FileHeader:
value.Set(reflect.ValueOf(files[0]))
return true, nil
}
case reflect.Struct:
switch value.Interface().(type) {
case multipart.FileHeader:
value.Set(reflect.ValueOf(*files[0]))
return true, nil
}
case reflect.Slice:
slice := reflect.MakeSlice(value.Type(), len(files), len(files))
isSetted, err = setArrayOfMultipartFormFiles(slice, field, files)
if err != nil || !isSetted {
return isSetted, err
}
value.Set(slice)
return true, nil
case reflect.Array:
return setArrayOfMultipartFormFiles(value, field, files)
}
return false, errors.New("unsupported field type for multipart.FileHeader")
}
func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
if value.Len() != len(files) {
return false, errors.New("unsupported len of array for []*multipart.FileHeader")
}
for i := range files {
setted, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1])
if err != nil || !setted {
return setted, err
}
}
return true, nil
}

View File

@ -0,0 +1,138 @@
// Copyright 2019 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"bytes"
"io/ioutil"
"mime/multipart"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFormMultipartBindingBindOneFile(t *testing.T) {
var s struct {
FileValue multipart.FileHeader `form:"file"`
FilePtr *multipart.FileHeader `form:"file"`
SliceValues []multipart.FileHeader `form:"file"`
SlicePtrs []*multipart.FileHeader `form:"file"`
ArrayValues [1]multipart.FileHeader `form:"file"`
ArrayPtrs [1]*multipart.FileHeader `form:"file"`
}
file := testFile{"file", "file1", []byte("hello")}
req := createRequestMultipartFiles(t, file)
err := FormMultipart.Bind(req, &s)
assert.NoError(t, err)
assertMultipartFileHeader(t, &s.FileValue, file)
assertMultipartFileHeader(t, s.FilePtr, file)
assert.Len(t, s.SliceValues, 1)
assertMultipartFileHeader(t, &s.SliceValues[0], file)
assert.Len(t, s.SlicePtrs, 1)
assertMultipartFileHeader(t, s.SlicePtrs[0], file)
assertMultipartFileHeader(t, &s.ArrayValues[0], file)
assertMultipartFileHeader(t, s.ArrayPtrs[0], file)
}
func TestFormMultipartBindingBindTwoFiles(t *testing.T) {
var s struct {
SliceValues []multipart.FileHeader `form:"file"`
SlicePtrs []*multipart.FileHeader `form:"file"`
ArrayValues [2]multipart.FileHeader `form:"file"`
ArrayPtrs [2]*multipart.FileHeader `form:"file"`
}
files := []testFile{
{"file", "file1", []byte("hello")},
{"file", "file2", []byte("world")},
}
req := createRequestMultipartFiles(t, files...)
err := FormMultipart.Bind(req, &s)
assert.NoError(t, err)
assert.Len(t, s.SliceValues, len(files))
assert.Len(t, s.SlicePtrs, len(files))
assert.Len(t, s.ArrayValues, len(files))
assert.Len(t, s.ArrayPtrs, len(files))
for i, file := range files {
assertMultipartFileHeader(t, &s.SliceValues[i], file)
assertMultipartFileHeader(t, s.SlicePtrs[i], file)
assertMultipartFileHeader(t, &s.ArrayValues[i], file)
assertMultipartFileHeader(t, s.ArrayPtrs[i], file)
}
}
func TestFormMultipartBindingBindError(t *testing.T) {
files := []testFile{
{"file", "file1", []byte("hello")},
{"file", "file2", []byte("world")},
}
for _, tt := range []struct {
name string
s interface{}
}{
{"wrong type", &struct {
Files int `form:"file"`
}{}},
{"wrong array size", &struct {
Files [1]*multipart.FileHeader `form:"file"`
}{}},
{"wrong slice type", &struct {
Files []int `form:"file"`
}{}},
} {
req := createRequestMultipartFiles(t, files...)
err := FormMultipart.Bind(req, tt.s)
assert.Error(t, err)
}
}
type testFile struct {
Fieldname string
Filename string
Content []byte
}
func createRequestMultipartFiles(t *testing.T, files ...testFile) *http.Request {
var body bytes.Buffer
mw := multipart.NewWriter(&body)
for _, file := range files {
fw, err := mw.CreateFormFile(file.Fieldname, file.Filename)
assert.NoError(t, err)
n, err := fw.Write(file.Content)
assert.NoError(t, err)
assert.Equal(t, len(file.Content), n)
}
err := mw.Close()
assert.NoError(t, err)
req, err := http.NewRequest("POST", "/", &body)
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+mw.Boundary())
return req
}
func assertMultipartFileHeader(t *testing.T, fh *multipart.FileHeader, file testFile) {
assert.Equal(t, file.Filename, fh.Filename)
// assert.Equal(t, int64(len(file.Content)), fh.Size) // fh.Size does not exist on go1.8
fl, err := fh.Open()
assert.NoError(t, err)
body, err := ioutil.ReadAll(fl)
assert.NoError(t, err)
assert.Equal(t, string(file.Content), string(body))
err = fl.Close()
assert.NoError(t, err)
}

36
gin/binding/protobuf.go Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"io/ioutil"
"net/http"
"github.com/golang/protobuf/proto"
)
type protobufBinding struct{}
func (protobufBinding) Name() string {
return "protobuf"
}
func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
buf, err := ioutil.ReadAll(req.Body)
if err != nil {
return err
}
return b.BindBody(buf, obj)
}
func (protobufBinding) BindBody(body []byte, obj interface{}) error {
if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
return err
}
// Here it's same to return validate(obj), but util now we can't add
// `binding:""` to the struct which automatically generate by gen-proto
return nil
// return validate(obj)
}

21
gin/binding/query.go Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import "net/http"
type queryBinding struct{}
func (queryBinding) Name() string {
return "query"
}
func (queryBinding) Bind(req *http.Request, obj interface{}) error {
values := req.URL.Query()
if err := mapForm(obj, values); err != nil {
return err
}
return validate(obj)
}

18
gin/binding/uri.go Normal file
View File

@ -0,0 +1,18 @@
// Copyright 2018 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
type uriBinding struct{}
func (uriBinding) Name() string {
return "uri"
}
func (uriBinding) BindUri(m map[string][]string, obj interface{}) error {
if err := mapUri(obj, m); err != nil {
return err
}
return validate(obj)
}

View File

@ -0,0 +1,228 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"bytes"
"testing"
"time"
"go-playground/validator/v10"
"github.com/stretchr/testify/assert"
)
type testInterface interface {
String() string
}
type substructNoValidation struct {
IString string
IInt int
}
type mapNoValidationSub map[string]substructNoValidation
type structNoValidationValues struct {
substructNoValidation
Boolean bool
Uinteger uint
Integer int
Integer8 int8
Integer16 int16
Integer32 int32
Integer64 int64
Uinteger8 uint8
Uinteger16 uint16
Uinteger32 uint32
Uinteger64 uint64
Float32 float32
Float64 float64
String string
Date time.Time
Struct substructNoValidation
InlinedStruct struct {
String []string
Integer int
}
IntSlice []int
IntPointerSlice []*int
StructPointerSlice []*substructNoValidation
StructSlice []substructNoValidation
InterfaceSlice []testInterface
UniversalInterface interface{}
CustomInterface testInterface
FloatMap map[string]float32
StructMap mapNoValidationSub
}
func createNoValidationValues() structNoValidationValues {
integer := 1
s := structNoValidationValues{
Boolean: true,
Uinteger: 1 << 29,
Integer: -10000,
Integer8: 120,
Integer16: -20000,
Integer32: 1 << 29,
Integer64: 1 << 61,
Uinteger8: 250,
Uinteger16: 50000,
Uinteger32: 1 << 31,
Uinteger64: 1 << 62,
Float32: 123.456,
Float64: 123.456789,
String: "text",
Date: time.Time{},
CustomInterface: &bytes.Buffer{},
Struct: substructNoValidation{},
IntSlice: []int{-3, -2, 1, 0, 1, 2, 3},
IntPointerSlice: []*int{&integer},
StructSlice: []substructNoValidation{},
UniversalInterface: 1.2,
FloatMap: map[string]float32{
"foo": 1.23,
"bar": 232.323,
},
StructMap: mapNoValidationSub{
"foo": substructNoValidation{},
"bar": substructNoValidation{},
},
// StructPointerSlice []noValidationSub
// InterfaceSlice []testInterface
}
s.InlinedStruct.Integer = 1000
s.InlinedStruct.String = []string{"first", "second"}
s.IString = "substring"
s.IInt = 987654
return s
}
func TestValidateNoValidationValues(t *testing.T) {
origin := createNoValidationValues()
test := createNoValidationValues()
empty := structNoValidationValues{}
assert.Nil(t, validate(test))
assert.Nil(t, validate(&test))
assert.Nil(t, validate(empty))
assert.Nil(t, validate(&empty))
assert.Equal(t, origin, test)
}
type structNoValidationPointer struct {
substructNoValidation
Boolean bool
Uinteger *uint
Integer *int
Integer8 *int8
Integer16 *int16
Integer32 *int32
Integer64 *int64
Uinteger8 *uint8
Uinteger16 *uint16
Uinteger32 *uint32
Uinteger64 *uint64
Float32 *float32
Float64 *float64
String *string
Date *time.Time
Struct *substructNoValidation
IntSlice *[]int
IntPointerSlice *[]*int
StructPointerSlice *[]*substructNoValidation
StructSlice *[]substructNoValidation
InterfaceSlice *[]testInterface
FloatMap *map[string]float32
StructMap *mapNoValidationSub
}
func TestValidateNoValidationPointers(t *testing.T) {
//origin := createNoValidation_values()
//test := createNoValidation_values()
empty := structNoValidationPointer{}
//assert.Nil(t, validate(test))
//assert.Nil(t, validate(&test))
assert.Nil(t, validate(empty))
assert.Nil(t, validate(&empty))
//assert.Equal(t, origin, test)
}
type Object map[string]interface{}
func TestValidatePrimitives(t *testing.T) {
obj := Object{"foo": "bar", "bar": 1}
assert.NoError(t, validate(obj))
assert.NoError(t, validate(&obj))
assert.Equal(t, Object{"foo": "bar", "bar": 1}, obj)
obj2 := []Object{{"foo": "bar", "bar": 1}, {"foo": "bar", "bar": 1}}
assert.NoError(t, validate(obj2))
assert.NoError(t, validate(&obj2))
nu := 10
assert.NoError(t, validate(nu))
assert.NoError(t, validate(&nu))
assert.Equal(t, 10, nu)
str := "value"
assert.NoError(t, validate(str))
assert.NoError(t, validate(&str))
assert.Equal(t, "value", str)
}
// structCustomValidation is a helper struct we use to check that
// custom validation can be registered on it.
// The `notone` binding directive is for custom validation and registered later.
type structCustomValidation struct {
Integer int `binding:"notone"`
}
func notOne(f1 validator.FieldLevel) bool {
if val, ok := f1.Field().Interface().(int); ok {
return val != 1
}
return false
}
func TestValidatorEngine(t *testing.T) {
// This validates that the function `notOne` matches
// the expected function signature by `defaultValidator`
// and by extension the validator library.
engine, ok := Validator.Engine().(*validator.Validate)
assert.True(t, ok)
err := engine.RegisterValidation("notone", notOne)
// Check that we can register custom validation without error
assert.Nil(t, err)
// Create an instance which will fail validation
withOne := structCustomValidation{Integer: 1}
errs := validate(withOne)
// Check that we got back non-nil errs
assert.NotNil(t, errs)
// Check that the error matches expectation
assert.Error(t, errs, "", "", "notone")
}

33
gin/binding/xml.go Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"bytes"
"encoding/xml"
"io"
"net/http"
)
type xmlBinding struct{}
func (xmlBinding) Name() string {
return "xml"
}
func (xmlBinding) Bind(req *http.Request, obj interface{}) error {
return decodeXML(req.Body, obj)
}
func (xmlBinding) BindBody(body []byte, obj interface{}) error {
return decodeXML(bytes.NewReader(body), obj)
}
func decodeXML(r io.Reader, obj interface{}) error {
decoder := xml.NewDecoder(r)
if err := decoder.Decode(obj); err != nil {
return err
}
return validate(obj)
}

25
gin/binding/xml_test.go Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2019 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestXMLBindingBindBody(t *testing.T) {
var s struct {
Foo string `xml:"foo"`
}
xmlBody := `<?xml version="1.0" encoding="UTF-8"?>
<root>
<foo>FOO</foo>
</root>`
err := xmlBinding{}.BindBody([]byte(xmlBody), &s)
require.NoError(t, err)
assert.Equal(t, "FOO", s.Foo)
}

35
gin/binding/yaml.go Normal file
View File

@ -0,0 +1,35 @@
// Copyright 2018 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"bytes"
"io"
"net/http"
"gopkg.in/yaml.v2"
)
type yamlBinding struct{}
func (yamlBinding) Name() string {
return "yaml"
}
func (yamlBinding) Bind(req *http.Request, obj interface{}) error {
return decodeYAML(req.Body, obj)
}
func (yamlBinding) BindBody(body []byte, obj interface{}) error {
return decodeYAML(bytes.NewReader(body), obj)
}
func decodeYAML(r io.Reader, obj interface{}) error {
decoder := yaml.NewDecoder(r)
if err := decoder.Decode(obj); err != nil {
return err
}
return validate(obj)
}

21
gin/binding/yaml_test.go Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2019 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestYAMLBindingBindBody(t *testing.T) {
var s struct {
Foo string `yaml:"foo"`
}
err := yamlBinding{}.BindBody([]byte("foo: FOO"), &s)
require.NoError(t, err)
assert.Equal(t, "FOO", s.Foo)
}

View File

@ -0,0 +1,23 @@
// Copyright 2020 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package bytesconv
import (
"reflect"
"unsafe"
)
// StringToBytes converts string to byte slice without a memory allocation.
func StringToBytes(s string) (b []byte) {
sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
return b
}
// BytesToString converts byte slice to string without a memory allocation.
func BytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}

View File

@ -0,0 +1,99 @@
// Copyright 2020 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package bytesconv
import (
"bytes"
"math/rand"
"strings"
"testing"
"time"
)
var testString = "Albert Einstein: Logic will get you from A to B. Imagination will take you everywhere."
var testBytes = []byte(testString)
func rawBytesToStr(b []byte) string {
return string(b)
}
func rawStrToBytes(s string) []byte {
return []byte(s)
}
// go test -v
func TestBytesToString(t *testing.T) {
data := make([]byte, 1024)
for i := 0; i < 100; i++ {
rand.Read(data)
if rawBytesToStr(data) != BytesToString(data) {
t.Fatal("don't match")
}
}
}
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
var src = rand.NewSource(time.Now().UnixNano())
func RandStringBytesMaskImprSrcSB(n int) string {
sb := strings.Builder{}
sb.Grow(n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
sb.WriteByte(letterBytes[idx])
i--
}
cache >>= letterIdxBits
remain--
}
return sb.String()
}
func TestStringToBytes(t *testing.T) {
for i := 0; i < 100; i++ {
s := RandStringBytesMaskImprSrcSB(64)
if !bytes.Equal(rawStrToBytes(s), StringToBytes(s)) {
t.Fatal("don't match")
}
}
}
// go test -v -run=none -bench=^BenchmarkBytesConv -benchmem=true
func BenchmarkBytesConvBytesToStrRaw(b *testing.B) {
for i := 0; i < b.N; i++ {
rawBytesToStr(testBytes)
}
}
func BenchmarkBytesConvBytesToStr(b *testing.B) {
for i := 0; i < b.N; i++ {
BytesToString(testBytes)
}
}
func BenchmarkBytesConvStrToBytesRaw(b *testing.B) {
for i := 0; i < b.N; i++ {
rawStrToBytes(testString)
}
}
func BenchmarkBytesConvStrToBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
StringToBytes(testString)
}
}

22
gin/internal/json/json.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2017 Bo-Yi Wu. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// +build !jsoniter
package json
import "encoding/json"
var (
// Marshal is exported by gin/json package.
Marshal = json.Marshal
// Unmarshal is exported by gin/json package.
Unmarshal = json.Unmarshal
// MarshalIndent is exported by gin/json package.
MarshalIndent = json.MarshalIndent
// NewDecoder is exported by gin/json package.
NewDecoder = json.NewDecoder
// NewEncoder is exported by gin/json package.
NewEncoder = json.NewEncoder
)

View File

@ -0,0 +1,23 @@
// Copyright 2017 Bo-Yi Wu. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// +build jsoniter
package json
import jsoniter "github.com/json-iterator/go"
var (
json = jsoniter.ConfigCompatibleWithStandardLibrary
// Marshal is exported by gin/json package.
Marshal = json.Marshal
// Unmarshal is exported by gin/json package.
Unmarshal = json.Unmarshal
// MarshalIndent is exported by gin/json package.
MarshalIndent = json.MarshalIndent
// NewDecoder is exported by gin/json package.
NewDecoder = json.NewDecoder
// NewEncoder is exported by gin/json package.
NewEncoder = json.NewEncoder
)

24
go-playground/locales/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

View File

@ -0,0 +1,26 @@
language: go
go:
- 1.13.1
- tip
matrix:
allow_failures:
- go: tip
notifications:
email:
recipients: dean.karn@gmail.com
on_success: change
on_failure: always
before_install:
- go install github.com/mattn/goveralls
# Only clone the most recent commit.
git:
depth: 1
script:
- go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
after_success: |
goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Go Playground
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,172 @@
## locales
<img align="right" src="https://raw.githubusercontent.com/go-playground/locales/master/logo.png">![Project status](https://img.shields.io/badge/version-0.13.0-green.svg)
[![Build Status](https://travis-ci.org/go-playground/locales.svg?branch=master)](https://travis-ci.org/go-playground/locales)
[![Go Report Card](https://goreportcard.com/badge/go-playground/locales)](https://goreportcard.com/report/go-playground/locales)
[![GoDoc](https://godoc.org/go-playground/locales?status.svg)](https://godoc.org/go-playground/locales)
![License](https://img.shields.io/dub/l/vibe-d.svg)
[![Gitter](https://badges.gitter.im/go-playground/locales.svg)](https://gitter.im/go-playground/locales?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Locales is a set of locales generated from the [Unicode CLDR Project](http://cldr.unicode.org/) which can be used independently or within
an i18n package; these were built for use with, but not exclusive to, [Universal Translator](https://github.com/go-playground/universal-translator).
Features
--------
- [x] Rules generated from the latest [CLDR](http://cldr.unicode.org/index/downloads) data, v31.0.1
- [x] Contains Cardinal, Ordinal and Range Plural Rules
- [x] Contains Month, Weekday and Timezone translations built in
- [x] Contains Date & Time formatting functions
- [x] Contains Number, Currency, Accounting and Percent formatting functions
- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere )
Full Tests
--------------------
I could sure use your help adding tests for every locale, it is a huge undertaking and I just don't have the free time to do it all at the moment;
any help would be **greatly appreciated!!!!** please see [issue](https://go-playground/locales/issues/1) for details.
Installation
-----------
Use go get
```shell
go get go-playground/locales
```
NOTES
--------
You'll notice most return types are []byte, this is because most of the time the results will be concatenated with a larger body
of text and can avoid some allocations if already appending to a byte array, otherwise just cast as string.
Usage
-------
```go
package main
import (
"fmt"
"time"
"go-playground/locales/currency"
"go-playground/locales/en_CA"
)
func main() {
loc, _ := time.LoadLocation("America/Toronto")
datetime := time.Date(2016, 02, 03, 9, 0, 1, 0, loc)
l := en_CA.New()
// Dates
fmt.Println(l.FmtDateFull(datetime))
fmt.Println(l.FmtDateLong(datetime))
fmt.Println(l.FmtDateMedium(datetime))
fmt.Println(l.FmtDateShort(datetime))
// Times
fmt.Println(l.FmtTimeFull(datetime))
fmt.Println(l.FmtTimeLong(datetime))
fmt.Println(l.FmtTimeMedium(datetime))
fmt.Println(l.FmtTimeShort(datetime))
// Months Wide
fmt.Println(l.MonthWide(time.January))
fmt.Println(l.MonthWide(time.February))
fmt.Println(l.MonthWide(time.March))
// ...
// Months Abbreviated
fmt.Println(l.MonthAbbreviated(time.January))
fmt.Println(l.MonthAbbreviated(time.February))
fmt.Println(l.MonthAbbreviated(time.March))
// ...
// Months Narrow
fmt.Println(l.MonthNarrow(time.January))
fmt.Println(l.MonthNarrow(time.February))
fmt.Println(l.MonthNarrow(time.March))
// ...
// Weekdays Wide
fmt.Println(l.WeekdayWide(time.Sunday))
fmt.Println(l.WeekdayWide(time.Monday))
fmt.Println(l.WeekdayWide(time.Tuesday))
// ...
// Weekdays Abbreviated
fmt.Println(l.WeekdayAbbreviated(time.Sunday))
fmt.Println(l.WeekdayAbbreviated(time.Monday))
fmt.Println(l.WeekdayAbbreviated(time.Tuesday))
// ...
// Weekdays Short
fmt.Println(l.WeekdayShort(time.Sunday))
fmt.Println(l.WeekdayShort(time.Monday))
fmt.Println(l.WeekdayShort(time.Tuesday))
// ...
// Weekdays Narrow
fmt.Println(l.WeekdayNarrow(time.Sunday))
fmt.Println(l.WeekdayNarrow(time.Monday))
fmt.Println(l.WeekdayNarrow(time.Tuesday))
// ...
var f64 float64
f64 = -10356.4523
// Number
fmt.Println(l.FmtNumber(f64, 2))
// Currency
fmt.Println(l.FmtCurrency(f64, 2, currency.CAD))
fmt.Println(l.FmtCurrency(f64, 2, currency.USD))
// Accounting
fmt.Println(l.FmtAccounting(f64, 2, currency.CAD))
fmt.Println(l.FmtAccounting(f64, 2, currency.USD))
f64 = 78.12
// Percent
fmt.Println(l.FmtPercent(f64, 0))
// Plural Rules for locale, so you know what rules you must cover
fmt.Println(l.PluralsCardinal())
fmt.Println(l.PluralsOrdinal())
// Cardinal Plural Rules
fmt.Println(l.CardinalPluralRule(1, 0))
fmt.Println(l.CardinalPluralRule(1.0, 0))
fmt.Println(l.CardinalPluralRule(1.0, 1))
fmt.Println(l.CardinalPluralRule(3, 0))
// Ordinal Plural Rules
fmt.Println(l.OrdinalPluralRule(21, 0)) // 21st
fmt.Println(l.OrdinalPluralRule(22, 0)) // 22nd
fmt.Println(l.OrdinalPluralRule(33, 0)) // 33rd
fmt.Println(l.OrdinalPluralRule(34, 0)) // 34th
// Range Plural Rules
fmt.Println(l.RangePluralRule(1, 0, 1, 0)) // 1-1
fmt.Println(l.RangePluralRule(1, 0, 2, 0)) // 1-2
fmt.Println(l.RangePluralRule(5, 0, 8, 0)) // 5-8
}
```
NOTES:
-------
These rules were generated from the [Unicode CLDR Project](http://cldr.unicode.org/), if you encounter any issues
I strongly encourage contributing to the CLDR project to get the locale information corrected and the next time
these locales are regenerated the fix will come with.
I do however realize that time constraints are often important and so there are two options:
1. Create your own locale, copy, paste and modify, and ensure it complies with the `Translator` interface.
2. Add an exception in the locale generation code directly and once regenerated, fix will be in place.
Please to not make fixes inside the locale files, they WILL get overwritten when the locales are regenerated.
License
------
Distributed under MIT License, please see license file in code for more details.

View File

@ -0,0 +1,308 @@
package currency
// Type is the currency type associated with the locales currency enum
type Type int
// locale currencies
const (
ADP Type = iota
AED
AFA
AFN
ALK
ALL
AMD
ANG
AOA
AOK
AON
AOR
ARA
ARL
ARM
ARP
ARS
ATS
AUD
AWG
AZM
AZN
BAD
BAM
BAN
BBD
BDT
BEC
BEF
BEL
BGL
BGM
BGN
BGO
BHD
BIF
BMD
BND
BOB
BOL
BOP
BOV
BRB
BRC
BRE
BRL
BRN
BRR
BRZ
BSD
BTN
BUK
BWP
BYB
BYN
BYR
BZD
CAD
CDF
CHE
CHF
CHW
CLE
CLF
CLP
CNH
CNX
CNY
COP
COU
CRC
CSD
CSK
CUC
CUP
CVE
CYP
CZK
DDM
DEM
DJF
DKK
DOP
DZD
ECS
ECV
EEK
EGP
ERN
ESA
ESB
ESP
ETB
EUR
FIM
FJD
FKP
FRF
GBP
GEK
GEL
GHC
GHS
GIP
GMD
GNF
GNS
GQE
GRD
GTQ
GWE
GWP
GYD
HKD
HNL
HRD
HRK
HTG
HUF
IDR
IEP
ILP
ILR
ILS
INR
IQD
IRR
ISJ
ISK
ITL
JMD
JOD
JPY
KES
KGS
KHR
KMF
KPW
KRH
KRO
KRW
KWD
KYD
KZT
LAK
LBP
LKR
LRD
LSL
LTL
LTT
LUC
LUF
LUL
LVL
LVR
LYD
MAD
MAF
MCF
MDC
MDL
MGA
MGF
MKD
MKN
MLF
MMK
MNT
MOP
MRO
MTL
MTP
MUR
MVP
MVR
MWK
MXN
MXP
MXV
MYR
MZE
MZM
MZN
NAD
NGN
NIC
NIO
NLG
NOK
NPR
NZD
OMR
PAB
PEI
PEN
PES
PGK
PHP
PKR
PLN
PLZ
PTE
PYG
QAR
RHD
ROL
RON
RSD
RUB
RUR
RWF
SAR
SBD
SCR
SDD
SDG
SDP
SEK
SGD
SHP
SIT
SKK
SLL
SOS
SRD
SRG
SSP
STD
STN
SUR
SVC
SYP
SZL
THB
TJR
TJS
TMM
TMT
TND
TOP
TPE
TRL
TRY
TTD
TWD
TZS
UAH
UAK
UGS
UGX
USD
USN
USS
UYI
UYP
UYU
UZS
VEB
VEF
VND
VNN
VUV
WST
XAF
XAG
XAU
XBA
XBB
XBC
XBD
XCD
XDR
XEU
XFO
XFU
XOF
XPD
XPF
XPT
XRE
XSU
XTS
XUA
XXX
YDD
YER
YUD
YUM
YUN
YUR
ZAL
ZAR
ZMK
ZMW
ZRN
ZRZ
ZWD
ZWL
ZWR
)

View File

@ -0,0 +1,3 @@
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,293 @@
package locales
import (
"strconv"
"time"
"go-playground/locales/currency"
)
// // ErrBadNumberValue is returned when the number passed for
// // plural rule determination cannot be parsed
// type ErrBadNumberValue struct {
// NumberValue string
// InnerError error
// }
// // Error returns ErrBadNumberValue error string
// func (e *ErrBadNumberValue) Error() string {
// return fmt.Sprintf("Invalid Number Value '%s' %s", e.NumberValue, e.InnerError)
// }
// var _ error = new(ErrBadNumberValue)
// PluralRule denotes the type of plural rules
type PluralRule int
// PluralRule's
const (
PluralRuleUnknown PluralRule = iota
PluralRuleZero // zero
PluralRuleOne // one - singular
PluralRuleTwo // two - dual
PluralRuleFew // few - paucal
PluralRuleMany // many - also used for fractions if they have a separate class
PluralRuleOther // other - required—general plural form—also used if the language only has a single form
)
const (
pluralsString = "UnknownZeroOneTwoFewManyOther"
)
// Translator encapsulates an instance of a locale
// NOTE: some values are returned as a []byte just in case the caller
// wishes to add more and can help avoid allocations; otherwise just cast as string
type Translator interface {
// The following Functions are for overriding, debugging or developing
// with a Translator Locale
// Locale returns the string value of the translator
Locale() string
// returns an array of cardinal plural rules associated
// with this translator
PluralsCardinal() []PluralRule
// returns an array of ordinal plural rules associated
// with this translator
PluralsOrdinal() []PluralRule
// returns an array of range plural rules associated
// with this translator
PluralsRange() []PluralRule
// returns the cardinal PluralRule given 'num' and digits/precision of 'v' for locale
CardinalPluralRule(num float64, v uint64) PluralRule
// returns the ordinal PluralRule given 'num' and digits/precision of 'v' for locale
OrdinalPluralRule(num float64, v uint64) PluralRule
// returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for locale
RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) PluralRule
// returns the locales abbreviated month given the 'month' provided
MonthAbbreviated(month time.Month) string
// returns the locales abbreviated months
MonthsAbbreviated() []string
// returns the locales narrow month given the 'month' provided
MonthNarrow(month time.Month) string
// returns the locales narrow months
MonthsNarrow() []string
// returns the locales wide month given the 'month' provided
MonthWide(month time.Month) string
// returns the locales wide months
MonthsWide() []string
// returns the locales abbreviated weekday given the 'weekday' provided
WeekdayAbbreviated(weekday time.Weekday) string
// returns the locales abbreviated weekdays
WeekdaysAbbreviated() []string
// returns the locales narrow weekday given the 'weekday' provided
WeekdayNarrow(weekday time.Weekday) string
// WeekdaysNarrowreturns the locales narrow weekdays
WeekdaysNarrow() []string
// returns the locales short weekday given the 'weekday' provided
WeekdayShort(weekday time.Weekday) string
// returns the locales short weekdays
WeekdaysShort() []string
// returns the locales wide weekday given the 'weekday' provided
WeekdayWide(weekday time.Weekday) string
// returns the locales wide weekdays
WeekdaysWide() []string
// The following Functions are common Formatting functionsfor the Translator's Locale
// returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v'
FmtNumber(num float64, v uint64) string
// returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v'
// NOTE: 'num' passed into FmtPercent is assumed to be in percent already
FmtPercent(num float64, v uint64) string
// returns the currency representation of 'num' with digits/precision of 'v' for locale
FmtCurrency(num float64, v uint64, currency currency.Type) string
// returns the currency representation of 'num' with digits/precision of 'v' for locale
// in accounting notation.
FmtAccounting(num float64, v uint64, currency currency.Type) string
// returns the short date representation of 't' for locale
FmtDateShort(t time.Time) string
// returns the medium date representation of 't' for locale
FmtDateMedium(t time.Time) string
// returns the long date representation of 't' for locale
FmtDateLong(t time.Time) string
// returns the full date representation of 't' for locale
FmtDateFull(t time.Time) string
// returns the short time representation of 't' for locale
FmtTimeShort(t time.Time) string
// returns the medium time representation of 't' for locale
FmtTimeMedium(t time.Time) string
// returns the long time representation of 't' for locale
FmtTimeLong(t time.Time) string
// returns the full time representation of 't' for locale
FmtTimeFull(t time.Time) string
}
// String returns the string value of PluralRule
func (p PluralRule) String() string {
switch p {
case PluralRuleZero:
return pluralsString[7:11]
case PluralRuleOne:
return pluralsString[11:14]
case PluralRuleTwo:
return pluralsString[14:17]
case PluralRuleFew:
return pluralsString[17:20]
case PluralRuleMany:
return pluralsString[20:24]
case PluralRuleOther:
return pluralsString[24:]
default:
return pluralsString[:7]
}
}
//
// Precision Notes:
//
// must specify a precision >= 0, and here is why https://play.golang.org/p/LyL90U0Vyh
//
// v := float64(3.141)
// i := float64(int64(v))
//
// fmt.Println(v - i)
//
// or
//
// s := strconv.FormatFloat(v-i, 'f', -1, 64)
// fmt.Println(s)
//
// these will not print what you'd expect: 0.14100000000000001
// and so this library requires a precision to be specified, or
// inaccurate plural rules could be applied.
//
//
//
// n - absolute value of the source number (integer and decimals).
// i - integer digits of n.
// v - number of visible fraction digits in n, with trailing zeros.
// w - number of visible fraction digits in n, without trailing zeros.
// f - visible fractional digits in n, with trailing zeros.
// t - visible fractional digits in n, without trailing zeros.
//
//
// Func(num float64, v uint64) // v = digits/precision and prevents -1 as a special case as this can lead to very unexpected behaviour, see precision note's above.
//
// n := math.Abs(num)
// i := int64(n)
// v := v
//
//
// w := strconv.FormatFloat(num-float64(i), 'f', int(v), 64) // then parse backwards on string until no more zero's....
// f := strconv.FormatFloat(n, 'f', int(v), 64) // then turn everything after decimal into an int64
// t := strconv.FormatFloat(n, 'f', int(v), 64) // then parse backwards on string until no more zero's....
//
//
//
// General Inclusion Rules
// - v will always be available inherently
// - all require n
// - w requires i
//
// W returns the number of visible fraction digits in N, without trailing zeros.
func W(n float64, v uint64) (w int64) {
s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
// with either be '0' or '0.xxxx', so if 1 then w will be zero
// otherwise need to parse
if len(s) != 1 {
s = s[2:]
end := len(s) + 1
for i := end; i >= 0; i-- {
if s[i] != '0' {
end = i + 1
break
}
}
w = int64(len(s[:end]))
}
return
}
// F returns the visible fractional digits in N, with trailing zeros.
func F(n float64, v uint64) (f int64) {
s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
// with either be '0' or '0.xxxx', so if 1 then f will be zero
// otherwise need to parse
if len(s) != 1 {
// ignoring error, because it can't fail as we generated
// the string internally from a real number
f, _ = strconv.ParseInt(s[2:], 10, 64)
}
return
}
// T returns the visible fractional digits in N, without trailing zeros.
func T(n float64, v uint64) (t int64) {
s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
// with either be '0' or '0.xxxx', so if 1 then t will be zero
// otherwise need to parse
if len(s) != 1 {
s = s[2:]
end := len(s) + 1
for i := end; i >= 0; i-- {
if s[i] != '0' {
end = i + 1
break
}
}
// ignoring error, because it can't fail as we generated
// the string internally from a real number
t, _ = strconv.ParseInt(s[:end], 10, 64)
}
return
}

View File

@ -0,0 +1,619 @@
package zh
import (
"math"
"strconv"
"time"
"go-playground/locales"
"go-playground/locales/currency"
)
type zh struct {
locale string
pluralsCardinal []locales.PluralRule
pluralsOrdinal []locales.PluralRule
pluralsRange []locales.PluralRule
decimal string
group string
minus string
percent string
perMille string
timeSeparator string
inifinity string
currencies []string // idx = enum of currency code
monthsAbbreviated []string
monthsNarrow []string
monthsWide []string
daysAbbreviated []string
daysNarrow []string
daysShort []string
daysWide []string
periodsAbbreviated []string
periodsNarrow []string
periodsShort []string
periodsWide []string
erasAbbreviated []string
erasNarrow []string
erasWide []string
timezones map[string]string
}
// New returns a new instance of translator for the 'zh' locale
func New() locales.Translator {
return &zh{
locale: "zh",
pluralsCardinal: []locales.PluralRule{6},
pluralsOrdinal: []locales.PluralRule{6},
pluralsRange: []locales.PluralRule{6},
decimal: ".",
group: ",",
minus: "-",
percent: "%",
perMille: "‰",
timeSeparator: ":",
inifinity: "∞",
currencies: []string{"ADP", "AED", "AFA", "AFN", "ALK", "ALL", "AMD", "ANG", "AOA", "AOK", "AON", "AOR", "ARA", "ARL", "ARM", "ARP", "ARS", "ATS", "AU$", "AWG", "AZM", "AZN", "BAD", "BAM", "BAN", "BBD", "BDT", "BEC", "BEF", "BEL", "BGL", "BGM", "BGN", "BGO", "BHD", "BIF", "BMD", "BND", "BOB", "BOL", "BOP", "BOV", "BRB", "BRC", "BRE", "R$", "BRN", "BRR", "BRZ", "BSD", "BTN", "BUK", "BWP", "BYB", "BYN", "BYR", "BZD", "CA$", "CDF", "CHE", "CHF", "CHW", "CLE", "CLF", "CLP", "CNH", "CNX", "¥", "COP", "COU", "CRC", "CSD", "CSK", "CUC", "CUP", "CVE", "CYP", "CZK", "DDM", "DEM", "DJF", "DKK", "DOP", "DZD", "ECS", "ECV", "EEK", "EGP", "ERN", "ESA", "ESB", "ESP", "ETB", "€", "FIM", "FJD", "FKP", "FRF", "£", "GEK", "GEL", "GHC", "GHS", "GIP", "GMD", "GNF", "GNS", "GQE", "GRD", "GTQ", "GWE", "GWP", "GYD", "HK$", "HNL", "HRD", "HRK", "HTG", "HUF", "IDR", "IEP", "ILP", "ILS", "₪", "₹", "IQD", "IRR", "ISJ", "ISK", "ITL", "JMD", "JOD", "JP¥", "KES", "KGS", "KHR", "KMF", "KPW", "KRH", "KRO", "₩", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LTL", "LTT", "LUC", "LUF", "LUL", "LVL", "LVR", "LYD", "MAD", "MAF", "MCF", "MDC", "MDL", "MGA", "MGF", "MKD", "MKN", "MLF", "MMK", "MNT", "MOP", "MRO", "MTL", "MTP", "MUR", "MVP", "MVR", "MWK", "MX$", "MXP", "MXV", "MYR", "MZE", "MZM", "MZN", "NAD", "NGN", "NIC", "NIO", "NLG", "NOK", "NPR", "NZ$", "OMR", "PAB", "PEI", "PEN", "PES", "PGK", "PHP", "PKR", "PLN", "PLZ", "PTE", "PYG", "QAR", "RHD", "ROL", "RON", "RSD", "RUB", "RUR", "RWF", "SAR", "SBD", "SCR", "SDD", "SDG", "SDP", "SEK", "SGD", "SHP", "SIT", "SKK", "SLL", "SOS", "SRD", "SRG", "SSP", "STD", "STN", "SUR", "SVC", "SYP", "SZL", "THB", "TJR", "TJS", "TMM", "TMT", "TND", "TOP", "TPE", "TRL", "TRY", "TTD", "NT$", "TZS", "UAH", "UAK", "UGS", "UGX", "US$", "USN", "USS", "UYI", "UYP", "UYU", "UZS", "VEB", "VEF", "₫", "VNN", "VUV", "WST", "FCFA", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "EC$", "XDR", "XEU", "XFO", "XFU", "CFA", "XPD", "CFPF", "XPT", "XRE", "XSU", "XTS", "XUA", "XXX", "YDD", "YER", "YUD", "YUM", "YUN", "YUR", "ZAL", "ZAR", "ZMK", "ZMW", "ZRN", "ZRZ", "ZWD", "ZWL", "ZWR"},
monthsAbbreviated: []string{"", "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"},
monthsNarrow: []string{"", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"},
monthsWide: []string{"", "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"},
daysAbbreviated: []string{"周日", "周一", "周二", "周三", "周四", "周五", "周六"},
daysNarrow: []string{"日", "一", "二", "三", "四", "五", "六"},
daysShort: []string{"周日", "周一", "周二", "周三", "周四", "周五", "周六"},
daysWide: []string{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"},
periodsAbbreviated: []string{"上午", "下午"},
periodsNarrow: []string{"上午", "下午"},
periodsWide: []string{"上午", "下午"},
erasAbbreviated: []string{"公元前", "公元"},
erasNarrow: []string{"公元前", "公元"},
erasWide: []string{"公元前", "公元"},
timezones: map[string]string{"UYST": "乌拉圭夏令时间", "HNPMX": "墨西哥太平洋标准时间", "MDT": "北美山区夏令时间", "WESZ": "西欧夏令时间", "AKST": "阿拉斯加标准时间", "ACWST": "澳大利亚中西部标准时间", "HENOMX": "墨西哥西北部夏令时间", "WIT": "印度尼西亚东部时间", "HEPMX": "墨西哥太平洋夏令时间", "JST": "日本标准时间", "SRT": "苏里南时间", "CLST": "智利夏令时间", "UYT": "乌拉圭标准时间", "AWDT": "澳大利亚西部夏令时间", "MST": "北美山区标准时间", "WAST": "西部非洲夏令时间", "NZST": "新西兰标准时间", "EAT": "东部非洲时间", "HECU": "古巴夏令时间", "BT": "不丹时间", "EDT": "北美东部夏令时间", "WARST": "阿根廷西部夏令时间", "HNPM": "圣皮埃尔和密克隆群岛标准时间", "HNCU": "古巴标准时间", "PDT": "北美太平洋夏令时间", "LHDT": "豪勋爵岛夏令时间", "CLT": "智利标准时间", "PST": "北美太平洋标准时间", "JDT": "日本夏令时间", "OEZ": "东欧标准时间", "TMT": "土库曼斯坦标准时间", "CST": "北美中部标准时间", "AWST": "澳大利亚西部标准时间", "AEST": "澳大利亚东部标准时间", "AKDT": "阿拉斯加夏令时间", "HKT": "香港标准时间", "LHST": "豪勋爵岛标准时间", "HNNOMX": "墨西哥西北部标准时间", "BOT": "玻利维亚标准时间", "HNOG": "格陵兰岛西部标准时间", "EST": "北美东部标准时间", "MESZ": "中欧夏令时间", "WITA": "印度尼西亚中部时间", "CAT": "中部非洲时间", "COST": "哥伦比亚夏令时间", "CHADT": "查坦夏令时间", "AST": "大西洋标准时间", "MYT": "马来西亚时间", "OESZ": "东欧夏令时间", "COT": "哥伦比亚标准时间", "SAST": "南非标准时间", "HEOG": "格陵兰岛西部夏令时间", "ACWDT": "澳大利亚中西部夏令时间", "MEZ": "中欧标准时间", "GYT": "圭亚那时间", "ADT": "大西洋夏令时间", "HEEG": "格陵兰岛东部夏令时间", "WART": "阿根廷西部标准时间", "VET": "委内瑞拉时间", "GMT": "格林尼治标准时间", "∅∅∅": "巴西利亚夏令时间", "SGT": "新加坡标准时间", "ACDT": "澳大利亚中部夏令时间", "HAT": "纽芬兰夏令时间", "HADT": "夏威夷-阿留申夏令时间", "CHAST": "查坦标准时间", "CDT": "北美中部夏令时间", "AEDT": "澳大利亚东部夏令时间", "WEZ": "西欧标准时间", "NZDT": "新西兰夏令时间", "ECT": "厄瓜多尔标准时间", "GFT": "法属圭亚那标准时间", "HKST": "香港夏令时间", "IST": "印度时间", "HNT": "纽芬兰标准时间", "ART": "阿根廷标准时间", "ChST": "查莫罗时间", "WAT": "西部非洲标准时间", "HNEG": "格陵兰岛东部标准时间", "HEPM": "圣皮埃尔和密克隆群岛夏令时间", "TMST": "土库曼斯坦夏令时间", "HAST": "夏威夷-阿留申标准时间", "WIB": "印度尼西亚西部时间", "ACST": "澳大利亚中部标准时间", "ARST": "阿根廷夏令时间"},
}
}
// Locale returns the current translators string locale
func (zh *zh) Locale() string {
return zh.locale
}
// PluralsCardinal returns the list of cardinal plural rules associated with 'zh'
func (zh *zh) PluralsCardinal() []locales.PluralRule {
return zh.pluralsCardinal
}
// PluralsOrdinal returns the list of ordinal plural rules associated with 'zh'
func (zh *zh) PluralsOrdinal() []locales.PluralRule {
return zh.pluralsOrdinal
}
// PluralsRange returns the list of range plural rules associated with 'zh'
func (zh *zh) PluralsRange() []locales.PluralRule {
return zh.pluralsRange
}
// CardinalPluralRule returns the cardinal PluralRule given 'num' and digits/precision of 'v' for 'zh'
func (zh *zh) CardinalPluralRule(num float64, v uint64) locales.PluralRule {
return locales.PluralRuleOther
}
// OrdinalPluralRule returns the ordinal PluralRule given 'num' and digits/precision of 'v' for 'zh'
func (zh *zh) OrdinalPluralRule(num float64, v uint64) locales.PluralRule {
return locales.PluralRuleOther
}
// RangePluralRule returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for 'zh'
func (zh *zh) RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) locales.PluralRule {
return locales.PluralRuleOther
}
// MonthAbbreviated returns the locales abbreviated month given the 'month' provided
func (zh *zh) MonthAbbreviated(month time.Month) string {
return zh.monthsAbbreviated[month]
}
// MonthsAbbreviated returns the locales abbreviated months
func (zh *zh) MonthsAbbreviated() []string {
return zh.monthsAbbreviated[1:]
}
// MonthNarrow returns the locales narrow month given the 'month' provided
func (zh *zh) MonthNarrow(month time.Month) string {
return zh.monthsNarrow[month]
}
// MonthsNarrow returns the locales narrow months
func (zh *zh) MonthsNarrow() []string {
return zh.monthsNarrow[1:]
}
// MonthWide returns the locales wide month given the 'month' provided
func (zh *zh) MonthWide(month time.Month) string {
return zh.monthsWide[month]
}
// MonthsWide returns the locales wide months
func (zh *zh) MonthsWide() []string {
return zh.monthsWide[1:]
}
// WeekdayAbbreviated returns the locales abbreviated weekday given the 'weekday' provided
func (zh *zh) WeekdayAbbreviated(weekday time.Weekday) string {
return zh.daysAbbreviated[weekday]
}
// WeekdaysAbbreviated returns the locales abbreviated weekdays
func (zh *zh) WeekdaysAbbreviated() []string {
return zh.daysAbbreviated
}
// WeekdayNarrow returns the locales narrow weekday given the 'weekday' provided
func (zh *zh) WeekdayNarrow(weekday time.Weekday) string {
return zh.daysNarrow[weekday]
}
// WeekdaysNarrow returns the locales narrow weekdays
func (zh *zh) WeekdaysNarrow() []string {
return zh.daysNarrow
}
// WeekdayShort returns the locales short weekday given the 'weekday' provided
func (zh *zh) WeekdayShort(weekday time.Weekday) string {
return zh.daysShort[weekday]
}
// WeekdaysShort returns the locales short weekdays
func (zh *zh) WeekdaysShort() []string {
return zh.daysShort
}
// WeekdayWide returns the locales wide weekday given the 'weekday' provided
func (zh *zh) WeekdayWide(weekday time.Weekday) string {
return zh.daysWide[weekday]
}
// WeekdaysWide returns the locales wide weekdays
func (zh *zh) WeekdaysWide() []string {
return zh.daysWide
}
// Decimal returns the decimal point of number
func (zh *zh) Decimal() string {
return zh.decimal
}
// Group returns the group of number
func (zh *zh) Group() string {
return zh.group
}
// Group returns the minus sign of number
func (zh *zh) Minus() string {
return zh.minus
}
// FmtNumber returns 'num' with digits/precision of 'v' for 'zh' and handles both Whole and Real numbers based on 'v'
func (zh *zh) FmtNumber(num float64, v uint64) string {
s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
l := len(s) + 2 + 1*len(s[:len(s)-int(v)-1])/3
count := 0
inWhole := v == 0
b := make([]byte, 0, l)
for i := len(s) - 1; i >= 0; i-- {
if s[i] == '.' {
b = append(b, zh.decimal[0])
inWhole = true
continue
}
if inWhole {
if count == 3 {
b = append(b, zh.group[0])
count = 1
} else {
count++
}
}
b = append(b, s[i])
}
if num < 0 {
b = append(b, zh.minus[0])
}
// reverse
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
return string(b)
}
// FmtPercent returns 'num' with digits/precision of 'v' for 'zh' and handles both Whole and Real numbers based on 'v'
// NOTE: 'num' passed into FmtPercent is assumed to be in percent already
func (zh *zh) FmtPercent(num float64, v uint64) string {
s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
l := len(s) + 3
b := make([]byte, 0, l)
for i := len(s) - 1; i >= 0; i-- {
if s[i] == '.' {
b = append(b, zh.decimal[0])
continue
}
b = append(b, s[i])
}
if num < 0 {
b = append(b, zh.minus[0])
}
// reverse
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
b = append(b, zh.percent...)
return string(b)
}
// FmtCurrency returns the currency representation of 'num' with digits/precision of 'v' for 'zh'
func (zh *zh) FmtCurrency(num float64, v uint64, currency currency.Type) string {
s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
symbol := zh.currencies[currency]
l := len(s) + len(symbol) + 2 + 1*len(s[:len(s)-int(v)-1])/3
count := 0
inWhole := v == 0
b := make([]byte, 0, l)
for i := len(s) - 1; i >= 0; i-- {
if s[i] == '.' {
b = append(b, zh.decimal[0])
inWhole = true
continue
}
if inWhole {
if count == 3 {
b = append(b, zh.group[0])
count = 1
} else {
count++
}
}
b = append(b, s[i])
}
for j := len(symbol) - 1; j >= 0; j-- {
b = append(b, symbol[j])
}
if num < 0 {
b = append(b, zh.minus[0])
}
// reverse
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
if int(v) < 2 {
if v == 0 {
b = append(b, zh.decimal...)
}
for i := 0; i < 2-int(v); i++ {
b = append(b, '0')
}
}
return string(b)
}
// FmtAccounting returns the currency representation of 'num' with digits/precision of 'v' for 'zh'
// in accounting notation.
func (zh *zh) FmtAccounting(num float64, v uint64, currency currency.Type) string {
s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
symbol := zh.currencies[currency]
l := len(s) + len(symbol) + 2 + 1*len(s[:len(s)-int(v)-1])/3
count := 0
inWhole := v == 0
b := make([]byte, 0, l)
for i := len(s) - 1; i >= 0; i-- {
if s[i] == '.' {
b = append(b, zh.decimal[0])
inWhole = true
continue
}
if inWhole {
if count == 3 {
b = append(b, zh.group[0])
count = 1
} else {
count++
}
}
b = append(b, s[i])
}
if num < 0 {
for j := len(symbol) - 1; j >= 0; j-- {
b = append(b, symbol[j])
}
b = append(b, zh.minus[0])
} else {
for j := len(symbol) - 1; j >= 0; j-- {
b = append(b, symbol[j])
}
}
// reverse
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
if int(v) < 2 {
if v == 0 {
b = append(b, zh.decimal...)
}
for i := 0; i < 2-int(v); i++ {
b = append(b, '0')
}
}
return string(b)
}
// FmtDateShort returns the short date representation of 't' for 'zh'
func (zh *zh) FmtDateShort(t time.Time) string {
b := make([]byte, 0, 32)
if t.Year() > 0 {
b = strconv.AppendInt(b, int64(t.Year()), 10)
} else {
b = strconv.AppendInt(b, int64(-t.Year()), 10)
}
b = append(b, []byte{0x2f}...)
b = strconv.AppendInt(b, int64(t.Month()), 10)
b = append(b, []byte{0x2f}...)
b = strconv.AppendInt(b, int64(t.Day()), 10)
return string(b)
}
// FmtDateMedium returns the medium date representation of 't' for 'zh'
func (zh *zh) FmtDateMedium(t time.Time) string {
b := make([]byte, 0, 32)
if t.Year() > 0 {
b = strconv.AppendInt(b, int64(t.Year()), 10)
} else {
b = strconv.AppendInt(b, int64(-t.Year()), 10)
}
b = append(b, []byte{0xe5, 0xb9, 0xb4}...)
b = strconv.AppendInt(b, int64(t.Month()), 10)
b = append(b, []byte{0xe6, 0x9c, 0x88}...)
b = strconv.AppendInt(b, int64(t.Day()), 10)
b = append(b, []byte{0xe6, 0x97, 0xa5}...)
return string(b)
}
// FmtDateLong returns the long date representation of 't' for 'zh'
func (zh *zh) FmtDateLong(t time.Time) string {
b := make([]byte, 0, 32)
if t.Year() > 0 {
b = strconv.AppendInt(b, int64(t.Year()), 10)
} else {
b = strconv.AppendInt(b, int64(-t.Year()), 10)
}
b = append(b, []byte{0xe5, 0xb9, 0xb4}...)
b = strconv.AppendInt(b, int64(t.Month()), 10)
b = append(b, []byte{0xe6, 0x9c, 0x88}...)
b = strconv.AppendInt(b, int64(t.Day()), 10)
b = append(b, []byte{0xe6, 0x97, 0xa5}...)
return string(b)
}
// FmtDateFull returns the full date representation of 't' for 'zh'
func (zh *zh) FmtDateFull(t time.Time) string {
b := make([]byte, 0, 32)
if t.Year() > 0 {
b = strconv.AppendInt(b, int64(t.Year()), 10)
} else {
b = strconv.AppendInt(b, int64(-t.Year()), 10)
}
b = append(b, []byte{0xe5, 0xb9, 0xb4}...)
b = strconv.AppendInt(b, int64(t.Month()), 10)
b = append(b, []byte{0xe6, 0x9c, 0x88}...)
b = strconv.AppendInt(b, int64(t.Day()), 10)
b = append(b, []byte{0xe6, 0x97, 0xa5}...)
b = append(b, zh.daysWide[t.Weekday()]...)
return string(b)
}
// FmtTimeShort returns the short time representation of 't' for 'zh'
func (zh *zh) FmtTimeShort(t time.Time) string {
b := make([]byte, 0, 32)
if t.Hour() < 12 {
b = append(b, zh.periodsAbbreviated[0]...)
} else {
b = append(b, zh.periodsAbbreviated[1]...)
}
h := t.Hour()
if h > 12 {
h -= 12
}
b = strconv.AppendInt(b, int64(h), 10)
b = append(b, zh.timeSeparator...)
if t.Minute() < 10 {
b = append(b, '0')
}
b = strconv.AppendInt(b, int64(t.Minute()), 10)
return string(b)
}
// FmtTimeMedium returns the medium time representation of 't' for 'zh'
func (zh *zh) FmtTimeMedium(t time.Time) string {
b := make([]byte, 0, 32)
if t.Hour() < 12 {
b = append(b, zh.periodsAbbreviated[0]...)
} else {
b = append(b, zh.periodsAbbreviated[1]...)
}
h := t.Hour()
if h > 12 {
h -= 12
}
b = strconv.AppendInt(b, int64(h), 10)
b = append(b, zh.timeSeparator...)
if t.Minute() < 10 {
b = append(b, '0')
}
b = strconv.AppendInt(b, int64(t.Minute()), 10)
b = append(b, zh.timeSeparator...)
if t.Second() < 10 {
b = append(b, '0')
}
b = strconv.AppendInt(b, int64(t.Second()), 10)
return string(b)
}
// FmtTimeLong returns the long time representation of 't' for 'zh'
func (zh *zh) FmtTimeLong(t time.Time) string {
b := make([]byte, 0, 32)
tz, _ := t.Zone()
b = append(b, tz...)
b = append(b, []byte{0x20}...)
if t.Hour() < 12 {
b = append(b, zh.periodsAbbreviated[0]...)
} else {
b = append(b, zh.periodsAbbreviated[1]...)
}
h := t.Hour()
if h > 12 {
h -= 12
}
b = strconv.AppendInt(b, int64(h), 10)
b = append(b, zh.timeSeparator...)
if t.Minute() < 10 {
b = append(b, '0')
}
b = strconv.AppendInt(b, int64(t.Minute()), 10)
b = append(b, zh.timeSeparator...)
if t.Second() < 10 {
b = append(b, '0')
}
b = strconv.AppendInt(b, int64(t.Second()), 10)
return string(b)
}
// FmtTimeFull returns the full time representation of 't' for 'zh'
func (zh *zh) FmtTimeFull(t time.Time) string {
b := make([]byte, 0, 32)
tz, _ := t.Zone()
if btz, ok := zh.timezones[tz]; ok {
b = append(b, btz...)
} else {
b = append(b, tz...)
}
b = append(b, []byte{0x20}...)
if t.Hour() < 12 {
b = append(b, zh.periodsAbbreviated[0]...)
} else {
b = append(b, zh.periodsAbbreviated[1]...)
}
h := t.Hour()
if h > 12 {
h -= 12
}
b = strconv.AppendInt(b, int64(h), 10)
b = append(b, zh.timeSeparator...)
if t.Minute() < 10 {
b = append(b, '0')
}
b = strconv.AppendInt(b, int64(t.Minute()), 10)
b = append(b, zh.timeSeparator...)
if t.Second() < 10 {
b = append(b, '0')
}
b = strconv.AppendInt(b, int64(t.Second()), 10)
return string(b)
}

View File

@ -0,0 +1,25 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
*.coverprofile

View File

@ -0,0 +1,27 @@
language: go
go:
- 1.13.4
- tip
matrix:
allow_failures:
- go: tip
notifications:
email:
recipients: dean.karn@gmail.com
on_success: change
on_failure: always
before_install:
- go install github.com/mattn/goveralls
# Only clone the most recent commit.
git:
depth: 1
script:
- go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
after_success: |
[ $TRAVIS_GO_VERSION = 1.13.4 ] &&
goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Go Playground
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,89 @@
## universal-translator
<img align="right" src="https://raw.githubusercontent.com/go-playground/universal-translator/master/logo.png">![Project status](https://img.shields.io/badge/version-0.17.0-green.svg)
[![Build Status](https://travis-ci.org/go-playground/universal-translator.svg?branch=master)](https://travis-ci.org/go-playground/universal-translator)
[![Coverage Status](https://coveralls.io/repos/github/go-playground/universal-translator/badge.svg)](https://coveralls.io/github/go-playground/universal-translator)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/universal-translator)](https://goreportcard.com/report/github.com/go-playground/universal-translator)
[![GoDoc](https://godoc.org/github.com/go-playground/universal-translator?status.svg)](https://godoc.org/github.com/go-playground/universal-translator)
![License](https://img.shields.io/dub/l/vibe-d.svg)
[![Gitter](https://badges.gitter.im/go-playground/universal-translator.svg)](https://gitter.im/go-playground/universal-translator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Universal Translator is an i18n Translator for Go/Golang using CLDR data + pluralization rules
Why another i18n library?
--------------------------
Because none of the plural rules seem to be correct out there, including the previous implementation of this package,
so I took it upon myself to create [locales](https://go-playground/locales) for everyone to use; this package
is a thin wrapper around [locales](https://go-playground/locales) in order to store and translate text for
use in your applications.
Features
--------
- [x] Rules generated from the [CLDR](http://cldr.unicode.org/index/downloads) data, v30.0.3
- [x] Contains Cardinal, Ordinal and Range Plural Rules
- [x] Contains Month, Weekday and Timezone translations built in
- [x] Contains Date & Time formatting functions
- [x] Contains Number, Currency, Accounting and Percent formatting functions
- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere )
- [x] Support loading translations from files
- [x] Exporting translations to file(s), mainly for getting them professionally translated
- [ ] Code Generation for translation files -> Go code.. i.e. after it has been professionally translated
- [ ] Tests for all languages, I need help with this, please see [here](https://go-playground/locales/issues/1)
Installation
-----------
Use go get
```shell
go get github.com/go-playground/universal-translator
```
Usage & Documentation
-------
Please see https://godoc.org/github.com/go-playground/universal-translator for usage docs
##### Examples:
- [Basic](https://github.com/go-playground/universal-translator/tree/master/_examples/basic)
- [Full - no files](https://github.com/go-playground/universal-translator/tree/master/_examples/full-no-files)
- [Full - with files](https://github.com/go-playground/universal-translator/tree/master/_examples/full-with-files)
File formatting
--------------
All types, Plain substitution, Cardinal, Ordinal and Range translations can all be contained withing the same file(s);
they are only separated for easy viewing.
##### Examples:
- [Formats](https://github.com/go-playground/universal-translator/tree/master/_examples/file-formats)
##### Basic Makeup
NOTE: not all fields are needed for all translation types, see [examples](https://github.com/go-playground/universal-translator/tree/master/_examples/file-formats)
```json
{
"locale": "en",
"key": "days-left",
"trans": "You have {0} day left.",
"type": "Cardinal",
"rule": "One",
"override": false
}
```
|Field|Description|
|---|---|
|locale|The locale for which the translation is for.|
|key|The translation key that will be used to store and lookup each translation; normally it is a string or integer.|
|trans|The actual translation text.|
|type|The type of translation Cardinal, Ordinal, Range or "" for a plain substitution(not required to be defined if plain used)|
|rule|The plural rule for which the translation is for eg. One, Two, Few, Many or Other.(not required to be defined if plain used)|
|override|If you wish to override an existing translation that has already been registered, set this to 'true'. 99% of the time there is no need to define it.|
Help With Tests
---------------
To anyone interesting in helping or contributing, I sure could use some help creating tests for each language.
Please see issue [here](https://go-playground/locales/issues/1) for details.
License
------
Distributed under MIT License, please see license file in code for more details.

View File

@ -0,0 +1,148 @@
package ut
import (
"errors"
"fmt"
"gin-valid/go-playground/locales"
)
var (
// ErrUnknowTranslation indicates the translation could not be found
ErrUnknowTranslation = errors.New("Unknown Translation")
)
var _ error = new(ErrConflictingTranslation)
var _ error = new(ErrRangeTranslation)
var _ error = new(ErrOrdinalTranslation)
var _ error = new(ErrCardinalTranslation)
var _ error = new(ErrMissingPluralTranslation)
var _ error = new(ErrExistingTranslator)
// ErrExistingTranslator is the error representing a conflicting translator
type ErrExistingTranslator struct {
locale string
}
// Error returns ErrExistingTranslator's internal error text
func (e *ErrExistingTranslator) Error() string {
return fmt.Sprintf("error: conflicting translator for locale '%s'", e.locale)
}
// ErrConflictingTranslation is the error representing a conflicting translation
type ErrConflictingTranslation struct {
locale string
key interface{}
rule locales.PluralRule
text string
}
// Error returns ErrConflictingTranslation's internal error text
func (e *ErrConflictingTranslation) Error() string {
if _, ok := e.key.(string); !ok {
return fmt.Sprintf("error: conflicting key '%#v' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale)
}
return fmt.Sprintf("error: conflicting key '%s' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale)
}
// ErrRangeTranslation is the error representing a range translation error
type ErrRangeTranslation struct {
text string
}
// Error returns ErrRangeTranslation's internal error text
func (e *ErrRangeTranslation) Error() string {
return e.text
}
// ErrOrdinalTranslation is the error representing an ordinal translation error
type ErrOrdinalTranslation struct {
text string
}
// Error returns ErrOrdinalTranslation's internal error text
func (e *ErrOrdinalTranslation) Error() string {
return e.text
}
// ErrCardinalTranslation is the error representing a cardinal translation error
type ErrCardinalTranslation struct {
text string
}
// Error returns ErrCardinalTranslation's internal error text
func (e *ErrCardinalTranslation) Error() string {
return e.text
}
// ErrMissingPluralTranslation is the error signifying a missing translation given
// the locales plural rules.
type ErrMissingPluralTranslation struct {
locale string
key interface{}
rule locales.PluralRule
translationType string
}
// Error returns ErrMissingPluralTranslation's internal error text
func (e *ErrMissingPluralTranslation) Error() string {
if _, ok := e.key.(string); !ok {
return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%#v' and locale '%s'", e.translationType, e.rule, e.key, e.locale)
}
return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%s' and locale '%s'", e.translationType, e.rule, e.key, e.locale)
}
// ErrMissingBracket is the error representing a missing bracket in a translation
// eg. This is a {0 <-- missing ending '}'
type ErrMissingBracket struct {
locale string
key interface{}
text string
}
// Error returns ErrMissingBracket error message
func (e *ErrMissingBracket) Error() string {
return fmt.Sprintf("error: missing bracket '{}', in translation. locale: '%s' key: '%v' text: '%s'", e.locale, e.key, e.text)
}
// ErrBadParamSyntax is the error representing a bad parameter definition in a translation
// eg. This is a {must-be-int}
type ErrBadParamSyntax struct {
locale string
param string
key interface{}
text string
}
// Error returns ErrBadParamSyntax error message
func (e *ErrBadParamSyntax) Error() string {
return fmt.Sprintf("error: bad parameter syntax, missing parameter '%s' in translation. locale: '%s' key: '%v' text: '%s'", e.param, e.locale, e.key, e.text)
}
// import/export errors
// ErrMissingLocale is the error representing an expected locale that could
// not be found aka locale not registered with the UniversalTranslator Instance
type ErrMissingLocale struct {
locale string
}
// Error returns ErrMissingLocale's internal error text
func (e *ErrMissingLocale) Error() string {
return fmt.Sprintf("error: locale '%s' not registered.", e.locale)
}
// ErrBadPluralDefinition is the error representing an incorrect plural definition
// usually found within translations defined within files during the import process.
type ErrBadPluralDefinition struct {
tl translation
}
// Error returns ErrBadPluralDefinition's internal error text
func (e *ErrBadPluralDefinition) Error() string {
return fmt.Sprintf("error: bad plural definition '%#v'", e.tl)
}

View File

@ -0,0 +1,4 @@
go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -0,0 +1,274 @@
package ut
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"io"
"go-playground/locales"
)
type translation struct {
Locale string `json:"locale"`
Key interface{} `json:"key"` // either string or integer
Translation string `json:"trans"`
PluralType string `json:"type,omitempty"`
PluralRule string `json:"rule,omitempty"`
OverrideExisting bool `json:"override,omitempty"`
}
const (
cardinalType = "Cardinal"
ordinalType = "Ordinal"
rangeType = "Range"
)
// ImportExportFormat is the format of the file import or export
type ImportExportFormat uint8
// supported Export Formats
const (
FormatJSON ImportExportFormat = iota
)
// Export writes the translations out to a file on disk.
//
// NOTE: this currently only works with string or int translations keys.
func (t *UniversalTranslator) Export(format ImportExportFormat, dirname string) error {
_, err := os.Stat(dirname)
fmt.Println(dirname, err, os.IsNotExist(err))
if err != nil {
if !os.IsNotExist(err) {
return err
}
if err = os.MkdirAll(dirname, 0744); err != nil {
return err
}
}
// build up translations
var trans []translation
var b []byte
var ext string
for _, locale := range t.translators {
for k, v := range locale.(*translator).translations {
trans = append(trans, translation{
Locale: locale.Locale(),
Key: k,
Translation: v.text,
})
}
for k, pluralTrans := range locale.(*translator).cardinalTanslations {
for i, plural := range pluralTrans {
// leave enough for all plural rules
// but not all are set for all languages.
if plural == nil {
continue
}
trans = append(trans, translation{
Locale: locale.Locale(),
Key: k.(string),
Translation: plural.text,
PluralType: cardinalType,
PluralRule: locales.PluralRule(i).String(),
})
}
}
for k, pluralTrans := range locale.(*translator).ordinalTanslations {
for i, plural := range pluralTrans {
// leave enough for all plural rules
// but not all are set for all languages.
if plural == nil {
continue
}
trans = append(trans, translation{
Locale: locale.Locale(),
Key: k.(string),
Translation: plural.text,
PluralType: ordinalType,
PluralRule: locales.PluralRule(i).String(),
})
}
}
for k, pluralTrans := range locale.(*translator).rangeTanslations {
for i, plural := range pluralTrans {
// leave enough for all plural rules
// but not all are set for all languages.
if plural == nil {
continue
}
trans = append(trans, translation{
Locale: locale.Locale(),
Key: k.(string),
Translation: plural.text,
PluralType: rangeType,
PluralRule: locales.PluralRule(i).String(),
})
}
}
switch format {
case FormatJSON:
b, err = json.MarshalIndent(trans, "", " ")
ext = ".json"
}
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dirname, fmt.Sprintf("%s%s", locale.Locale(), ext)), b, 0644)
if err != nil {
return err
}
trans = trans[0:0]
}
return nil
}
// Import reads the translations out of a file or directory on disk.
//
// NOTE: this currently only works with string or int translations keys.
func (t *UniversalTranslator) Import(format ImportExportFormat, dirnameOrFilename string) error {
fi, err := os.Stat(dirnameOrFilename)
if err != nil {
return err
}
processFn := func(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
return t.ImportByReader(format, f)
}
if !fi.IsDir() {
return processFn(dirnameOrFilename)
}
// recursively go through directory
walker := func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
switch format {
case FormatJSON:
// skip non JSON files
if filepath.Ext(info.Name()) != ".json" {
return nil
}
}
return processFn(path)
}
return filepath.Walk(dirnameOrFilename, walker)
}
// ImportByReader imports the the translations found within the contents read from the supplied reader.
//
// NOTE: generally used when assets have been embedded into the binary and are already in memory.
func (t *UniversalTranslator) ImportByReader(format ImportExportFormat, reader io.Reader) error {
b, err := ioutil.ReadAll(reader)
if err != nil {
return err
}
var trans []translation
switch format {
case FormatJSON:
err = json.Unmarshal(b, &trans)
}
if err != nil {
return err
}
for _, tl := range trans {
locale, found := t.FindTranslator(tl.Locale)
if !found {
return &ErrMissingLocale{locale: tl.Locale}
}
pr := stringToPR(tl.PluralRule)
if pr == locales.PluralRuleUnknown {
err = locale.Add(tl.Key, tl.Translation, tl.OverrideExisting)
if err != nil {
return err
}
continue
}
switch tl.PluralType {
case cardinalType:
err = locale.AddCardinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
case ordinalType:
err = locale.AddOrdinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
case rangeType:
err = locale.AddRange(tl.Key, tl.Translation, pr, tl.OverrideExisting)
default:
return &ErrBadPluralDefinition{tl: tl}
}
if err != nil {
return err
}
}
return nil
}
func stringToPR(s string) locales.PluralRule {
switch s {
case "One":
return locales.PluralRuleOne
case "Two":
return locales.PluralRuleTwo
case "Few":
return locales.PluralRuleFew
case "Many":
return locales.PluralRuleMany
case "Other":
return locales.PluralRuleOther
default:
return locales.PluralRuleUnknown
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,420 @@
package ut
import (
"fmt"
"strconv"
"strings"
"gin-valid/go-playground/locales"
)
const (
paramZero = "{0}"
paramOne = "{1}"
unknownTranslation = ""
)
// Translator is universal translators
// translator instance which is a thin wrapper
// around locales.Translator instance providing
// some extra functionality
type Translator interface {
locales.Translator
// adds a normal translation for a particular language/locale
// {#} is the only replacement type accepted and are ad infinitum
// eg. one: '{0} day left' other: '{0} days left'
Add(key interface{}, text string, override bool) error
// adds a cardinal plural translation for a particular language/locale
// {0} is the only replacement type accepted and only one variable is accepted as
// multiple cannot be used for a plural rule determination, unless it is a range;
// see AddRange below.
// eg. in locale 'en' one: '{0} day left' other: '{0} days left'
AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error
// adds an ordinal plural translation for a particular language/locale
// {0} is the only replacement type accepted and only one variable is accepted as
// multiple cannot be used for a plural rule determination, unless it is a range;
// see AddRange below.
// eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring'
// - 1st, 2nd, 3rd...
AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error
// adds a range plural translation for a particular language/locale
// {0} and {1} are the only replacement types accepted and only these are accepted.
// eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left'
AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error
// creates the translation for the locale given the 'key' and params passed in
T(key interface{}, params ...string) (string, error)
// creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments
// and param passed in
C(key interface{}, num float64, digits uint64, param string) (string, error)
// creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments
// and param passed in
O(key interface{}, num float64, digits uint64, param string) (string, error)
// creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and
// 'digit2' arguments and 'param1' and 'param2' passed in
R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error)
// VerifyTranslations checks to ensures that no plural rules have been
// missed within the translations.
VerifyTranslations() error
}
var _ Translator = new(translator)
var _ locales.Translator = new(translator)
type translator struct {
locales.Translator
translations map[interface{}]*transText
cardinalTanslations map[interface{}][]*transText // array index is mapped to locales.PluralRule index + the locales.PluralRuleUnknown
ordinalTanslations map[interface{}][]*transText
rangeTanslations map[interface{}][]*transText
}
type transText struct {
text string
indexes []int //记录每个占位符的始末位置,也就是 { 和 } 的位置
}
func newTranslator(trans locales.Translator) Translator {
return &translator{
Translator: trans,
translations: make(map[interface{}]*transText), // translation text broken up by byte index
cardinalTanslations: make(map[interface{}][]*transText),
ordinalTanslations: make(map[interface{}][]*transText),
rangeTanslations: make(map[interface{}][]*transText),
}
}
// Add adds a normal translation for a particular language/locale
// {#} is the only replacement type accepted and are ad infinitum
// eg. one: '{0} day left' other: '{0} days left'
func (t *translator) Add(key interface{}, text string, override bool) error {
if _, ok := t.translations[key]; ok && !override {
return &ErrConflictingTranslation{locale: t.Locale(), key: key, text: text}
}
lb := strings.Count(text, "{")
rb := strings.Count(text, "}")
if lb != rb {
return &ErrMissingBracket{locale: t.Locale(), key: key, text: text}
}
trans := &transText{
text: text,
}
var idx int
for i := 0; i < lb; i++ {
s := "{" + strconv.Itoa(i) + "}"
idx = strings.Index(text, s)
if idx == -1 {
return &ErrBadParamSyntax{locale: t.Locale(), param: s, key: key, text: text}
}
trans.indexes = append(trans.indexes, idx)
trans.indexes = append(trans.indexes, idx+len(s))
}
t.translations[key] = trans
return nil
}
// AddCardinal adds a cardinal plural translation for a particular language/locale
// {0} is the only replacement type accepted and only one variable is accepted as
// multiple cannot be used for a plural rule determination, unless it is a range;
// see AddRange below.
// eg. in locale 'en' one: '{0} day left' other: '{0} days left'
func (t *translator) AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error {
var verified bool
// verify plural rule exists for locale
for _, pr := range t.PluralsCardinal() {
if pr == rule {
verified = true
break
}
}
if !verified {
return &ErrCardinalTranslation{text: fmt.Sprintf("error: cardinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
}
tarr, ok := t.cardinalTanslations[key]
if ok {
// verify not adding a conflicting record
if len(tarr) > 0 && tarr[rule] != nil && !override {
return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
}
} else {
tarr = make([]*transText, 7, 7)
t.cardinalTanslations[key] = tarr
}
trans := &transText{
text: text,
indexes: make([]int, 2, 2),
}
tarr[rule] = trans
idx := strings.Index(text, paramZero)
if idx == -1 {
tarr[rule] = nil
return &ErrCardinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
}
trans.indexes[0] = idx
trans.indexes[1] = idx + len(paramZero)
return nil
}
// AddOrdinal adds an ordinal plural translation for a particular language/locale
// {0} is the only replacement type accepted and only one variable is accepted as
// multiple cannot be used for a plural rule determination, unless it is a range;
// see AddRange below.
// eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring' - 1st, 2nd, 3rd...
func (t *translator) AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error {
var verified bool
// verify plural rule exists for locale
for _, pr := range t.PluralsOrdinal() {
if pr == rule {
verified = true
break
}
}
if !verified {
return &ErrOrdinalTranslation{text: fmt.Sprintf("error: ordinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
}
tarr, ok := t.ordinalTanslations[key]
if ok {
// verify not adding a conflicting record
if len(tarr) > 0 && tarr[rule] != nil && !override {
return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
}
} else {
tarr = make([]*transText, 7, 7)
t.ordinalTanslations[key] = tarr
}
trans := &transText{
text: text,
indexes: make([]int, 2, 2),
}
tarr[rule] = trans
idx := strings.Index(text, paramZero)
if idx == -1 {
tarr[rule] = nil
return &ErrOrdinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
}
trans.indexes[0] = idx
trans.indexes[1] = idx + len(paramZero)
return nil
}
// AddRange adds a range plural translation for a particular language/locale
// {0} and {1} are the only replacement types accepted and only these are accepted.
// eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left'
func (t *translator) AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error {
var verified bool
// verify plural rule exists for locale
for _, pr := range t.PluralsRange() {
if pr == rule {
verified = true
break
}
}
if !verified {
return &ErrRangeTranslation{text: fmt.Sprintf("error: range plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
}
tarr, ok := t.rangeTanslations[key]
if ok {
// verify not adding a conflicting record
if len(tarr) > 0 && tarr[rule] != nil && !override {
return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
}
} else {
tarr = make([]*transText, 7, 7)
t.rangeTanslations[key] = tarr
}
trans := &transText{
text: text,
indexes: make([]int, 4, 4),
}
tarr[rule] = trans
idx := strings.Index(text, paramZero)
if idx == -1 {
tarr[rule] = nil
return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, are you sure you're adding a Range Translation? locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
}
trans.indexes[0] = idx
trans.indexes[1] = idx + len(paramZero)
idx = strings.Index(text, paramOne)
if idx == -1 {
tarr[rule] = nil
return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, a Range Translation requires two parameters. locale: '%s' key: '%v' text: '%s'", paramOne, t.Locale(), key, text)}
}
trans.indexes[2] = idx
trans.indexes[3] = idx + len(paramOne)
return nil
}
// T creates the translation for the locale given the 'key' and params passed in
func (t *translator) T(key interface{}, params ...string) (string, error) {
trans, ok := t.translations[key]
if !ok {
return unknownTranslation, ErrUnknowTranslation
}
b := make([]byte, 0, 64)
var start, end, count int
for i := 0; i < len(trans.indexes); i++ {
end = trans.indexes[i]
b = append(b, trans.text[start:end]...)
b = append(b, params[count]...)
i++
start = trans.indexes[i]
count++
}
b = append(b, trans.text[start:]...)
return string(b), nil
}
// C creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in
func (t *translator) C(key interface{}, num float64, digits uint64, param string) (string, error) {
tarr, ok := t.cardinalTanslations[key]
if !ok {
return unknownTranslation, ErrUnknowTranslation
}
rule := t.CardinalPluralRule(num, digits)
trans := tarr[rule]
b := make([]byte, 0, 64)
b = append(b, trans.text[:trans.indexes[0]]...)
b = append(b, param...)
b = append(b, trans.text[trans.indexes[1]:]...)
return string(b), nil
}
// O creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in
func (t *translator) O(key interface{}, num float64, digits uint64, param string) (string, error) {
tarr, ok := t.ordinalTanslations[key]
if !ok {
return unknownTranslation, ErrUnknowTranslation
}
rule := t.OrdinalPluralRule(num, digits)
trans := tarr[rule]
b := make([]byte, 0, 64)
b = append(b, trans.text[:trans.indexes[0]]...)
b = append(b, param...)
b = append(b, trans.text[trans.indexes[1]:]...)
return string(b), nil
}
// R creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and 'digit2' arguments
// and 'param1' and 'param2' passed in
func (t *translator) R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error) {
tarr, ok := t.rangeTanslations[key]
if !ok {
return unknownTranslation, ErrUnknowTranslation
}
rule := t.RangePluralRule(num1, digits1, num2, digits2)
trans := tarr[rule]
b := make([]byte, 0, 64)
b = append(b, trans.text[:trans.indexes[0]]...)
b = append(b, param1...)
b = append(b, trans.text[trans.indexes[1]:trans.indexes[2]]...)
b = append(b, param2...)
b = append(b, trans.text[trans.indexes[3]:]...)
return string(b), nil
}
// VerifyTranslations checks to ensures that no plural rules have been
// missed within the translations.
func (t *translator) VerifyTranslations() error {
for k, v := range t.cardinalTanslations {
for _, rule := range t.PluralsCardinal() {
if v[rule] == nil {
return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "plural", rule: rule, key: k}
}
}
}
for k, v := range t.ordinalTanslations {
for _, rule := range t.PluralsOrdinal() {
if v[rule] == nil {
return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "ordinal", rule: rule, key: k}
}
}
}
for k, v := range t.rangeTanslations {
for _, rule := range t.PluralsRange() {
if v[rule] == nil {
return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "range", rule: rule, key: k}
}
}
}
return nil
}

View File

@ -0,0 +1,113 @@
package ut
import (
"strings"
"go-playground/locales"
)
// UniversalTranslator holds all locale & translation data
type UniversalTranslator struct {
translators map[string]Translator
fallback Translator
}
// New returns a new UniversalTranslator instance set with
// the fallback locale and locales it should support
func New(fallback locales.Translator, supportedLocales ...locales.Translator) *UniversalTranslator {
t := &UniversalTranslator{
translators: make(map[string]Translator),
}
for _, v := range supportedLocales {
trans := newTranslator(v)
t.translators[strings.ToLower(trans.Locale())] = trans
if fallback.Locale() == v.Locale() {
t.fallback = trans
}
}
if t.fallback == nil && fallback != nil {
t.fallback = newTranslator(fallback)
}
return t
}
// FindTranslator trys to find a Translator based on an array of locales
// and returns the first one it can find, otherwise returns the
// fallback translator.
func (t *UniversalTranslator) FindTranslator(locales ...string) (trans Translator, found bool) {
for _, locale := range locales {
if trans, found = t.translators[strings.ToLower(locale)]; found {
return
}
}
return t.fallback, false
}
// GetTranslator returns the specified translator for the given locale,
// or fallback if not found
func (t *UniversalTranslator) GetTranslator(locale string) (trans Translator, found bool) {
if trans, found = t.translators[strings.ToLower(locale)]; found {
return
}
return t.fallback, false
}
// GetFallback returns the fallback locale
func (t *UniversalTranslator) GetFallback() Translator {
return t.fallback
}
// AddTranslator adds the supplied translator, if it already exists the override param
// will be checked and if false an error will be returned, otherwise the translator will be
// overridden; if the fallback matches the supplied translator it will be overridden as well
// NOTE: this is normally only used when translator is embedded within a library
func (t *UniversalTranslator) AddTranslator(translator locales.Translator, override bool) error {
lc := strings.ToLower(translator.Locale())
_, ok := t.translators[lc]
if ok && !override {
return &ErrExistingTranslator{locale: translator.Locale()}
}
trans := newTranslator(translator)
if t.fallback.Locale() == translator.Locale() {
// because it's optional to have a fallback, I don't impose that limitation
// don't know why you wouldn't but...
if !override {
return &ErrExistingTranslator{locale: translator.Locale()}
}
t.fallback = trans
}
t.translators[lc] = trans
return nil
}
// VerifyTranslations runs through all locales and identifies any issues
// eg. missing plural rules for a locale
func (t *UniversalTranslator) VerifyTranslations() (err error) {
for _, trans := range t.translators {
err = trans.VerifyTranslations()
if err != nil {
return
}
}
return
}

View File

@ -0,0 +1,9 @@
# Contribution Guidelines
## Quality Standard
To ensure the continued stability of this package, tests are required to be written or already exist in order for a pull request to be merged.
## Reporting issues
Please open an issue or join the gitter chat [![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) for any issues, questions or possible enhancements to the package.

View File

@ -0,0 +1,13 @@
### Package version eg. v8, v9:
### Issue, Question or Enhancement:
### Code sample, to showcase or reproduce:
```go
```

View File

@ -0,0 +1,13 @@
Fixes Or Enhances # .
**Make sure that you've checked the boxes below before you submit PR:**
- [ ] Tests exist or have been written that cover this particular change.
Change Details:
-
-
-
@go-playground/admins

30
go-playground/validator/v10/.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
bin
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
*.test
*.out
*.txt
cover.html
README.html

View File

@ -0,0 +1,29 @@
language: go
go:
- 1.15.2
- tip
matrix:
allow_failures:
- go: tip
notifications:
email:
recipients: dean.karn@gmail.com
on_success: change
on_failure: always
before_install:
- go install github.com/mattn/goveralls
- mkdir -p $GOPATH/src/gopkg.in
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/validator.v9
# Only clone the most recent commit.
git:
depth: 1
script:
- go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
after_success: |
[ $TRAVIS_GO_VERSION = 1.15.2 ] &&
goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Dean Karn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,18 @@
GOCMD=GO111MODULE=on go
linters-install:
@golangci-lint --version >/dev/null 2>&1 || { \
echo "installing linting tools..."; \
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.21.0; \
}
lint: linters-install
$(PWD)/bin/golangci-lint run
test:
$(GOCMD) test -cover -race ./...
bench:
$(GOCMD) test -bench=. -benchmem ./...
.PHONY: test lint linters-install

View File

@ -0,0 +1,299 @@
Package validator
================
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-10.4.1-green.svg)
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
[![GoDoc](https://godoc.org/github.com/go-playground/validator?status.svg)](https://pkg.go.dev/gin-valid/go-playground/validator/v10)
![License](https://img.shields.io/dub/l/vibe-d.svg)
Package validator implements value validations for structs and individual fields based on tags.
It has the following **unique** features:
- Cross Field and Cross Struct validations by using validation tags or custom validators.
- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
- Ability to dive into both map keys and values for validation
- Handles type interface by determining it's underlying type prior to validation.
- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29)
- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs
- Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError
- Customizable i18n aware error messages.
- Default validator for the [gin](https://github.com/gin-gonic/gin) web framework; upgrading from v8 to v9 in gin see [here](https://github.com/go-playground/validator/tree/master/_examples/gin-upgrading-overriding)
Installation
------------
Use go get.
go get gin-valid/go-playground/validator/v10
Then import the validator package into your own code.
import "gin-valid/go-playground/validator/v10"
Error Return Value
-------
Validation functions return type error
They return type error to avoid the issue discussed in the following, where err is always != nil:
* http://stackoverflow.com/a/29138676/3158232
* https://github.com/go-playground/validator/issues/134
Validator only InvalidValidationError for bad validation input, nil or ValidationErrors as type error; so, in your code all you need to do is check if the error returned is not nil, and if it's not check if error is InvalidValidationError ( if necessary, most of the time it isn't ) type cast it to type ValidationErrors like so:
```go
err := validate.Struct(mystruct)
validationErrors := err.(validator.ValidationErrors)
```
Usage and documentation
------
Please see https://godoc.org/github.com/go-playground/validator for detailed usage docs.
##### Examples:
- [Simple](https://github.com/go-playground/validator/blob/master/_examples/simple/main.go)
- [Custom Field Types](https://github.com/go-playground/validator/blob/master/_examples/custom/main.go)
- [Struct Level](https://github.com/go-playground/validator/blob/master/_examples/struct-level/main.go)
- [Translations & Custom Errors](https://github.com/go-playground/validator/blob/master/_examples/translations/main.go)
- [Gin upgrade and/or override validator](https://github.com/go-playground/validator/tree/v9/_examples/gin-upgrading-overriding)
- [wash - an example application putting it all together](https://github.com/bluesuncorp/wash)
Baked-in Validations
------
### Fields:
| Tag | Description |
| - | - |
| eqcsfield | Field Equals Another Field (relative)|
| eqfield | Field Equals Another Field |
| fieldcontains | NOT DOCUMENTED IN doc.go |
| fieldexcludes | NOT DOCUMENTED IN doc.go |
| gtcsfield | Field Greater Than Another Relative Field |
| gtecsfield | Field Greater Than or Equal To Another Relative Field |
| gtefield | Field Greater Than or Equal To Another Field |
| gtfield | Field Greater Than Another Field |
| ltcsfield | Less Than Another Relative Field |
| ltecsfield | Less Than or Equal To Another Relative Field |
| ltefield | Less Than or Equal To Another Field |
| ltfield | Less Than Another Field |
| necsfield | Field Does Not Equal Another Field (relative) |
| nefield | Field Does Not Equal Another Field |
### Network:
| Tag | Description |
| - | - |
| cidr | Classless Inter-Domain Routing CIDR |
| cidrv4 | Classless Inter-Domain Routing CIDRv4 |
| cidrv6 | Classless Inter-Domain Routing CIDRv6 |
| datauri | Data URL |
| fqdn | Full Qualified Domain Name (FQDN) |
| hostname | Hostname RFC 952 |
| hostname_port | HostPort |
| hostname_rfc1123 | Hostname RFC 1123 |
| ip | Internet Protocol Address IP |
| ip4_addr | Internet Protocol Address IPv4 |
| ip6_addr |Internet Protocol Address IPv6 |
| ip_addr | Internet Protocol Address IP |
| ipv4 | Internet Protocol Address IPv4 |
| ipv6 | Internet Protocol Address IPv6 |
| mac | Media Access Control Address MAC |
| tcp4_addr | Transmission Control Protocol Address TCPv4 |
| tcp6_addr | Transmission Control Protocol Address TCPv6 |
| tcp_addr | Transmission Control Protocol Address TCP |
| udp4_addr | User Datagram Protocol Address UDPv4 |
| udp6_addr | User Datagram Protocol Address UDPv6 |
| udp_addr | User Datagram Protocol Address UDP |
| unix_addr | Unix domain socket end point Address |
| uri | URI String |
| url | URL String |
| url_encoded | URL Encoded |
| urn_rfc2141 | Urn RFC 2141 String |
### Strings:
| Tag | Description |
| - | - |
| alpha | Alpha Only |
| alphanum | Alphanumeric |
| alphanumunicode | Alphanumeric Unicode |
| alphaunicode | Alpha Unicode |
| ascii | ASCII |
| contains | Contains |
| containsany | Contains Any |
| containsrune | Contains Rune |
| endswith | Ends With |
| lowercase | Lowercase |
| multibyte | Multi-Byte Characters |
| number | NOT DOCUMENTED IN doc.go |
| numeric | Numeric |
| printascii | Printable ASCII |
| startswith | Starts With |
| uppercase | Uppercase |
### Format:
| Tag | Description |
| - | - |
| base64 | Base64 String |
| base64url | Base64URL String |
| btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| datetime | Datetime |
| e164 | e164 formatted phone number |
| email | E-mail String
| eth_addr | Ethereum Address |
| hexadecimal | Hexadecimal String |
| hexcolor | Hexcolor String |
| hsl | HSL String |
| hsla | HSLA String |
| html | HTML Tags |
| html_encoded | HTML Encoded |
| isbn | International Standard Book Number |
| isbn10 | International Standard Book Number 10 |
| isbn13 | International Standard Book Number 13 |
| json | JSON |
| latitude | Latitude |
| longitude | Longitude |
| rgb | RGB String |
| rgba | RGBA String |
| ssn | Social Security Number SSN |
| uuid | Universally Unique Identifier UUID |
| uuid3 | Universally Unique Identifier UUID v3 |
| uuid3_rfc4122 | Universally Unique Identifier UUID v3 RFC4122 |
| uuid4 | Universally Unique Identifier UUID v4 |
| uuid4_rfc4122 | Universally Unique Identifier UUID v4 RFC4122 |
| uuid5 | Universally Unique Identifier UUID v5 |
| uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 |
| uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 |
### Comparisons:
| Tag | Description |
| - | - |
| eq | Equals |
| gt | Greater than|
| gte |Greater than or equal |
| lt | Less Than |
| lte | Less Than or Equal |
| ne | Not Equal |
### Other:
| Tag | Description |
| - | - |
| dir | Directory |
| endswith | Ends With |
| excludes | Excludes |
| excludesall | Excludes All |
| excludesrune | Excludes Rune |
| file | File path |
| isdefault | Is Default |
| len | Length |
| max | Maximum |
| min | Minimum |
| oneof | One Of |
| required | Required |
| required_if | Required If |
| required_unless | Required Unless |
| required_with | Required With |
| required_with_all | Required With All |
| required_without | Required Without |
| required_without_all | Required Without All |
| excluded_with | Excluded With |
| excluded_with_all | Excluded With All |
| excluded_without | Excluded Without |
| excluded_without_all | Excluded Without All |
| unique | Unique |
Benchmarks
------
###### Run on MacBook Pro (15-inch, 2017) go version go1.10.2 darwin/amd64
```go
goos: darwin
goarch: amd64
pkg: github.com/go-playground/validator
BenchmarkFieldSuccess-8 20000000 83.6 ns/op 0 B/op 0 allocs/op
BenchmarkFieldSuccessParallel-8 50000000 26.8 ns/op 0 B/op 0 allocs/op
BenchmarkFieldFailure-8 5000000 291 ns/op 208 B/op 4 allocs/op
BenchmarkFieldFailureParallel-8 20000000 107 ns/op 208 B/op 4 allocs/op
BenchmarkFieldArrayDiveSuccess-8 2000000 623 ns/op 201 B/op 11 allocs/op
BenchmarkFieldArrayDiveSuccessParallel-8 10000000 237 ns/op 201 B/op 11 allocs/op
BenchmarkFieldArrayDiveFailure-8 2000000 859 ns/op 412 B/op 16 allocs/op
BenchmarkFieldArrayDiveFailureParallel-8 5000000 335 ns/op 413 B/op 16 allocs/op
BenchmarkFieldMapDiveSuccess-8 1000000 1292 ns/op 432 B/op 18 allocs/op
BenchmarkFieldMapDiveSuccessParallel-8 3000000 467 ns/op 432 B/op 18 allocs/op
BenchmarkFieldMapDiveFailure-8 1000000 1082 ns/op 512 B/op 16 allocs/op
BenchmarkFieldMapDiveFailureParallel-8 5000000 425 ns/op 512 B/op 16 allocs/op
BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1539 ns/op 480 B/op 21 allocs/op
BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 613 ns/op 480 B/op 21 allocs/op
BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1413 ns/op 721 B/op 21 allocs/op
BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 575 ns/op 721 B/op 21 allocs/op
BenchmarkFieldCustomTypeSuccess-8 10000000 216 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.2 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-8 5000000 274 ns/op 208 B/op 4 allocs/op
BenchmarkFieldCustomTypeFailureParallel-8 20000000 116 ns/op 208 B/op 4 allocs/op
BenchmarkFieldOrTagSuccess-8 2000000 740 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagSuccessParallel-8 3000000 474 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagFailure-8 3000000 471 ns/op 224 B/op 5 allocs/op
BenchmarkFieldOrTagFailureParallel-8 3000000 414 ns/op 224 B/op 5 allocs/op
BenchmarkStructLevelValidationSuccess-8 10000000 213 ns/op 32 B/op 2 allocs/op
BenchmarkStructLevelValidationSuccessParallel-8 20000000 91.8 ns/op 32 B/op 2 allocs/op
BenchmarkStructLevelValidationFailure-8 3000000 473 ns/op 304 B/op 8 allocs/op
BenchmarkStructLevelValidationFailureParallel-8 10000000 234 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-8 5000000 385 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 161 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeFailure-8 2000000 640 ns/op 424 B/op 9 allocs/op
BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 318 ns/op 440 B/op 10 allocs/op
BenchmarkStructFilteredSuccess-8 2000000 597 ns/op 288 B/op 9 allocs/op
BenchmarkStructFilteredSuccessParallel-8 10000000 266 ns/op 288 B/op 9 allocs/op
BenchmarkStructFilteredFailure-8 3000000 454 ns/op 256 B/op 7 allocs/op
BenchmarkStructFilteredFailureParallel-8 10000000 214 ns/op 256 B/op 7 allocs/op
BenchmarkStructPartialSuccess-8 3000000 502 ns/op 256 B/op 6 allocs/op
BenchmarkStructPartialSuccessParallel-8 10000000 225 ns/op 256 B/op 6 allocs/op
BenchmarkStructPartialFailure-8 2000000 702 ns/op 480 B/op 11 allocs/op
BenchmarkStructPartialFailureParallel-8 5000000 329 ns/op 480 B/op 11 allocs/op
BenchmarkStructExceptSuccess-8 2000000 793 ns/op 496 B/op 12 allocs/op
BenchmarkStructExceptSuccessParallel-8 10000000 193 ns/op 240 B/op 5 allocs/op
BenchmarkStructExceptFailure-8 2000000 639 ns/op 464 B/op 10 allocs/op
BenchmarkStructExceptFailureParallel-8 5000000 300 ns/op 464 B/op 10 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 417 ns/op 72 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 163 ns/op 72 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldFailure-8 2000000 645 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 285 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 588 ns/op 80 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 221 ns/op 80 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 868 ns/op 320 B/op 9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 337 ns/op 320 B/op 9 allocs/op
BenchmarkStructSimpleSuccess-8 5000000 260 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleSuccessParallel-8 20000000 90.6 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleFailure-8 2000000 619 ns/op 424 B/op 9 allocs/op
BenchmarkStructSimpleFailureParallel-8 5000000 296 ns/op 424 B/op 9 allocs/op
BenchmarkStructComplexSuccess-8 1000000 1454 ns/op 128 B/op 8 allocs/op
BenchmarkStructComplexSuccessParallel-8 3000000 579 ns/op 128 B/op 8 allocs/op
BenchmarkStructComplexFailure-8 300000 4140 ns/op 3041 B/op 53 allocs/op
BenchmarkStructComplexFailureParallel-8 1000000 2127 ns/op 3041 B/op 53 allocs/op
BenchmarkOneof-8 10000000 140 ns/op 0 B/op 0 allocs/op
BenchmarkOneofParallel-8 20000000 70.1 ns/op 0 B/op 0 allocs/op
```
Complementary Software
----------------------
Here is a list of software that complements using this library either pre or post validation.
* [form](https://github.com/go-playground/form) - Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support.
* [mold](https://github.com/go-playground/mold) - A general library to help modify or set data within data structures and other objects
How to Contribute
------
Make a pull request...
License
------
Distributed under MIT License, please see license file within the code for more details.

View File

@ -0,0 +1,39 @@
package main
import (
"fmt"
"gin-valid/go-playground/validator/v10"
)
// MyStruct ..
type MyStruct struct {
String string `validate:"is-awesome"`
}
// use a single instance of Validate, it caches struct info
var validate *validator.Validate
func main() {
validate = validator.New()
validate.RegisterValidation("is-awesome", ValidateMyVal)
s := MyStruct{String: "awesome"}
err := validate.Struct(s)
if err != nil {
fmt.Printf("Err(s):\n%+v\n", err)
}
s.String = "not awesome"
err = validate.Struct(s)
if err != nil {
fmt.Printf("Err(s):\n%+v\n", err)
}
}
// ValidateMyVal implements validator.Func
func ValidateMyVal(fl validator.FieldLevel) bool {
return fl.Field().String() == "awesome"
}

View File

@ -0,0 +1,51 @@
package main
import (
"database/sql"
"database/sql/driver"
"fmt"
"reflect"
"gin-valid/go-playground/validator/v10"
)
// DbBackedUser User struct
type DbBackedUser struct {
Name sql.NullString `validate:"required"`
Age sql.NullInt64 `validate:"required"`
}
// use a single instance of Validate, it caches struct info
var validate *validator.Validate
func main() {
validate = validator.New()
// register all sql.Null* types to use the ValidateValuer CustomTypeFunc
validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
// build object for validation
x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
err := validate.Struct(x)
if err != nil {
fmt.Printf("Err(s):\n%+v\n", err)
}
}
// ValidateValuer implements validator.CustomTypeFunc
func ValidateValuer(field reflect.Value) interface{} {
if valuer, ok := field.Interface().(driver.Valuer); ok {
val, err := valuer.Value()
if err == nil {
return val
}
// handle the error how you want
}
return nil
}

View File

@ -0,0 +1,39 @@
package main
import (
"fmt"
"gin-valid/go-playground/validator/v10"
)
// Test ...
type Test struct {
Array []string `validate:"required,gt=0,dive,required"`
Map map[string]string `validate:"required,gt=0,dive,keys,keymax,endkeys,required,max=1000"`
}
// use a single instance of Validate, it caches struct info
var validate *validator.Validate
func main() {
validate = validator.New()
// registering alias so we can see the differences between
// map key, value validation errors
validate.RegisterAlias("keymax", "max=10")
var test Test
val(test)
test.Array = []string{""}
test.Map = map[string]string{"test > than 10": ""}
val(test)
}
func val(test Test) {
fmt.Println("testing")
err := validate.Struct(test)
fmt.Println(err)
}

View File

@ -0,0 +1,10 @@
package main
import "gin-valid/gin/binding"
func main() {
binding.Validator = new(defaultValidator)
// regular gin logic
}

View File

@ -0,0 +1,55 @@
package main
import (
"reflect"
"sync"
"gin-valid/gin/binding"
"gin-valid/go-playground/validator/v10"
)
type defaultValidator struct {
once sync.Once
validate *validator.Validate
}
var _ binding.StructValidator = &defaultValidator{}
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
if kindOfData(obj) == reflect.Struct {
v.lazyinit()
if err := v.validate.Struct(obj); err != nil {
return err
}
}
return nil
}
func (v *defaultValidator) Engine() interface{} {
v.lazyinit()
return v.validate
}
func (v *defaultValidator) lazyinit() {
v.once.Do(func() {
v.validate = validator.New()
v.validate.SetTagName("binding")
// add any custom validations etc. here
})
}
func kindOfData(data interface{}) reflect.Kind {
value := reflect.ValueOf(data)
valueType := value.Kind()
if valueType == reflect.Ptr {
valueType = value.Elem().Kind()
}
return valueType
}

View File

@ -0,0 +1,101 @@
package main
import (
"fmt"
"gin-valid/go-playground/validator/v10"
)
// User contains user information
type User struct {
FirstName string `validate:"required"`
LastName string `validate:"required"`
Age uint8 `validate:"gte=0,lte=130"`
Email string `validate:"required,email"`
FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla'
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
// Address houses a users address information
type Address struct {
Street string `validate:"required"`
City string `validate:"required"`
Planet string `validate:"required"`
Phone string `validate:"required"`
}
// use a single instance of Validate, it caches struct info
var validate *validator.Validate
func main() {
validate = validator.New()
validateStruct()
validateVariable()
}
func validateStruct() {
address := &Address{
Street: "Eavesdown Docks",
Planet: "Persphone",
Phone: "none",
}
user := &User{
FirstName: "Badger",
LastName: "Smith",
Age: 135,
Email: "Badger.Smith@gmail.com",
FavouriteColor: "#000-",
Addresses: []*Address{address},
}
// returns nil or ValidationErrors ( []FieldError )
err := validate.Struct(user)
if err != nil {
// this check is only needed when your code could produce
// an invalid value for validation such as interface with nil
// value most including myself do not usually have code like this.
if _, ok := err.(*validator.InvalidValidationError); ok {
fmt.Println(err)
return
}
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.Namespace())
fmt.Println(err.Field())
fmt.Println(err.StructNamespace())
fmt.Println(err.StructField())
fmt.Println(err.Tag())
fmt.Println(err.ActualTag())
fmt.Println(err.Kind())
fmt.Println(err.Type())
fmt.Println(err.Value())
fmt.Println(err.Param())
fmt.Println()
}
// from here you can create your own error messages in whatever language you wish
return
}
// save user to database
}
func validateVariable() {
myEmail := "joeybloggs.gmail.com"
errs := validate.Var(myEmail, "required,email")
if errs != nil {
fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
return
}
// email ok, move on
}

View File

@ -0,0 +1,120 @@
package main
import (
"fmt"
"reflect"
"strings"
"gin-valid/go-playground/validator/v10"
)
// User contains user information
type User struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Age uint8 `validate:"gte=0,lte=130"`
Email string `json:"e-mail" validate:"required,email"`
FavouriteColor string `validate:"hexcolor|rgb|rgba"`
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
// Address houses a users address information
type Address struct {
Street string `validate:"required"`
City string `validate:"required"`
Planet string `validate:"required"`
Phone string `validate:"required"`
}
// use a single instance of Validate, it caches struct info
var validate *validator.Validate
func main() {
validate = validator.New()
// register function to get tag name from json tags.
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
// register validation for 'User'
// NOTE: only have to register a non-pointer type for 'User', validator
// interanlly dereferences during it's type checks.
validate.RegisterStructValidation(UserStructLevelValidation, User{})
// build 'User' info, normally posted data etc...
address := &Address{
Street: "Eavesdown Docks",
Planet: "Persphone",
Phone: "none",
City: "Unknown",
}
user := &User{
FirstName: "",
LastName: "",
Age: 45,
Email: "Badger.Smith@gmail",
FavouriteColor: "#000",
Addresses: []*Address{address},
}
// returns InvalidValidationError for bad validation input, nil or ValidationErrors ( []FieldError )
err := validate.Struct(user)
if err != nil {
// this check is only needed when your code could produce
// an invalid value for validation such as interface with nil
// value most including myself do not usually have code like this.
if _, ok := err.(*validator.InvalidValidationError); ok {
fmt.Println(err)
return
}
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.Namespace()) // can differ when a custom TagNameFunc is registered or
fmt.Println(err.Field()) // by passing alt name to ReportError like below
fmt.Println(err.StructNamespace())
fmt.Println(err.StructField())
fmt.Println(err.Tag())
fmt.Println(err.ActualTag())
fmt.Println(err.Kind())
fmt.Println(err.Type())
fmt.Println(err.Value())
fmt.Println(err.Param())
fmt.Println()
}
// from here you can create your own error messages in whatever language you wish
return
}
// save user to database
}
// UserStructLevelValidation contains custom struct level validations that don't always
// make sense at the field validation level. For Example this function validates that either
// FirstName or LastName exist; could have done that with a custom field validation but then
// would have had to add it to both fields duplicating the logic + overhead, this way it's
// only validated once.
//
// NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
// hooks right into validator and you can combine with validation tags and still have a
// common error output format.
func UserStructLevelValidation(sl validator.StructLevel) {
user := sl.Current().Interface().(User)
if len(user.FirstName) == 0 && len(user.LastName) == 0 {
sl.ReportError(user.FirstName, "fname", "FirstName", "fnameorlname", "")
sl.ReportError(user.LastName, "lname", "LastName", "fnameorlname", "")
}
// plus can do more, even with different tag than "fnameorlname"
}

View File

@ -0,0 +1,129 @@
package main
import (
"fmt"
"gin-valid/go-playground/locales/en"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
en_translations "gin-valid/go-playground/validator/v10/translations/en"
)
// User contains user information
type User struct {
FirstName string `validate:"required"`
LastName string `validate:"required"`
Age uint8 `validate:"gte=0,lte=130"`
Email string `validate:"required,email"`
FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla'
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
// Address houses a users address information
type Address struct {
Street string `validate:"required"`
City string `validate:"required"`
Planet string `validate:"required"`
Phone string `validate:"required"`
}
// use a single instance , it caches struct info
var (
uni *ut.UniversalTranslator
validate *validator.Validate
)
func main() {
// NOTE: ommitting allot of error checking for brevity
en := en.New()
uni = ut.New(en, en)
// this is usually know or extracted from http 'Accept-Language' header
// also see uni.FindTranslator(...)
trans, _ := uni.GetTranslator("en")
validate = validator.New()
en_translations.RegisterDefaultTranslations(validate, trans)
translateAll(trans)
translateIndividual(trans)
translateOverride(trans) // yep you can specify your own in whatever locale you want!
}
func translateAll(trans ut.Translator) {
type User struct {
Username string `validate:"required"`
Tagline string `validate:"required,lt=10"`
Tagline2 string `validate:"required,gt=1"`
}
user := User{
Username: "Joeybloggs",
Tagline: "This tagline is way too long.",
Tagline2: "1",
}
err := validate.Struct(user)
if err != nil {
// translate all error at once
errs := err.(validator.ValidationErrors)
// returns a map with key = namespace & value = translated error
// NOTICE: 2 errors are returned and you'll see something surprising
// translations are i18n aware!!!!
// eg. '10 characters' vs '1 character'
fmt.Println(errs.Translate(trans))
}
}
func translateIndividual(trans ut.Translator) {
type User struct {
Username string `validate:"required"`
}
var user User
err := validate.Struct(user)
if err != nil {
errs := err.(validator.ValidationErrors)
for _, e := range errs {
// can translate each error one at a time.
fmt.Println(e.Translate(trans))
}
}
}
func translateOverride(trans ut.Translator) {
validate.RegisterTranslation("required", trans, func(ut ut.Translator) error {
return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("required", fe.Field())
return t
})
type User struct {
Username string `validate:"required"`
}
var user User
err := validate.Struct(user)
if err != nil {
errs := err.(validator.ValidationErrors)
for _, e := range errs {
// can translate each error one at a time.
fmt.Println(e.Translate(trans))
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,322 @@
package validator
import (
"fmt"
"reflect"
"strings"
"sync"
"sync/atomic"
)
type tagType uint8
const (
typeDefault tagType = iota
typeOmitEmpty
typeIsDefault
typeNoStructLevel
typeStructOnly
typeDive
typeOr
typeKeys
typeEndKeys
)
const (
invalidValidation = "Invalid validation tag on field '%s'"
undefinedValidation = "Undefined validation function '%s' on field '%s'"
keysTagNotDefined = "'" + endKeysTag + "' tag encountered without a corresponding '" + keysTag + "' tag"
)
type structCache struct {
lock sync.Mutex
m atomic.Value // map[reflect.Type]*cStruct
}
func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) {
c, found = sc.m.Load().(map[reflect.Type]*cStruct)[key] //有一个断言的封装
return
}
func (sc *structCache) Set(key reflect.Type, value *cStruct) {
m := sc.m.Load().(map[reflect.Type]*cStruct)
nm := make(map[reflect.Type]*cStruct, len(m)+1)
for k, v := range m {
nm[k] = v
}
nm[key] = value
sc.m.Store(nm)
}
type tagCache struct {
lock sync.Mutex
m atomic.Value // map[string]*cTag
}
func (tc *tagCache) Get(key string) (c *cTag, found bool) {
c, found = tc.m.Load().(map[string]*cTag)[key]
return
}
func (tc *tagCache) Set(key string, value *cTag) {
m := tc.m.Load().(map[string]*cTag)
nm := make(map[string]*cTag, len(m)+1)
for k, v := range m {
nm[k] = v
}
nm[key] = value
tc.m.Store(nm)
}
type cStruct struct {
name string
fields []*cField
fn StructLevelFuncCtx
}
type cField struct {
idx int
name string // field.name
altName string // 如果没有自定义 tagNameFunc,就是field.name, 如果有,比如可以按 json_tag中的, 报错的时候就会按照这个 报错
namesEqual bool // field.name 和 altName是否相同
cTags *cTag //以链表的形式存在
}
type cTag struct {
tag string // (gte=10) 实际是 gte,不带参数
aliasTag string // 应该是别名, 不过在没有别名的时候等同于 tag
actualAliasTag string // 实际名字,而不是在func *Validate.parseFieldTagsRecursive 解析后的 别名
param string
keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation
next *cTag
fn FuncCtx
typeof tagType
hasTag bool
hasAlias bool
hasParam bool // true if parameter used eg. eq= where the equal sign has been set
isBlockEnd bool // indicates the current tag represents the last validation in the block , 表示当前field的最后一个,比如 `require,gte=10`,那么到gte就是true
runValidationWhenNil bool
}
func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct {
v.structCache.lock.Lock()
defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise!
typ := current.Type()
// could have been multiple trying to access, but once first is done this ensures struct
// isn't parsed again.
cs, ok := v.structCache.Get(typ)
if ok {
return cs
}
cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]}
numFields := current.NumField()
var ctag *cTag
var fld reflect.StructField
var tag string
var customName string
for i := 0; i < numFields; i++ {
fld = typ.Field(i)
if !fld.Anonymous && len(fld.PkgPath) > 0 { // 如果不是 嵌套field 且是 未导出字段, (大写的字段 pakPath为空)
continue
}
tag = fld.Tag.Get(v.tagName)
if tag == skipValidationTag {
continue
}
customName = fld.Name
if v.hasTagNameFunc { // 自定义的获取 名字的func
name := v.tagNameFunc(fld)
if len(name) > 0 {
customName = name
}
}
// NOTE: cannot use shared tag cache, because tags may be equal, but things like alias may be different
// and so only struct level caching can be used instead of combined with Field tag caching
if len(tag) > 0 {
ctag, _ = v.parseFieldTagsRecursive(tag, fld.Name, "", false) //返回链头
} else {
// even if field doesn't have validations need cTag for traversing to potential inner/nested
// elements of the field.
ctag = new(cTag)
}
cs.fields = append(cs.fields, &cField{
idx: i,
name: fld.Name,
altName: customName,
cTags: ctag,
namesEqual: fld.Name == customName,
})
}
v.structCache.Set(typ, cs)
return cs
}
func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) {
var t string
noAlias := len(alias) == 0
tags := strings.Split(tag, tagSeparator)
for i := 0; i < len(tags); i++ {
t = tags[i]
if noAlias {
alias = t
}
// check map for alias and process new tags, otherwise process as usual
if tagsVal, found := v.aliases[t]; found {
if i == 0 {
firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
} else {
next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
current.next, current = next, curr
}
continue
}
var prevTag tagType
if i == 0 {
current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true, typeof: typeDefault}
firstCtag = current
} else {
prevTag = current.typeof // 此刻之前current 还是 上一个的状态
current.next = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true}
current = current.next
}
switch t {
case diveTag:
current.typeof = typeDive
continue
case keysTag:
current.typeof = typeKeys
if i == 0 || prevTag != typeDive {
panic(fmt.Sprintf("'%s' tag must be immediately preceded by the '%s' tag", keysTag, diveTag))
}
current.typeof = typeKeys
// need to pass along only keys tag
// need to increment i to skip over the keys tags
b := make([]byte, 0, 64)
i++
for ; i < len(tags); i++ {
b = append(b, tags[i]...)
b = append(b, ',')
if tags[i] == endKeysTag {
break
}
}
current.keys, _ = v.parseFieldTagsRecursive(string(b[:len(b)-1]), fieldName, "", false)
continue
case endKeysTag:
current.typeof = typeEndKeys
// if there are more in tags then there was no keysTag defined
// and an error should be thrown
if i != len(tags)-1 {
panic(keysTagNotDefined)
}
return
case omitempty:
current.typeof = typeOmitEmpty
continue
case structOnlyTag:
current.typeof = typeStructOnly
continue
case noStructLevelTag:
current.typeof = typeNoStructLevel
continue
default:
if t == isdefault {
current.typeof = typeIsDefault
}
// if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
orVals := strings.Split(t, orSeparator)
for j := 0; j < len(orVals); j++ {
vals := strings.SplitN(orVals[j], tagKeySeparator, 2) // binding:"gte=10" 的 =
if noAlias {
alias = vals[0] //如果没有别名就 gte , 前面的是 gte=10
current.aliasTag = alias
} else {
current.actualAliasTag = t // 原始名字 gte=10
}
if j > 0 { //这里的current.next 不应该是 .pre 前一个么,不过前一个也不对啊,后面的赋值还是给 current赋值啊
current.next = &cTag{aliasTag: alias, actualAliasTag: current.actualAliasTag, hasAlias: hasAlias, hasTag: true}
current = current.next
}
current.hasParam = len(vals) > 1
current.tag = vals[0]
if len(current.tag) == 0 {
panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName)))
}
if wrapper, ok := v.validations[current.tag]; ok {
current.fn = wrapper.fn
current.runValidationWhenNil = wrapper.runValidatinOnNil
} else {
panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName)))
}
if len(orVals) > 1 {
current.typeof = typeOr
}
if len(vals) > 1 {
current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
}
}
current.isBlockEnd = true
}
}
return
}
func (v *Validate) fetchCacheTag(tag string) *cTag {
// find cached tag
ctag, found := v.tagCache.Get(tag)
if !found {
v.tagCache.lock.Lock()
defer v.tagCache.lock.Unlock()
// could have been multiple trying to access, but once first is done this ensures tag
// isn't parsed again.
ctag, found = v.tagCache.Get(tag)
if !found {
ctag, _ = v.parseFieldTagsRecursive(tag, "", "", false)
v.tagCache.Set(tag, ctag)
}
}
return ctag
}

View File

@ -0,0 +1,162 @@
package validator
var iso3166_1_alpha2 = map[string]bool{
// see: https://www.iso.org/iso-3166-country-codes.html
"AF": true, "AX": true, "AL": true, "DZ": true, "AS": true,
"AD": true, "AO": true, "AI": true, "AQ": true, "AG": true,
"AR": true, "AM": true, "AW": true, "AU": true, "AT": true,
"AZ": true, "BS": true, "BH": true, "BD": true, "BB": true,
"BY": true, "BE": true, "BZ": true, "BJ": true, "BM": true,
"BT": true, "BO": true, "BQ": true, "BA": true, "BW": true,
"BV": true, "BR": true, "IO": true, "BN": true, "BG": true,
"BF": true, "BI": true, "KH": true, "CM": true, "CA": true,
"CV": true, "KY": true, "CF": true, "TD": true, "CL": true,
"CN": true, "CX": true, "CC": true, "CO": true, "KM": true,
"CG": true, "CD": true, "CK": true, "CR": true, "CI": true,
"HR": true, "CU": true, "CW": true, "CY": true, "CZ": true,
"DK": true, "DJ": true, "DM": true, "DO": true, "EC": true,
"EG": true, "SV": true, "GQ": true, "ER": true, "EE": true,
"ET": true, "FK": true, "FO": true, "FJ": true, "FI": true,
"FR": true, "GF": true, "PF": true, "TF": true, "GA": true,
"GM": true, "GE": true, "DE": true, "GH": true, "GI": true,
"GR": true, "GL": true, "GD": true, "GP": true, "GU": true,
"GT": true, "GG": true, "GN": true, "GW": true, "GY": true,
"HT": true, "HM": true, "VA": true, "HN": true, "HK": true,
"HU": true, "IS": true, "IN": true, "ID": true, "IR": true,
"IQ": true, "IE": true, "IM": true, "IL": true, "IT": true,
"JM": true, "JP": true, "JE": true, "JO": true, "KZ": true,
"KE": true, "KI": true, "KP": true, "KR": true, "KW": true,
"KG": true, "LA": true, "LV": true, "LB": true, "LS": true,
"LR": true, "LY": true, "LI": true, "LT": true, "LU": true,
"MO": true, "MK": true, "MG": true, "MW": true, "MY": true,
"MV": true, "ML": true, "MT": true, "MH": true, "MQ": true,
"MR": true, "MU": true, "YT": true, "MX": true, "FM": true,
"MD": true, "MC": true, "MN": true, "ME": true, "MS": true,
"MA": true, "MZ": true, "MM": true, "NA": true, "NR": true,
"NP": true, "NL": true, "NC": true, "NZ": true, "NI": true,
"NE": true, "NG": true, "NU": true, "NF": true, "MP": true,
"NO": true, "OM": true, "PK": true, "PW": true, "PS": true,
"PA": true, "PG": true, "PY": true, "PE": true, "PH": true,
"PN": true, "PL": true, "PT": true, "PR": true, "QA": true,
"RE": true, "RO": true, "RU": true, "RW": true, "BL": true,
"SH": true, "KN": true, "LC": true, "MF": true, "PM": true,
"VC": true, "WS": true, "SM": true, "ST": true, "SA": true,
"SN": true, "RS": true, "SC": true, "SL": true, "SG": true,
"SX": true, "SK": true, "SI": true, "SB": true, "SO": true,
"ZA": true, "GS": true, "SS": true, "ES": true, "LK": true,
"SD": true, "SR": true, "SJ": true, "SZ": true, "SE": true,
"CH": true, "SY": true, "TW": true, "TJ": true, "TZ": true,
"TH": true, "TL": true, "TG": true, "TK": true, "TO": true,
"TT": true, "TN": true, "TR": true, "TM": true, "TC": true,
"TV": true, "UG": true, "UA": true, "AE": true, "GB": true,
"US": true, "UM": true, "UY": true, "UZ": true, "VU": true,
"VE": true, "VN": true, "VG": true, "VI": true, "WF": true,
"EH": true, "YE": true, "ZM": true, "ZW": true,
}
var iso3166_1_alpha3 = map[string]bool{
// see: https://www.iso.org/iso-3166-country-codes.html
"AFG": true, "ALB": true, "DZA": true, "ASM": true, "AND": true,
"AGO": true, "AIA": true, "ATA": true, "ATG": true, "ARG": true,
"ARM": true, "ABW": true, "AUS": true, "AUT": true, "AZE": true,
"BHS": true, "BHR": true, "BGD": true, "BRB": true, "BLR": true,
"BEL": true, "BLZ": true, "BEN": true, "BMU": true, "BTN": true,
"BOL": true, "BES": true, "BIH": true, "BWA": true, "BVT": true,
"BRA": true, "IOT": true, "BRN": true, "BGR": true, "BFA": true,
"BDI": true, "CPV": true, "KHM": true, "CMR": true, "CAN": true,
"CYM": true, "CAF": true, "TCD": true, "CHL": true, "CHN": true,
"CXR": true, "CCK": true, "COL": true, "COM": true, "COD": true,
"COG": true, "COK": true, "CRI": true, "HRV": true, "CUB": true,
"CUW": true, "CYP": true, "CZE": true, "CIV": true, "DNK": true,
"DJI": true, "DMA": true, "DOM": true, "ECU": true, "EGY": true,
"SLV": true, "GNQ": true, "ERI": true, "EST": true, "SWZ": true,
"ETH": true, "FLK": true, "FRO": true, "FJI": true, "FIN": true,
"FRA": true, "GUF": true, "PYF": true, "ATF": true, "GAB": true,
"GMB": true, "GEO": true, "DEU": true, "GHA": true, "GIB": true,
"GRC": true, "GRL": true, "GRD": true, "GLP": true, "GUM": true,
"GTM": true, "GGY": true, "GIN": true, "GNB": true, "GUY": true,
"HTI": true, "HMD": true, "VAT": true, "HND": true, "HKG": true,
"HUN": true, "ISL": true, "IND": true, "IDN": true, "IRN": true,
"IRQ": true, "IRL": true, "IMN": true, "ISR": true, "ITA": true,
"JAM": true, "JPN": true, "JEY": true, "JOR": true, "KAZ": true,
"KEN": true, "KIR": true, "PRK": true, "KOR": true, "KWT": true,
"KGZ": true, "LAO": true, "LVA": true, "LBN": true, "LSO": true,
"LBR": true, "LBY": true, "LIE": true, "LTU": true, "LUX": true,
"MAC": true, "MDG": true, "MWI": true, "MYS": true, "MDV": true,
"MLI": true, "MLT": true, "MHL": true, "MTQ": true, "MRT": true,
"MUS": true, "MYT": true, "MEX": true, "FSM": true, "MDA": true,
"MCO": true, "MNG": true, "MNE": true, "MSR": true, "MAR": true,
"MOZ": true, "MMR": true, "NAM": true, "NRU": true, "NPL": true,
"NLD": true, "NCL": true, "NZL": true, "NIC": true, "NER": true,
"NGA": true, "NIU": true, "NFK": true, "MKD": true, "MNP": true,
"NOR": true, "OMN": true, "PAK": true, "PLW": true, "PSE": true,
"PAN": true, "PNG": true, "PRY": true, "PER": true, "PHL": true,
"PCN": true, "POL": true, "PRT": true, "PRI": true, "QAT": true,
"ROU": true, "RUS": true, "RWA": true, "REU": true, "BLM": true,
"SHN": true, "KNA": true, "LCA": true, "MAF": true, "SPM": true,
"VCT": true, "WSM": true, "SMR": true, "STP": true, "SAU": true,
"SEN": true, "SRB": true, "SYC": true, "SLE": true, "SGP": true,
"SXM": true, "SVK": true, "SVN": true, "SLB": true, "SOM": true,
"ZAF": true, "SGS": true, "SSD": true, "ESP": true, "LKA": true,
"SDN": true, "SUR": true, "SJM": true, "SWE": true, "CHE": true,
"SYR": true, "TWN": true, "TJK": true, "TZA": true, "THA": true,
"TLS": true, "TGO": true, "TKL": true, "TON": true, "TTO": true,
"TUN": true, "TUR": true, "TKM": true, "TCA": true, "TUV": true,
"UGA": true, "UKR": true, "ARE": true, "GBR": true, "UMI": true,
"USA": true, "URY": true, "UZB": true, "VUT": true, "VEN": true,
"VNM": true, "VGB": true, "VIR": true, "WLF": true, "ESH": true,
"YEM": true, "ZMB": true, "ZWE": true, "ALA": true,
}
var iso3166_1_alpha_numeric = map[int]bool{
// see: https://www.iso.org/iso-3166-country-codes.html
4: true, 8: true, 12: true, 16: true, 20: true,
24: true, 660: true, 10: true, 28: true, 32: true,
51: true, 533: true, 36: true, 40: true, 31: true,
44: true, 48: true, 50: true, 52: true, 112: true,
56: true, 84: true, 204: true, 60: true, 64: true,
68: true, 535: true, 70: true, 72: true, 74: true,
76: true, 86: true, 96: true, 100: true, 854: true,
108: true, 132: true, 116: true, 120: true, 124: true,
136: true, 140: true, 148: true, 152: true, 156: true,
162: true, 166: true, 170: true, 174: true, 180: true,
178: true, 184: true, 188: true, 191: true, 192: true,
531: true, 196: true, 203: true, 384: true, 208: true,
262: true, 212: true, 214: true, 218: true, 818: true,
222: true, 226: true, 232: true, 233: true, 748: true,
231: true, 238: true, 234: true, 242: true, 246: true,
250: true, 254: true, 258: true, 260: true, 266: true,
270: true, 268: true, 276: true, 288: true, 292: true,
300: true, 304: true, 308: true, 312: true, 316: true,
320: true, 831: true, 324: true, 624: true, 328: true,
332: true, 334: true, 336: true, 340: true, 344: true,
348: true, 352: true, 356: true, 360: true, 364: true,
368: true, 372: true, 833: true, 376: true, 380: true,
388: true, 392: true, 832: true, 400: true, 398: true,
404: true, 296: true, 408: true, 410: true, 414: true,
417: true, 418: true, 428: true, 422: true, 426: true,
430: true, 434: true, 438: true, 440: true, 442: true,
446: true, 450: true, 454: true, 458: true, 462: true,
466: true, 470: true, 584: true, 474: true, 478: true,
480: true, 175: true, 484: true, 583: true, 498: true,
492: true, 496: true, 499: true, 500: true, 504: true,
508: true, 104: true, 516: true, 520: true, 524: true,
528: true, 540: true, 554: true, 558: true, 562: true,
566: true, 570: true, 574: true, 807: true, 580: true,
578: true, 512: true, 586: true, 585: true, 275: true,
591: true, 598: true, 600: true, 604: true, 608: true,
612: true, 616: true, 620: true, 630: true, 634: true,
642: true, 643: true, 646: true, 638: true, 652: true,
654: true, 659: true, 662: true, 663: true, 666: true,
670: true, 882: true, 674: true, 678: true, 682: true,
686: true, 688: true, 690: true, 694: true, 702: true,
534: true, 703: true, 705: true, 90: true, 706: true,
710: true, 239: true, 728: true, 724: true, 144: true,
729: true, 740: true, 744: true, 752: true, 756: true,
760: true, 158: true, 762: true, 834: true, 764: true,
626: true, 768: true, 772: true, 776: true, 780: true,
788: true, 792: true, 795: true, 796: true, 798: true,
800: true, 804: true, 784: true, 826: true, 581: true,
840: true, 858: true, 860: true, 548: true, 862: true,
704: true, 92: true, 850: true, 876: true, 732: true,
887: true, 894: true, 716: true, 248: true,
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,295 @@
package validator
import (
"bytes"
"fmt"
"reflect"
"strings"
ut "gin-valid/go-playground/universal-translator"
)
const (
fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag"
)
// ValidationErrorsTranslations is the translation return type
type ValidationErrorsTranslations map[string]string
// InvalidValidationError describes an invalid argument passed to
// `Struct`, `StructExcept`, StructPartial` or `Field`
type InvalidValidationError struct {
Type reflect.Type
}
// Error returns InvalidValidationError message
func (e *InvalidValidationError) Error() string {
if e.Type == nil {
return "validator: (nil)"
}
return "validator: (nil " + e.Type.String() + ")"
}
// ValidationErrors is an array of FieldError's
// for use in custom error messages post validation.
type ValidationErrors []FieldError
// Error is intended for use in development + debugging and not intended to be a production error message.
// It allows ValidationErrors to subscribe to the Error interface.
// All information to create an error message specific to your application is contained within
// the FieldError found within the ValidationErrors array
func (ve ValidationErrors) Error() string {
buff := bytes.NewBufferString("")
var fe *fieldError
for i := 0; i < len(ve); i++ {
fe = ve[i].(*fieldError)
buff.WriteString(fe.Error())
buff.WriteString("\n")
}
return strings.TrimSpace(buff.String())
}
// yang 修改
// Translate translates all of the ValidationErrors
//func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations {
//
// trans := make(ValidationErrorsTranslations)
//
// var fe *fieldError
//
// for i := 0; i < len(ve); i++ {
// fe = ve[i].(*fieldError)
//
// // // in case an Anonymous struct was used, ensure that the key
// // // would be 'Username' instead of ".Username"
// // if len(fe.ns) > 0 && fe.ns[:1] == "." {
// // trans[fe.ns[1:]] = fe.Translate(ut)
// // continue
// // }
//
// trans[fe.ns] = fe.Translate(ut)
// }
//
// return trans
//}
type TransValidError struct {
ErrorString string
}
func (e TransValidError) Error() string {
return e.ErrorString
}
func (ve ValidationErrors) Translate(ut ut.Translator) TransValidError {
var result TransValidError
var fe *fieldError
if len(ve) == 0 {
return result
}
fe = ve[0].(*fieldError)
result.ErrorString = fe.Translate(ut)
return result
}
// yang 修改结束
// FieldError contains all functions to get error details
type FieldError interface {
// returns the validation tag that failed. if the
// validation was an alias, this will return the
// alias name and not the underlying tag that failed.
//
// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
// will return "iscolor"
Tag() string
// returns the validation tag that failed, even if an
// alias the actual tag within the alias will be returned.
// If an 'or' validation fails the entire or will be returned.
//
// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
// will return "hexcolor|rgb|rgba|hsl|hsla"
ActualTag() string
// returns the namespace for the field error, with the tag
// name taking precedence over the field's actual name.
//
// eg. JSON name "User.fname"
//
// See StructNamespace() for a version that returns actual names.
//
// NOTE: this field can be blank when validating a single primitive field
// using validate.Field(...) as there is no way to extract it's name
Namespace() string
// returns the namespace for the field error, with the field's
// actual name.
//
// eq. "User.FirstName" see Namespace for comparison
//
// NOTE: this field can be blank when validating a single primitive field
// using validate.Field(...) as there is no way to extract its name
StructNamespace() string
// returns the fields name with the tag name taking precedence over the
// field's actual name.
//
// eq. JSON name "fname"
// see StructField for comparison
Field() string
// returns the field's actual name from the struct, when able to determine.
//
// eq. "FirstName"
// see Field for comparison
StructField() string
// returns the actual field's value in case needed for creating the error
// message
Value() interface{}
// returns the param value, in string form for comparison; this will also
// help with generating an error message
Param() string
// Kind returns the Field's reflect Kind
//
// eg. time.Time's kind is a struct
Kind() reflect.Kind
// Type returns the Field's reflect Type
//
// // eg. time.Time's type is time.Time
Type() reflect.Type
// returns the FieldError's translated error
// from the provided 'ut.Translator' and registered 'TranslationFunc'
//
// NOTE: if no registered translator can be found it returns the same as
// calling fe.Error()
Translate(ut ut.Translator) string
// Error returns the FieldError's message
Error() string
}
// compile time interface checks
var _ FieldError = new(fieldError)
var _ error = new(fieldError)
// fieldError contains a single field's validation error along
// with other properties that may be needed for error message creation
// it complies with the FieldError interface
type fieldError struct {
v *Validate
tag string
actualTag string
ns string
structNs string
fieldLen uint8
structfieldLen uint8
value interface{}
param string
kind reflect.Kind
typ reflect.Type
}
// Tag returns the validation tag that failed.
func (fe *fieldError) Tag() string {
return fe.tag
}
// ActualTag returns the validation tag that failed, even if an
// alias the actual tag within the alias will be returned.
func (fe *fieldError) ActualTag() string {
return fe.actualTag
}
// Namespace returns the namespace for the field error, with the tag
// name taking precedence over the field's actual name.
func (fe *fieldError) Namespace() string {
return fe.ns
}
// StructNamespace returns the namespace for the field error, with the field's
// actual name.
func (fe *fieldError) StructNamespace() string {
return fe.structNs
}
// Field returns the field's name with the tag name taking precedence over the
// field's actual name.
func (fe *fieldError) Field() string {
return fe.ns[len(fe.ns)-int(fe.fieldLen):]
// // return fe.field
// fld := fe.ns[len(fe.ns)-int(fe.fieldLen):]
// log.Println("FLD:", fld)
// if len(fld) > 0 && fld[:1] == "." {
// return fld[1:]
// }
// return fld
}
// returns the field's actual name from the struct, when able to determine.
func (fe *fieldError) StructField() string {
// return fe.structField
return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):]
}
// Value returns the actual field's value in case needed for creating the error
// message
func (fe *fieldError) Value() interface{} {
return fe.value
}
// Param returns the param value, in string form for comparison; this will
// also help with generating an error message
func (fe *fieldError) Param() string {
return fe.param
}
// Kind returns the Field's reflect Kind
func (fe *fieldError) Kind() reflect.Kind {
return fe.kind
}
// Type returns the Field's reflect Type
func (fe *fieldError) Type() reflect.Type {
return fe.typ
}
// Error returns the fieldError's error message
func (fe *fieldError) Error() string {
return fmt.Sprintf(fieldErrMsg, fe.ns, fe.Field(), fe.tag)
}
// Translate returns the FieldError's translated error
// from the provided 'ut.Translator' and registered 'TranslationFunc'
//
// NOTE: if no registered translation can be found, it returns the original
// untranslated error message.
func (fe *fieldError) Translate(ut ut.Translator) string {
m, ok := fe.v.transTagFunc[ut]
if !ok {
return fe.Error()
}
fn, ok := m[fe.tag]
if !ok {
return fe.Error()
}
return fn(ut, fe)
}

View File

@ -0,0 +1,119 @@
package validator
import "reflect"
// FieldLevel contains all the information and helper functions
// to validate a field
type FieldLevel interface {
// returns the top level struct, if any
Top() reflect.Value
// returns the current fields parent struct, if any or
// the comparison value if called 'VarWithValue'
Parent() reflect.Value
// returns current field for validation
Field() reflect.Value
// returns the field's name with the tag
// name taking precedence over the fields actual name.
FieldName() string
// returns the struct field's name
StructFieldName() string
// returns param for validation against current field
Param() string
// GetTag returns the current validations tag name
GetTag() string
// ExtractType gets the actual underlying type of field value.
// It will dive into pointers, customTypes and return you the
// underlying value and it's kind.
ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)
// traverses the parent struct to retrieve a specific field denoted by the provided namespace
// in the param and returns the field, field kind and whether is was successful in retrieving
// the field at all.
//
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist.
//
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
GetStructFieldOK() (reflect.Value, reflect.Kind, bool)
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
//
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool)
// traverses the parent struct to retrieve a specific field denoted by the provided namespace
// in the param and returns the field, field kind, if it's a nullable type and whether is was successful in retrieving
// the field at all.
//
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist.
GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool)
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool)
}
var _ FieldLevel = new(validate)
// Field returns current field for validation
func (v *validate) Field() reflect.Value {
return v.flField
}
// FieldName returns the field's name with the tag
// name taking precedence over the fields actual name.
func (v *validate) FieldName() string {
return v.cf.altName
}
// GetTag returns the current validations tag name
func (v *validate) GetTag() string {
return v.ct.tag
}
// StructFieldName returns the struct field's name
func (v *validate) StructFieldName() string {
return v.cf.name
}
// Param returns param for validation against current field
func (v *validate) Param() string {
return v.ct.param
}
// GetStructFieldOK returns Param returns param for validation against current field
//
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) {
current, kind, _, found := v.getStructFieldOKInternal(v.slflParent, v.ct.param)
return current, kind, found
}
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
//
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) {
current, kind, _, found := v.GetStructFieldOKAdvanced2(val, namespace)
return current, kind, found
}
// GetStructFieldOK returns Param returns param for validation against current field
func (v *validate) GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) {
return v.getStructFieldOKInternal(v.slflParent, v.ct.param)
}
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
func (v *validate) GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) {
return v.getStructFieldOKInternal(val, namespace)
}

View File

@ -0,0 +1,48 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
gin-valid/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
gin-valid/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
gin-valid/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
gin-valid/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
gin-valid/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,25 @@
package validators
import (
"reflect"
"strings"
"gin-valid/go-playground/validator/v10"
)
// NotBlank is the validation function for validating if the current field
// has a value or length greater than zero, or is not a space only string.
func NotBlank(fl validator.FieldLevel) bool {
field := fl.Field()
switch field.Kind() {
case reflect.String:
return len(strings.TrimSpace(field.String())) > 0
case reflect.Chan, reflect.Map, reflect.Slice, reflect.Array:
return field.Len() > 0
case reflect.Ptr, reflect.Interface, reflect.Func:
return !field.IsNil()
default:
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
}
}

View File

@ -0,0 +1,65 @@
package validators
import (
"testing"
"gin-valid/go-playground/validator/v10"
"github.com/go-playground/assert/v2"
)
type test struct {
String string `validate:"notblank"`
Array []int `validate:"notblank"`
Pointer *int `validate:"notblank"`
Number int `validate:"notblank"`
Interface interface{} `validate:"notblank"`
Func func() `validate:"notblank"`
}
func TestNotBlank(t *testing.T) {
v := validator.New()
err := v.RegisterValidation("notblank", NotBlank)
assert.Equal(t, nil, err)
// Errors
var x *int
invalid := test{
String: " ",
Array: []int{},
Pointer: x,
Number: 0,
Interface: nil,
Func: nil,
}
fieldsWithError := []string{
"String",
"Array",
"Pointer",
"Number",
"Interface",
"Func",
}
errors := v.Struct(invalid).(validator.ValidationErrors)
var fields []string
for _, err := range errors {
fields = append(fields, err.Field())
}
assert.Equal(t, fieldsWithError, fields)
// No errors
y := 1
x = &y
valid := test{
String: "str",
Array: []int{1},
Pointer: x,
Number: 1,
Interface: "value",
Func: func() {},
}
err = v.Struct(valid)
assert.Equal(t, nil, err)
}

View File

@ -0,0 +1,101 @@
package validator
import "regexp"
const (
alphaRegexString = "^[a-zA-Z]+$"
alphaNumericRegexString = "^[a-zA-Z0-9]+$"
alphaUnicodeRegexString = "^[\\p{L}]+$"
alphaUnicodeNumericRegexString = "^[\\p{L}\\p{N}]+$"
numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$"
numberRegexString = "^[0-9]+$"
hexadecimalRegexString = "^(0[xX])?[0-9a-fA-F]+$"
hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$"
rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$"
hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
e164RegexString = "^\\+[1-9]?[0-9]{7,14}$"
base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$"
iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$"
iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$"
uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
uUID3RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-3[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
uUID4RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
uUIDRFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
aSCIIRegexString = "^[\x00-\x7F]*$"
printableASCIIRegexString = "^[\x20-\x7E]*$"
multibyteRegexString = "[^\x00-\x7F]"
dataURIRegexString = `^data:((?:\w+\/(?:([^;]|;[^;]).)+)?)`
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$`
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62})(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
ethAddressRegexString = `^0x[0-9a-fA-F]{40}$`
ethAddressUpperRegexString = `^0x[0-9A-F]{40}$`
ethAddressLowerRegexString = `^0x[0-9a-f]{40}$`
uRLEncodedRegexString = `(%[A-Fa-f0-9]{2})`
hTMLEncodedRegexString = `&#[x]?([0-9a-fA-F]{2})|(&gt)|(&lt)|(&quot)|(&amp)+[;]?`
hTMLRegexString = `<[/]?([a-zA-Z]+).*?>`
splitParamsRegexString = `'[^']*'|\S+`
)
var (
alphaRegex = regexp.MustCompile(alphaRegexString)
alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString)
alphaUnicodeRegex = regexp.MustCompile(alphaUnicodeRegexString)
alphaUnicodeNumericRegex = regexp.MustCompile(alphaUnicodeNumericRegexString)
numericRegex = regexp.MustCompile(numericRegexString)
numberRegex = regexp.MustCompile(numberRegexString)
hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString)
hexcolorRegex = regexp.MustCompile(hexcolorRegexString)
rgbRegex = regexp.MustCompile(rgbRegexString)
rgbaRegex = regexp.MustCompile(rgbaRegexString)
hslRegex = regexp.MustCompile(hslRegexString)
hslaRegex = regexp.MustCompile(hslaRegexString)
e164Regex = regexp.MustCompile(e164RegexString)
emailRegex = regexp.MustCompile(emailRegexString)
base64Regex = regexp.MustCompile(base64RegexString)
base64URLRegex = regexp.MustCompile(base64URLRegexString)
iSBN10Regex = regexp.MustCompile(iSBN10RegexString)
iSBN13Regex = regexp.MustCompile(iSBN13RegexString)
uUID3Regex = regexp.MustCompile(uUID3RegexString)
uUID4Regex = regexp.MustCompile(uUID4RegexString)
uUID5Regex = regexp.MustCompile(uUID5RegexString)
uUIDRegex = regexp.MustCompile(uUIDRegexString)
uUID3RFC4122Regex = regexp.MustCompile(uUID3RFC4122RegexString)
uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString)
uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString)
uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString)
aSCIIRegex = regexp.MustCompile(aSCIIRegexString)
printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString)
multibyteRegex = regexp.MustCompile(multibyteRegexString)
dataURIRegex = regexp.MustCompile(dataURIRegexString)
latitudeRegex = regexp.MustCompile(latitudeRegexString)
longitudeRegex = regexp.MustCompile(longitudeRegexString)
sSNRegex = regexp.MustCompile(sSNRegexString)
hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952)
hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123)
fqdnRegexRFC1123 = regexp.MustCompile(fqdnRegexStringRFC1123)
btcAddressRegex = regexp.MustCompile(btcAddressRegexString)
btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32)
btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32)
ethAddressRegex = regexp.MustCompile(ethAddressRegexString)
ethaddressRegexUpper = regexp.MustCompile(ethAddressUpperRegexString)
ethAddressRegexLower = regexp.MustCompile(ethAddressLowerRegexString)
uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString)
hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString)
hTMLRegex = regexp.MustCompile(hTMLRegexString)
splitParamsRegex = regexp.MustCompile(splitParamsRegexString)
)

View File

@ -0,0 +1,175 @@
package validator
import (
"context"
"reflect"
)
// StructLevelFunc accepts all values needed for struct level validation
type StructLevelFunc func(sl StructLevel)
// StructLevelFuncCtx accepts all values needed for struct level validation
// but also allows passing of contextual validation information via context.Context.
type StructLevelFuncCtx func(ctx context.Context, sl StructLevel)
// wrapStructLevelFunc wraps normal StructLevelFunc makes it compatible with StructLevelFuncCtx
func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx {
return func(ctx context.Context, sl StructLevel) {
fn(sl)
}
}
// StructLevel contains all the information and helper functions
// to validate a struct
type StructLevel interface {
// returns the main validation object, in case one wants to call validations internally.
// this is so you don't have to use anonymous functions to get access to the validate
// instance.
Validator() *Validate
// returns the top level struct, if any
Top() reflect.Value
// returns the current fields parent struct, if any
Parent() reflect.Value
// returns the current struct.
Current() reflect.Value
// ExtractType gets the actual underlying type of field value.
// It will dive into pointers, customTypes and return you the
// underlying value and its kind.
ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)
// reports an error just by passing the field and tag information
//
// NOTES:
//
// fieldName and altName get appended to the existing namespace that
// validator is on. e.g. pass 'FirstName' or 'Names[0]' depending
// on the nesting
//
// tag can be an existing validation tag or just something you make up
// and process on the flip side it's up to you.
ReportError(field interface{}, fieldName, structFieldName string, tag, param string)
// reports an error just by passing ValidationErrors
//
// NOTES:
//
// relativeNamespace and relativeActualNamespace get appended to the
// existing namespace that validator is on.
// e.g. pass 'User.FirstName' or 'Users[0].FirstName' depending
// on the nesting. most of the time they will be blank, unless you validate
// at a level lower the the current field depth
ReportValidationErrors(relativeNamespace, relativeActualNamespace string, errs ValidationErrors)
}
var _ StructLevel = new(validate)
// Top returns the top level struct
//
// NOTE: this can be the same as the current struct being validated
// if not is a nested struct.
//
// this is only called when within Struct and Field Level validation and
// should not be relied upon for an acurate value otherwise.
func (v *validate) Top() reflect.Value {
return v.top
}
// Parent returns the current structs parent
//
// NOTE: this can be the same as the current struct being validated
// if not is a nested struct.
//
// this is only called when within Struct and Field Level validation and
// should not be relied upon for an acurate value otherwise.
func (v *validate) Parent() reflect.Value {
return v.slflParent
}
// Current returns the current struct.
func (v *validate) Current() reflect.Value {
return v.slCurrent
}
// Validator returns the main validation object, in case one want to call validations internally.
func (v *validate) Validator() *Validate {
return v.v
}
// ExtractType gets the actual underlying type of field value.
func (v *validate) ExtractType(field reflect.Value) (reflect.Value, reflect.Kind, bool) {
return v.extractTypeInternal(field, false)
}
// ReportError reports an error just by passing the field and tag information
func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag, param string) {
fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false)
if len(structFieldName) == 0 {
structFieldName = fieldName
}
v.str1 = string(append(v.ns, fieldName...))
if v.v.hasTagNameFunc || fieldName != structFieldName {
v.str2 = string(append(v.actualNs, structFieldName...))
} else {
v.str2 = v.str1
}
if kind == reflect.Invalid {
v.errs = append(v.errs,
&fieldError{
v: v.v,
tag: tag,
actualTag: tag,
ns: v.str1,
structNs: v.str2,
fieldLen: uint8(len(fieldName)),
structfieldLen: uint8(len(structFieldName)),
param: param,
kind: kind,
},
)
return
}
v.errs = append(v.errs,
&fieldError{
v: v.v,
tag: tag,
actualTag: tag,
ns: v.str1,
structNs: v.str2,
fieldLen: uint8(len(fieldName)),
structfieldLen: uint8(len(structFieldName)),
value: fv.Interface(),
param: param,
kind: kind,
typ: fv.Type(),
},
)
}
// ReportValidationErrors reports ValidationErrors obtained from running validations within the Struct Level validation.
//
// NOTE: this function prepends the current namespace to the relative ones.
func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNamespace string, errs ValidationErrors) {
var err *fieldError
for i := 0; i < len(errs); i++ {
err = errs[i].(*fieldError)
err.ns = string(append(append(v.ns, relativeNamespace...), err.ns...))
err.structNs = string(append(append(v.actualNs, relativeStructNamespace...), err.structNs...))
v.errs = append(v.errs, err)
}
}

View File

@ -0,0 +1 @@
package testdata

View File

@ -0,0 +1,11 @@
package validator
import ut "gin-valid/go-playground/universal-translator"
// TranslationFunc is the function type used to register or override
// custom translations
type TranslationFunc func(ut ut.Translator, fe FieldError) string
// RegisterTranslationsFunc allows for registering of translations
// for a 'ut.Translator' for use within the 'TranslationFunc'
type RegisterTranslationsFunc func(ut ut.Translator) error

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,676 @@
package en
import (
"testing"
"time"
english "gin-valid/go-playground/locales/en"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
)
func TestTranslations(t *testing.T) {
eng := english.New()
uni := ut.New(eng, eng)
trans, _ := uni.GetTranslator("en")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
JSONString string `validate:"json"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor must be a valid color",
},
{
ns: "Test.MAC",
expected: "MAC must contain a valid MAC address",
},
{
ns: "Test.IPAddr",
expected: "IPAddr must be a resolvable IP address",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 must be a resolvable IPv4 address",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 must be a resolvable IPv6 address",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr must be a valid UDP address",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 must be a valid IPv4 UDP address",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 must be a valid IPv6 UDP address",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr must be a valid TCP address",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 must be a valid IPv4 TCP address",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 must be a valid IPv6 TCP address",
},
{
ns: "Test.CIDR",
expected: "CIDR must contain a valid CIDR notation",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 must contain a valid CIDR notation for an IPv4 address",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 must contain a valid CIDR notation for an IPv6 address",
},
{
ns: "Test.SSN",
expected: "SSN must be a valid SSN number",
},
{
ns: "Test.IP",
expected: "IP must be a valid IP address",
},
{
ns: "Test.IPv4",
expected: "IPv4 must be a valid IPv4 address",
},
{
ns: "Test.IPv6",
expected: "IPv6 must be a valid IPv6 address",
},
{
ns: "Test.DataURI",
expected: "DataURI must contain a valid Data URI",
},
{
ns: "Test.Latitude",
expected: "Latitude must contain valid latitude coordinates",
},
{
ns: "Test.Longitude",
expected: "Longitude must contain a valid longitude coordinates",
},
{
ns: "Test.MultiByte",
expected: "MultiByte must contain multibyte characters",
},
{
ns: "Test.ASCII",
expected: "ASCII must contain only ascii characters",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII must contain only printable ascii characters",
},
{
ns: "Test.UUID",
expected: "UUID must be a valid UUID",
},
{
ns: "Test.UUID3",
expected: "UUID3 must be a valid version 3 UUID",
},
{
ns: "Test.UUID4",
expected: "UUID4 must be a valid version 4 UUID",
},
{
ns: "Test.UUID5",
expected: "UUID5 must be a valid version 5 UUID",
},
{
ns: "Test.ISBN",
expected: "ISBN must be a valid ISBN number",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 must be a valid ISBN-10 number",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 must be a valid ISBN-13 number",
},
{
ns: "Test.Excludes",
expected: "Excludes cannot contain the text 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll cannot contain any of the following characters '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune cannot contain the following '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny must contain at least one of the following characters '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains must contain the text 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 must be a valid Base64 string",
},
{
ns: "Test.Email",
expected: "Email must be a valid email address",
},
{
ns: "Test.URL",
expected: "URL must be a valid URL",
},
{
ns: "Test.URI",
expected: "URI must be a valid URI",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString must be a valid RGB color",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString must be a valid RGBA color",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString must be a valid HSL color",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString must be a valid HSLA color",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString must be a valid hexadecimal",
},
{
ns: "Test.HexColorString",
expected: "HexColorString must be a valid HEX color",
},
{
ns: "Test.NumberString",
expected: "NumberString must be a valid number",
},
{
ns: "Test.NumericString",
expected: "NumericString must be a valid numeric value",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString can only contain alphanumeric characters",
},
{
ns: "Test.AlphaString",
expected: "AlphaString can only contain alphabetic characters",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString must be less than MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString must be less than or equal to MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString must be greater than MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString must be greater than or equal to MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString cannot be equal to EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString must be less than Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString must be less than or equal to Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString must be greater than Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString must be greater than or equal to Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString cannot be equal to Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString must be equal to Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString must be equal to MaxString",
},
{
ns: "Test.GteString",
expected: "GteString must be at least 3 characters in length",
},
{
ns: "Test.GteNumber",
expected: "GteNumber must be 5.56 or greater",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple must contain at least 2 items",
},
{
ns: "Test.GteTime",
expected: "GteTime must be greater than or equal to the current Date & Time",
},
{
ns: "Test.GtString",
expected: "GtString must be greater than 3 characters in length",
},
{
ns: "Test.GtNumber",
expected: "GtNumber must be greater than 5.56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple must contain more than 2 items",
},
{
ns: "Test.GtTime",
expected: "GtTime must be greater than the current Date & Time",
},
{
ns: "Test.LteString",
expected: "LteString must be at maximum 3 characters in length",
},
{
ns: "Test.LteNumber",
expected: "LteNumber must be 5.56 or less",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple must contain at maximum 2 items",
},
{
ns: "Test.LteTime",
expected: "LteTime must be less than or equal to the current Date & Time",
},
{
ns: "Test.LtString",
expected: "LtString must be less than 3 characters in length",
},
{
ns: "Test.LtNumber",
expected: "LtNumber must be less than 5.56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple must contain less than 2 items",
},
{
ns: "Test.LtTime",
expected: "LtTime must be less than the current Date & Time",
},
{
ns: "Test.NeString",
expected: "NeString should not be equal to ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber should not be equal to 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple should not be equal to 0",
},
{
ns: "Test.EqString",
expected: "EqString is not equal to 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber is not equal to 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple is not equal to 7",
},
{
ns: "Test.MaxString",
expected: "MaxString must be a maximum of 3 characters in length",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber must be 1,113.00 or less",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple must contain at maximum 7 items",
},
{
ns: "Test.MinString",
expected: "MinString must be at least 1 character in length",
},
{
ns: "Test.MinNumber",
expected: "MinNumber must be 1,113.00 or greater",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple must contain at least 7 items",
},
{
ns: "Test.LenString",
expected: "LenString must be 1 character in length",
},
{
ns: "Test.LenNumber",
expected: "LenNumber must be equal to 1,113.00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple must contain 7 items",
},
{
ns: "Test.RequiredString",
expected: "RequiredString is a required field",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber is a required field",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple is a required field",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen must be at least 10 characters in length",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen must be a maximum of 1 character in length",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen must be 2 characters in length",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt must be less than 1 character in length",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte must be at maximum 1 character in length",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt must be greater than 10 characters in length",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte must be at least 10 characters in length",
},
{
ns: "Test.OneOfString",
expected: "OneOfString must be one of [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt must be one of [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice must contain unique values",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray must contain unique values",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap must contain unique values",
},
{
ns: "Test.JSONString",
expected: "JSONString must be a valid json string",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString must be a lowercase string",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString must be an uppercase string",
},
{
ns: "Test.Datetime",
expected: "Datetime does not match the 2006-01-02 format",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,652 @@
package es
import (
"testing"
"time"
spanish "gin-valid/go-playground/locales/es"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
)
func TestTranslations(t *testing.T) {
spa := spanish.New()
uni := ut.New(spa, spa)
trans, _ := uni.GetTranslator("es")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor debe ser un color válido",
},
{
ns: "Test.MAC",
expected: "MAC debe contener una dirección MAC válida",
},
{
ns: "Test.IPAddr",
expected: "IPAddr debe ser una dirección IP resoluble",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 debe ser una dirección IPv4 resoluble",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 debe ser una dirección IPv6 resoluble",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr debe ser una dirección UDP válida",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 debe ser una dirección IPv4 UDP válida",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 debe ser una dirección IPv6 UDP válida",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr debe ser una dirección TCP válida",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 debe ser una dirección IPv4 TCP válida",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 debe ser una dirección IPv6 TCP válida",
},
{
ns: "Test.CIDR",
expected: "CIDR debe contener una anotación válida del CIDR",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 debe contener una notación CIDR válida para una dirección IPv4",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 debe contener una notación CIDR válida para una dirección IPv6",
},
{
ns: "Test.SSN",
expected: "SSN debe ser un número válido de SSN",
},
{
ns: "Test.IP",
expected: "IP debe ser una dirección IP válida",
},
{
ns: "Test.IPv4",
expected: "IPv4 debe ser una dirección IPv4 válida",
},
{
ns: "Test.IPv6",
expected: "IPv6 debe ser una dirección IPv6 válida",
},
{
ns: "Test.DataURI",
expected: "DataURI debe contener un URI de datos válido",
},
{
ns: "Test.Latitude",
expected: "Latitude debe contener coordenadas de latitud válidas",
},
{
ns: "Test.Longitude",
expected: "Longitude debe contener unas coordenadas de longitud válidas",
},
{
ns: "Test.MultiByte",
expected: "MultiByte debe contener caracteres multibyte",
},
{
ns: "Test.ASCII",
expected: "ASCII debe contener sólo caracteres ascii",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII debe contener sólo caracteres ASCII imprimibles",
},
{
ns: "Test.UUID",
expected: "UUID debe ser un UUID válido",
},
{
ns: "Test.UUID3",
expected: "UUID3 debe ser una versión válida 3 UUID",
},
{
ns: "Test.UUID4",
expected: "UUID4 debe ser una versión válida 4 UUID",
},
{
ns: "Test.UUID5",
expected: "UUID5 debe ser una versión válida 5 UUID",
},
{
ns: "Test.ISBN",
expected: "ISBN debe ser un número ISBN válido",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 debe ser un número ISBN-10 válido",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 debe ser un número ISBN-13 válido",
},
{
ns: "Test.Excludes",
expected: "Excludes no puede contener el texto 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll no puede contener ninguno de los siguientes caracteres '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune no puede contener lo siguiente '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny debe contener al menos uno de los siguientes caracteres '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains debe contener el texto 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 debe ser una cadena de Base64 válida",
},
{
ns: "Test.Email",
expected: "Email debe ser una dirección de correo electrónico válida",
},
{
ns: "Test.URL",
expected: "URL debe ser un URL válido",
},
{
ns: "Test.URI",
expected: "URI debe ser una URI válida",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString debe ser un color RGB válido",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString debe ser un color RGBA válido",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString debe ser un color HSL válido",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString debe ser un color HSL válido",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString debe ser un hexadecimal válido",
},
{
ns: "Test.HexColorString",
expected: "HexColorString debe ser un color HEX válido",
},
{
ns: "Test.NumberString",
expected: "NumberString debe ser un número válido",
},
{
ns: "Test.NumericString",
expected: "NumericString debe ser un valor numérico válido",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString sólo puede contener caracteres alfanuméricos",
},
{
ns: "Test.AlphaString",
expected: "AlphaString sólo puede contener caracteres alfabéticos",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString debe ser menor que MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString debe ser menor o igual a MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString debe ser mayor que MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString debe ser mayor o igual a MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString no puede ser igual a EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString debe ser menor que Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString debe ser menor o igual a Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString debe ser mayor que Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString debe ser mayor o igual a Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString no puede ser igual a Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString debe ser igual a Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString debe ser igual a MaxString",
},
{
ns: "Test.GteString",
expected: "GteString debe tener al menos 3 caracteres de longitud",
},
{
ns: "Test.GteNumber",
expected: "GteNumber debe ser 5,56 o mayor",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple debe contener al menos 2 elementos",
},
{
ns: "Test.GteTime",
expected: "GteTime debe ser después o durante la fecha y hora actuales",
},
{
ns: "Test.GtString",
expected: "GtString debe ser mayor que 3 caracteres en longitud",
},
{
ns: "Test.GtNumber",
expected: "GtNumber debe ser mayor que 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple debe contener más de 2 elementos",
},
{
ns: "Test.GtTime",
expected: "GtTime debe ser después de la fecha y hora actual",
},
{
ns: "Test.LteString",
expected: "LteString debe tener un máximo de 3 caracteres de longitud",
},
{
ns: "Test.LteNumber",
expected: "LteNumber debe ser 5,56 o menos",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple debe contener como máximo 2 elementos",
},
{
ns: "Test.LteTime",
expected: "LteTime debe ser antes o durante la fecha y hora actual",
},
{
ns: "Test.LtString",
expected: "LtString debe tener menos de 3 caracteres de longitud",
},
{
ns: "Test.LtNumber",
expected: "LtNumber debe ser menos de 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple debe contener menos de 2 elementos",
},
{
ns: "Test.LtTime",
expected: "LtTime debe ser antes de la fecha y hora actual",
},
{
ns: "Test.NeString",
expected: "NeString no debería ser igual a ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber no debería ser igual a 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple no debería ser igual a 0",
},
{
ns: "Test.EqString",
expected: "EqString no es igual a 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber no es igual a 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple no es igual a 7",
},
{
ns: "Test.MaxString",
expected: "MaxString debe tener un máximo de 3 caracteres de longitud",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber debe ser 1.113,00 o menos",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple debe contener como máximo 7 elementos",
},
{
ns: "Test.MinString",
expected: "MinString debe tener al menos 1 carácter de longitud",
},
{
ns: "Test.MinNumber",
expected: "MinNumber debe ser 1.113,00 o más",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple debe contener al menos 7 elementos",
},
{
ns: "Test.LenString",
expected: "LenString debe tener 1 carácter de longitud",
},
{
ns: "Test.LenNumber",
expected: "LenNumber debe ser igual a 1.113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple debe contener 7 elementos",
},
{
ns: "Test.RequiredString",
expected: "RequiredString es un campo requerido",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber es un campo requerido",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple es un campo requerido",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen debe tener al menos 10 caracteres de longitud",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen debe tener un máximo de 1 carácter de longitud",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen debe tener 2 caracteres de longitud",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt debe tener menos de 1 carácter de longitud",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte debe tener un máximo de 1 carácter de longitud",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt debe ser mayor que 10 caracteres en longitud",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte debe tener al menos 10 caracteres de longitud",
},
{
ns: "Test.OneOfString",
expected: "OneOfString debe ser uno de [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt debe ser uno de [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice debe contener valores únicos",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray debe contener valores únicos",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap debe contener valores únicos",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,634 @@
package fr
import (
"testing"
"time"
french "gin-valid/go-playground/locales/fr"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
)
func TestTranslations(t *testing.T) {
fre := french.New()
uni := ut.New(fre, fre)
trans, _ := uni.GetTranslator("fr")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor doit être une couleur valide",
},
{
ns: "Test.MAC",
expected: "MAC doit contenir une adresse MAC valide",
},
{
ns: "Test.IPAddr",
expected: "IPAddr doit être une adresse IP résolvable",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 doit être une adresse IPv4 résolvable",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 doit être une adresse IPv6 résolvable",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr doit être une adressse UDP valide",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 doit être une adressse IPv4 UDP valide",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 doit être une adressse IPv6 UDP valide",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr doit être une adressse TCP valide",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 doit être une adressse IPv4 TCP valide",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 doit être une adressse IPv6 TCP valide",
},
{
ns: "Test.CIDR",
expected: "CIDR doit contenir une notation CIDR valide",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 doit contenir une notation CIDR valide pour une adresse IPv4",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 doit contenir une notation CIDR valide pour une adresse IPv6",
},
{
ns: "Test.SSN",
expected: "SSN doit être un numéro SSN valide",
},
{
ns: "Test.IP",
expected: "IP doit être une adressse IP valide",
},
{
ns: "Test.IPv4",
expected: "IPv4 doit être une adressse IPv4 valide",
},
{
ns: "Test.IPv6",
expected: "IPv6 doit être une adressse IPv6 valide",
},
{
ns: "Test.DataURI",
expected: "DataURI doit contenir une URI data valide",
},
{
ns: "Test.Latitude",
expected: "Latitude doit contenir des coordonnées latitude valides",
},
{
ns: "Test.Longitude",
expected: "Longitude doit contenir des coordonnées longitudes valides",
},
{
ns: "Test.MultiByte",
expected: "MultiByte doit contenir des caractères multioctets",
},
{
ns: "Test.ASCII",
expected: "ASCII ne doit contenir que des caractères ascii",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII ne doit contenir que des caractères ascii affichables",
},
{
ns: "Test.UUID",
expected: "UUID doit être un UUID valid",
},
{
ns: "Test.UUID3",
expected: "UUID3 doit être un UUID version 3 valid",
},
{
ns: "Test.UUID4",
expected: "UUID4 doit être un UUID version 4 valid",
},
{
ns: "Test.UUID5",
expected: "UUID5 doit être un UUID version 5 valid",
},
{
ns: "Test.ISBN",
expected: "ISBN doit être un numéro ISBN valid",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 doit être un numéro ISBN-10 valid",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 doit être un numéro ISBN-13 valid",
},
{
ns: "Test.Excludes",
expected: "Excludes ne doit pas contenir le texte 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll ne doit pas contenir l'un des caractères suivants '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune ne doit pas contenir ce qui suit '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny doit contenir au moins l' un des caractères suivants '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains doit contenir le texte 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 doit être une chaîne de caractères au format Base64 valide",
},
{
ns: "Test.Email",
expected: "Email doit être une adresse email valide",
},
{
ns: "Test.URL",
expected: "URL doit être une URL valide",
},
{
ns: "Test.URI",
expected: "URI doit être une URI valide",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString doit être une couleur au format RGB valide",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString doit être une couleur au format RGBA valide",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString doit être une couleur au format HSL valide",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString doit être une couleur au format HSLA valide",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString doit être une chaîne de caractères au format hexadécimal valide",
},
{
ns: "Test.HexColorString",
expected: "HexColorString doit être une couleur au format HEX valide",
},
{
ns: "Test.NumberString",
expected: "NumberString doit être un nombre valid",
},
{
ns: "Test.NumericString",
expected: "NumericString doit être une valeur numérique valide",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString ne doit contenir que des caractères alphanumériques",
},
{
ns: "Test.AlphaString",
expected: "AlphaString ne doit contenir que des caractères alphabétiques",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString doit être inférieur à MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString doit être inférieur ou égal à MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString doit être supérieur à MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString doit être supérieur ou égal à MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString ne doit pas être égal à EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString doit être inférieur à Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString doit être inférieur ou égal à Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString doit être supérieur à Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString doit être supérieur ou égal à Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString ne doit pas être égal à Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString doit être égal à Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString doit être égal à MaxString",
},
{
ns: "Test.GteString",
expected: "GteString doit faire une taille d'au moins 3 caractères",
},
{
ns: "Test.GteNumber",
expected: "GteNumber doit être 5,56 ou plus",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple doit contenir au moins 2 elements",
},
{
ns: "Test.GteTime",
expected: "GteTime doit être après ou pendant la date et l'heure actuelle",
},
{
ns: "Test.GtString",
expected: "GtString doit avoir une taille supérieur à 3 caractères",
},
{
ns: "Test.GtNumber",
expected: "GtNumber doit être supérieur à 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple doit contenir plus de 2 elements",
},
{
ns: "Test.GtTime",
expected: "GtTime doit être après la date et l'heure actuelle",
},
{
ns: "Test.LteString",
expected: "LteString doit faire une taille maximum de 3 caractères",
},
{
ns: "Test.LteNumber",
expected: "LteNumber doit faire 5,56 ou moins",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple doit contenir un maximum de 2 elements",
},
{
ns: "Test.LteTime",
expected: "LteTime doit être avant ou pendant la date et l'heure actuelle",
},
{
ns: "Test.LtString",
expected: "LtString doit avoir une taille inférieure à 3 caractères",
},
{
ns: "Test.LtNumber",
expected: "LtNumber doit être inférieur à 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple doit contenir mois de 2 elements",
},
{
ns: "Test.LtTime",
expected: "LtTime doit être avant la date et l'heure actuelle",
},
{
ns: "Test.NeString",
expected: "NeString ne doit pas être égal à ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber ne doit pas être égal à 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple ne doit pas être égal à 0",
},
{
ns: "Test.EqString",
expected: "EqString n'est pas égal à 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber n'est pas égal à 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple n'est pas égal à 7",
},
{
ns: "Test.MaxString",
expected: "MaxString doit faire une taille maximum de 3 caractères",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber doit être égal à 1 113,00 ou moins",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple doit contenir au maximum 7 elements",
},
{
ns: "Test.MinString",
expected: "MinString doit faire une taille minimum de 1 caractère",
},
{
ns: "Test.MinNumber",
expected: "MinNumber doit être égal à 1 113,00 ou plus",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple doit contenir au moins 7 elements",
},
{
ns: "Test.LenString",
expected: "LenString doit faire une taille de 1 caractère",
},
{
ns: "Test.LenNumber",
expected: "LenNumber doit être égal à 1 113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple doit contenir 7 elements",
},
{
ns: "Test.RequiredString",
expected: "RequiredString est un champ obligatoire",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber est un champ obligatoire",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple est un champ obligatoire",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen doit faire une taille minimum de 10 caractères",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen doit faire une taille maximum de 1 caractère",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen doit faire une taille de 2 caractères",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt doit avoir une taille inférieure à 1 caractère",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte doit faire une taille maximum de 1 caractère",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt doit avoir une taille supérieur à 10 caractères",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte doit faire une taille d'au moins 10 caractères",
},
{
ns: "Test.OneOfString",
expected: "OneOfString doit être l'un des choix suivants [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt doit être l'un des choix suivants [5 63]",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,634 @@
package id
import (
"testing"
"time"
indonesia "gin-valid/go-playground/locales/id"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
)
func TestTranslations(t *testing.T) {
idn := indonesia.New()
uni := ut.New(idn, idn)
trans, _ := uni.GetTranslator("id")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=tujuan"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=merah hijau"`
OneOfInt int `validate:"oneof=5 63"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor harus berupa warna yang valid",
},
{
ns: "Test.MAC",
expected: "MAC harus berisi alamat MAC yang valid",
},
{
ns: "Test.IPAddr",
expected: "IPAddr harus berupa alamat IP yang dapat dipecahkan",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 harus berupa alamat IPv4 yang dapat diatasi",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 harus berupa alamat IPv6 yang dapat diatasi",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr harus berupa alamat UDP yang valid",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 harus berupa alamat IPv4 UDP yang valid",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 harus berupa alamat IPv6 UDP yang valid",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr harus berupa alamat TCP yang valid",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 harus berupa alamat TCP IPv4 yang valid",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 harus berupa alamat TCP IPv6 yang valid",
},
{
ns: "Test.CIDR",
expected: "CIDR harus berisi notasi CIDR yang valid",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 harus berisi notasi CIDR yang valid untuk alamat IPv4",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 harus berisi notasi CIDR yang valid untuk alamat IPv6",
},
{
ns: "Test.SSN",
expected: "SSN harus berupa nomor SSN yang valid",
},
{
ns: "Test.IP",
expected: "IP harus berupa alamat IP yang valid",
},
{
ns: "Test.IPv4",
expected: "IPv4 harus berupa alamat IPv4 yang valid",
},
{
ns: "Test.IPv6",
expected: "IPv6 harus berupa alamat IPv6 yang valid",
},
{
ns: "Test.DataURI",
expected: "DataURI harus berisi URI Data yang valid",
},
{
ns: "Test.Latitude",
expected: "Latitude harus berisi koordinat lintang yang valid",
},
{
ns: "Test.Longitude",
expected: "Longitude harus berisi koordinat bujur yang valid",
},
{
ns: "Test.MultiByte",
expected: "MultiByte harus berisi karakter multibyte",
},
{
ns: "Test.ASCII",
expected: "ASCII hanya boleh berisi karakter ascii",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII hanya boleh berisi karakter ascii yang dapat dicetak",
},
{
ns: "Test.UUID",
expected: "UUID harus berupa UUID yang valid",
},
{
ns: "Test.UUID3",
expected: "UUID3 harus berupa UUID versi 3 yang valid",
},
{
ns: "Test.UUID4",
expected: "UUID4 harus berupa UUID versi 4 yang valid",
},
{
ns: "Test.UUID5",
expected: "UUID5 harus berupa UUID versi 5 yang valid",
},
{
ns: "Test.ISBN",
expected: "ISBN harus berupa nomor ISBN yang valid",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 harus berupa nomor ISBN-10 yang valid",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 harus berupa nomor ISBN-13 yang valid",
},
{
ns: "Test.Excludes",
expected: "Excludes tidak boleh berisi teks 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll tidak boleh berisi salah satu karakter berikut '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune tidak boleh berisi '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny harus berisi setidaknya salah satu karakter berikut '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains harus berisi teks 'tujuan'",
},
{
ns: "Test.Base64",
expected: "Base64 harus berupa string Base64 yang valid",
},
{
ns: "Test.Email",
expected: "Email harus berupa alamat email yang valid",
},
{
ns: "Test.URL",
expected: "URL harus berupa URL yang valid",
},
{
ns: "Test.URI",
expected: "URI harus berupa URI yang valid",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString harus berupa warna RGB yang valid",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString harus berupa warna RGBA yang valid",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString harus berupa warna HSL yang valid",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString harus berupa warna HSLA yang valid",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString harus berupa heksadesimal yang valid",
},
{
ns: "Test.HexColorString",
expected: "HexColorString harus berupa warna HEX yang valid",
},
{
ns: "Test.NumberString",
expected: "NumberString harus berupa angka yang valid",
},
{
ns: "Test.NumericString",
expected: "NumericString harus berupa nilai numerik yang valid",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString hanya dapat berisi karakter alfanumerik",
},
{
ns: "Test.AlphaString",
expected: "AlphaString hanya dapat berisi karakter abjad",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString harus kurang dari MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString harus kurang dari atau sama dengan MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString harus lebih besar dari MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString harus lebih besar dari atau sama dengan MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString tidak sama dengan EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString harus kurang dari Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString harus kurang dari atau sama dengan Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString harus lebih besar dari Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString harus lebih besar dari atau sama dengan Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString tidak sama dengan Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString harus sama dengan Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString harus sama dengan MaxString",
},
{
ns: "Test.GteString",
expected: "panjang minimal GteString adalah 3 karakter",
},
{
ns: "Test.GteNumber",
expected: "GteNumber harus 5,56 atau lebih besar",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple harus berisi setidaknya 2 item",
},
{
ns: "Test.GteTime",
expected: "GteTime harus lebih besar dari atau sama dengan tanggal & waktu saat ini",
},
{
ns: "Test.GtString",
expected: "panjang GtString harus lebih dari 3 karakter",
},
{
ns: "Test.GtNumber",
expected: "GtNumber harus lebih besar dari 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple harus berisi lebih dari 2 item",
},
{
ns: "Test.GtTime",
expected: "GtTime harus lebih besar dari tanggal & waktu saat ini",
},
{
ns: "Test.LteString",
expected: "panjang maksimal LteString adalah 3 karakter",
},
{
ns: "Test.LteNumber",
expected: "LteNumber harus 5,56 atau kurang",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple harus berisi maksimal 2 item",
},
{
ns: "Test.LteTime",
expected: "LteTime harus kurang dari atau sama dengan tanggal & waktu saat ini",
},
{
ns: "Test.LtString",
expected: "panjang LtString harus kurang dari 3 karakter",
},
{
ns: "Test.LtNumber",
expected: "LtNumber harus kurang dari 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple harus berisi kurang dari 2 item",
},
{
ns: "Test.LtTime",
expected: "LtTime harus kurang dari tanggal & waktu saat ini",
},
{
ns: "Test.NeString",
expected: "NeString tidak sama dengan ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber tidak sama dengan 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple tidak sama dengan 0",
},
{
ns: "Test.EqString",
expected: "EqString tidak sama dengan 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber tidak sama dengan 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple tidak sama dengan 7",
},
{
ns: "Test.MaxString",
expected: "panjang maksimal MaxString adalah 3 karakter",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber harus 1.113,00 atau kurang",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple harus berisi maksimal 7 item",
},
{
ns: "Test.MinString",
expected: "panjang minimal MinString adalah 1 karakter",
},
{
ns: "Test.MinNumber",
expected: "MinNumber harus 1.113,00 atau lebih besar",
},
{
ns: "Test.MinMultiple",
expected: "panjang minimal MinMultiple adalah 7 item",
},
{
ns: "Test.LenString",
expected: "panjang LenString harus 1 karakter",
},
{
ns: "Test.LenNumber",
expected: "LenNumber harus sama dengan 1.113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple harus berisi 7 item",
},
{
ns: "Test.RequiredString",
expected: "RequiredString wajib diisi",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber wajib diisi",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple wajib diisi",
},
{
ns: "Test.StrPtrMinLen",
expected: "panjang minimal StrPtrMinLen adalah 10 karakter",
},
{
ns: "Test.StrPtrMaxLen",
expected: "panjang maksimal StrPtrMaxLen adalah 1 karakter",
},
{
ns: "Test.StrPtrLen",
expected: "panjang StrPtrLen harus 2 karakter",
},
{
ns: "Test.StrPtrLt",
expected: "panjang StrPtrLt harus kurang dari 1 karakter",
},
{
ns: "Test.StrPtrLte",
expected: "panjang maksimal StrPtrLte adalah 1 karakter",
},
{
ns: "Test.StrPtrGt",
expected: "panjang StrPtrGt harus lebih dari 10 karakter",
},
{
ns: "Test.StrPtrGte",
expected: "panjang minimal StrPtrGte adalah 10 karakter",
},
{
ns: "Test.OneOfString",
expected: "OneOfString harus berupa salah satu dari [merah hijau]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt harus berupa salah satu dari [5 63]",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,634 @@
package ja
import (
"testing"
"time"
ja_locale "gin-valid/go-playground/locales/ja"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
)
func TestTranslations(t *testing.T) {
japanese := ja_locale.New()
uni := ut.New(japanese, japanese)
trans, _ := uni.GetTranslator("ja")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColorは正しい色でなければなりません",
},
{
ns: "Test.MAC",
expected: "MACは正しいMACアドレスを含まなければなりません",
},
{
ns: "Test.IPAddr",
expected: "IPAddrは解決可能なIPアドレスでなければなりません",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4は解決可能なIPv4アドレスでなければなりません",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6は解決可能なIPv6アドレスでなければなりません",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddrは正しいUDPアドレスでなければなりません",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4は正しいIPv4のUDPアドレスでなければなりません",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6は正しいIPv6のUDPアドレスでなければなりません",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddrは正しいTCPアドレスでなければなりません",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4は正しいIPv4のTCPアドレスでなければなりません",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6は正しいIPv6のTCPアドレスでなければなりません",
},
{
ns: "Test.CIDR",
expected: "CIDRは正しいCIDR表記を含まなければなりません",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4はIPv4アドレスの正しいCIDR表記を含まなければなりません",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6はIPv6アドレスの正しいCIDR表記を含まなければなりません",
},
{
ns: "Test.SSN",
expected: "SSNは正しい社会保障番号でなければなりません",
},
{
ns: "Test.IP",
expected: "IPは正しいIPアドレスでなければなりません",
},
{
ns: "Test.IPv4",
expected: "IPv4は正しいIPv4アドレスでなければなりません",
},
{
ns: "Test.IPv6",
expected: "IPv6は正しいIPv6アドレスでなければなりません",
},
{
ns: "Test.DataURI",
expected: "DataURIは正しいデータURIを含まなければなりません",
},
{
ns: "Test.Latitude",
expected: "Latitudeは正しい緯度の座標を含まなければなりません",
},
{
ns: "Test.Longitude",
expected: "Longitudeは正しい経度の座標を含まなければなりません",
},
{
ns: "Test.MultiByte",
expected: "MultiByteはマルチバイト文字を含まなければなりません",
},
{
ns: "Test.ASCII",
expected: "ASCIIはASCII文字のみを含まなければなりません",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCIIは印刷可能なASCII文字のみを含まなければなりません",
},
{
ns: "Test.UUID",
expected: "UUIDは正しいUUIDでなければなりません",
},
{
ns: "Test.UUID3",
expected: "UUID3はバージョンが3の正しいUUIDでなければなりません",
},
{
ns: "Test.UUID4",
expected: "UUID4はバージョンが4の正しいUUIDでなければなりません",
},
{
ns: "Test.UUID5",
expected: "UUID5はバージョンが4の正しいUUIDでなければなりません",
},
{
ns: "Test.ISBN",
expected: "ISBNは正しいISBN番号でなければなりません",
},
{
ns: "Test.ISBN10",
expected: "ISBN10は正しいISBN-10番号でなければなりません",
},
{
ns: "Test.ISBN13",
expected: "ISBN13は正しいISBN-13番号でなければなりません",
},
{
ns: "Test.Excludes",
expected: "Excludesには'text'というテキストを含むことはできません",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAllには'!@#$'のどれも含めることはできません",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRuneには'☻'を含めることはできません",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAnyは'!@#$'の少なくとも1つを含まなければなりません",
},
{
ns: "Test.Contains",
expected: "Containsは'purpose'を含まなければなりません",
},
{
ns: "Test.Base64",
expected: "Base64は正しいBase64文字列でなければなりません",
},
{
ns: "Test.Email",
expected: "Emailは正しいメールアドレスでなければなりません",
},
{
ns: "Test.URL",
expected: "URLは正しいURLでなければなりません",
},
{
ns: "Test.URI",
expected: "URIは正しいURIでなければなりません",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorStringは正しいRGBカラーコードでなければなりません",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorStringは正しいRGBAカラーコードでなければなりません",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorStringは正しいHSLカラーコードでなければなりません",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorStringは正しいHSLAカラーコードでなければなりません",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalStringは正しい16進表記でなければなりません",
},
{
ns: "Test.HexColorString",
expected: "HexColorStringは正しいHEXカラーコードでなければなりません",
},
{
ns: "Test.NumberString",
expected: "NumberStringは正しい数でなければなりません",
},
{
ns: "Test.NumericString",
expected: "NumericStringは正しい数字でなければなりません",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumStringはアルファベットと数字のみを含むことができます",
},
{
ns: "Test.AlphaString",
expected: "AlphaStringはアルファベットのみを含むことができます",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldStringはMaxStringよりも小さくなければなりません",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldStringはMaxString以下でなければなりません",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldStringはMaxStringよりも大きくなければなりません",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldStringはMaxString以上でなければなりません",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldStringはEqFieldStringとは異ならなければなりません",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldStringはInner.LtCSFieldStringよりも小さくなければなりません",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldStringはInner.LteCSFieldString以下でなければなりません",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldStringはInner.GtCSFieldStringよりも大きくなければなりません",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldStringはInner.GteCSFieldString以上でなければなりません",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldStringはInner.NeCSFieldStringとは異ならなければなりません",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldStringはInner.EqCSFieldStringと等しくなければなりません",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldStringはMaxStringと等しくなければなりません",
},
{
ns: "Test.GteString",
expected: "GteStringの長さは少なくとも3文字以上はなければなりません",
},
{
ns: "Test.GteNumber",
expected: "GteNumberは5.56かより大きくなければなりません",
},
{
ns: "Test.GteMultiple",
expected: "GteMultipleは少なくとも2つの項目を含まなければなりません",
},
{
ns: "Test.GteTime",
expected: "GteTimeは現時刻以降でなければなりません",
},
{
ns: "Test.GtString",
expected: "GtStringの長さは3文字よりも多くなければなりません",
},
{
ns: "Test.GtNumber",
expected: "GtNumberは5.56よりも大きくなければなりません",
},
{
ns: "Test.GtMultiple",
expected: "GtMultipleは2つの項目よりも多い項目を含まなければなりません",
},
{
ns: "Test.GtTime",
expected: "GtTimeは現時刻よりも後でなければなりません",
},
{
ns: "Test.LteString",
expected: "LteStringの長さは最大でも3文字でなければなりません",
},
{
ns: "Test.LteNumber",
expected: "LteNumberは5.56かより小さくなければなりません",
},
{
ns: "Test.LteMultiple",
expected: "LteMultipleは最大でも2つの項目を含まなければなりません",
},
{
ns: "Test.LteTime",
expected: "LteTimeは現時刻以前でなければなりません",
},
{
ns: "Test.LtString",
expected: "LtStringの長さは3文字よりも少なくなければなりません",
},
{
ns: "Test.LtNumber",
expected: "LtNumberは5.56よりも小さくなければなりません",
},
{
ns: "Test.LtMultiple",
expected: "LtMultipleは2つの項目よりも少ない項目を含まなければなりません",
},
{
ns: "Test.LtTime",
expected: "LtTimeは現時刻よりも前でなければなりません",
},
{
ns: "Test.NeString",
expected: "NeStringはと異ならなければなりません",
},
{
ns: "Test.NeNumber",
expected: "NeNumberは0.00と異ならなければなりません",
},
{
ns: "Test.NeMultiple",
expected: "NeMultipleの項目の数は0個と異ならなければなりません",
},
{
ns: "Test.EqString",
expected: "EqStringは3と等しくありません",
},
{
ns: "Test.EqNumber",
expected: "EqNumberは2.33と等しくありません",
},
{
ns: "Test.EqMultiple",
expected: "EqMultipleは7と等しくありません",
},
{
ns: "Test.MaxString",
expected: "MaxStringの長さは最大でも3文字でなければなりません",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumberは1,113.00かより小さくなければなりません",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultipleは最大でも7つの項目を含まなければなりません",
},
{
ns: "Test.MinString",
expected: "MinStringの長さは少なくとも1文字はなければなりません",
},
{
ns: "Test.MinNumber",
expected: "MinNumberは1,113.00かより大きくなければなりません",
},
{
ns: "Test.MinMultiple",
expected: "MinMultipleは少なくとも7つの項目を含まなければなりません",
},
{
ns: "Test.LenString",
expected: "LenStringの長さは1文字でなければなりません",
},
{
ns: "Test.LenNumber",
expected: "LenNumberは1,113.00と等しくなければなりません",
},
{
ns: "Test.LenMultiple",
expected: "LenMultipleは7つの項目を含まなければなりません",
},
{
ns: "Test.RequiredString",
expected: "RequiredStringは必須フィールドです",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumberは必須フィールドです",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultipleは必須フィールドです",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLenの長さは少なくとも10文字はなければなりません",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLenの長さは最大でも1文字でなければなりません",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLenの長さは2文字でなければなりません",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLtの長さは1文字よりも少なくなければなりません",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLteの長さは最大でも1文字でなければなりません",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGtの長さは10文字よりも多くなければなりません",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGteの長さは少なくとも10文字以上はなければなりません",
},
{
ns: "Test.OneOfString",
expected: "OneOfStringは[red green]のうちのいずれかでなければなりません",
},
{
ns: "Test.OneOfInt",
expected: "OneOfIntは[5 63]のうちのいずれかでなければなりません",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,634 @@
package nl
import (
"testing"
"time"
english "gin-valid/go-playground/locales/en"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
)
func TestTranslations(t *testing.T) {
eng := english.New()
uni := ut.New(eng, eng)
trans, _ := uni.GetTranslator("en")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor moet een geldige kleur zijn",
},
{
ns: "Test.MAC",
expected: "MAC moet een geldig MAC adres bevatten",
},
{
ns: "Test.IPAddr",
expected: "IPAddr moet een oplosbaar IP adres zijn",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 moet een oplosbaar IPv4 adres zijn",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 moet een oplosbaar IPv6 adres zijn",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr moet een geldig UDP adres zijn",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 moet een geldig IPv4 UDP adres zijn",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 moet een geldig IPv6 UDP adres zijn",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr moet een geldig TCP adres zijn",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 moet een geldig IPv4 TCP adres zijn",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 moet een geldig IPv6 TCP adres zijn",
},
{
ns: "Test.CIDR",
expected: "CIDR moet een geldige CIDR notatie bevatten",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 moet een geldige CIDR notatie voor een IPv4 adres bevatten",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 moet een geldige CIDR notatie voor een IPv6 adres bevatten",
},
{
ns: "Test.SSN",
expected: "SSN moet een geldig SSN nummer zijn",
},
{
ns: "Test.IP",
expected: "IP moet een geldig IP adres zijn",
},
{
ns: "Test.IPv4",
expected: "IPv4 moet een geldig IPv4 adres zijn",
},
{
ns: "Test.IPv6",
expected: "IPv6 moet een geldig IPv6 adres zijn",
},
{
ns: "Test.DataURI",
expected: "DataURI moet een geldige Data URI bevatten",
},
{
ns: "Test.Latitude",
expected: "Latitude moet geldige breedtegraadcoördinaten bevatten",
},
{
ns: "Test.Longitude",
expected: "Longitude moet geldige lengtegraadcoördinaten bevatten",
},
{
ns: "Test.MultiByte",
expected: "MultiByte moet multibyte karakters bevatten",
},
{
ns: "Test.ASCII",
expected: "ASCII mag alleen ascii karakters bevatten",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII mag alleen afdrukbare ascii karakters bevatten",
},
{
ns: "Test.UUID",
expected: "UUID moet een geldige UUID zijn",
},
{
ns: "Test.UUID3",
expected: "UUID3 moet een geldige versie 3 UUID zijn",
},
{
ns: "Test.UUID4",
expected: "UUID4 moet een geldige versie 4 UUID zijn",
},
{
ns: "Test.UUID5",
expected: "UUID5 moet een geldige versie 5 UUID zijn",
},
{
ns: "Test.ISBN",
expected: "ISBN moet een geldig ISBN nummer zijn",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 moet een geldig ISBN-10 nummer zijn",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 moet een geldig ISBN-13 nummer zijn",
},
{
ns: "Test.Excludes",
expected: "Excludes mag niet de tekst 'text' bevatten",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll mag niet een van de volgende karakters bevatten '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune mag niet het volgende bevatten '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny moet tenminste een van de volgende karakters bevatten '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains moet de tekst 'purpose' bevatten",
},
{
ns: "Test.Base64",
expected: "Base64 moet een geldige Base64 string zijn",
},
{
ns: "Test.Email",
expected: "Email moet een geldig email adres zijn",
},
{
ns: "Test.URL",
expected: "URL moet een geldige URL zijn",
},
{
ns: "Test.URI",
expected: "URI moet een geldige URI zijn",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString moet een geldige RGB kleur zijn",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString moet een geldige RGBA kleur zijn",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString moet een geldige HSL kleur zijn",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString moet een geldige HSLA kleur zijn",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString moet een geldig hexadecimaal getal zijn",
},
{
ns: "Test.HexColorString",
expected: "HexColorString moet een geldige HEX kleur zijn",
},
{
ns: "Test.NumberString",
expected: "NumberString moet een geldig getal zijn",
},
{
ns: "Test.NumericString",
expected: "NumericString moet een geldige numerieke waarde zijn",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString mag alleen alfanumerieke karakters bevatten",
},
{
ns: "Test.AlphaString",
expected: "AlphaString mag alleen alfabetische karakters bevatten",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString moet kleiner zijn dan MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString moet kleiner dan of gelijk aan MaxString zijn",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString moet groter zijn dan MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString moet groter dan of gelijk aan MaxString zijn",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString mag niet gelijk zijn aan EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString moet kleiner zijn dan Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString moet kleiner dan of gelijk aan Inner.LteCSFieldString zijn",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString moet groter zijn dan Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString moet groter dan of gelijk aan Inner.GteCSFieldString zijn",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString mag niet gelijk zijn aan Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString moet gelijk zijn aan Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString moet gelijk zijn aan MaxString",
},
{
ns: "Test.GteString",
expected: "GteString moet tenminste 3 karakters lang zijn",
},
{
ns: "Test.GteNumber",
expected: "GteNumber moet 5.56 of groter zijn",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple moet tenminste 2 items bevatten",
},
{
ns: "Test.GteTime",
expected: "GteTime moet groter dan of gelijk zijn aan de huidige datum & tijd",
},
{
ns: "Test.GtString",
expected: "GtString moet langer dan 3 karakters zijn",
},
{
ns: "Test.GtNumber",
expected: "GtNumber moet groter zijn dan 5.56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple moet meer dan 2 items bevatten",
},
{
ns: "Test.GtTime",
expected: "GtTime moet groter zijn dan de huidige datum & tijd",
},
{
ns: "Test.LteString",
expected: "LteString mag maximaal 3 karakters lang zijn",
},
{
ns: "Test.LteNumber",
expected: "LteNumber moet 5.56 of minder zijn",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple mag maximaal 2 items bevatten",
},
{
ns: "Test.LteTime",
expected: "LteTime moet kleiner dan of gelijk aan de huidige datum & tijd zijn",
},
{
ns: "Test.LtString",
expected: "LtString moet minder dan 3 karakters lang zijn",
},
{
ns: "Test.LtNumber",
expected: "LtNumber moet kleiner zijn dan 5.56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple moet minder dan 2 items bevatten",
},
{
ns: "Test.LtTime",
expected: "LtTime moet kleiner zijn dan de huidige datum & tijd",
},
{
ns: "Test.NeString",
expected: "NeString mag niet gelijk zijn aan ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber mag niet gelijk zijn aan 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple mag niet gelijk zijn aan 0",
},
{
ns: "Test.EqString",
expected: "EqString is niet gelijk aan 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber is niet gelijk aan 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple is niet gelijk aan 7",
},
{
ns: "Test.MaxString",
expected: "MaxString mag maximaal 3 karakters lang zijn",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber moet 1,113.00 of kleiner zijn",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple mag maximaal 7 items bevatten",
},
{
ns: "Test.MinString",
expected: "MinString moet tenminste 1 karakter lang zijn",
},
{
ns: "Test.MinNumber",
expected: "MinNumber moet 1,113.00 of groter zijn",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple moet tenminste 7 items bevatten",
},
{
ns: "Test.LenString",
expected: "LenString moet 1 karakter lang zijn",
},
{
ns: "Test.LenNumber",
expected: "LenNumber moet gelijk zijn aan 1,113.00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple moet 7 items bevatten",
},
{
ns: "Test.RequiredString",
expected: "RequiredString is een verplicht veld",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber is een verplicht veld",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple is een verplicht veld",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen moet tenminste 10 karakters lang zijn",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen mag maximaal 1 karakter lang zijn",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen moet 2 karakters lang zijn",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt moet minder dan 1 karakter lang zijn",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte mag maximaal 1 karakter lang zijn",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt moet langer dan 10 karakters zijn",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte moet tenminste 10 karakters lang zijn",
},
{
ns: "Test.OneOfString",
expected: "OneOfString moet een van de volgende zijn [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt moet een van de volgende zijn [5 63]",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,677 @@
package pt
import (
"testing"
"time"
"gin-valid/go-playground/locales/pt"
ut "gin-valid/go-playground/universal-translator"
. "github.com/go-playground/assert/v2"
"gin-valid/go-playground/validator/v10"
)
func TestTranslations(t *testing.T) {
pt := pt.New()
uni := ut.New(pt, pt)
trans, _ := uni.GetTranslator("pt")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
JSONString string `validate:"json"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor deve ser uma cor válida",
},
{
ns: "Test.MAC",
expected: "MAC deve conter um endereço MAC válido",
},
{
ns: "Test.IPAddr",
expected: "IPAddr deve ser um endereço IP resolvível",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 deve ser um endereço IPv4 resolvível",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 deve ser um endereço IPv6 resolvível",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr deve ser um endereço UDP válido",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 deve ser um endereço UDP IPv4 válido",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 deve ser um endereço UDP IPv6 válido",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr deve ser um endereço TCP válido",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 deve ser um endereço TCP IPv4 válido",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 deve ser um endereço TCP IPv6 válido",
},
{
ns: "Test.CIDR",
expected: "CIDR não respeita a notação CIDR",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 não respeita a notação CIDR para um endereço IPv4",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 não respeita a notação CIDR para um endereço IPv6",
},
{
ns: "Test.SSN",
expected: "SSN deve ser um número SSN válido",
},
{
ns: "Test.IP",
expected: "IP deve ser um endereço IP válido",
},
{
ns: "Test.IPv4",
expected: "IPv4 deve ser um endereço IPv4 válido",
},
{
ns: "Test.IPv6",
expected: "IPv6 deve ser um endereço IPv6 válido",
},
{
ns: "Test.DataURI",
expected: "DataURI deve conter um Data URI válido",
},
{
ns: "Test.Latitude",
expected: "Latitude deve conter uma coordenada de latitude válida",
},
{
ns: "Test.Longitude",
expected: "Longitude deve conter uma coordenada de longitude válida",
},
{
ns: "Test.MultiByte",
expected: "MultiByte deve conter caracteres multibyte",
},
{
ns: "Test.ASCII",
expected: "ASCII deve conter apenas caracteres ascii",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII deve conter apenas caracteres ascii imprimíveis",
},
{
ns: "Test.UUID",
expected: "UUID deve ser um UUID válido",
},
{
ns: "Test.UUID3",
expected: "UUID3 deve ser um UUID versão 3 válido",
},
{
ns: "Test.UUID4",
expected: "UUID4 deve ser um UUID versão 4 válido",
},
{
ns: "Test.UUID5",
expected: "UUID5 deve ser um UUID versão 5 válido",
},
{
ns: "Test.ISBN",
expected: "ISBN deve ser um número de ISBN válido",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 deve ser um número ISBN-10 válido",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 deve ser um número ISBN-13 válido",
},
{
ns: "Test.Excludes",
expected: "Excludes não deve conter o texto 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll não deve conter os seguintes caracteres '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune não pode conter o seguinte '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny deve conter pelo menos um dos seguintes caracteres '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains deve conter o texto 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 deve ser uma string Base64 válida",
},
{
ns: "Test.Email",
expected: "Email deve ser um endereço de e-mail válido",
},
{
ns: "Test.URL",
expected: "URL deve ser um URL válido",
},
{
ns: "Test.URI",
expected: "URI deve ser um URI válido",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString deve ser uma cor RGB válida",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString deve ser uma cor RGBA válida",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString deve ser uma cor HSL válida",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString deve ser uma cor HSLA válida",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString deve ser um hexadecimal válido",
},
{
ns: "Test.HexColorString",
expected: "HexColorString deve ser uma cor HEX válida",
},
{
ns: "Test.NumberString",
expected: "NumberString deve ser um número válido",
},
{
ns: "Test.NumericString",
expected: "NumericString deve ser um valor numérico válido",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString deve conter apenas caracteres alfanuméricos",
},
{
ns: "Test.AlphaString",
expected: "AlphaString deve conter apenas caracteres alfabéticos",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString deve ser menor que MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString deve ser menor ou igual que MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString deve ser maior que MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString deve ser maior ou igual que MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString não deve ser igual a EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString deve ser menor que Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString deve ser menor ou igual que Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString deve ser maior que Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString deve ser maior ou igual que Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString não deve ser igual a Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString deve ser igual a Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString deve ser igual a MaxString",
},
{
ns: "Test.GteString",
expected: "GteString deve ter pelo menos 3 caracteres",
},
{
ns: "Test.GteNumber",
expected: "GteNumber deve ser maior ou igual a 5,56",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple deve conter pelo menos 2 items",
},
{
ns: "Test.GteTime",
expected: "GteTime deve ser posterior ou igual à data/hora atual",
},
{
ns: "Test.GtString",
expected: "GtString deve conter mais de 3 caracteres",
},
{
ns: "Test.GtNumber",
expected: "GtNumber deve ser maior que 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple deve conter mais de 2 items",
},
{
ns: "Test.GtTime",
expected: "GtTime deve ser posterior à data/hora atual",
},
{
ns: "Test.LteString",
expected: "LteString deve ter no máximo 3 caracteres",
},
{
ns: "Test.LteNumber",
expected: "LteNumber deve ser menor ou igual a 5,56",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple deve conter no máximo 2 items",
},
{
ns: "Test.LteTime",
expected: "LteTime deve ser anterior ou igual à data/hora atual",
},
{
ns: "Test.LtString",
expected: "LtString deve ter menos de 3 caracteres",
},
{
ns: "Test.LtNumber",
expected: "LtNumber deve ser menor que 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple deve conter menos de 2 items",
},
{
ns: "Test.LtTime",
expected: "LtTime deve ser anterior à data / hora atual",
},
{
ns: "Test.NeString",
expected: "NeString não deve ser igual a ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber não deve ser igual a 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple não deve ser igual a 0",
},
{
ns: "Test.EqString",
expected: "EqString não é igual a 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber não é igual a 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple não é igual a 7",
},
{
ns: "Test.MaxString",
expected: "MaxString deve ter no máximo 3 caracteres",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber deve ser 1.113,00 ou menos",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple deve conter no máximo 7 items",
},
{
ns: "Test.MinString",
expected: "MinString deve ter pelo menos 1 caractere",
},
{
ns: "Test.MinNumber",
expected: "MinNumber deve ser 1.113,00 ou superior",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple deve conter pelo menos 7 items",
},
{
ns: "Test.LenString",
expected: "LenString deve ter 1 caractere",
},
{
ns: "Test.LenNumber",
expected: "LenNumber deve ser igual a 1.113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple deve conter 7 items",
},
{
ns: "Test.RequiredString",
expected: "RequiredString é obrigatório",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber é obrigatório",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple é obrigatório",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen deve ter pelo menos 10 caracteres",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen deve ter no máximo 1 caractere",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen deve ter 2 caracteres",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt deve ter menos de 1 caractere",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte deve ter no máximo 1 caractere",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt deve conter mais de 10 caracteres",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte deve ter pelo menos 10 caracteres",
},
{
ns: "Test.OneOfString",
expected: "OneOfString deve ser um de [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt deve ser um de [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice deve conter valores únicos",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray deve conter valores únicos",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap deve conter valores únicos",
},
{
ns: "Test.JSONString",
expected: "JSONString deve ser uma string json válida",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString deve estar em minuscúlas",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString deve estar em maiúsculas",
},
{
ns: "Test.Datetime",
expected: "Datetime não está no formato 2006-01-02",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,634 @@
package pt_BR
import (
"testing"
"time"
brazilian_portuguese "gin-valid/go-playground/locales/pt_BR"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
)
func TestTranslations(t *testing.T) {
ptbr := brazilian_portuguese.New()
uni := ut.New(ptbr, ptbr)
trans, _ := uni.GetTranslator("pt_BR")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "este é um texto de teste"
test.ExcludesAll = "Isso é Ótimo!"
test.ExcludesRune = "Amo isso ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor deve ser uma cor válida",
},
{
ns: "Test.MAC",
expected: "MAC deve conter um endereço MAC válido",
},
{
ns: "Test.IPAddr",
expected: "IPAddr deve ser um endereço IP resolvível",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 deve ser um endereço IPv4 resolvível",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 deve ser um endereço IPv6 resolvível",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr deve ser um endereço UDP válido",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 deve ser um endereço IPv4 UDP válido",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 deve ser um endereço IPv6 UDP válido",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr deve ser um endereço TCP válido",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 deve ser um endereço IPv4 TCP válido",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 deve ser um endereço IPv6 TCP válido",
},
{
ns: "Test.CIDR",
expected: "CIDR deve conter uma notação CIDR válida",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 deve conter uma notação CIDR válida para um endereço IPv4",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 deve conter uma notação CIDR válida para um endereço IPv6",
},
{
ns: "Test.SSN",
expected: "SSN deve ser um número SSN válido",
},
{
ns: "Test.IP",
expected: "IP deve ser um endereço de IP válido",
},
{
ns: "Test.IPv4",
expected: "IPv4 deve ser um endereço IPv4 válido",
},
{
ns: "Test.IPv6",
expected: "IPv6 deve ser um endereço IPv6 válido",
},
{
ns: "Test.DataURI",
expected: "DataURI deve conter um URI data válido",
},
{
ns: "Test.Latitude",
expected: "Latitude deve conter uma coordenada de latitude válida",
},
{
ns: "Test.Longitude",
expected: "Longitude deve conter uma coordenada de longitude válida",
},
{
ns: "Test.MultiByte",
expected: "MultiByte deve conter caracteres multibyte",
},
{
ns: "Test.ASCII",
expected: "ASCII deve conter apenas caracteres ascii",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII deve conter apenas caracteres ascii imprimíveis",
},
{
ns: "Test.UUID",
expected: "UUID deve ser um UUID válido",
},
{
ns: "Test.UUID3",
expected: "UUID3 deve ser um UUID versão 3 válido",
},
{
ns: "Test.UUID4",
expected: "UUID4 deve ser um UUID versão 4 válido",
},
{
ns: "Test.UUID5",
expected: "UUID5 deve ser um UUID versão 5 válido",
},
{
ns: "Test.ISBN",
expected: "ISBN deve ser um número ISBN válido",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 deve ser um número ISBN-10 válido",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 deve ser um número ISBN-13 válido",
},
{
ns: "Test.Excludes",
expected: "Excludes não deve conter o texto 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll não deve conter nenhum dos caracteres '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune não deve conter '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny deve conter pelo menos um dos caracteres '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains deve conter o texto 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 deve ser uma string Base64 válida",
},
{
ns: "Test.Email",
expected: "Email deve ser um endereço de e-mail válido",
},
{
ns: "Test.URL",
expected: "URL deve ser uma URL válida",
},
{
ns: "Test.URI",
expected: "URI deve ser uma URI válida",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString deve ser uma cor RGB válida",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString deve ser uma cor RGBA válida",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString deve ser uma cor HSL válida",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString deve ser uma cor HSLA válida",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString deve ser um hexadecimal válido",
},
{
ns: "Test.HexColorString",
expected: "HexColorString deve ser uma cor HEX válida",
},
{
ns: "Test.NumberString",
expected: "NumberString deve ser um número válido",
},
{
ns: "Test.NumericString",
expected: "NumericString deve ser um valor numérico válido",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString deve conter caracteres alfanuméricos",
},
{
ns: "Test.AlphaString",
expected: "AlphaString deve conter caracteres alfabéticos",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString deve ser menor que MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString deve ser menor ou igual a MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString deve ser maior do que MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString deve ser maior ou igual a MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString não deve ser igual a EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString deve ser menor que Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString deve ser menor ou igual a Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString deve ser maior do que Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString deve ser maior ou igual a Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString não deve ser igual a Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString deve ser igual a Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString deve ser igual a MaxString",
},
{
ns: "Test.GteString",
expected: "GteString deve ter pelo menos 3 caracteres",
},
{
ns: "Test.GteNumber",
expected: "GteNumber deve ser 5,56 ou superior",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple deve conter pelo menos 2 itens",
},
{
ns: "Test.GteTime",
expected: "GteTime deve ser maior ou igual à Data e Hora atual",
},
{
ns: "Test.GtString",
expected: "GtString deve ter mais de 3 caracteres",
},
{
ns: "Test.GtNumber",
expected: "GtNumber deve ser maior do que 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple deve conter mais de 2 itens",
},
{
ns: "Test.GtTime",
expected: "GtTime deve ser maior que a Data e Hora atual",
},
{
ns: "Test.LteString",
expected: "LteString deve ter no máximo 3 caracteres",
},
{
ns: "Test.LteNumber",
expected: "LteNumber deve ser 5,56 ou menor",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple deve conter no máximo 2 itens",
},
{
ns: "Test.LteTime",
expected: "LteTime deve ser menor ou igual à Data e Hora atual",
},
{
ns: "Test.LtString",
expected: "LtString deve ter menos de 3 caracteres",
},
{
ns: "Test.LtNumber",
expected: "LtNumber deve ser menor que 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple deve conter menos de 2 itens",
},
{
ns: "Test.LtTime",
expected: "LtTime deve ser inferior à Data e Hora atual",
},
{
ns: "Test.NeString",
expected: "NeString não deve ser igual a ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber não deve ser igual a 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple não deve ser igual a 0",
},
{
ns: "Test.EqString",
expected: "EqString não é igual a 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber não é igual a 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple não é igual a 7",
},
{
ns: "Test.MaxString",
expected: "MaxString deve ter no máximo 3 caracteres",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber deve ser 1.113,00 ou menor",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple deve conter no máximo 7 itens",
},
{
ns: "Test.MinString",
expected: "MinString deve ter pelo menos 1 caractere",
},
{
ns: "Test.MinNumber",
expected: "MinNumber deve ser 1.113,00 ou superior",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple deve conter pelo menos 7 itens",
},
{
ns: "Test.LenString",
expected: "LenString deve ter 1 caractere",
},
{
ns: "Test.LenNumber",
expected: "LenNumber deve ser igual a 1.113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple deve conter 7 itens",
},
{
ns: "Test.RequiredString",
expected: "RequiredString é um campo requerido",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber é um campo requerido",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple é um campo requerido",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen deve ter pelo menos 10 caracteres",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen deve ter no máximo 1 caractere",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen deve ter 2 caracteres",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt deve ter menos de 1 caractere",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte deve ter no máximo 1 caractere",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt deve ter mais de 10 caracteres",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte deve ter pelo menos 10 caracteres",
},
{
ns: "Test.OneOfString",
expected: "OneOfString deve ser um de [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt deve ser um de [5 63]",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,656 @@
package ru
import (
"log"
//"github.com/rustery/validator"
"testing"
"time"
russian "gin-valid/go-playground/locales/en"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
)
func TestTranslations(t *testing.T) {
ru := russian.New()
uni := ut.New(ru, ru)
trans, _ := uni.GetTranslator("ru")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor должен быть цветом",
},
{
ns: "Test.MAC",
expected: "MAC должен содержать MAC адрес",
},
{
ns: "Test.IPAddr",
expected: "IPAddr должен быть распознаваемым IP адресом",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 должен быть распознаваемым IPv4 адресом",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 должен быть распознаваемым IPv6 адресом",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr должен быть UDP адресом",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 должен быть IPv4 UDP адресом",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 должен быть IPv6 UDP адресом",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr должен быть TCP адресом",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 должен быть IPv4 TCP адресом",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 должен быть IPv6 TCP адресом",
},
{
ns: "Test.CIDR",
expected: "CIDR должен содержать CIDR обозначения",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 должен содержать CIDR обозначения для IPv4 адреса",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 должен содержать CIDR обозначения для IPv6 адреса",
},
{
ns: "Test.SSN",
expected: "SSN должен быть SSN номером",
},
{
ns: "Test.IP",
expected: "IP должен быть IP адресом",
},
{
ns: "Test.IPv4",
expected: "IPv4 должен быть IPv4 адресом",
},
{
ns: "Test.IPv6",
expected: "IPv6 должен быть IPv6 адресом",
},
{
ns: "Test.DataURI",
expected: "DataURI должен содержать Data URI",
},
{
ns: "Test.Latitude",
expected: "Latitude должен содержать координаты широты",
},
{
ns: "Test.Longitude",
expected: "Longitude должен содержать координаты долготы",
},
{
ns: "Test.MultiByte",
expected: "MultiByte должен содержать мультибайтные символы",
},
{
ns: "Test.ASCII",
expected: "ASCII должен содержать только ascii символы",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII должен содержать только доступные для печати ascii символы",
},
{
ns: "Test.UUID",
expected: "UUID должен быть UUID",
},
{
ns: "Test.UUID3",
expected: "UUID3 должен быть UUID 3 версии",
},
{
ns: "Test.UUID4",
expected: "UUID4 должен быть UUID 4 версии",
},
{
ns: "Test.UUID5",
expected: "UUID5 должен быть UUID 5 версии",
},
{
ns: "Test.ISBN",
expected: "ISBN должен быть ISBN номером",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 должен быть ISBN-10 номером",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 должен быть ISBN-13 номером",
},
{
ns: "Test.Excludes",
expected: "Excludes не должен содержать текст 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll не должен содержать символы '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune не должен содержать '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny должен содержать минимум один из символов '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains должен содержать текст 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 должен быть Base64 строкой",
},
{
ns: "Test.Email",
expected: "Email должен быть email адресом",
},
{
ns: "Test.URL",
expected: "URL должен быть URL",
},
{
ns: "Test.URI",
expected: "URI должен быть URI",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString должен быть RGB цветом",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString должен быть RGBA цветом",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString должен быть HSL цветом",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString должен быть HSLA цветом",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString должен быть шестнадцатеричной строкой",
},
{
ns: "Test.HexColorString",
expected: "HexColorString должен быть HEX цветом",
},
{
ns: "Test.NumberString",
expected: "NumberString должен быть цифрой",
},
{
ns: "Test.NumericString",
expected: "NumericString должен быть цифровым значением",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString должен содержать только буквы и цифры",
},
{
ns: "Test.AlphaString",
expected: "AlphaString должен содержать только буквы",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString должен быть менее MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString должен быть менее или равен MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString должен быть больше MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString должен быть больше или равен MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString не должен быть равен EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString должен быть менее Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString должен быть менее или равен Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString должен быть больше Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString должен быть больше или равен Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString не должен быть равен Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString должен быть равен Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString должен быть равен MaxString",
},
{
ns: "Test.GteString",
expected: "GteString должен содержать минимум 3 символы",
},
{
ns: "Test.GteNumber",
expected: "GteNumber должен быть больше или равно 5.56",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple должен содержать минимум 2 элементы",
},
{
ns: "Test.GteTime",
expected: "GteTime должна быть позже или равна текущему моменту",
},
{
ns: "Test.GtString",
expected: "GtString должен быть длиннее 3 символы",
},
{
ns: "Test.GtNumber",
expected: "GtNumber должен быть больше 5.56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple должен содержать более 2 элементы",
},
{
ns: "Test.GtTime",
expected: "GtTime должна быть позже текущего момента",
},
{
ns: "Test.LteString",
expected: "LteString должен содержать максимум 3 символы",
},
{
ns: "Test.LteNumber",
expected: "LteNumber должен быть менее или равен 5.56",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple должен содержать максимум 2 элементы",
},
{
ns: "Test.LteTime",
expected: "LteTime must be less than or equal to the current Date & Time",
},
{
ns: "Test.LtString",
expected: "LtString должен иметь менее 3 символы",
},
{
ns: "Test.LtNumber",
expected: "LtNumber должен быть менее 5.56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple должен содержать менее 2 элементы",
},
{
ns: "Test.LtTime",
expected: "LtTime must be less than the current Date & Time",
},
{
ns: "Test.NeString",
expected: "NeString должен быть не равен ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber должен быть не равен 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple должен быть не равен 0",
},
{
ns: "Test.EqString",
expected: "EqString не равен 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber не равен 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple не равен 7",
},
{
ns: "Test.MaxString",
expected: "MaxString должен содержать максимум 3 символы",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber должен быть меньше или равно 1,113.00",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple должен содержать максимум 7 элементы",
},
{
ns: "Test.MinString",
expected: "MinString должен содержать минимум 1 символ",
},
{
ns: "Test.MinNumber",
expected: "MinNumber должен быть больше или равно 1,113.00",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple должен содержать минимум 7 элементы",
},
{
ns: "Test.LenString",
expected: "LenString должен быть длиной в 1 символ",
},
{
ns: "Test.LenNumber",
expected: "LenNumber должен быть равен 1,113.00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple должен содержать 7 элементы",
},
{
ns: "Test.RequiredString",
expected: "RequiredString обязательное поле",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber обязательное поле",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple обязательное поле",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen должен содержать минимум 10 символы",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen должен содержать максимум 1 символ",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen должен быть длиной в 2 символы",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt должен иметь менее 1 символ",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte должен содержать максимум 1 символ",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt должен быть длиннее 10 символы",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte должен содержать минимум 10 символы",
},
{
ns: "Test.OneOfString",
expected: "OneOfString должен быть одним из [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt должен быть одним из [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice должен содержать уникальные значения",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray должен содержать уникальные значения",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap должен содержать уникальные значения",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
log.Println(fe)
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,652 @@
package tr
import (
"testing"
"time"
turkish "gin-valid/go-playground/locales/tr"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
)
func TestTranslations(t *testing.T) {
tr := turkish.New()
uni := ut.New(tr, tr)
trans, _ := uni.GetTranslator("tr")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor geçerli bir renk olmalıdır",
},
{
ns: "Test.MAC",
expected: "MAC geçerli bir MAC adresi içermelidir",
},
{
ns: "Test.IPAddr",
expected: "IPAddr çözülebilir bir IP adresi olmalıdır",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 çözülebilir bir IPv4 adresi olmalıdır",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 çözülebilir bir IPv6 adresi olmalıdır",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr geçerli bir UDP adresi olmalıdır",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 geçerli bir IPv4 UDP adresi olmalıdır",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 geçerli bir IPv6 UDP adresi olmalıdır",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr geçerli bir TCP adresi olmalıdır",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 geçerli bir IPv4 TCP adresi olmalıdır",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 geçerli bir IPv6 TCP adresi olmalıdır",
},
{
ns: "Test.CIDR",
expected: "CIDR geçerli bir CIDR gösterimi içermelidir",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 bir IPv4 adresi için geçerli bir CIDR gösterimi içermelidir",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 bir IPv6 adresi için geçerli bir CIDR gösterimi içermelidir",
},
{
ns: "Test.SSN",
expected: "SSN geçerli bir SSN numarası olmalıdır",
},
{
ns: "Test.IP",
expected: "IP geçerli bir IP adresi olmalıdır",
},
{
ns: "Test.IPv4",
expected: "IPv4 geçerli bir IPv4 adresi olmalıdır",
},
{
ns: "Test.IPv6",
expected: "IPv6 geçerli bir IPv6 adresi olmalıdır",
},
{
ns: "Test.DataURI",
expected: "DataURI geçerli bir Veri URI içermelidir",
},
{
ns: "Test.Latitude",
expected: "Latitude geçerli bir enlem koordinatı içermelidir",
},
{
ns: "Test.Longitude",
expected: "Longitude geçerli bir boylam koordinatı içermelidir",
},
{
ns: "Test.MultiByte",
expected: "MultiByte çok baytlı karakterler içermelidir",
},
{
ns: "Test.ASCII",
expected: "ASCII yalnızca ascii karakterler içermelidir",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII yalnızca yazdırılabilir ascii karakterleri içermelidir",
},
{
ns: "Test.UUID",
expected: "UUID geçerli bir UUID olmalıdır",
},
{
ns: "Test.UUID3",
expected: "UUID3 geçerli bir sürüm 3 UUID olmalıdır",
},
{
ns: "Test.UUID4",
expected: "UUID4 geçerli bir sürüm 4 UUID olmalıdır",
},
{
ns: "Test.UUID5",
expected: "UUID5 geçerli bir sürüm 5 UUID olmalıdır",
},
{
ns: "Test.ISBN",
expected: "ISBN geçerli bir ISBN numarası olmalıdır",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 geçerli bir ISBN-10 numarası olmalıdır",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 geçerli bir ISBN-13 numarası olmalıdır",
},
{
ns: "Test.Excludes",
expected: "Excludes, 'text' metnini içeremez",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll, '!@#$' karakterlerinden hiçbirini içeremez",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune, '☻' ifadesini içeremez",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny, '!@#$' karakterlerinden en az birini içermelidir",
},
{
ns: "Test.Contains",
expected: "Contains, 'purpose' metnini içermelidir",
},
{
ns: "Test.Base64",
expected: "Base64 geçerli bir Base64 karakter dizesi olmalıdır",
},
{
ns: "Test.Email",
expected: "Email geçerli bir e-posta adresi olmalıdır",
},
{
ns: "Test.URL",
expected: "URL geçerli bir URL olmalıdır",
},
{
ns: "Test.URI",
expected: "URI geçerli bir URI olmalıdır",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString geçerli bir RGB rengi olmalıdır",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString geçerli bir RGBA rengi olmalıdır",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString geçerli bir HSL rengi olmalıdır",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString geçerli bir HSLA rengi olmalıdır",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString geçerli bir onaltılık olmalıdır",
},
{
ns: "Test.HexColorString",
expected: "HexColorString geçerli bir HEX rengi olmalıdır",
},
{
ns: "Test.NumberString",
expected: "NumberString geçerli bir sayı olmalıdır",
},
{
ns: "Test.NumericString",
expected: "NumericString geçerli bir sayısal değer olmalıdır",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString yalnızca alfanümerik karakterler içerebilir",
},
{
ns: "Test.AlphaString",
expected: "AlphaString yalnızca alfabetik karakterler içerebilir",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString, MaxString değerinden küçük olmalıdır",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString, MaxString değerinden küçük veya ona eşit olmalıdır",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString, MaxString değerinden büyük olmalıdır",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString, MaxString değerinden büyük veya ona eşit olmalıdır",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString, EqFieldString değerine eşit olmamalıdır",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString, Inner.LtCSFieldString değerinden küçük olmalıdır",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString, Inner.LteCSFieldString değerinden küçük veya ona eşit olmalıdır",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString, Inner.GtCSFieldString değerinden büyük olmalıdır",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString, Inner.GteCSFieldString değerinden küçük veya ona eşit olmalıdır",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString, Inner.NeCSFieldString değerine eşit olmamalıdır",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString, Inner.EqCSFieldString değerine eşit olmalıdır",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString, MaxString değerine eşit olmalıdır",
},
{
ns: "Test.GteString",
expected: "GteString en az 3 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.GteNumber",
expected: "GteNumber, 5,56 veya daha büyük olmalıdır",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple en az 2 öğe içermelidir",
},
{
ns: "Test.GteTime",
expected: "GteTime geçerli Tarih ve Saatten büyük veya ona eşit olmalıdır",
},
{
ns: "Test.GtString",
expected: "GtString, 3 karakter uzunluğundan fazla olmalıdır",
},
{
ns: "Test.GtNumber",
expected: "GtNumber, 5,56 değerinden büyük olmalıdır",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple, 2 öğeden daha fazla içermelidir",
},
{
ns: "Test.GtTime",
expected: "GtTime geçerli Tarih ve Saatten büyük olmalıdır",
},
{
ns: "Test.LteString",
expected: "LteString en fazla 3 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.LteNumber",
expected: "LteNumber, 5,56 veya daha az olmalıdır",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple, maksimum 2 öğe içermelidir",
},
{
ns: "Test.LteTime",
expected: "LteTime geçerli Tarih ve Saate eşit veya daha küçük olmalıdır",
},
{
ns: "Test.LtString",
expected: "LtString, 3 karakter uzunluğundan daha az olmalıdır",
},
{
ns: "Test.LtNumber",
expected: "LtNumber, 5,56 değerinden küçük olmalıdır",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple, 2 öğeden daha az içermelidir",
},
{
ns: "Test.LtTime",
expected: "LtTime geçerli Tarih ve Saatten daha az olmalıdır",
},
{
ns: "Test.NeString",
expected: "NeString, değerine eşit olmamalıdır",
},
{
ns: "Test.NeNumber",
expected: "NeNumber, 0.00 değerine eşit olmamalıdır",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple, 0 değerine eşit olmamalıdır",
},
{
ns: "Test.EqString",
expected: "EqString, 3 değerine eşit değil",
},
{
ns: "Test.EqNumber",
expected: "EqNumber, 2.33 değerine eşit değil",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple, 7 değerine eşit değil",
},
{
ns: "Test.MaxString",
expected: "MaxString uzunluğu en fazla 3 karakter olmalıdır",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber, 1.113,00 veya daha az olmalıdır",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple maksimum 7 öğe içermelidir",
},
{
ns: "Test.MinString",
expected: "MinString en az 1 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.MinNumber",
expected: "MinNumber, 1.113,00 veya daha büyük olmalıdır",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple en az 7 öğe içermelidir",
},
{
ns: "Test.LenString",
expected: "LenString uzunluğu 1 karakter olmalıdır",
},
{
ns: "Test.LenNumber",
expected: "LenNumber, 1.113,00 değerine eşit olmalıdır",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple, 7 öğe içermelidir",
},
{
ns: "Test.RequiredString",
expected: "RequiredString zorunlu bir alandır",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber zorunlu bir alandır",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple zorunlu bir alandır",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen en az 10 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen uzunluğu en fazla 1 karakter olmalıdır",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen uzunluğu 2 karakter olmalıdır",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt, 1 karakter uzunluğundan daha az olmalıdır",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte en fazla 1 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt, 10 karakter uzunluğundan fazla olmalıdır",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte en az 10 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.OneOfString",
expected: "OneOfString, [red green]'dan biri olmalıdır",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt, [5 63]'dan biri olmalıdır",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice benzersiz değerler içermelidir",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray benzersiz değerler içermelidir",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap benzersiz değerler içermelidir",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,661 @@
package zh
import (
"testing"
"time"
zhongwen "gin-valid/go-playground/locales/zh"
ut "gin-valid/go-playground/universal-translator"
"gin-valid/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
)
func TestTranslations(t *testing.T) {
zh := zhongwen.New()
uni := ut.New(zh, zh)
trans, _ := uni.GetTranslator("zh")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
JsonString string `validate:"json"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.JsonString = "{\"foo\":\"bar\",}"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
test.Datetime = "20060102"
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor必须是一个有效的颜色",
},
{
ns: "Test.MAC",
expected: "MAC必须是一个有效的MAC地址",
},
{
ns: "Test.IPAddr",
expected: "IPAddr必须是一个有效的IP地址",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4必须是一个有效的IPv4地址",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6必须是一个有效的IPv6地址",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr必须是一个有效的UDP地址",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4必须是一个有效的IPv4 UDP地址",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6必须是一个有效的IPv6 UDP地址",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr必须是一个有效的TCP地址",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4必须是一个有效的IPv4 TCP地址",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6必须是一个有效的IPv6 TCP地址",
},
{
ns: "Test.CIDR",
expected: "CIDR必须是一个有效的无类别域间路由(CIDR)",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4必须是一个包含IPv4地址的有效无类别域间路由(CIDR)",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6必须是一个包含IPv6地址的有效无类别域间路由(CIDR)",
},
{
ns: "Test.SSN",
expected: "SSN必须是一个有效的社会安全号码(SSN)",
},
{
ns: "Test.IP",
expected: "IP必须是一个有效的IP地址",
},
{
ns: "Test.IPv4",
expected: "IPv4必须是一个有效的IPv4地址",
},
{
ns: "Test.IPv6",
expected: "IPv6必须是一个有效的IPv6地址",
},
{
ns: "Test.DataURI",
expected: "DataURI必须包含有效的数据URI",
},
{
ns: "Test.Latitude",
expected: "Latitude必须包含有效的纬度坐标",
},
{
ns: "Test.Longitude",
expected: "Longitude必须包含有效的经度坐标",
},
{
ns: "Test.MultiByte",
expected: "MultiByte必须包含多字节字符",
},
{
ns: "Test.ASCII",
expected: "ASCII必须只包含ascii字符",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII必须只包含可打印的ascii字符",
},
{
ns: "Test.UUID",
expected: "UUID必须是一个有效的UUID",
},
{
ns: "Test.UUID3",
expected: "UUID3必须是一个有效的V3 UUID",
},
{
ns: "Test.UUID4",
expected: "UUID4必须是一个有效的V4 UUID",
},
{
ns: "Test.UUID5",
expected: "UUID5必须是一个有效的V5 UUID",
},
{
ns: "Test.ISBN",
expected: "ISBN必须是一个有效的ISBN编号",
},
{
ns: "Test.ISBN10",
expected: "ISBN10必须是一个有效的ISBN-10编号",
},
{
ns: "Test.ISBN13",
expected: "ISBN13必须是一个有效的ISBN-13编号",
},
{
ns: "Test.Excludes",
expected: "Excludes不能包含文本'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll不能包含以下任何字符'!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune不能包含'☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny必须包含至少一个以下字符'!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains必须包含文本'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64必须是一个有效的Base64字符串",
},
{
ns: "Test.Email",
expected: "Email必须是一个有效的邮箱",
},
{
ns: "Test.URL",
expected: "URL必须是一个有效的URL",
},
{
ns: "Test.URI",
expected: "URI必须是一个有效的URI",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString必须是一个有效的RGB颜色",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString必须是一个有效的RGBA颜色",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString必须是一个有效的HSL颜色",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString必须是一个有效的HSLA颜色",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString必须是一个有效的十六进制",
},
{
ns: "Test.HexColorString",
expected: "HexColorString必须是一个有效的十六进制颜色",
},
{
ns: "Test.NumberString",
expected: "NumberString必须是一个有效的数字",
},
{
ns: "Test.NumericString",
expected: "NumericString必须是一个有效的数值",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString只能包含字母和数字",
},
{
ns: "Test.AlphaString",
expected: "AlphaString只能包含字母",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString必须小于MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString必须小于或等于MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString必须大于MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString必须大于或等于MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString不能等于EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString必须小于Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString必须小于或等于Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString必须大于Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString必须大于或等于Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString不能等于Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString必须等于Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString必须等于MaxString",
},
{
ns: "Test.GteString",
expected: "GteString长度必须至少为3个字符",
},
{
ns: "Test.GteNumber",
expected: "GteNumber必须大于或等于5.56",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple必须至少包含2项",
},
{
ns: "Test.GteTime",
expected: "GteTime必须大于或等于当前日期和时间",
},
{
ns: "Test.GtString",
expected: "GtString长度必须大于3个字符",
},
{
ns: "Test.GtNumber",
expected: "GtNumber必须大于5.56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple必须大于2项",
},
{
ns: "Test.GtTime",
expected: "GtTime必须大于当前日期和时间",
},
{
ns: "Test.LteString",
expected: "LteString长度不能超过3个字符",
},
{
ns: "Test.LteNumber",
expected: "LteNumber必须小于或等于5.56",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple最多只能包含2项",
},
{
ns: "Test.LteTime",
expected: "LteTime必须小于或等于当前日期和时间",
},
{
ns: "Test.LtString",
expected: "LtString长度必须小于3个字符",
},
{
ns: "Test.LtNumber",
expected: "LtNumber必须小于5.56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple必须包含少于2项",
},
{
ns: "Test.LtTime",
expected: "LtTime必须小于当前日期和时间",
},
{
ns: "Test.NeString",
expected: "NeString不能等于",
},
{
ns: "Test.NeNumber",
expected: "NeNumber不能等于0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple不能等于0",
},
{
ns: "Test.EqString",
expected: "EqString不等于3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber不等于2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple不等于7",
},
{
ns: "Test.MaxString",
expected: "MaxString长度不能超过3个字符",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber必须小于或等于1,113.00",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple最多只能包含7项",
},
{
ns: "Test.MinString",
expected: "MinString长度必须至少为1个字符",
},
{
ns: "Test.MinNumber",
expected: "MinNumber最小只能为1,113.00",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple必须至少包含7项",
},
{
ns: "Test.LenString",
expected: "LenString长度必须是1个字符",
},
{
ns: "Test.LenNumber",
expected: "LenNumber必须等于1,113.00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple必须包含7项",
},
{
ns: "Test.RequiredString",
expected: "RequiredString为必填字段",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber为必填字段",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple为必填字段",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen长度必须至少为10个字符",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen长度不能超过1个字符",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen长度必须是2个字符",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt长度必须小于1个字符",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte长度不能超过1个字符",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt长度必须大于10个字符",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte长度必须至少为10个字符",
},
{
ns: "Test.OneOfString",
expected: "OneOfString必须是[red green]中的一个",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt必须是[5 63]中的一个",
},
{
ns: "Test.JsonString",
expected: "JsonString必须是一个JSON字符串",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString必须是小写字母",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString必须是大写字母",
},
{
ns: "Test.Datetime",
expected: "Datetime的格式必须是2006-01-02",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More