Skip to content

Validator Validasi Library

Alamat Resmi: go-playground/validator: 💯Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving (github.com)

Alamat Dokumentasi: validator/README.md at master · go-playground/validator (github.com)

Contoh Resmi: validator/_examples at master · go-playground/validator (github.com)

Benchmark: go-playground/validator: 💯Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving (github.com)

Pengantar

go-playground/validator mengimplementasikan validator nilai berdasarkan tag struktur dengan fitur unik berikut:

  • Dapat menggunakan tag validasi dan validator kustom untuk validasi cross-field dan cross-struct

  • Slice array map atau dimensi multidimensi apa pun dapat divalidasi

  • Dapat melakukan validasi mendalam untuk key dan value map

  • Menentukan cara penanganan berdasarkan tipe dasar sebelum validasi

  • Dapat menangani tipe field kustom

  • Mendukung tag alias yang memungkinkan beberapa validasi dipetakan ke satu tag untuk memudahkan definisi validasi untuk struktur

  • Dapat mengekstrak nama field kustom seperti nama JSON untuk ditampilkan dalam pesan error

  • Pesan error multi-bahasa kustom

  • Komponen validasi default standar untuk framework gin

Instalasi

powershell
go get github.com/go-playground/validator/v10

Import

go
import "github.com/go-playground/validator/v10"

Tag

Validator memiliki banyak tag validasi dasar semua fungsi validasi yang sesuai dengan tag dapat ditemukan di file baked_in.go tag struktur validator adalah validate

Misalnya

go
type User {
  age int `validate:"gte=18"` //menunjukkan lebih besar atau sama dengan 18 tahun
}

Juga dapat mengubah tag default melalui method setTagName.

Field

TagDeskripsi
eqcsfieldDalam struktur terpisah validasi nilai field saat ini sama dengan field yang ditentukan oleh nilai param
eqfieldValidasi nilai field saat ini sama dengan field yang ditentukan oleh nilai parameter
fieldcontainsValidasi nilai field saat ini berisi field yang ditentukan oleh nilai parameter
fieldexcludesValidasi nilai field saat ini tidak berisi field yang ditentukan oleh nilai parameter
gtcsfieldDalam struktur terpisah validasi nilai field saat ini lebih besar dari field yang ditentukan oleh nilai parameter
gtecsfieldDalam struktur terpisah validasi nilai field saat ini lebih besar atau sama dengan field yang ditentukan oleh nilai parameter
gtefieldValidasi nilai field saat ini lebih besar atau sama dengan field yang ditentukan oleh nilai parameter
gtfieldValidasi nilai field saat ini lebih besar dari field yang ditentukan oleh nilai parameter
ltcsfieldDalam struktur terpisah validasi nilai field saat ini lebih kecil dari field yang ditentukan oleh nilai parameter
ltecsfieldDalam struktur terpisah validasi nilai field saat ini lebih kecil atau sama dengan field yang ditentukan oleh nilai parameter
ltefieldValidasi nilai field saat ini lebih kecil atau sama dengan field yang ditentukan oleh nilai parameter
ltfieldValidasi nilai field saat ini lebih kecil dari field yang ditentukan oleh nilai parameter
necsfieldValidasi nilai field saat ini tidak sama dengan field dalam struktur terpisah yang ditentukan oleh nilai parameter
nefieldValidasi nilai field saat ini tidak sama dengan field yang ditentukan oleh nilai parameter

Network

TagDeskripsi
cidrClassless Inter-Domain Routing CIDR
cidrv4Classless Inter-Domain Routing CIDRv4
cidrv6Classless Inter-Domain Routing CIDRv6
datauriData Uniform Resource Locator
fqdnFully Qualified Domain Name (FQDN)
hostnameHostname RFC 952
hostname_portValidasi field kombinasi <dns>:<port> yang umum digunakan untuk alamat socket
hostname_rfc1123Hostname RFC 952
ipInternet Protocol Address IP
ip4_addrInternet Protocol Address IPv4
ip6_addrInternet Protocol Address IPv6
ip_addrInternet Protocol Address IP
ipv4Internet Protocol Address IPv4
ipv6Internet Protocol Address IPv6
macMedia Access Control Address juga dikenal sebagai alamat LAN
tcp4_addrTransmission Control Protocol Address TCP4
tcp6_addrTransmission Control Protocol Address TCPv6
tcp_addrTransmission Control Protocol Address TCP
udp4_addrUser Datagram Protocol Address UDPv4
udp6_addrUser Datagram Protocol Address UDPv6
udp_addrUser Datagram Protocol Address UDP
unix_addrUnix Domain Socket Endpoint Address
uriUniform Resource Identifier
urlUniform Resource Locator
url_encodedUniform Resource Identifier Encoding
urn_rfc2141RFC 2141 Uniform Resource Name

