Repository: go-playground/validator
Pull request #826 (2021-09-06 13:08:28)

Added ULID validation
## Enhances

This PR adds format validation for [ULID](https://github.com/ulid/spec) strings.

**Make sure that you've checked the boxes below before you submit PR:**
- [x] Tests exist or have been written that cover this particular change.

@go-playground/validator-maintainers

coveralls: 
[![Coverage Status](https://coveralls.io/builds/42676643/badge)](https://coveralls.io/builds/42676643)

Coverage increased (+0.006%) to 75.106% when pulling **25a96eeca5066c3aa1c86b524bba56df6225fb53 on uberswe:ulid** into **ce34f361cca51e25715399ab801c5e1c01d02d89 on go-playground:master**.


Patch:
diff --git a/README.md b/README.md
index f56cff1..c5bb81d 100644
--- a/README.md
+++ b/README.md
@@ -189,6 +189,7 @@ Baked-in Validations
 | uuid5 | Universally Unique Identifier UUID v5 |
 | uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 |
 | uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 |
+| ulid | Universally Unique Lexicographically Sortable Identifier ULID |
 
 ### Comparisons:
 | Tag | Description |
diff --git a/baked_in.go b/baked_in.go
index d80ec48..4b82abb 100644
--- a/baked_in.go
+++ b/baked_in.go
@@ -148,6 +148,7 @@ var (
 		"uuid3_rfc4122":                 isUUID3RFC4122,
 		"uuid4_rfc4122":                 isUUID4RFC4122,
 		"uuid5_rfc4122":                 isUUID5RFC4122,
+		"ulid":                          isULID,
 		"ascii":                         isASCII,
 		"printascii":                    isPrintableASCII,
 		"multibyte":                     hasMultiByteCharacter,
@@ -499,6 +500,11 @@ func isUUIDRFC4122(fl FieldLevel) bool {
 	return uUIDRFC4122Regex.MatchString(fl.Field().String())
 }
 
+// isULID is the validation function for validating if the field's value is a valid ULID.
+func isULID(fl FieldLevel) bool {
+	return uLIDRegex.MatchString(fl.Field().String())
+}
+
 // isISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN.
 func isISBN(fl FieldLevel) bool {
 	return isISBN10(fl) || isISBN13(fl)
diff --git a/doc.go b/doc.go
index a5d394a..1df38e7 100644
--- a/doc.go
+++ b/doc.go
@@ -1007,6 +1007,12 @@ This validates that a string value contains a valid version 5 UUID.  Uppercase U
 
 	Usage: uuid5
 
+Universally Unique Lexicographically Sortable Identifier ULID
+
+This validates that a string value contains a valid ULID value.
+
+	Usage: ulid
+
 ASCII
 
 This validates that a string value contains only ASCII characters.
diff --git a/regexes.go b/regexes.go
index 19e7c29..58d8766 100644
--- a/regexes.go
+++ b/regexes.go
@@ -29,6 +29,7 @@ const (
 	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}$"
+	uLIDRegexString                  = "^[A-HJKMNP-TV-Z0-9]{26}$"
 	aSCIIRegexString                 = "^[\x00-\x7F]*$"
 	printableASCIIRegexString        = "^[\x20-\x7E]*$"
 	multibyteRegexString             = "[^\x00-\x7F]"
@@ -81,6 +82,7 @@ var (
 	uUID4RFC4122Regex          = regexp.MustCompile(uUID4RFC4122RegexString)
 	uUID5RFC4122Regex          = regexp.MustCompile(uUID5RFC4122RegexString)
 	uUIDRFC4122Regex           = regexp.MustCompile(uUIDRFC4122RegexString)
+	uLIDRegex                  = regexp.MustCompile(uLIDRegexString)
 	aSCIIRegex                 = regexp.MustCompile(aSCIIRegexString)
 	printableASCIIRegex        = regexp.MustCompile(printableASCIIRegexString)
 	multibyteRegex             = regexp.MustCompile(multibyteRegexString)
diff --git a/translations/en/en.go b/translations/en/en.go
index b95f7dd..5ed76ae 100644
--- a/translations/en/en.go
+++ b/translations/en/en.go
@@ -1136,6 +1136,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} must be a valid version 5 UUID",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0} must be a valid ULID",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0} must contain only ascii characters",
diff --git a/translations/es/es.go b/translations/es/es.go
index 2b87f10..2635408 100644
--- a/translations/es/es.go
+++ b/translations/es/es.go
@@ -1178,6 +1178,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} debe ser una versión válida 5 UUID",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0} debe ser un ULID válido",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0} debe contener sólo caracteres ascii",
diff --git a/translations/fa/fa.go b/translations/fa/fa.go
index fc21aa0..b7c100b 100644
--- a/translations/fa/fa.go
+++ b/translations/fa/fa.go
@@ -1136,6 +1136,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} باید یک UUID نسخه 5 معتبر باشد",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0} باید یک ULID معتبر باشد",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0} باید فقط شامل کاراکترهای اسکی باشد",
diff --git a/translations/fr/fr.go b/translations/fr/fr.go
index ddebdf9..c455195 100644
--- a/translations/fr/fr.go
+++ b/translations/fr/fr.go
@@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} doit être un UUID version 5 valid",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0} doit être une ULID valide",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0} ne doit contenir que des caractères ascii",
diff --git a/translations/id/id.go b/translations/id/id.go
index d185dfc..08b6ad5 100644
--- a/translations/id/id.go
+++ b/translations/id/id.go
@@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} harus berupa UUID versi 5 yang valid",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0} harus berupa ULID yang valid",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0} hanya boleh berisi karakter ascii",
diff --git a/translations/ja/ja.go b/translations/ja/ja.go
index 7b887c0..4502e2d 100644
--- a/translations/ja/ja.go
+++ b/translations/ja/ja.go
@@ -1229,6 +1229,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0}はバージョンが5の正しいUUIDでなければなりません",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0}は正しいULIDでなければなりません",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0}はASCII文字のみを含まなければなりません",
diff --git a/translations/nl/nl.go b/translations/nl/nl.go
index a64815d..ca7c554 100644
--- a/translations/nl/nl.go
+++ b/translations/nl/nl.go
@@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} moet een geldige versie 5 UUID zijn",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0} moet een geldige ULID zijn",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0} mag alleen ascii karakters bevatten",
diff --git a/translations/pt/pt.go b/translations/pt/pt.go
index 9dbaf5f..4748331 100644
--- a/translations/pt/pt.go
+++ b/translations/pt/pt.go
@@ -1178,6 +1178,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} deve ser um UUID versão 5 válido",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0} deve ser um ULID válido",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0} deve conter apenas caracteres ascii",
diff --git a/translations/pt_BR/pt_BR.go b/translations/pt_BR/pt_BR.go
index df4fc67..ef04496 100644
--- a/translations/pt_BR/pt_BR.go
+++ b/translations/pt_BR/pt_BR.go
@@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} deve ser um UUID versão 5 válido",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0} deve ser uma ULID válida",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0} deve conter apenas caracteres ascii",
diff --git a/translations/ru/ru.go b/translations/ru/ru.go
index 7a12bd8..25400c5 100644
--- a/translations/ru/ru.go
+++ b/translations/ru/ru.go
@@ -1291,6 +1291,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} должен быть UUID 5 версии",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0} должен быть ULID",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0} должен содержать только ascii символы",
diff --git a/translations/tr/tr.go b/translations/tr/tr.go
index 2709e4b..2e88a20 100644
--- a/translations/tr/tr.go
+++ b/translations/tr/tr.go
@@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} geçerli bir sürüm 5 UUID olmalıdır",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0} geçerli bir ULID olmalıdır",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0} yalnızca ascii karakterler içermelidir",
diff --git a/translations/zh/zh.go b/translations/zh/zh.go
index 5fff6b4..3dc7356 100644
--- a/translations/zh/zh.go
+++ b/translations/zh/zh.go
@@ -1225,6 +1225,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0}必须是一个有效的V5 UUID",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0}必须是一个有效的ULID",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0}必须只包含ascii字符",
diff --git a/translations/zh_tw/zh_tw.go b/translations/zh_tw/zh_tw.go
index 391a010..c39a8d7 100644
--- a/translations/zh_tw/zh_tw.go
+++ b/translations/zh_tw/zh_tw.go
@@ -1166,6 +1166,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0}必須是一個有效的V5 UUID",
 			override:    false,
 		},
