Skip to content

Commit add373f

Browse files
authored
feat: add bcp 47 language tag validator (#730)
1 parent 6824818 commit add373f

File tree

5 files changed

+71
-1
lines changed

5 files changed

+71
-1
lines changed

baked_in.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"unicode/utf8"
1919

2020
"golang.org/x/crypto/sha3"
21+
"golang.org/x/text/language"
2122

2223
urn "github.com/leodido/go-urn"
2324
)
@@ -188,6 +189,7 @@ var (
188189
"iso3166_1_alpha2": isIso3166Alpha2,
189190
"iso3166_1_alpha3": isIso3166Alpha3,
190191
"iso3166_1_alpha_numeric": isIso3166AlphaNumeric,
192+
"bcp47_language_tag": isBCP47LanguageTag,
191193
}
192194
)
193195

@@ -2286,3 +2288,15 @@ func isIso3166AlphaNumeric(fl FieldLevel) bool {
22862288
}
22872289
return iso3166_1_alpha_numeric[code]
22882290
}
2291+
2292+
// isBCP47LanguageTag is the validation function for validating if the current field's value is a valid BCP 47 language tag, as parsed by language.Parse
2293+
func isBCP47LanguageTag(fl FieldLevel) bool {
2294+
field := fl.Field()
2295+
2296+
if field.Kind() == reflect.String {
2297+
_, err := language.Parse(field.String())
2298+
return err == nil
2299+
}
2300+
2301+
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2302+
}

doc.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1221,14 +1221,21 @@ see: https://www.iso.org/iso-3166-country-codes.html
12211221
12221222
Usage: iso3166_1_alpha3
12231223
1224+
BCP 47 Language Tag
1225+
1226+
This validates that a string value is a valid BCP 47 language tag, as parsed by language.Parse.
1227+
More information on https://pkg.go.dev/golang.org/x/text/language
1228+
1229+
Usage: bcp47_language_tag
1230+
12241231
TimeZone
12251232
12261233
This validates that a string value is a valid time zone based on the time zone database present on the system.
12271234
Although empty value and Local value are allowed by time.LoadLocation golang function, they are not allowed by this validator.
12281235
More information on https://golang.org/pkg/time/#LoadLocation
12291236
12301237
Usage: timezone
1231-
1238+
12321239
12331240
Alias Validators and Tags
12341241

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ require (
88
github.com/go-playground/universal-translator v0.17.0
99
github.com/leodido/go-urn v1.2.0
1010
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
11+
golang.org/x/text v0.3.2 // indirect
1112
)

go.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,22 @@ github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
1010
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
1111
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1212
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
13+
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
1314
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1415
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
1516
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
1617
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
1718
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
1819
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
20+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
1921
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
2022
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
23+
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
2124
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
2225
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
26+
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
2327
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
28+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
2429
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
2530
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2631
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

validator_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11158,3 +11158,46 @@ func TestDurationType(t *testing.T) {
1115811158
})
1115911159
}
1116011160
}
11161+
11162+
func TestBCP47LanguageTagValidation(t *testing.T) {
11163+
tests := []struct {
11164+
value string `validate:"bcp47_language_tag"`
11165+
tag string
11166+
expected bool
11167+
}{
11168+
{"en-US", "bcp47_language_tag", true},
11169+
{"en_GB", "bcp47_language_tag", true},
11170+
{"es", "bcp47_language_tag", true},
11171+
{"English", "bcp47_language_tag", false},
11172+
{"ESES", "bcp47_language_tag", false},
11173+
{"az-Cyrl-AZ", "bcp47_language_tag", true},
11174+
{"en-029", "bcp47_language_tag", true},
11175+
{"xog", "bcp47_language_tag", true},
11176+
}
11177+
11178+
validate := New()
11179+
11180+
for i, test := range tests {
11181+
11182+
errs := validate.Var(test.value, test.tag)
11183+
11184+
if test.expected {
11185+
if !IsEqual(errs, nil) {
11186+
t.Fatalf("Index: %d locale failed Error: %s", i, errs)
11187+
}
11188+
} else {
11189+
if IsEqual(errs, nil) {
11190+
t.Fatalf("Index: %d locale failed Error: %s", i, errs)
11191+
} else {
11192+
val := getError(errs, "", "")
11193+
if val.Tag() != "bcp47_language_tag" {
11194+
t.Fatalf("Index: %d locale failed Error: %s", i, errs)
11195+
}
11196+
}
11197+
}
11198+
}
11199+
11200+
PanicMatches(t, func() {
11201+
_ = validate.Var(2, "bcp47_language_tag")
11202+
}, "Bad field type int")
11203+
}

0 commit comments

Comments
 (0)