String

TagDeskripsi
alphaValidasi nilai field saat ini adalah huruf yang valid
alphanumValidasi nilai field saat ini adalah alfanumerik yang valid
alphanumunicodeValidasi nilai field saat ini adalah nilai unicode alfanumerik yang valid
alphaunicodeValidasi nilai field saat ini adalah nilai unicode huruf yang valid
asciiValidasi nilai field adalah karakter ASCII yang valid
booleanValidasi nilai field saat ini adalah nilai boolean yang valid atau dapat dikonversi dengan aman ke boolean
containsValidasi nilai field berisi teks yang ditentukan dalam parameter
containsanyValidasi nilai field berisi karakter apa pun yang ditentukan dalam parameter
containsruneValidasi nilai field berisi rune yang ditentukan dalam parameter
endsnotwithValidasi nilai field tidak diakhiri dengan teks yang ditentukan dalam parameter
endswithValidasi nilai field diakhiri dengan teks yang ditentukan dalam parameter
excludesValidasi nilai field tidak berisi teks yang ditentukan dalam parameter
excludesallValidasi nilai field tidak berisi karakter apa pun yang ditentukan dalam parameter
excludesruneValidasi nilai field tidak berisi karakter yang ditentukan dalam parameter
lowercaseValidasi nilai field saat ini adalah string huruf kecil
multibyteValidasi nilai field memiliki karakter multi-byte
numberValidasi nilai field saat ini adalah angka yang valid
numericValidasi nilai field saat ini adalah nilai numerik yang valid
printasciiValidasi nilai field adalah karakter ASCII yang dapat dicetak yang valid
startsnotwithValidasi nilai field tidak dimulai dengan teks yang ditentukan dalam parameter
startswithValidasi nilai field dimulai dengan teks yang ditentukan dalam parameter
uppercaseValidasi nilai field saat ini adalah string huruf besar

Format

