|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/go-playground/locales/en"
|
|
|
|
ut "github.com/go-playground/universal-translator"
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
|
|
en_translations "github.com/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))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|