+		{
+			tag:         "ulid",
+			translation: "{0}必須是一個有效的ULID",
+			override:    false,
+		},
 		{
 			tag:         "ascii",
 			translation: "{0}必須只包含ascii字元",


Test patch:
diff --git a/translations/en/en_test.go b/translations/en/en_test.go
index 0f20768..146c475 100644
--- a/translations/en/en_test.go
+++ b/translations/en/en_test.go
@@ -103,6 +103,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
 		UUID5             string            `validate:"uuid5"`
+		ULID              string            `validate:"ulid"`
 		ASCII             string            `validate:"ascii"`
 		PrintableASCII    string            `validate:"printascii"`
 		MultiByte         string            `validate:"multibyte"`
@@ -323,6 +324,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5 must be a valid version 5 UUID",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID must be a valid ULID",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN must be a valid ISBN number",
diff --git a/translations/es/es_test.go b/translations/es/es_test.go
index 070e3a8..814f2ab 100644
--- a/translations/es/es_test.go
+++ b/translations/es/es_test.go
@@ -4,9 +4,9 @@ import (
 	"testing"
 	"time"
 
+	. "github.com/go-playground/assert/v2"
 	spanish "github.com/go-playground/locales/es"
 	ut "github.com/go-playground/universal-translator"
-	. "github.com/go-playground/assert/v2"
 	"github.com/go-playground/validator/v10"
 )
 
@@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
 		UUID5             string            `validate:"uuid5"`
+		ULID              string            `validate:"ulid"`
 		ASCII             string            `validate:"ascii"`
 		PrintableASCII    string            `validate:"printascii"`
 		MultiByte         string            `validate:"multibyte"`
@@ -312,6 +313,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5 debe ser una versión válida 5 UUID",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID debe ser un ULID válido",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN debe ser un número ISBN válido",
diff --git a/translations/fa/fa_test.go b/translations/fa/fa_test.go
index 611f23e..1acb8cf 100644
--- a/translations/fa/fa_test.go
+++ b/translations/fa/fa_test.go
@@ -103,6 +103,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
 		UUID5             string            `validate:"uuid5"`
+		ULID              string            `validate:"ulid"`
 		ASCII             string            `validate:"ascii"`
 		PrintableASCII    string            `validate:"printascii"`
 		MultiByte         string            `validate:"multibyte"`
@@ -322,6 +323,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5 باید یک UUID نسخه 5 معتبر باشد",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID باید یک ULID معتبر باشد",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN باید یک شابک معتبر باشد",
diff --git a/translations/fr/fr_test.go b/translations/fr/fr_test.go
index c2c88d0..f1ba280 100644
--- a/translations/fr/fr_test.go
+++ b/translations/fr/fr_test.go
@@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
 		UUID5             string    `validate:"uuid5"`
+		ULID              string    `validate:"ulid"`
 		ASCII             string    `validate:"ascii"`
 		PrintableASCII    string    `validate:"printascii"`
 		MultiByte         string    `validate:"multibyte"`
@@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5 doit être un UUID version 5 valid",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID doit être une ULID valide",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN doit être un numéro ISBN valid",
diff --git a/translations/id/id_test.go b/translations/id/id_test.go
index 83a72aa..5d80940 100644
--- a/translations/id/id_test.go
+++ b/translations/id/id_test.go
@@ -4,9 +4,9 @@ import (
 	"testing"
 	"time"
 
+	. "github.com/go-playground/assert/v2"
 	indonesia "github.com/go-playground/locales/id"
 	ut "github.com/go-playground/universal-translator"
-	. "github.com/go-playground/assert/v2"
 	"github.com/go-playground/validator/v10"
 )
 
@@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
 		UUID5             string    `validate:"uuid5"`
+		ULID              string    `validate:"ulid"`
 		ASCII             string    `validate:"ascii"`
 		PrintableASCII    string    `validate:"printascii"`
 		MultiByte         string    `validate:"multibyte"`
@@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5 harus berupa UUID versi 5 yang valid",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID harus berupa ULID yang valid",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN harus berupa nomor ISBN yang valid",
diff --git a/translations/ja/ja_test.go b/translations/ja/ja_test.go
index eaa2743..393bcb3 100644
--- a/translations/ja/ja_test.go
+++ b/translations/ja/ja_test.go
@@ -4,9 +4,9 @@ import (
 	"testing"
 	"time"
 
+	. "github.com/go-playground/assert/v2"
 	ja_locale "github.com/go-playground/locales/ja"
 	ut "github.com/go-playground/universal-translator"
-	. "github.com/go-playground/assert/v2"
 	"github.com/go-playground/validator/v10"
 )
 
@@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
 		UUID5             string    `validate:"uuid5"`
+		ULID              string    `validate:"ulid"`
 		ASCII             string    `validate:"ascii"`
 		PrintableASCII    string    `validate:"printascii"`
 		MultiByte         string    `validate:"multibyte"`
@@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5はバージョンが5の正しいUUIDでなければなりません",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULIDは正しいULIDでなければなりません",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBNは正しいISBN番号でなければなりません",
diff --git a/translations/nl/nl_test.go b/translations/nl/nl_test.go
index 55f5048..137e0c4 100644
--- a/translations/nl/nl_test.go
+++ b/translations/nl/nl_test.go
@@ -4,9 +4,9 @@ import (
 	"testing"
 	"time"
 
+	. "github.com/go-playground/assert/v2"
 	english "github.com/go-playground/locales/en"
 	ut "github.com/go-playground/universal-translator"
-	. "github.com/go-playground/assert/v2"
 	"github.com/go-playground/validator/v10"
 )
 
@@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
 		UUID5             string    `validate:"uuid5"`
+		ULID              string    `validate:"ulid"`
 		ASCII             string    `validate:"ascii"`
 		PrintableASCII    string    `validate:"printascii"`
 		MultiByte         string    `validate:"multibyte"`
@@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5 moet een geldige versie 5 UUID zijn",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID moet een geldige ULID zijn",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN moet een geldig ISBN nummer zijn",
diff --git a/translations/pt/pt_test.go b/translations/pt/pt_test.go
index ceec15f..0422ab0 100644
--- a/translations/pt/pt_test.go
+++ b/translations/pt/pt_test.go
@@ -105,6 +105,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
 		UUID5             string            `validate:"uuid5"`
+		ULID              string            `validate:"ulid"`
 		ASCII             string            `validate:"ascii"`
 		PrintableASCII    string            `validate:"printascii"`
 		MultiByte         string            `validate:"multibyte"`
@@ -321,6 +322,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5 deve ser um UUID versão 5 válido",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID deve ser um ULID válido",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN deve ser um número de ISBN válido",
diff --git a/translations/pt_BR/pt_BR_test.go b/translations/pt_BR/pt_BR_test.go
index 32125f6..c8a6482 100644
--- a/translations/pt_BR/pt_BR_test.go
+++ b/translations/pt_BR/pt_BR_test.go
@@ -4,10 +4,10 @@ import (
 	"testing"
 	"time"
 
+	. "github.com/go-playground/assert/v2"
 	brazilian_portuguese "github.com/go-playground/locales/pt_BR"
 	ut "github.com/go-playground/universal-translator"
 	"github.com/go-playground/validator/v10"
-	. "github.com/go-playground/assert/v2"
 )
 
 func TestTranslations(t *testing.T) {
@@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
 		UUID5             string    `validate:"uuid5"`
+		ULID              string    `validate:"ulid"`
 		ASCII             string    `validate:"ascii"`
 		PrintableASCII    string    `validate:"printascii"`
 		MultiByte         string    `validate:"multibyte"`
@@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5 deve ser um UUID versão 5 válido",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID deve ser uma ULID válida",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN deve ser um número ISBN válido",
diff --git a/translations/ru/ru_test.go b/translations/ru/ru_test.go
index e1a1069..c74f0d5 100644
--- a/translations/ru/ru_test.go
+++ b/translations/ru/ru_test.go
@@ -120,6 +120,7 @@ func TestTranslations(t *testing.T) {
 		UUID3                   string            `validate:"uuid3"`
 		UUID4                   string            `validate:"uuid4"`
 		UUID5                   string            `validate:"uuid5"`
+		ULID                    string            `validate:"ulid"`
 		ASCII                   string            `validate:"ascii"`
 		PrintableASCII          string            `validate:"printascii"`
 		MultiByte               string            `validate:"multibyte"`
@@ -340,6 +341,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5 должен быть UUID 5 версии",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID должен быть ULID",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN должен быть ISBN номером",
diff --git a/translations/tr/tr_test.go b/translations/tr/tr_test.go
index 7cfa5a4..b72329e 100644
--- a/translations/tr/tr_test.go
+++ b/translations/tr/tr_test.go
@@ -4,9 +4,9 @@ import (
 	"testing"
 	"time"
 
+	. "github.com/go-playground/assert/v2"
 	turkish "github.com/go-playground/locales/tr"
 	ut "github.com/go-playground/universal-translator"
-	. "github.com/go-playground/assert/v2"
 	"github.com/go-playground/validator/v10"
 )
 
@@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
 		UUID5             string            `validate:"uuid5"`
+		ULID              string            `validate:"ulid"`
 		ASCII             string            `validate:"ascii"`
 		PrintableASCII    string            `validate:"printascii"`
 		MultiByte         string            `validate:"multibyte"`
@@ -312,6 +313,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5 geçerli bir sürüm 5 UUID olmalıdır",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID geçerli bir ULID olmalıdır",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN geçerli bir ISBN numarası olmalıdır",
diff --git a/translations/zh/zh_test.go b/translations/zh/zh_test.go
index 1d73c65..cf76590 100644
--- a/translations/zh/zh_test.go
+++ b/translations/zh/zh_test.go
@@ -109,6 +109,7 @@ func TestTranslations(t *testing.T) {
 		UUID3                 string    `validate:"uuid3"`
 		UUID4                 string    `validate:"uuid4"`
 		UUID5                 string    `validate:"uuid5"`
+		ULID                  string    `validate:"ulid"`
 		ASCII                 string    `validate:"ascii"`
 		PrintableASCII        string    `validate:"printascii"`
 		MultiByte             string    `validate:"multibyte"`
@@ -327,6 +328,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5必须是一个有效的V5 UUID",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID必须是一个有效的ULID",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN必须是一个有效的ISBN编号",
diff --git a/translations/zh_tw/zh_tw_test.go b/translations/zh_tw/zh_tw_test.go
index cf60d1d..129c51a 100644
--- a/translations/zh_tw/zh_tw_test.go
+++ b/translations/zh_tw/zh_tw_test.go
@@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
 		UUID5             string    `validate:"uuid5"`
+		ULID              string    `validate:"ulid"`
 		ASCII             string    `validate:"ascii"`
 		PrintableASCII    string    `validate:"printascii"`
 		MultiByte         string    `validate:"multibyte"`
@@ -309,6 +310,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.UUID5",
 			expected: "UUID5必須是一個有效的V5 UUID",
 		},
+		{
+			ns:       "Test.ULID",
+			expected: "ULID必須是一個有效的ULID",
+		},
 		{
 			ns:       "Test.ISBN",
 			expected: "ISBN必須是一個有效的ISBN編號",
diff --git a/validator_test.go b/validator_test.go
index 799efdf..c86f8da 100644
--- a/validator_test.go
+++ b/validator_test.go
@@ -4269,6 +4269,46 @@ func TestUUIDRFC4122Validation(t *testing.T) {
 	}
 }
 
+func TestULIDValidation(t *testing.T) {
+	tests := []struct {
+		param    string
+		expected bool
+	}{
+		{"", false},
+		{"01BX5ZZKBKACT-V9WEVGEMMVRZ", false},
+		{"01bx5zzkbkactav9wevgemmvrz", false},
+		{"a987Fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false},
+		{"01BX5ZZKBKACTAV9WEVGEMMVRZABC", false},
+		{"01BX5ZZKBKACTAV9WEVGEMMVRZABC", false},
+		{"0IBX5ZZKBKACTAV9WEVGEMMVRZ", false},
+		{"O1BX5ZZKBKACTAV9WEVGEMMVRZ", false},
+		{"01BX5ZZKBKACTAVLWEVGEMMVRZ", false},
+		{"01BX5ZZKBKACTAV9WEVGEMMVRZ", true},
+	}
+
+	validate := New()
+
+	for i, test := range tests {
+
+		errs := validate.Var(test.param, "ulid")
+
+		if test.expected {
+			if !IsEqual(errs, nil) {
+				t.Fatalf("Index: %d ULID failed Error: %s", i, errs)
+			}
+		} else {
+			if IsEqual(errs, nil) {
+				t.Fatalf("Index: %d ULID failed Error: %s", i, errs)
+			} else {
+				val := getError(errs, "", "")
+				if val.Tag() != "ulid" {
+					t.Fatalf("Index: %d ULID failed Error: %s", i, errs)
+				}
+			}
+		}
+	}
+}
+
 func TestISBNValidation(t *testing.T) {
 	tests := []struct {
 		param    string