TagDeskripsi
base64String Base64
base64urlString Base64URL
bicValidasi nilai field saat ini adalah kode BIC yang valid (SWIFT code) seperti yang didefinisikan dalam ISO 9362
bcp47_language_tagValidasi nilai field saat ini adalah tag bahasa spesifikasi BCP47
btc_addrValidasi nilai field adalah alamat BTC yang valid
btc_addr_bech32Validasi nilai field adalah alamat bech32 BTC yang valid
credit_cardValidasi nilai field saat ini adalah nomor kartu kredit yang valid
datetimeValidasi nilai field saat ini adalah string tanggal waktu yang valid
e164Validasi nilai field saat ini adalah nomor telepon format e.164 yang valid
emailValidasi nilai field saat ini adalah alamat email yang valid
eth_addrValidasi nilai field adalah alamat Ethereum yang valid
hexadecimalValidasi nilai field saat ini adalah heksadesimal yang valid
hexcolorValidasi nilai field saat ini adalah warna heksadesimal yang valid
hslValidasi nilai field saat ini adalah warna HSL yang valid
hslaValidasi nilai field saat ini adalah warna HSLA yang valid
htmlValidasi nilai field saat ini adalah HTML yang valid
html_encodedValidasi nilai field saat ini adalah encoding HTML yang valid
isbnValidasi nilai field adalah ISBN v10 atau v13 yang valid (International Standard Book Number)
isbn10Validasi nilai field adalah ISBN v10 yang valid (International Standard Book Number)
isbn13Validasi nilai field adalah ISBN v13 yang valid (International Standard Book Number)
iso3166_1_alpha2Validasi nilai field saat ini adalah kode negara iso3166-1 alpha-2 yang valid
iso3166_1_alpha3Validasi nilai field saat ini adalah kode negara iso3166-1 alpha-3 yang valid
iso3166_1_alpha_numericValidasi nilai field saat ini adalah kode negara alfanumerik iso3166-1 yang valid
iso3166_2Validasi nilai field saat ini adalah kode wilayah negara yang valid (ISO 3166-2)
iso4217Validasi nilai field saat ini adalah kode mata uang yang valid (ISO 4217)
jsonValidasi nilai field saat ini adalah string json yang valid
jwtValidasi nilai field saat ini adalah string JWT yang valid
latitudeValidasi nilai field adalah koordinat lintang yang valid
longitudeValidasi nilai field adalah koordinat bujur yang valid
postcode_iso3166_alpha2Validasi berdasarkan nilai kode negara dalam iso 3166 alpha 2
postcode_iso3166_alpha2_fieldValidasi melalui field yang menunjukkan nilai kode negara dalam iso 3166 alpha 2
rgbValidasi nilai field saat ini adalah warna RGB yang valid
rgbaValidasi nilai field saat ini adalah warna RGBA yang valid
ssnValidasi nilai field adalah SSN yang valid
timezoneValidasi nilai field saat ini adalah string zona waktu yang valid
uuidValidasi nilai field adalah UUID yang valid versi apa pun
uuid3Validasi nilai field adalah UUID v3 yang valid
uuid3_rfc4122Validasi nilai field adalah UUID v3 RFC4122 yang valid
uuid4Validasi nilai field adalah UUID v4 yang valid
uuid4_rfc4122Validasi nilai field adalah UUID v4 RFC4122 yang valid
uuid5Validasi nilai field adalah UUID v5 yang valid
uuid5_rfc4122Validasi nilai field adalah UUID v5 RFC4122 yang valid
uuid_rfc4122Validasi nilai field adalah UUID RFC4122 yang valid versi apa pun
md4Validasi nilai field adalah MD4 yang valid
md5Validasi nilai field adalah MD5 yang valid
sha256Validasi nilai field adalah SHA256 yang valid
sha384Validasi nilai field adalah SHA384 yang valid
sha512Validasi nilai field adalah SHA512 yang valid
ripemd128Validasi nilai field adalah RIPEMD128 yang valid
ripemd160Validasi nilai field adalah RIPEMD160 yang valid
tiger128Validasi nilai field adalah TIGER128 yang valid
tiger160Validasi nilai field adalah TIGER160 yang valid
tiger192Validasi nilai field adalah TIGER192 yang valid
semverValidasi nilai field saat ini adalah versi semver yang valid seperti yang didefinisikan dalam Semantic Versioning 2.0.0
ulidValidasi nilai field adalah ULID yang valid

Perbandingan

TagDeskripsi
eqSama dengan
gtLebih besar dari
gteLebih besar atau sama dengan
ltLebih kecil dari
lteLebih kecil atau sama dengan
neTidak sama dengan

Lainnya

TagDeskripsi
dirDirektori file
filePath file
isdefaultValidasi nilai field saat ini adalah nilai statis default
lenPanjang field
maxNilai maksimum
minNilai minimum
oneofApakah salah satu dari nilai yang terdaftar
omitemptyJika field tidak diatur abaikan field ini
requiredNilai wajib
required_ifField harus ada dan tidak kosong hanya ketika semua field lain yang ditentukan sama dengan nilai yang ditentukan
required_unlessField harus ada dan tidak kosong kecuali semua field lain yang ditentukan sama dengan nilai yang ditentukan
required_withField harus ada dan tidak kosong ketika salah satu field yang ditentukan ada
required_with_allField harus ada dan tidak kosong ketika semua field yang ditentukan ada
required_withoutField harus ada dan tidak kosong ketika salah satu field yang ditentukan tidak ada
required_without_allField harus ada dan tidak kosong ketika semua field yang ditentukan tidak ada
excluded_ifField dapat tidak ada atau kosong hanya ketika semua field lain yang ditentukan sama dengan nilai yang ditentukan
excluded_unlessField dapat tidak ada atau kosong kecuali semua field lain yang ditentukan sama dengan nilai yang ditentukan
excluded_withField dapat tidak ada atau kosong ketika salah satu field yang ditentukan ada
excluded_with_allField dapat tidak ada atau kosong ketika semua field yang ditentukan ada
excluded_withoutField dapat tidak ada atau kosong ketika salah satu field yang ditentukan tidak ada
excluded_without_allField dapat tidak ada atau kosong ketika semua field yang ditentukan tidak ada
uniqueValidasi setiap nilai arr map slice adalah unik

Alias

TagDeskripsi
iscolorhexcolor|rgb|rgba|hsl|hsla
country_codeiso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric

