Validator Validasi Library
Alamat Dokumentasi: validator/README.md at master · go-playground/validator (github.com)
Contoh Resmi: validator/_examples at master · go-playground/validator (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
go get github.com/go-playground/validator/v10Import
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
type User {
age int `validate:"gte=18"` //menunjukkan lebih besar atau sama dengan 18 tahun
}Juga dapat mengubah tag default melalui method setTagName.
Field
| Tag | Deskripsi |
|---|---|
eqcsfield | Dalam struktur terpisah validasi nilai field saat ini sama dengan field yang ditentukan oleh nilai param |
eqfield | Validasi nilai field saat ini sama dengan field yang ditentukan oleh nilai parameter |
fieldcontains | Validasi nilai field saat ini berisi field yang ditentukan oleh nilai parameter |
fieldexcludes | Validasi nilai field saat ini tidak berisi field yang ditentukan oleh nilai parameter |
gtcsfield | Dalam struktur terpisah validasi nilai field saat ini lebih besar dari field yang ditentukan oleh nilai parameter |
gtecsfield | Dalam struktur terpisah validasi nilai field saat ini lebih besar atau sama dengan field yang ditentukan oleh nilai parameter |
gtefield | Validasi nilai field saat ini lebih besar atau sama dengan field yang ditentukan oleh nilai parameter |
gtfield | Validasi nilai field saat ini lebih besar dari field yang ditentukan oleh nilai parameter |
ltcsfield | Dalam struktur terpisah validasi nilai field saat ini lebih kecil dari field yang ditentukan oleh nilai parameter |
ltecsfield | Dalam struktur terpisah validasi nilai field saat ini lebih kecil atau sama dengan field yang ditentukan oleh nilai parameter |
ltefield | Validasi nilai field saat ini lebih kecil atau sama dengan field yang ditentukan oleh nilai parameter |
ltfield | Validasi nilai field saat ini lebih kecil dari field yang ditentukan oleh nilai parameter |
necsfield | Validasi nilai field saat ini tidak sama dengan field dalam struktur terpisah yang ditentukan oleh nilai parameter |
nefield | Validasi nilai field saat ini tidak sama dengan field yang ditentukan oleh nilai parameter |
Network
| Tag | Deskripsi |
|---|---|
cidr | Classless Inter-Domain Routing CIDR |
cidrv4 | Classless Inter-Domain Routing CIDRv4 |
cidrv6 | Classless Inter-Domain Routing CIDRv6 |
datauri | Data Uniform Resource Locator |
fqdn | Fully Qualified Domain Name (FQDN) |
hostname | Hostname RFC 952 |
hostname_port | Validasi field kombinasi <dns>:<port> yang umum digunakan untuk alamat socket |
hostname_rfc1123 | Hostname RFC 952 |
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 juga dikenal sebagai alamat LAN |
tcp4_addr | Transmission Control Protocol Address TCP4 |
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 Endpoint Address |
uri | Uniform Resource Identifier |
url | Uniform Resource Locator |
url_encoded | Uniform Resource Identifier Encoding |
urn_rfc2141 | RFC 2141 Uniform Resource Name |
String
| Tag | Deskripsi |
|---|---|
alpha | Validasi nilai field saat ini adalah huruf yang valid |
alphanum | Validasi nilai field saat ini adalah alfanumerik yang valid |
alphanumunicode | Validasi nilai field saat ini adalah nilai unicode alfanumerik yang valid |
alphaunicode | Validasi nilai field saat ini adalah nilai unicode huruf yang valid |
ascii | Validasi nilai field adalah karakter ASCII yang valid |
boolean | Validasi nilai field saat ini adalah nilai boolean yang valid atau dapat dikonversi dengan aman ke boolean |
contains | Validasi nilai field berisi teks yang ditentukan dalam parameter |
containsany | Validasi nilai field berisi karakter apa pun yang ditentukan dalam parameter |
containsrune | Validasi nilai field berisi rune yang ditentukan dalam parameter |
endsnotwith | Validasi nilai field tidak diakhiri dengan teks yang ditentukan dalam parameter |
endswith | Validasi nilai field diakhiri dengan teks yang ditentukan dalam parameter |
excludes | Validasi nilai field tidak berisi teks yang ditentukan dalam parameter |
excludesall | Validasi nilai field tidak berisi karakter apa pun yang ditentukan dalam parameter |
excludesrune | Validasi nilai field tidak berisi karakter yang ditentukan dalam parameter |
lowercase | Validasi nilai field saat ini adalah string huruf kecil |
multibyte | Validasi nilai field memiliki karakter multi-byte |
number | Validasi nilai field saat ini adalah angka yang valid |
numeric | Validasi nilai field saat ini adalah nilai numerik yang valid |
printascii | Validasi nilai field adalah karakter ASCII yang dapat dicetak yang valid |
startsnotwith | Validasi nilai field tidak dimulai dengan teks yang ditentukan dalam parameter |
startswith | Validasi nilai field dimulai dengan teks yang ditentukan dalam parameter |
uppercase | Validasi nilai field saat ini adalah string huruf besar |
Format
| Tag | Deskripsi |
|---|---|
base64 | String Base64 |
base64url | String Base64URL |
bic | Validasi nilai field saat ini adalah kode BIC yang valid (SWIFT code) seperti yang didefinisikan dalam ISO 9362 |
bcp47_language_tag | Validasi nilai field saat ini adalah tag bahasa spesifikasi BCP47 |
btc_addr | Validasi nilai field adalah alamat BTC yang valid |
btc_addr_bech32 | Validasi nilai field adalah alamat bech32 BTC yang valid |
credit_card | Validasi nilai field saat ini adalah nomor kartu kredit yang valid |
datetime | Validasi nilai field saat ini adalah string tanggal waktu yang valid |
e164 | Validasi nilai field saat ini adalah nomor telepon format e.164 yang valid |
email | Validasi nilai field saat ini adalah alamat email yang valid |
eth_addr | Validasi nilai field adalah alamat Ethereum yang valid |
hexadecimal | Validasi nilai field saat ini adalah heksadesimal yang valid |
hexcolor | Validasi nilai field saat ini adalah warna heksadesimal yang valid |
hsl | Validasi nilai field saat ini adalah warna HSL yang valid |
hsla | Validasi nilai field saat ini adalah warna HSLA yang valid |
html | Validasi nilai field saat ini adalah HTML yang valid |
html_encoded | Validasi nilai field saat ini adalah encoding HTML yang valid |
isbn | Validasi nilai field adalah ISBN v10 atau v13 yang valid (International Standard Book Number) |
isbn10 | Validasi nilai field adalah ISBN v10 yang valid (International Standard Book Number) |
isbn13 | Validasi nilai field adalah ISBN v13 yang valid (International Standard Book Number) |
iso3166_1_alpha2 | Validasi nilai field saat ini adalah kode negara iso3166-1 alpha-2 yang valid |
iso3166_1_alpha3 | Validasi nilai field saat ini adalah kode negara iso3166-1 alpha-3 yang valid |
iso3166_1_alpha_numeric | Validasi nilai field saat ini adalah kode negara alfanumerik iso3166-1 yang valid |
iso3166_2 | Validasi nilai field saat ini adalah kode wilayah negara yang valid (ISO 3166-2) |
iso4217 | Validasi nilai field saat ini adalah kode mata uang yang valid (ISO 4217) |
json | Validasi nilai field saat ini adalah string json yang valid |
jwt | Validasi nilai field saat ini adalah string JWT yang valid |
latitude | Validasi nilai field adalah koordinat lintang yang valid |
longitude | Validasi nilai field adalah koordinat bujur yang valid |
postcode_iso3166_alpha2 | Validasi berdasarkan nilai kode negara dalam iso 3166 alpha 2 |
postcode_iso3166_alpha2_field | Validasi melalui field yang menunjukkan nilai kode negara dalam iso 3166 alpha 2 |
rgb | Validasi nilai field saat ini adalah warna RGB yang valid |
rgba | Validasi nilai field saat ini adalah warna RGBA yang valid |
ssn | Validasi nilai field adalah SSN yang valid |
timezone | Validasi nilai field saat ini adalah string zona waktu yang valid |
uuid | Validasi nilai field adalah UUID yang valid versi apa pun |
uuid3 | Validasi nilai field adalah UUID v3 yang valid |
uuid3_rfc4122 | Validasi nilai field adalah UUID v3 RFC4122 yang valid |
uuid4 | Validasi nilai field adalah UUID v4 yang valid |
uuid4_rfc4122 | Validasi nilai field adalah UUID v4 RFC4122 yang valid |
uuid5 | Validasi nilai field adalah UUID v5 yang valid |
uuid5_rfc4122 | Validasi nilai field adalah UUID v5 RFC4122 yang valid |
uuid_rfc4122 | Validasi nilai field adalah UUID RFC4122 yang valid versi apa pun |
md4 | Validasi nilai field adalah MD4 yang valid |
md5 | Validasi nilai field adalah MD5 yang valid |
sha256 | Validasi nilai field adalah SHA256 yang valid |
sha384 | Validasi nilai field adalah SHA384 yang valid |
sha512 | Validasi nilai field adalah SHA512 yang valid |
ripemd128 | Validasi nilai field adalah RIPEMD128 yang valid |
ripemd160 | Validasi nilai field adalah RIPEMD160 yang valid |
tiger128 | Validasi nilai field adalah TIGER128 yang valid |
tiger160 | Validasi nilai field adalah TIGER160 yang valid |
tiger192 | Validasi nilai field adalah TIGER192 yang valid |
semver | Validasi nilai field saat ini adalah versi semver yang valid seperti yang didefinisikan dalam Semantic Versioning 2.0.0 |
ulid | Validasi nilai field adalah ULID yang valid |
Perbandingan
| Tag | Deskripsi |
|---|---|
eq | Sama dengan |
gt | Lebih besar dari |
gte | Lebih besar atau sama dengan |
lt | Lebih kecil dari |
lte | Lebih kecil atau sama dengan |
ne | Tidak sama dengan |
Lainnya
| Tag | Deskripsi |
|---|---|
dir | Direktori file |
file | Path file |
isdefault | Validasi nilai field saat ini adalah nilai statis default |
len | Panjang field |
max | Nilai maksimum |
min | Nilai minimum |
oneof | Apakah salah satu dari nilai yang terdaftar |
omitempty | Jika field tidak diatur abaikan field ini |
required | Nilai wajib |
required_if | Field harus ada dan tidak kosong hanya ketika semua field lain yang ditentukan sama dengan nilai yang ditentukan |
required_unless | Field harus ada dan tidak kosong kecuali semua field lain yang ditentukan sama dengan nilai yang ditentukan |
required_with | Field harus ada dan tidak kosong ketika salah satu field yang ditentukan ada |
required_with_all | Field harus ada dan tidak kosong ketika semua field yang ditentukan ada |
required_without | Field harus ada dan tidak kosong ketika salah satu field yang ditentukan tidak ada |
required_without_all | Field harus ada dan tidak kosong ketika semua field yang ditentukan tidak ada |
excluded_if | Field dapat tidak ada atau kosong hanya ketika semua field lain yang ditentukan sama dengan nilai yang ditentukan |
excluded_unless | Field dapat tidak ada atau kosong kecuali semua field lain yang ditentukan sama dengan nilai yang ditentukan |
excluded_with | Field dapat tidak ada atau kosong ketika salah satu field yang ditentukan ada |
excluded_with_all | Field dapat tidak ada atau kosong ketika semua field yang ditentukan ada |
excluded_without | Field dapat tidak ada atau kosong ketika salah satu field yang ditentukan tidak ada |
excluded_without_all | Field dapat tidak ada atau kosong ketika semua field yang ditentukan tidak ada |
unique | Validasi setiap nilai arr map slice adalah unik |
Alias
| Tag | Deskripsi |
|---|---|
iscolor | hexcolor|rgb|rgba|hsl|hsla |
country_code | iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric |
Operator
| Tag | Deskripsi | Hex |
|---|---|---|
, | Operator AND menggunakan beberapa tag validasi harus memenuhi semua kondisi tidak ada spasi di antara koma | 0x2c |
| | Operator OR menggunakan beberapa tag validasi hanya perlu memenuhi salah satu | 0x7c |
- | Field ini melewati validasi | 0x2d |
= | Simbol pencocokan parameter | 0x3d |
TIP
Saat mencocokkan operator dalam validasi field perlu mengganti dengan representasi heksadesimal utf8 misalnya
filed string `validate:"contains=0x2c"`Penggunaan
Di bawah ini akan介绍 beberapa penggunaan dasar Validator beserta contoh kode.
Singleton
var validate *validator.ValidateSaat 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.
validate = validator.New()Validasi Struct
func (v *Validate) Struct(s interface{}) errorMethod 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
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' tagValidasi Map
func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{}Melalui tag map untuk validasi key-value pair.
Contoh
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
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' tagValidasi struct untuk setiap user dalam slice
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' tagValidasi Variabel
Cukup sederhana dan mudah dipahami tidak akan dijelaskan terlalu banyak
Contoh 1
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' tagContoh 2
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' tagTIP
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.
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:
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:
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
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' tagFungsi 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:
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
<nil>
Key: 'Example.Name' Error:Field validation for 'Name' failed on the 'is666' tagTIP
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:
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
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:
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 '' tagMulti-bahasa
Komponen translator
go get github.com/go-playground/universal-translatorKomponen region
go get github.com/go-playground/localesBahasa 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:
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
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.
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:
// 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
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:
{
"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:
{
"地址": "地址 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
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:
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
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)
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
[
{
"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.