Operator

TagDeskripsiHex
,Operator AND menggunakan beberapa tag validasi harus memenuhi semua kondisi tidak ada spasi di antara koma0x2c
|Operator OR menggunakan beberapa tag validasi hanya perlu memenuhi salah satu0x7c
-Field ini melewati validasi0x2d
=Simbol pencocokan parameter0x3d

TIP

Saat mencocokkan operator dalam validasi field perlu mengganti dengan representasi heksadesimal utf8 misalnya

go
filed string `validate:"contains=0x2c"`

Penggunaan

Di bawah ini akan介绍 beberapa penggunaan dasar Validator beserta contoh kode.

Singleton

go
var validate *validator.Validate

Saat menggunakan官方 menyarankan hanya ada satu instance validator dalam seluruh siklus hidup program ini akan menguntungkan caching beberapa data.

Membuat Validator

Saat menggunakan Validator secara terpisah tanpa mengintegrasikan framework lain perlu membuat validator secara manual.

go
validate = validator.New()

Validasi Struct

go
func (v *Validate) Struct(s interface{}) error

Method Struct digunakan untuk memvalidasi semua field publik dari struct secara default akan melakukan validasi struct nested secara otomatis ketika mengirim nilai yang tidak valid atau nilai nil akan mengembalikan InvalidValidationError jika error validasi gagal maka mengembalikan ValidationErrors.

Contoh

go
package validate

import (
  "fmt"
  "github.com/go-playground/validator/v10"
  "testing"
)

type User struct {
  Name    string `validate:"contains=jack"` //nama berisi jack
  Age     int    `validate:"gte=18"`        //lebih besar atau sama dengan 18 tahun
  Address string `validate:"endswith=市"`    //berakhir dengan 市
}

func TestStruct(t *testing.T) {
  validate := validator.New()
  user := User{
    Name:    "jacklove",
    Age:     17,
    Address: "滔博市",
  }
  err := validate.Struct(user)
  for _, err := range err.(validator.ValidationErrors) {
    fmt.Println(err.Namespace()) //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()
  }
  fmt.Println(err)
}

Output

User.Age
Age
User.Age
Age
gte
gte
int
int
17
18

Key: 'User.Age' Error:Field validation for 'Age' failed on the 'gte' tag

Validasi Map

go
func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{}

Melalui tag map untuk validasi key-value pair.

Contoh

go
func TestMap(t *testing.T) {
   user := map[string]interface{}{
      "name":    "jak",
      "age":     17,
      "address": "滔博市",
   }
   rules := map[string]interface{}{
      "name":    "contains=jacklove",
      "age":     "gte=18",
      "address": "endswith=市",
   }

   validate := validator.New()

   validateMap := validate.ValidateMap(user, rules)
   fmt.Println(validateMap)
}

Output

map[age:Key: '' Error:Field validation for '' failed on the 'gte' tag name:Key: '' Error:Field validation for '' failed on the 'contains' tag]

Validasi Slice

Validasi slice string tag sebelum dive adalah untuk validasi slice tag setelah dive adalah untuk validasi nilai dalam slice slice nested juga prinsip yang sama berapa dimensi menggunakan berapa banyak dive

go
func TestSlice1(t *testing.T) {
  list := []string{"jack", "mike", "lisa", "golang"}
  err := validator.New().Var(list, "max=5,dive,contains=a,min=5") //panjang slice maksimum 5 elemen harus berisi karakter a dan panjang minimum 5
  fmt.Println(err)
}

Output

Key: '[0]' Error:Field validation for '[0]' failed on the 'min' tag
Key: '[1]' Error:Field validation for '[1]' failed on the 'contains' tag
Key: '[2]' Error:Field validation for '[2]' failed on the 'min' tag

Validasi struct untuk setiap user dalam slice

go
func TestSlice(t *testing.T) {
   userList := make([]User, 0)
   user := User{
      Name:    "jacklove",
      Age:     17,
      Address: "滔博市",
   }
   userList = append(userList, user)
   err := validator.New().Var(userList, "dive") //"dive" berarti validasi mendalam ketika elemen adalah struct akan otomatis melakukan validasi struct
   fmt.Println(err)
}

Output

Key: '[0].Age' Error:Field validation for 'Age' failed on the 'gte' tag

Validasi Variabel

Cukup sederhana dan mudah dipahami tidak akan dijelaskan terlalu banyak

Contoh 1

go
func TestVar(t *testing.T) {
   name := "jack"
   err := validator.New().Var(name, "max=5,contains=a,min=1,endswith=l") //panjang maksimum 5 panjang minimum 1 berisi huruf a berakhir dengan huruf l
   fmt.Println(err)
}

Output

Key: '' Error:Field validation for '' failed on the 'endswith' tag

Contoh 2

go
func TestVar1(t *testing.T) {
   age := 18
   err := validator.New().Var(age, "gte=19")
   fmt.Println(err)
}

Output

Key: '' Error:Field validation for '' failed on the 'gte' tag

TIP

Method Var dapat memvalidasi tipe yang mencakup struct variabel slice map harus dikombinasikan dengan tag dive secara wajar.

Validasi Field

Parameter validasi field bukan lagi tipe dasar tetapi nama field struct bisa field sendiri atau field struct nested.

go
type Password struct {
   FirstPassword  string `validate:"eqfield=SecondPassword"` //validasi apakah dua password yang dimasukkan sama
   SecondPassword string
}

type RegisterUser struct {
   Username string `validate:"necsfield=Password.FirstPassword"` //untuk keamanan saat registrasi dilarang password sama dengan username
   Password Password
}

func TestCrossStructFieldValidate(t *testing.T) {
   validate = validator.New()
   // gagal
   fmt.Println(validate.Struct(RegisterUser{
      Username: "gopher",
      Password: Password{
         FirstPassword:  "gopher",
         SecondPassword: "gophers",
      },
   }))
   // berhasil
   fmt.Println(validate.Struct(RegisterUser{
      Username: "gophers",
      Password: Password{
         FirstPassword:  "gopher",
         SecondPassword: "gopher",
      },
   }))
}

Output

Key: 'RegisterUser.Username' Error:Field validation for 'Username' failed on the 'necsfield' tag
Key: 'RegisterUser.Password.FirstPassword' Error:Field validation for 'FirstPassword' failed on the 'eqfield' tag
<nil>

WARNING

Saat menggunakan validasi field ketika field atau struct yang ditentukan sebagai parameter Tag tidak ada akan langsung dianggap sebagai validasi gagal misalnya:

go
type Password struct {
   FirstPassword  string `validate:"eqfield=SeconddPaswod"` // SeconddPaswod != SecondPassword
   SecondPassword string
}

Untuk kesalahan penulisan seperti ini sulit dideteksi dan saat validasi hanya akan ditampilkan sebagai tidak lolos validasi perlu sangat diperhatikan.

Lanjutan

Selanjutnya akan讲解 teknik penggunaan lanjutan dan lebih banyak operasi kustom.

Alias Kustom

Dalam beberapa situasi untuk satu field ada sangat banyak tag validasi ketika ingin menggunakannya kembali ke field lain mungkin akan langsung copy paste namun ini bukan solusi terbaik metode yang lebih baik adalah melalui registrasi alias untuk meningkatkan reusabilitas lihat contoh berikut:

go
var validate *validator.Validate

const PERSON_NAME_RULES = "max=10,min=1,contains=jack"

func TestAlias(t *testing.T) {
  validate = validator.New()
    // registrasi alias
  validate.RegisterAlias("namerules", PERSON_NAME_RULES)
  type person struct {
    FirstName string `validate:"namerules"` // menggunakan alias
    LastName  string `validate:"namerules"`
  }

  err := validate.Struct(person{
    FirstName: "",
    LastName:  "",
  })

  fmt.Println(err)
}

Output

go
Key: 'person.FirstName' Error:Field validation for 'FirstName' failed on the 'namerules' tag
Key: 'person.LastName' Error:Field validation for 'LastName' failed on the 'namerules' tag

Fungsi Validasi Kustom

Meskipun tag validasi yang disertakan dengan komponen sudah cukup untuk memenuhi kebutuhan dasar namun untuk beberapa kebutuhan khusus harus mendefinisikan logika sendiri Validator menyediakan API terkait untuk kustomisasi fungsi validasi. Lihat contoh berikut:

go
func TestCustomValidate(t *testing.T) {
   validate = validator.New()
   fmt.Println(validate.RegisterValidation("is666", is666))
   type Example struct {
      Name string `validate:"is666"`
   }
   fmt.Println(validate.Struct(Example{Name: "777"}))
   fmt.Println(validate.Struct(Example{Name: "666"}))
}

func is666(fl validator.FieldLevel) bool {
   return fl.Field().String() == "666"
}

Membuat fungsi untuk menentukan apakah nilai field sama dengan "666" dan Tag yang sesuai adalah is666 output sebagai berikut

go
<nil>
Key: 'Example.Name' Error:Field validation for 'Name' failed on the 'is666' tag

TIP

Jika Tag yang terdaftar sudah ada akan ditimpa oleh yang existing artinya dapat "menulis ulang" logika validasi Tag default.

Fungsi Validasi Tipe Kustom

Fungsi validasi tipe khusus untuk tipe tertentu biasanya digunakan untuk beberapa tipe non-dasar juga dapat menimpa validasi tipe dasar default lihat contoh berikut:

go
type Address struct {
  name string
}

func TestCustomTypeValidate(t *testing.T) {
  validate = validator.New()
  validate.RegisterCustomTypeFunc(ValidateAddress, Address{}) // registrasi fungsi validasi tipe dan tipe yang sesuai
  type Example struct {
    Address Address `validate:"required"`
  }
  fmt.Println(validate.Struct(Example{Address: Address{name: ""}}))
  fmt.Println(validate.Struct(Example{Address: Address{name: "cn"}}))
}

func ValidateAddress(value reflect.Value) interface{} {
  if address, ok := value.Interface().(Address); ok {
    //penanganan error
    if address.name == "" {
      return address.name
    }

    return value //mengembalikan field berarti validasi berhasil
  }
  return nil
}

Output

go
Key: 'Example.Address' Error:Field validation for 'Address' failed on the 'required' tag
<nil>

TIP

Mendaftarkan beberapa tipe ke satu fungsi juga prinsip yang sama

Fungsi Validasi Struct Kustom

Perbedaan fungsi validasi struct adalah parameter fungsi lainnya adalah field sedangkan parameter fungsi ini adalah struct lihat contoh berikut:

go
type People struct {
   FirstName string
   LastName  string
}

func TestCustomStructLevel(t *testing.T) {
   validate = validator.New()
   validate.RegisterStructValidation(PeopleValidate, People{}) //sama seperti registrasi tipe dapat mengirim lebih dari satu struct
   err := validate.Struct(People{
      FirstName: "",
      LastName:  "",
   })
   fmt.Println(err)
}

func PeopleValidate(sl validator.StructLevel) {
   people := sl.Current().Interface().(People)

   if people.FirstName == "" || people.LastName == "" {
      sl.ReportError(people.FirstName, "FirstName", "FirstName", "", "")
      sl.ReportError(people.FirstName, "LastName", "LastName", "", "")
   }
}

Output

Key: 'People.FirstName' Error:Field validation for 'FirstName' failed on the '' tag
Key: 'People.LastName' Error:Field validation for 'LastName' failed on the '' tag

Multi-bahasa

Komponen translator

go get github.com/go-playground/universal-translator

Komponen region

go get github.com/go-playground/locales

Bahasa default validator adalah Inggris sedangkan dalam pengembangan program mungkin menggunakan lebih dari satu bahasa saat ini perlu menggunakan komponen internasionalisasi multi-bahasa lihat contoh berikut:

go
import (
  "fmt"
  "github.com/go-playground/locales/zh"
  ut "github.com/go-playground/universal-translator"
  "github.com/go-playground/validator/v10"
  zh_trans "github.com/go-playground/validator/v10/translations/zh"
  "reflect"
  "testing"
)

type User struct {
   Name    string `validate:"contains=jack"` //nama berisi jack
   Age     int    `validate:"gte=18"`        //lebih besar atau sama dengan 18 tahun
   Address string `validate:"endswith=市"`    //berakhir dengan 市
}

var (
   uni      *ut.UniversalTranslator
   validate *validator.Validate
)

func TestTranslate(t *testing.T) {
   zh := zh.New()
   //yang pertama adalah cadangan yang berikutnya adalah bahasa yang didukung bisa lebih dari satu
   uni = ut.New(zh, zh)
   //bahasa di sini biasanya dapat diperoleh dari Accept-Language di header request http untuk mendapatkan bahasa yang sesuai
   trans, found := uni.GetTranslator(zh.Locale())
   validate = validator.New()
   if found {
      zh_trans.RegisterDefaultTranslations(validate, trans) //registrasi translator default
   }
   err := validate.Struct(User{
      Name:    "",
      Age:     0,
      Address: "",
   })
   fmt.Println(err.(validator.ValidationErrors).Translate(trans))
}

Output

map[User.Address:Address harus berakhir dengan teks '市' User.Age:Age harus lebih besar atau sama dengan 18 User.Name:Name harus berisi teks 'jack']

Juga dapat menerjemahkan setiap error secara terpisah

go
for _, fieldError := range err.(validator.ValidationErrors) {
   fmt.Println(fieldError.Translate(trans))
}

Output

Name harus berisi teks 'jack'
Age harus lebih besar atau sama dengan 18
Address harus berakhir dengan teks '市'

Dapat dilihat nilai return adalah map dapat melihat informasi error dasar sudah diterjemahkan namun belum cukup untuk digunakan perlu mempercantik informasi error agar lebih baik untuk对接 dengan customer atau frontend.

go
type User struct {

   Name    string `validate:"contains=jack" label:"姓名"` //nama berisi jack
   Age     int    `validate:"gte=18" label:"年龄"`        //lebih besar atau sama dengan 18 tahun
   Address string `validate:"endswith=市" label:"地址"`    //berakhir dengan 市
   Sex     string `validate:"required" label:"性别"`
}

Pertama kustom tag label nilainya adalah nama Mandarin field kemudian melalui validator registrasi TagNameFunc fungsinya adalah saat mendapatkan nama field atau mengganti nama asli. Pada komentar method Filed() string di file errors.go mengatakan "Nama field dengan label name lebih diprioritaskan daripada nama field aktual" jadi saat terjadi error dapat menggunakan nama Mandarin kustom untuk menggantikan kata Inggris. TagNameFunc sebagai berikut:

go
// Kami menambahkan tag kustom tag ini digunakan untuk nama Mandarin field struct akan menggantikan nama field asli
func CustomTagNameFunc(field reflect.StructField) string {
   label := field.Tag.Get("label")
   if len(label) == 0 {
      return field.Name
   }
   return label
}

Terakhir registrasi

go
validate.RegisterTagNameFunc(CustomTagNameFunc)

Lakukan output lagi

姓名 harus berisi teks 'jack'
年龄 harus lebih besar atau sama dengan 18
地址 harus berakhir dengan teks '市'

Namun ini masih belum cukup untuk dikembalikan sebagai informasi error ke frontend perlu memformat informasi ke json atau format apa pun yang cocok untuk transmisi pesan mungkin berpikir untuk langsung serialisasi map ke json ini adalah solusi namun mungkin mendapatkan hasil berikut:

json
{
  "User.地址": "地址 harus berakhir dengan teks '市'",
  "User.姓名": "姓名 harus berisi teks 'back'",
  "User.年龄": "年龄 harus lebih besar atau sama dengan 18",
  "User.性别": "性别 adalah field wajib"
}

Dengan memproses key map得到 hasil berikut:

json
{
  "地址": "地址 harus berakhir dengan teks '市'",
  "姓名": "姓名 harus berisi teks 'back'",
  "年龄": "年龄 harus lebih besar atau sama dengan 18",
  "性别": "性别 adalah field wajib"
}

Namun tidak menyarankan mengembalikan informasi seperti di atas ke frontend dapat memprosesnya menjadi string sebagai informasi yang dikembalikan

姓名 harus berisi teks 'back', 年龄 harus lebih besar atau sama dengan 18, 地址 harus berakhir dengan teks '市', 性别 adalah field wajib,

Kode Lengkap

go
import (
   "fmt"
   "github.com/go-playground/locales/zh"
   ut "github.com/go-playground/universal-translator"
   "github.com/go-playground/validator/v10"
   zh_trans "github.com/go-playground/validator/v10/translations/zh"
   "reflect"
   "strings"
   "testing"
)

type User struct {
   Name    string `validate:"contains=back" label:"姓名"` //nama berisi jack
   Age     int    `validate:"gte=18" label:"年龄"`        //lebih besar atau sama dengan 18 tahun
   Address string `validate:"endswith=市" label:"地址"`    //berakhir dengan 市
   Sex     string `validate:"required" label:"性别"`
}

var (
   uni      *ut.UniversalTranslator
   validate *validator.Validate
)

// Kami menambahkan tag kustom tag ini digunakan untuk nama Mandarin field struct akan menggantikan nama field asli
func CustomTagNameFunc(field reflect.StructField) string {
   label := field.Tag.Get("label")
   if len(label) == 0 {
      return field.Name
   }
   return label
}

func TestTranslate(t *testing.T) {
   zh := zh.New()
   uni = ut.New(zh, zh)
   //bahasa di sini biasanya dapat diperoleh dari Accept-Language di header request http untuk mendapatkan bahasa yang sesuai
   trans, found := uni.GetTranslator(zh.Locale())
   validate = validator.New()
   if found {
      zh_trans.RegisterDefaultTranslations(validate, trans) //registrasi translator default
   }
   validate.RegisterTagNameFunc(CustomTagNameFunc)
   err := validate.Struct(User{
      Name:    "",
      Age:     0,
      Address: "",
   })
   translate := errInfoFormat(err.(validator.ValidationErrors), trans)
   fmt.Println(translate)
}

func errInfoFormat(errors validator.ValidationErrors, trans ut.Translator) string {
   builder := strings.Builder{}
   for _, err := range errors {
      builder.WriteString(err.Translate(trans))
      builder.WriteString(", ")
   }
   return builder.String()
}

Terakhir jika merasa informasi error terlalu dingin dan ingin lebih manusiawi dapat menulis ulang informasi error untuk tag tertentu ini memerlukan method RegisterTranslation juga memerlukan dua jenis fungsi yaitu RegisterTranslationsFunc yang bertanggung jawab untuk registrasi template terjemahan untuk Tag yang sesuai dan TranslationFunc yang bertanggung jawab untuk memproses template得到 konten terjemahan akhir. Di sini menggunakan required sebagai contoh:

go
func requiredOverrideRegister(ut ut.Translator) error { //fungsi ini berfungsi untuk registrasi template terjemahan
  return ut.Add("required", "{} adalah field yang harus diisi", true) // {} adalah placeholder true berarti menimpa template yang sudah ada
}

func requiredOverrideTranslation(ut ut.Translator, fe validator.FieldError) string { // fungsi ini berfungsi untuk menerjemahkan konten
  t, _ := ut.T("required", fe.Field()) //parameter bisa lebih dari satu tergantung berapa banyak placeholder di template Tag yang sesuai
  return t
}

Terakhir registrasi

go
validate.RegisterTranslation("required", trans, requiredOverrideRegister, requiredOverrideTranslation)

Hasil

姓名 harus berisi teks 'back', 年龄 harus lebih besar atau sama dengan 18, 地址 harus berakhir dengan teks '市', 性别 adalah field yang harus diisi,

File Bahasa

Faktanya menulis kode registrasi satu per satu sangat merepotkan universal-translator menyediakan cara melalui编写 file konfigurasi JSON untuk terjemahan: universal-translator/examples/full-with-files at master · go-playground/universal-translator (github.com)

go
func TestFilei18n(t *testing.T) {
   validate = validator.New()
   zh := zh.New()
   universalTranslator := ut.New(zh, zh)
   translator, _ := universalTranslator.GetTranslator(zh.Locale())
   zh_trans.RegisterDefaultTranslations(validate, translator)
   er := universalTranslator.Import(ut.FormatJSON, "./zh.json") //disarankan untuk import setelah registrasi agar dapat menimpa Tag yang sudah ada
   if er != nil {
      log.Fatal(er)
   }
   type Gopher struct {
      Language string `validate:"required"`
   }

   err := validate.Struct(Gopher{
      "",
   })
   fmt.Println(err.(validator.ValidationErrors).Translate(translator))
}

File JSON

json
[
  {
    "locale": "zh",
    "key": "required",
    "trans": "Ini adalah field yang sangat penting{0} Anda harus mengisinya",
    "override": true
  }
]

Output

map[Gopher.Language:Ini adalah field yang sangat pentingLanguage Anda harus mengisinya]

TIP

universal-translator memiliki banyak jebakan saat digunakan jika ingin menimpa Tag yang sudah ada type dan rule bisa tidak diisi karena konfigurasi yang sudah ada juga tidak diisi sebaiknya保持一致. Mengisi type apa akan menambahkan konfigurasi ke map yang sesuai jika Cardinal atau type lainnya dan rule dikonfigurasi dengan one dan lainnya maka perlu konfigurasi lokal yang sesuai agar dapat digunakan dengan normal jika tidak akan error.

Golang by www.golangdev.cn edit