Compare commits

...

243 Commits

Author SHA1 Message Date
Dean Karn f5e5146b31 update version README 2 years ago
Cyro Dubeux 56e2f31322
pt_BR translation improvements (#967) 2 years ago
Joselito Viveiros Nogueira Filho e9f2d9f21a
fix: removed unnecessary cast so we can mock ValidationErrors (#980) (#981) 2 years ago
Eduardo Mello b43d437012
feat: add cve validator (#983) 2 years ago
João Fonseca 58d420de41
add kosovo (#1081) 2 years ago
Aleksander Dejewski c6f8637983
Fixed NotBlank validator to cover all whitespace characters (#997) 2 years ago
Gatis Rīders f6f934c4e9
Added Latvian translations (#1005) 2 years ago
konstantin ae3e728763
Introduce Luhn Checksum Validation (#1009) 2 years ago
Tanryberdi 0665b9518a
Refactor: fix for lint errors (#1011) 2 years ago
jnst 21b88d1a25
Fix the Japanese translations of gte and lte (#1012) 2 years ago
jnst 4f5cf1d499
Update Japanese translations (#1013) 2 years ago
Kmilo Denis González 2f3efda84f
Added a genre enum to the user model and a custom validation (#1014) 2 years ago
brent s 2e43671001
(New validator) Validate non-existing but valid file/directory paths (#1022) 2 years ago
just Alan f560fd4e07
Feat: support mongodb objectID validation (#1023) 2 years ago
Yasoob Haider 29d50ba963
add validation for base64 url without = padding (#1024) 2 years ago
zemzale 1c1f70d35b
feat(unique): Add support for struct memember validation (#1048) 2 years ago
Yann Salaün cc768b176d
fix unique= when struct field is a nil pointer (#1041) 2 years ago
Alberto Forcato 3ee65f8c59
Add cron support (#1045) 2 years ago
Owen Gong 89b91cea99
Added "http_url" tag to validate if the variable is a HTTP(s) URL (#1047) 2 years ago
Vijay Nallagatla f2078f7696
Fix unique validator for map with Pointer value (#1062) 2 years ago
Arthur Jenoudet ef342b6f7c
fix validator excluded_unless (#1064) 2 years ago
Dean Karn f3086daf63 fix README 2 years ago
Vitor Hugo 75ba9f6a84
Update README.md (#1066) 2 years ago
Dean Karn 13ad6f5b30 update deps 2 years ago
Simon Tremblay c242c49c02
Update go.mod/go.sum using go mod tidy (#1071) 2 years ago
Dean Karn f17b3458fa fix README 2 years ago
Dean Karn d1178535f7 change eq and ne ignore case tag names 2 years ago
sgallizia 72a3e75460
added IsEqIgnoreCase and IsNeIgnoreCase (#1076) 2 years ago
Phil Porada c0b3430b1f
Fix spelling error in doc (#1078) 2 years ago
Mohamed Sohail b95730fb11
feat: add eth_addr_checksum validation, update eth_addr (#1080) 2 years ago
Dean Karn 8f07b03682
update deps (#1061) 2 years ago
Roberto Ramírez 7edc63a0be
#updated Added required_id spanish translation (#1026) 2 years ago
Marc Brugger 3201fe42fe
Add FQDN en translation (#1058) 2 years ago
wjl110 5aa5f93593
update gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b to 3.0.0 (#1057) 2 years ago
tomaisthorpe f036dd475d
update golang.org/x/text to resolve CVE-2022-32149 (#1015) 2 years ago
Dean Karn c7e0172e0f
Update README.md 2 years ago
Vaibhav Dighe 1e8c614c2a
Fixed boolean validation to handle bool kind (#988) 2 years ago
Dean Karn 9e2ea40380 update ci action versions 3 years ago
Dean Karn 29bf2a5bd0 bump version 3 years ago
Dean Karn 02c12dd7ef update RegisterStructValidationMapRules docs 3 years ago
Leo Liang d0d0c355aa
Feat: support validate struct without struct tag (#934) 3 years ago
Jason.Zhang d3e4be3e44
Enhanced ValidationCtx method to support nested map in slice #915 (#917) 3 years ago
gosua 99922fccc7
Enhance example in function docu (#858) 3 years ago
Sec Cake 4f55647bd7
Added some hash validation (#875) 3 years ago
mrkongo 39aa2e3a27
add it_IT translation (#694) 3 years ago
Vuong 7fa836dc0a
Add Vietnamese translations (#820) 3 years ago
Ayoob Mohammed 21a103f428
Add Arabic translations (#825) 3 years ago
Eduardo Mello e3f29bf088
Boolean translation (#930) 3 years ago
Renato Alves Torres d37da5e53c
fix: add en translation for required_if (#884) 3 years ago
Ciprian 0a26ee57e4
Updated `endsnotwith` description in Readme (#824) 3 years ago
Lauris BH f09500fca7
Fix support for aliased time.Time types (#890) 3 years ago
Massimo Costa 090afeb8ac
enhancement: add italian translation (#914) 3 years ago
XIE Long af72f63d39
Result is wrong while there are multiple group of OR operators #910 (#911) 3 years ago
Stefan Dillenburg f2d3ff7f98
fix: Remove underscore from RFC-1123 based regex (#912) 3 years ago
hehe.bu aea8782168
fix ja typos (#898) 3 years ago
Sol 24b5175a34
Fix typo (#891) 3 years ago
NgeKaworu bc9f9dd2eb
Update zh.go (#856) 3 years ago
alessmar dd2857a4cb
Credit card validation (#924) 3 years ago
Dean Karn bb30072b48 update ci config 3 years ago
Jacob Hochstetler c0195b2b40
added excluded_if/excluded_unless tags + tests (#847) 3 years ago
Dean Karn 58d5778b18
Update README.md 3 years ago
Johannes 3e49fe4eb8
bump golang.org/x/text to fix CVE-2021-38561 (#881) 3 years ago
Anmar85 dd2de9c1f7
update /x/crypto to resolve CVE-2021-43565 (#904) 3 years ago
Dean Karn 88976f39f6
Update README.md 3 years ago
jtseng-godaddy 06ec79d987
Including regex version validation (#831) 3 years ago
Markus Tenghamn 8fe074c546
Added ulid validation (#826) 3 years ago
Kazuki Onishi ec2071b383
Add DNS RFC 1035 label format validator (#833) 3 years ago
nesty92 c67d01d610
Add support for all hex color notations (#866) (#867) 3 years ago
Markus Tenghamn 19f8e61dba
Fixed country_code validation to properly handle strings (#873) 3 years ago
Dean Karn ce34f361cc
update ut dep (#816) 3 years ago
Metin Oğurlu d4271985b4
Feature/currency codes (#811) 3 years ago
András Czigány 42525d89ab
document thread safety and recommended singleton usage (#809) 3 years ago
Xavier Vello a67baa74f1
feature/boolean (#804) 3 years ago
Dmitriy 14221d02a5
fix: Fixed panic when use ru lang for translation, added and fixed tests (#814) 3 years ago
Dean Karn 6dbf61e046
update deps (#815) 3 years ago
Dean Karn 9a5bce3253
Update README.md 3 years ago
Dean Karn b03f1f453e
update all deps to latest versions (#801) 3 years ago
cokezhang 61a982014e
enhancement: add zh translations for tag alphaunicode,alphanumunicode,containsrune,startswith,endswith (#799) 3 years ago
Oleg Nikachev 44c2696cbd
Minor fixes. (#798) 3 years ago
steveorourke e40bece342
Add support for jwt validation (#783) 3 years ago
Brian Leishman 8cfa1e93b6
Update README.md (#765) 3 years ago
Saed SayedAhmed f1befad848
Addressing security vulnerabilities (#774) 3 years ago
Dean Karn 65bb123677
Update README.md 3 years ago
amir e3e38634b1
Add persian translations (#771) 3 years ago
N/A af8b8d1c88
Add iso-3166-2 (#788) 3 years ago
Levani 8bfa88a462
Update README.md (#786) 3 years ago
Dean Karn 9104fe2461
Update README.md 3 years ago
masafumi-i 90ab42ba67
fix ja translation (#792) 3 years ago
Alonso Villegas b1b32b2064
Fix map diving validation (#793) 3 years ago
Dean Karn 76b917f432
Update README.md 4 years ago
Dean Karn 95ac68b6d7
fix url_encoded regex (#769) 4 years ago
Dean Karn d07eb88fb0
Pr cleanup (#767) 4 years ago
ѵµσɳɠ a53d64fc35
Postcode validator for value and field (#759) 4 years ago
Oleksii Kulikov c2066206fe
Add BIC ISO format validator (#758) 4 years ago
zytell3301 b95d43b789
Add ability to validate map data (#752) 4 years ago
Zane DeGraffenried 31dd62f1c9
Fix boolean support for required_if, required_unless and eqfield (#754) 4 years ago
Dean Karn b926bf0df9
Update README.md 4 years ago
Josh Slate add373fff7
feat: add bcp 47 language tag validator (#730) 4 years ago
Dean Karn 68248183b4
Update README.md 4 years ago
Deri Herdianto 526b0968b3
Update errors.go (#671) 4 years ago
Sergei Isaev 2b75c46052
version removed from installation and import (#710) 4 years ago
frederikhors 3c8db83c19
New official Documentation URL (#706) 4 years ago
Peperoncino 5b8f3b8a0b
corresponds to boolean value of nefield (#712) 4 years ago
Torwang 882ec0def4
fix StructLevel Parent() (#716) 4 years ago
Qalifah 0e674563ff
error in spelling (#732) 4 years ago
Michał Treter ec1baac93d
Fix accessing fields in composed structs (#733) 4 years ago
João Francisco 514c9dbd77
Fix excludedWithoutAll comment (#673) 4 years ago
Jake-Convictional 9c415c4565
Add support for excluded_with on pointer fields (#746) 4 years ago
Dean Karn f6584a41c8
split listing into separate job (#680) 4 years ago
Dean Karn 3bb4866602
Github Actions Changeover (#679) 4 years ago
Dean Karn 456221b630
Fix time.Duration parsing for int param (#678) 4 years ago
Flávio CF Oliveira d6b17fd90b
Added Translation for pt (#613) 4 years ago
krhubert f32fea830e
Add country code validation (#615) 4 years ago
Brandon Lee 704a814277
accept 0x(0X) start in 'hexadecimal' tag (#620) 4 years ago
Elias Rodrigues 4407f4dea0
Add checksum validation for Ethereum address (#630) 4 years ago
Dean Karn 8941cbd00e
Merge pull request #635 from taybart/master 4 years ago
Dean Karn 8154ee6ca8
Merge branch 'master' into master 4 years ago
Dean Karn f0805c61ba
Merge pull request #641 from math-nao/master 4 years ago
Dean Karn 76981ccf75
Merge pull request #642 from elias19r/issue-584-add-support-for-time-duration-type 4 years ago
Dean Karn 57b4fab34b
Merge pull request #644 from longit644/bugfix/make-unique-tag-work-with-pointer-fields 4 years ago
Dean Karn d9e95d55e9
Update errors.go 4 years ago
Dean Karn 2a5412a1bc
Merge pull request #657 from nigelis/bugfix/omitempty-not-first 4 years ago
Dean Karn 839d050f74
Merge pull request #658 from hzwwen/master 4 years ago
Dean Karn e6eee3ea88
Merge branch 'master' into master 4 years ago
Dean Karn b1ccceeb11
Merge pull request #664 from erma07/master 4 years ago
Dean Karn 4cd2e40256
Merge pull request #666 from xy-planning-network/patch 4 years ago
Dean Karn ace513eddd
Update .travis.yml 4 years ago
Dean Karn 0d657b4731
Update .travis.yml 4 years ago
Dean Karn 0e7e593daa
Merge pull request #667 from arkark/fix/endswith-readme 4 years ago
Takeshi Kaneko f2ce6a4f92 Fix a category of endswith in README 4 years ago
David Ketch 1bf11c9299 Includes Error in FieldError's interface 4 years ago
root ca0533bb97 Closes #660 readme updates 4 years ago
root 490e253792 Closes #660 added excluded with and without tests 4 years ago
root 742006d126 Closes #660 added excluded with and without 4 years ago
hzw 44a150f359 add required_if and required_unless 4 years ago
nigelis 6f81b7d8d0 Validate tag omitempty, only base on field value 4 years ago
math-nao 7dbe685a02 Update timeZone tag name to timezone 4 years ago
Long Bui 67c4fdf0de Make unique tag work with pointer fields. 4 years ago
Elias Rodrigues 1cbd3081e4 Add function asIntFromType 4 years ago
Elias Rodrigues 33ffa4a31f Move var declarations to the top in test functions 4 years ago
Elias Rodrigues 55e0f418d6 Minor edits 4 years ago
Elias Rodrigues 04749bc4d4 Fix golangci-lint issues 4 years ago
Elias Rodrigues fc1b28803a Update docs for baked-in time.Duration supported tags 4 years ago
Elias Rodrigues f525f0b64f Add tests for baked-in time.Duration supported tags 4 years ago
Elias Rodrigues 48684f91aa Add baked-in support for time.Duration 4 years ago
Elias Rodrigues 0c71d5cc08 Add var timeDurationType 4 years ago
Elias Rodrigues 94470e68f3 Add function asIntFromTimeDuration() 4 years ago
Elias Rodrigues 1c4e56307b Add bin to .gitignore 4 years ago
math-nao 44f3347f13 Add support for time zone validation 4 years ago
Taylor 07a4b7aa38 Add wiki link for E.164 4 years ago
Taylor 0bfda52ce1 Add e164 to doc.go 4 years ago
Dean Karn ea924ce89a
Update README.md 5 years ago
Dean Karn 059510c3bf
Merge pull request #612 from Giulianos/bugfix/fqdn 5 years ago
Dean Karn 50166a5a06
Merge branch 'master' into bugfix/fqdn 5 years ago
Dean Karn ab4e67f050
Merge pull request #609 from fourcels/master 5 years ago
Dean Karn ae8ecbca8a
Merge branch 'master' into master 5 years ago
Dean Karn 0ab8e0351d
Merge pull request #607 from johnbailon/number_documentation 5 years ago
Dean Karn 7e978ff872
Merge pull request #600 from amiraliucsc/fix/RFC952-host 5 years ago
Dean Karn 957a452839
Merge pull request #598 from cncal/enhance_zh_translation 5 years ago
Dean Karn 257f202dd1
Merge pull request #588 from erikaleigh/fix-grammatical-errors 5 years ago
Dean Karn dbfcad250f
Merge pull request #583 from metalinspired/master 5 years ago
Dean Karn 07f2314af2
Merge pull request #582 from alfonsoprado/feature/spanish-translation-v10 5 years ago
Dean Karn a5e9e63a9a
Merge pull request #579 from psampaz/readme_updates 5 years ago
Dean Karn 8203121676
Merge pull request #578 from psampaz/validations_list 5 years ago
Dean Karn 175275dfea
Merge pull request #577 from psampaz/travis_1_13_7 5 years ago
Giuliano Scaglioni b610155475 format code 5 years ago
Giuliano Scaglioni 7d6236a898 add updated regex for fqdn 5 years ago
Giuliano Scaglioni ee37674003 add breaking test 5 years ago
Kiyan Gauss b15bf1dead Add zh,zh_tw datetime translation 5 years ago
John Bailon 198646cbd1 Add number validator in documentation 5 years ago
John Bailon 7807a1f36f Add number validator in documentation 5 years ago
amiraliucsc c7dc8812a2 Add more tests 5 years ago
amiraliucsc 1611f1e72c Add test regarding multiple dots in hostname 5 years ago
amiraliucsc 18fba4ba8a Fix the regex for RFC952 hostname validator 5 years ago
cncal 85098861d3 enhancement: add zh translations for tag json, lowercase, uppercase, datetime 5 years ago
erikaleigh 3909783b73 Fix grammatical errors in errors.go 5 years ago
Milan Divkovic f3b347cc29 Add validators that check if string does not start/end with supplied parameter 5 years ago
alfonsoprado a98911a366 fix spanish translations 5 years ago
alfonsoprado c1b73a784d Add spanish translations 5 years ago
Pantelis Sampaziotis afacb47f2b Updated version and go doc link 5 years ago
Pantelis Sampaziotis 9f175c7ece Add list of validation in README 5 years ago
Pantelis Sampaziotis 8a19420dcf Run tests on latest stable version 1.13.7 5 years ago
Dean Karn c68441b7f4
Merge pull request #575 from psampaz/datetime 5 years ago
Pantelis Sampaziotis 912fcd16a8 resovle conflict 5 years ago
Pantelis Sampaziotis 14f963703b Add datetime validation 5 years ago
Dean Karn b7430f8e39
Merge pull request #571 from psampaz/json 5 years ago
Dean Karn 0c80f876cd Merge branch 'master' into json 5 years ago
Dean Karn 68e8e137fa Merge pull request #563 from terala/master 5 years ago
Dean Karn 1f676c755a
Merge branch 'master' into master 5 years ago
Dean Karn fa149de298 Merge pull request #569 from Rustery/feature/russian-translation 5 years ago
Dean Karn fdd54d3fc2 Merge pull request #572 from psampaz/lowercase 5 years ago
Dean Karn b541462b5a Merge pull request #574 from shenyiling/master 5 years ago
Pantelis Sampaziotis 2109f86248 Update documenation 5 years ago
Pantelis Sampaziotis 9e58bcdee1 Empty strings are not lowercase or uppercase 5 years ago
shenyiling 6d2c659585 fix: typo 5 years ago
Pantelis Sampaziotis 5b51bff4e6 Add isLowercase and isUppercase valitation 5 years ago
Pantelis Sampaziotis 581d2698d5 Added isJSON validation 5 years ago
Rustam eb11afd9a8 Added russian translation 5 years ago
Ravi Terala a1ac82a865 Add documentation 5 years ago
Ravi Terala 893747e5ee Add hostname_port validator feature 5 years ago
Dean Karn fb6c45823a
Update README.md 5 years ago
Dean Karn 5b97da4cbf
Merge pull request #560 from go-playground/cherry-pick-v9 5 years ago
Dean Karn c2546fb355 Add test + docs for FieldLevel.GetTag 5 years ago
richi f8a081fd83 Added the function GetTag. 5 years ago
Shi Han NG e73ec5f5ab Update doc for unique=field 5 years ago
Shi Han NG 8f604265e0 Implement unique=FieldName 5 years ago
Jean-Philippe Moal 1effcb06a7 Clarify and complete tag names example 5 years ago
Jean-Philippe Moal 703c0b681a Rework the non standard validators documentation 5 years ago
Taylor e7c70798c9 Add e.164 support 5 years ago
Dean Karn a6a294bfeb
Merge pull request #541 from JonathanWThom/jt/space-separated-oneof 5 years ago
Dean Karn c067a8f197
Merge pull request #539 from sclaire-1/master 5 years ago
Dean Karn 1d93fb999e
Merge pull request #547 from atomicleads/master 5 years ago
Dean Karn 5356060ec3
Merge pull request #548 from JonathanWThom/jt/datauri 5 years ago
Dean Karn 361bf41fb9
Merge pull request #550 from Streppel/patch-1 5 years ago
Dean Karn ae15ced5e1
Merge pull request #558 from whatvn/master 5 years ago
Nguyễn Văn Hưng da6897303a error is always nil 5 years ago
Streppel 0849b1840e
removing unecessary type conversion 5 years ago
Jonathan Thom bb5fd36827
Improves accuracy of datauri regex 5 years ago
Jonathan Thom 685d3e21f3
Makes regex constant & moves single quote replace logic to parseOneOfParam2 5 years ago
Dmitry M. Gavrovsky d149847e8a test case for “eq” comparing boolean values #547 5 years ago
Dmitry M. Gavrovsky dfc652373a Added boolean type support for “eq” validator 5 years ago
Dean Karn 691a5f5f25
Update README.md 5 years ago
Dean Karn bec732e9de
Merge pull request #543 from go-playground/fix-required-without 5 years ago
Dean Karn a346f2eea4 Merge branch 'master' into fix-required-without 5 years ago
Dean Karn b11fb5f16e fix typo 5 years ago
Dean Karn c0f05c7b3d fix required_* 5 years ago
Dean Karn fe89c745f6
Merge pull request #542 from kaelanfouwels/master 5 years ago
Kaelan Thijs Fouwels 8678802a39
Correction of documentation URL 5 years ago
Jonathan Thom 432c17028a
Adds ability to validate oneof for space separated strings 5 years ago
sclaire-1 4be5d19c39
Edit CONTRIBUTING.md 5 years ago
Dean Karn dbbe6958b3 update doc v9 url to v10 5 years ago
Dean Karn 7e57ca0cf5 convert to go modules 5 years ago
Dean Karn cd1bd58169
Update README.md 5 years ago
Dean Karn 883a9e06c8
Merge pull request #523 from go-playground/fixes 5 years ago
Dean Karn 9593a0f77e add test for new nil validation 5 years ago
Dean Karn 06f92248de linting cleanup 5 years ago
Dean Karn 6484d9f2fb fix required_with_* 5 years ago
Dean Karn cc25246f01 fix required_without_* 5 years ago
Dean Karn 51fcc303b3
Merge pull request #507 from UgurAsaner/v9 5 years ago
Dean Karn 205b733988
Merge pull request #522 from teejays/v9 5 years ago
Talha Ansari 4e2a13b9bf
Updated tests to reflect the import change 5 years ago
Talha Ansari 2367d307c5
Fix the import path for non-standard validators 5 years ago
baran.asaner 80a8449877 Add Turkish translations 5 years ago
  1. 1
      .github/CODEOWNERS
  2. 2
      .github/CONTRIBUTING.md
  3. 5
      .github/ISSUE_TEMPLATE.md
  4. 12
      .github/PULL_REQUEST_TEMPLATE.md
  5. 51
      .github/workflows/workflow.yml
  6. 2
      .gitignore
  7. 16
      MAINTAINERS.md
  8. 10
      Makefile
  9. 227
      README.md
  10. 2
      _examples/custom-validation/main.go
  11. 2
      _examples/custom/main.go
  12. 2
      _examples/dive/main.go
  13. 4
      _examples/gin-upgrading-overriding/v8_to_v9.go
  14. 87
      _examples/map-validation/main.go
  15. 6
      _examples/simple/main.go
  16. 101
      _examples/struct-level/main.go
  17. 92
      _examples/struct-map-rules-validation/main.go
  18. 4
      _examples/translations/main.go
  19. 1614
      baked_in.go
  20. 235
      benchmarks_test.go
  21. 58
      cache.go
  22. 1132
      country_codes.go
  23. 79
      currency_codes.go
  24. 696
      doc.go
  25. 50
      errors.go
  26. 83
      examples_test.go
  27. 67
      field_level.go
  28. 14
      go.mod
  29. 31
      go.sum
  30. 4
      non-standard/validators/notblank.go
  31. 6
      non-standard/validators/notblank_test.go
  32. 173
      postcode_regexes.go
  33. 66
      regexes.go
  34. 12
      struct_level.go
  35. 1389
      translations/ar/ar.go
  36. 695
      translations/ar/ar_test.go
  37. 152
      translations/en/en.go
  38. 76
      translations/en/en_test.go
  39. 1385
      translations/es/es.go
  40. 658
      translations/es/es_test.go
  41. 1384
      translations/fa/fa.go
  42. 690
      translations/fa/fa_test.go
  43. 7
      translations/fr/fr.go
  44. 15
      translations/fr/fr_test.go
  45. 7
      translations/id/id.go
  46. 9
      translations/id/id_test.go
  47. 1261
      translations/it/it.go
  48. 725
      translations/it/it_test.go
  49. 133
      translations/ja/ja.go
  50. 304
      translations/ja/ja_test.go
  51. 1399
      translations/lv/lv.go
  52. 709
      translations/lv/lv_test.go
  53. 7
      translations/nl/nl.go
  54. 9
      translations/nl/nl_test.go
  55. 1410
      translations/pt/pt.go
  56. 682
      translations/pt/pt_test.go
  57. 19
      translations/pt_BR/pt_BR.go
  58. 27
      translations/pt_BR/pt_BR_test.go
  59. 1493
      translations/ru/ru.go
  60. 767
      translations/ru/ru_test.go
  61. 1375
      translations/tr/tr.go
  62. 657
      translations/tr/tr_test.go
  63. 1384
      translations/vi/vi.go
  64. 690
      translations/vi/vi_test.go
  65. 141
      translations/zh/zh.go
  66. 284
      translations/zh/zh_test.go
  67. 43
      translations/zh_tw/zh_tw.go
  68. 20
      translations/zh_tw/zh_tw_test.go
  69. 43
      util.go
  70. 70
      validator.go
  71. 202
      validator_instance.go
  72. 10767
      validator_test.go

@ -0,0 +1 @@
* @go-playground/validator-maintainers

@ -2,7 +2,7 @@
## Quality Standard ## Quality Standard
To ensure the continued stability of this package tests are required to be written or already exist in order for a pull request to be merged. To ensure the continued stability of this package, tests are required that cover the change in order for a pull request to be merged.
## Reporting issues ## Reporting issues

@ -1,4 +1,7 @@
### Package version eg. v8, v9: - [ ] I have looked at the documentation [here](https://pkg.go.dev/github.com/go-playground/validator/v10#section-documentation) first?
- [ ] I have looked at the examples provided that may showcase my question [here](/_examples)?
### Package version eg. v9, v10:

@ -1,13 +1,7 @@
Fixes Or Enhances # . ## Fixes Or Enhances
**Make sure that you've checked the boxes below before you submit PR:** **Make sure that you've checked the boxes below before you submit PR:**
- [ ] Tests exist or have been written that cover this particular change. - [ ] Tests exist or have been written that cover this particular change.
Change Details: @go-playground/validator-maintainers
-
-
-
@go-playground/admins

@ -0,0 +1,51 @@
on:
push:
branches:
- master
pull_request:
name: Test
jobs:
test:
strategy:
matrix:
go-version: [1.19.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v3
- name: Restore Cache
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-v1-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-v1-go-
- name: Test
run: go test -race -covermode=atomic -coverprofile="profile.cov" ./...
- name: Send Coverage
if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.19.x'
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: profile.cov
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.19.x
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.50.1

2
.gitignore vendored

@ -6,6 +6,7 @@
# Folders # Folders
_obj _obj
_test _test
bin
# Architecture specific extensions/prefixes # Architecture specific extensions/prefixes
*.[568vq] *.[568vq]
@ -27,3 +28,4 @@ _testmain.go
*.txt *.txt
cover.html cover.html
README.html README.html
.idea

@ -0,0 +1,16 @@
## Maintainers Guide
### Semantic Versioning
Semantic versioning as defined [here](https://semver.org) must be strictly adhered to.
### External Dependencies
Any new external dependencies MUST:
- Have a compatible LICENSE present.
- Be actively maintained.
- Be approved by @go-playground/admins
### PR Merge Requirements
- Up-to-date branch.
- Passing tests and linting.
- CODEOWNERS approval.
- Tests that cover both the Happy and Unhappy paths.

@ -1,11 +1,13 @@
GOCMD=go GOCMD=GO111MODULE=on go
linters-install: linters-install:
$(GOCMD) get -u github.com/alecthomas/gometalinter @golangci-lint --version >/dev/null 2>&1 || { \
gometalinter --install echo "installing linting tools..."; \
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.41.1; \
}
lint: linters-install lint: linters-install
gometalinter --vendor --disable-all --enable=vet --enable=vetshadow --enable=golint --enable=maligned --enable=megacheck --enable=ineffassign --enable=misspell --enable=errcheck --enable=goconst ./... golangci-lint run
test: test:
$(GOCMD) test -cover -race ./... $(GOCMD) test -cover -race ./...

@ -1,11 +1,11 @@
Package validator Package validator
================ =================
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) <img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v10/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-9.29.1-green.svg) ![Project status](https://img.shields.io/badge/version-10.12.0-green.svg)
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator) [![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github)](https://coveralls.io/github/go-playground/validator?branch=v9) [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
[![GoDoc](https://godoc.org/gopkg.in/go-playground/validator.v9?status.svg)](https://godoc.org/gopkg.in/go-playground/validator.v9) [![GoDoc](https://godoc.org/github.com/go-playground/validator?status.svg)](https://pkg.go.dev/github.com/go-playground/validator/v10)
![License](https://img.shields.io/dub/l/vibe-d.svg) ![License](https://img.shields.io/dub/l/vibe-d.svg)
Package validator implements value validations for structs and individual fields based on tags. Package validator implements value validations for structs and individual fields based on tags.
@ -20,18 +20,18 @@ It has the following **unique** features:
- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs - Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs
- Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError - Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError
- Customizable i18n aware error messages. - Customizable i18n aware error messages.
- Default validator for the [gin](https://github.com/gin-gonic/gin) web framework; upgrading from v8 to v9 in gin see [here](https://github.com/go-playground/validator/tree/v9/_examples/gin-upgrading-overriding) - Default validator for the [gin](https://github.com/gin-gonic/gin) web framework; upgrading from v8 to v9 in gin see [here](https://github.com/go-playground/validator/tree/master/_examples/gin-upgrading-overriding)
Installation Installation
------------ ------------
Use go get. Use go get.
go get gopkg.in/go-playground/validator.v9 go get github.com/go-playground/validator/v10
Then import the validator package into your own code. Then import the validator package into your own code.
import "gopkg.in/go-playground/validator.v9" import "github.com/go-playground/validator/v10"
Error Return Value Error Return Value
------- -------
@ -43,7 +43,7 @@ They return type error to avoid the issue discussed in the following, where err
* http://stackoverflow.com/a/29138676/3158232 * http://stackoverflow.com/a/29138676/3158232
* https://github.com/go-playground/validator/issues/134 * https://github.com/go-playground/validator/issues/134
Validator only InvalidValidationError for bad validation input, nil or ValidationErrors as type error; so, in your code all you need to do is check if the error returned is not nil, and if it's not check if error is InvalidValidationError ( if necessary, most of the time it isn't ) type cast it to type ValidationErrors like so: Validator returns only InvalidValidationError for bad validation input, nil or ValidationErrors as type error; so, in your code all you need to do is check if the error returned is not nil, and if it's not check if error is InvalidValidationError ( if necessary, most of the time it isn't ) type cast it to type ValidationErrors like so:
```go ```go
err := validate.Struct(mystruct) err := validate.Struct(mystruct)
@ -53,17 +53,207 @@ validationErrors := err.(validator.ValidationErrors)
Usage and documentation Usage and documentation
------ ------
Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usage docs. Please see https://pkg.go.dev/github.com/go-playground/validator/v10 for detailed usage docs.
##### Examples: ##### Examples:
- [Simple](https://github.com/go-playground/validator/blob/v9/_examples/simple/main.go) - [Simple](https://github.com/go-playground/validator/blob/master/_examples/simple/main.go)
- [Custom Field Types](https://github.com/go-playground/validator/blob/v9/_examples/custom/main.go) - [Custom Field Types](https://github.com/go-playground/validator/blob/master/_examples/custom/main.go)
- [Struct Level](https://github.com/go-playground/validator/blob/v9/_examples/struct-level/main.go) - [Struct Level](https://github.com/go-playground/validator/blob/master/_examples/struct-level/main.go)
- [Translations & Custom Errors](https://github.com/go-playground/validator/blob/v9/_examples/translations/main.go) - [Translations & Custom Errors](https://github.com/go-playground/validator/blob/master/_examples/translations/main.go)
- [Gin upgrade and/or override validator](https://github.com/go-playground/validator/tree/v9/_examples/gin-upgrading-overriding) - [Gin upgrade and/or override validator](https://github.com/go-playground/validator/tree/v9/_examples/gin-upgrading-overriding)
- [wash - an example application putting it all together](https://github.com/bluesuncorp/wash) - [wash - an example application putting it all together](https://github.com/bluesuncorp/wash)
Baked-in Validations
------
### Fields:
| Tag | Description |
| - | - |
| eqcsfield | Field Equals Another Field (relative)|
| eqfield | Field Equals Another Field |
| fieldcontains | Check the indicated characters are present in the Field |
| fieldexcludes | Check the indicated characters are not present in the field |
| gtcsfield | Field Greater Than Another Relative Field |
| gtecsfield | Field Greater Than or Equal To Another Relative Field |
| gtefield | Field Greater Than or Equal To Another Field |
| gtfield | Field Greater Than Another Field |
| ltcsfield | Less Than Another Relative Field |
| ltecsfield | Less Than or Equal To Another Relative Field |
| ltefield | Less Than or Equal To Another Field |
| ltfield | Less Than Another Field |
| necsfield | Field Does Not Equal Another Field (relative) |
| nefield | Field Does Not Equal Another Field |
### Network:
| Tag | Description |
| - | - |
| cidr | Classless Inter-Domain Routing CIDR |
| cidrv4 | Classless Inter-Domain Routing CIDRv4 |
| cidrv6 | Classless Inter-Domain Routing CIDRv6 |
| datauri | Data URL |
| fqdn | Full Qualified Domain Name (FQDN) |
| hostname | Hostname RFC 952 |
| hostname_port | HostPort |
| hostname_rfc1123 | Hostname RFC 1123 |
| 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 MAC |
| tcp4_addr | Transmission Control Protocol Address TCPv4 |
| 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 end point Address |
| uri | URI String |
| url | URL String |
| http_url | HTTP URL String |
| url_encoded | URL Encoded |
| urn_rfc2141 | Urn RFC 2141 String |
### Strings:
| Tag | Description |
| - | - |
| alpha | Alpha Only |
| alphanum | Alphanumeric |
| alphanumunicode | Alphanumeric Unicode |
| alphaunicode | Alpha Unicode |
| ascii | ASCII |
| boolean | Boolean |
| contains | Contains |
| containsany | Contains Any |
| containsrune | Contains Rune |
| endsnotwith | Ends Not With |
| endswith | Ends With |
| excludes | Excludes |
| excludesall | Excludes All |
| excludesrune | Excludes Rune |
| lowercase | Lowercase |
| multibyte | Multi-Byte Characters |
| number | Number |
| numeric | Numeric |
| printascii | Printable ASCII |
| startsnotwith | Starts Not With |
| startswith | Starts With |
| uppercase | Uppercase |
### Format:
| Tag | Description |
| - | - |
| base64 | Base64 String |
| base64url | Base64URL String |
| base64rawurl | Base64RawURL String |
| bic | Business Identifier Code (ISO 9362) |
| bcp47_language_tag | Language tag (BCP 47) |
| btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| credit_card | Credit Card Number |
| mongodb | MongoDB ObjectID |
| cron | Cron |
| datetime | Datetime |
| e164 | e164 formatted phone number |
| email | E-mail String
| eth_addr | Ethereum Address |
| hexadecimal | Hexadecimal String |
| hexcolor | Hexcolor String |
| hsl | HSL String |
| hsla | HSLA String |
| html | HTML Tags |
| html_encoded | HTML Encoded |
| isbn | International Standard Book Number |
| isbn10 | International Standard Book Number 10 |
| isbn13 | International Standard Book Number 13 |
| iso3166_1_alpha2 | Two-letter country code (ISO 3166-1 alpha-2) |
| iso3166_1_alpha3 | Three-letter country code (ISO 3166-1 alpha-3) |
| iso3166_1_alpha_numeric | Numeric country code (ISO 3166-1 numeric) |
| iso3166_2 | Country subdivision code (ISO 3166-2) |
| iso4217 | Currency code (ISO 4217) |
| json | JSON |
| jwt | JSON Web Token (JWT) |
| latitude | Latitude |
| longitude | Longitude |
| luhn_checksum | Luhn Algorithm Checksum (for strings and (u)int) |
| postcode_iso3166_alpha2 | Postcode |
| postcode_iso3166_alpha2_field | Postcode |
| rgb | RGB String |
| rgba | RGBA String |
| ssn | Social Security Number SSN |
| timezone | Timezone |
| uuid | Universally Unique Identifier UUID |
| uuid3 | Universally Unique Identifier UUID v3 |
| uuid3_rfc4122 | Universally Unique Identifier UUID v3 RFC4122 |
| uuid4 | Universally Unique Identifier UUID v4 |
| uuid4_rfc4122 | Universally Unique Identifier UUID v4 RFC4122 |
| uuid5 | Universally Unique Identifier UUID v5 |
| uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 |
| uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 |
| md4 | MD4 hash |
| md5 | MD5 hash |
| sha256 | SHA256 hash |
| sha384 | SHA384 hash |
| sha512 | SHA512 hash |
| ripemd128 | RIPEMD-128 hash |
| ripemd128 | RIPEMD-160 hash |
| tiger128 | TIGER128 hash |
| tiger160 | TIGER160 hash |
| tiger192 | TIGER192 hash |
| semver | Semantic Versioning 2.0.0 |
| ulid | Universally Unique Lexicographically Sortable Identifier ULID |
| cve | Common Vulnerabilities and Exposures Identifier (CVE id) |
### Comparisons:
| Tag | Description |
| - | - |
| eq | Equals |
| eq_ignore_case | Equals ignoring case |
| gt | Greater than|
| gte | Greater than or equal |
| lt | Less Than |
| lte | Less Than or Equal |
| ne | Not Equal |
| ne_ignore_case | Not Equal ignoring case |
### Other:
| Tag | Description |
| - | - |
| dir | Existing Directory |
| dirpath | Directory Path |
| file | Existing File |
| filepath | File Path |
| isdefault | Is Default |
| len | Length |
| max | Maximum |
| min | Minimum |
| oneof | One Of |
| required | Required |
| required_if | Required If |
| required_unless | Required Unless |
| required_with | Required With |
| required_with_all | Required With All |
| required_without | Required Without |
| required_without_all | Required Without All |
| excluded_if | Excluded If |
| excluded_unless | Excluded Unless |
| excluded_with | Excluded With |
| excluded_with_all | Excluded With All |
| excluded_without | Excluded Without |
| excluded_without_all | Excluded Without All |
| unique | Unique |
#### Aliases:
| Tag | Description |
| - | - |
| iscolor | hexcolor\|rgb\|rgba\|hsl\|hsla |
| country_code | iso3166_1_alpha2\|iso3166_1_alpha3\|iso3166_1_alpha_numeric |
Benchmarks Benchmarks
------ ------
###### Run on MacBook Pro (15-inch, 2017) go version go1.10.2 darwin/amd64 ###### Run on MacBook Pro (15-inch, 2017) go version go1.10.2 darwin/amd64
@ -149,5 +339,10 @@ How to Contribute
Make a pull request... Make a pull request...
License License
------ -------
Distributed under MIT License, please see license file within the code for more details. Distributed under MIT License, please see license file within the code for more details.
Maintainers
-----------
This project has grown large enough that more than one person is required to properly support the community.
If you are interested in becoming a maintainer please reach out to me https://github.com/deankarn

@ -3,7 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// MyStruct .. // MyStruct ..

@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// DbBackedUser User struct // DbBackedUser User struct

@ -3,7 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// Test ... // Test ...

@ -5,7 +5,7 @@ import (
"sync" "sync"
"github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/binding"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
type defaultValidator struct { type defaultValidator struct {
@ -22,7 +22,7 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error {
v.lazyinit() v.lazyinit()
if err := v.validate.Struct(obj); err != nil { if err := v.validate.Struct(obj); err != nil {
return error(err) return err
} }
} }

@ -0,0 +1,87 @@
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
var validate *validator.Validate
func main() {
validate = validator.New()
validateMap()
validateNestedMap()
}
func validateMap() {
user := map[string]interface{}{"name": "Arshiya Kiani", "email": "zytel3301@gmail.com"}
// Every rule will be applied to the item of the data that the offset of rule is pointing to.
// So if you have a field "email": "omitempty,required,email", the validator will apply these
// rules to offset of email in user data
rules := map[string]interface{}{"name": "required,min=8,max=32", "email": "omitempty,required,email"}
// ValidateMap will return map[string]error.
// The offset of every item in errs is the name of invalid field and the value
// is the message of error. If there was no error, ValidateMap method will
// return an EMPTY map of errors, not nil. If you want to check that
// if there was an error or not, you must check the length of the return value
errs := validate.ValidateMap(user, rules)
if len(errs) > 0 {
fmt.Println(errs)
// The user is invalid
}
// The user is valid
}
func validateNestedMap() {
data := map[string]interface{}{
"name": "Arshiya Kiani",
"email": "zytel3301@gmail.com",
"details": map[string]interface{}{
"family_members": map[string]interface{}{
"father_name": "Micheal",
"mother_name": "Hannah",
},
"salary": "1000",
"phones": []map[string]interface{}{
{
"number": "11-111-1111",
"remark": "home",
},
{
"number": "22-222-2222",
"remark": "work",
},
},
},
}
// Rules must be set as the structure as the data itself. If you want to dive into the
// map, just declare its rules as a map
rules := map[string]interface{}{
"name": "min=4,max=32",
"email": "required,email",
"details": map[string]interface{}{
"family_members": map[string]interface{}{
"father_name": "required,min=4,max=32",
"mother_name": "required,min=4,max=32",
},
"salary": "number",
"phones": map[string]interface{}{
"number": "required,min=4,max=32",
"remark": "required,min=1,max=32",
},
},
}
if len(validate.ValidateMap(data, rules)) == 0 {
// Data is valid
}
// Data is invalid
}

@ -3,7 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// User contains user information // User contains user information
@ -68,8 +68,8 @@ func validateStruct() {
fmt.Println(err.Namespace()) fmt.Println(err.Namespace())
fmt.Println(err.Field()) fmt.Println(err.Field())
fmt.Println(err.StructNamespace()) // can differ when a custom TagNameFunc is registered or fmt.Println(err.StructNamespace())
fmt.Println(err.StructField()) // by passing alt name to ReportError like below fmt.Println(err.StructField())
fmt.Println(err.Tag()) fmt.Println(err.Tag())
fmt.Println(err.ActualTag()) fmt.Println(err.ActualTag())
fmt.Println(err.Kind()) fmt.Println(err.Kind())

@ -1,19 +1,53 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"reflect"
"strings"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
type validationError struct {
Namespace string `json:"namespace"` // can differ when a custom TagNameFunc is registered or
Field string `json:"field"` // by passing alt name to ReportError like below
StructNamespace string `json:"structNamespace"`
StructField string `json:"structField"`
Tag string `json:"tag"`
ActualTag string `json:"actualTag"`
Kind string `json:"kind"`
Type string `json:"type"`
Value string `json:"value"`
Param string `json:"param"`
Message string `json:"message"`
}
type Gender uint
const (
Male Gender = iota + 1
Female
Intersex
)
func (gender Gender) String() string {
terms := []string{"Male", "Female", "Intersex"}
if gender < Male || gender > Intersex {
return "unknown"
}
return terms[gender]
}
// User contains user information // User contains user information
type User struct { type User struct {
FirstName string `json:"fname"` FirstName string `json:"fname"`
LastName string `json:"lname"` LastName string `json:"lname"`
Age uint8 `validate:"gte=0,lte=130"` Age uint8 `validate:"gte=0,lte=130"`
Email string `validate:"required,email"` Email string `json:"e-mail" validate:"required,email"`
FavouriteColor string `validate:"hexcolor|rgb|rgba"` FavouriteColor string `validate:"hexcolor|rgb|rgba"`
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
Gender Gender `json:"gender" validate:"required,gender_custom_validation"`
} }
// Address houses a users address information // Address houses a users address information
@ -31,11 +65,31 @@ func main() {
validate = validator.New() validate = validator.New()
// register function to get tag name from json tags.
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
// register validation for 'User' // register validation for 'User'
// NOTE: only have to register a non-pointer type for 'User', validator // NOTE: only have to register a non-pointer type for 'User', validator
// interanlly dereferences during it's type checks. // internally dereferences during it's type checks.
validate.RegisterStructValidation(UserStructLevelValidation, User{}) validate.RegisterStructValidation(UserStructLevelValidation, User{})
// register a custom validation for user genre on a line
// validates that an enum is within the interval
err := validate.RegisterValidation("gender_custom_validation", func(fl validator.FieldLevel) bool {
value := fl.Field().Interface().(Gender)
return value.String() != "unknown"
})
if err != nil {
fmt.Println(err)
return
}
// build 'User' info, normally posted data etc... // build 'User' info, normally posted data etc...
address := &Address{ address := &Address{
Street: "Eavesdown Docks", Street: "Eavesdown Docks",
@ -48,13 +102,13 @@ func main() {
FirstName: "", FirstName: "",
LastName: "", LastName: "",
Age: 45, Age: 45,
Email: "Badger.Smith@gmail.com", Email: "Badger.Smith@gmail",
FavouriteColor: "#000", FavouriteColor: "#000",
Addresses: []*Address{address}, Addresses: []*Address{address},
} }
// returns InvalidValidationError for bad validation input, nil or ValidationErrors ( []FieldError ) // returns InvalidValidationError for bad validation input, nil or ValidationErrors ( []FieldError )
err := validate.Struct(user) err = validate.Struct(user)
if err != nil { if err != nil {
// this check is only needed when your code could produce // this check is only needed when your code could produce
@ -66,18 +120,27 @@ func main() {
} }
for _, err := range err.(validator.ValidationErrors) { for _, err := range err.(validator.ValidationErrors) {
e := validationError{
fmt.Println(err.Namespace()) Namespace: err.Namespace(),
fmt.Println(err.Field()) Field: err.Field(),
fmt.Println(err.StructNamespace()) // can differ when a custom TagNameFunc is registered or StructNamespace: err.StructNamespace(),
fmt.Println(err.StructField()) // by passing alt name to ReportError like below StructField: err.StructField(),
fmt.Println(err.Tag()) Tag: err.Tag(),
fmt.Println(err.ActualTag()) ActualTag: err.ActualTag(),
fmt.Println(err.Kind()) Kind: fmt.Sprintf("%v", err.Kind()),
fmt.Println(err.Type()) Type: fmt.Sprintf("%v", err.Type()),
fmt.Println(err.Value()) Value: fmt.Sprintf("%v", err.Value()),
fmt.Println(err.Param()) Param: err.Param(),
fmt.Println() Message: err.Error(),
}
indent, err := json.MarshalIndent(e, "", " ")
if err != nil {
fmt.Println(err)
panic(err)
}
fmt.Println(string(indent))
} }
// from here you can create your own error messages in whatever language you wish // from here you can create your own error messages in whatever language you wish
@ -101,8 +164,8 @@ func UserStructLevelValidation(sl validator.StructLevel) {
user := sl.Current().Interface().(User) user := sl.Current().Interface().(User)
if len(user.FirstName) == 0 && len(user.LastName) == 0 { if len(user.FirstName) == 0 && len(user.LastName) == 0 {
sl.ReportError(user.FirstName, "FirstName", "fname", "fnameorlname", "") sl.ReportError(user.FirstName, "fname", "FirstName", "fnameorlname", "")
sl.ReportError(user.LastName, "LastName", "lname", "fnameorlname", "") sl.ReportError(user.LastName, "lname", "LastName", "fnameorlname", "")
} }
// plus can do more, even with different tag than "fnameorlname" // plus can do more, even with different tag than "fnameorlname"

@ -0,0 +1,92 @@
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Data struct {
Name string
Email string
Details *Details
}
type Details struct {
FamilyMembers *FamilyMembers
Salary string
}
type FamilyMembers struct {
FatherName string
MotherName string
}
type Data2 struct {
Name string
Age uint32
}
var validate = validator.New()
func main() {
validateStruct()
// output
// Key: 'Data2.Name' Error:Field validation for 'Name' failed on the 'min' tag
// Key: 'Data2.Age' Error:Field validation for 'Age' failed on the 'max' tag
validateStructNested()
// output
// Key: 'Data.Name' Error:Field validation for 'Name' failed on the 'max' tag
// Key: 'Data.Details.FamilyMembers' Error:Field validation for 'FamilyMembers' failed on the 'required' tag
}
func validateStruct() {
data := Data2{
Name: "leo",
Age: 1000,
}
rules := map[string]string{
"Name": "min=4,max=6",
"Age": "min=4,max=6",
}
validate.RegisterStructValidationMapRules(rules, Data2{})
err := validate.Struct(data)
fmt.Println(err)
fmt.Println()
}
func validateStructNested() {
data := Data{
Name: "11sdfddd111",
Email: "zytel3301@mail.com",
Details: &Details{
Salary: "1000",
},
}
rules1 := map[string]string{
"Name": "min=4,max=6",
"Email": "required,email",
"Details": "required",
}
rules2 := map[string]string{
"Salary": "number",
"FamilyMembers": "required",
}
rules3 := map[string]string{
"FatherName": "required,min=4,max=32",
"MotherName": "required,min=4,max=32",
}
validate.RegisterStructValidationMapRules(rules1, Data{})
validate.RegisterStructValidationMapRules(rules2, Details{})
validate.RegisterStructValidationMapRules(rules3, FamilyMembers{})
err := validate.Struct(data)
fmt.Println(err)
}

@ -5,8 +5,8 @@ import (
"github.com/go-playground/locales/en" "github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
en_translations "gopkg.in/go-playground/validator.v9/translations/en" en_translations "github.com/go-playground/validator/v10/translations/en"
) )
// User contains user information // User contains user information

File diff suppressed because it is too large Load Diff

@ -8,236 +8,200 @@ import (
) )
func BenchmarkFieldSuccess(b *testing.B) { func BenchmarkFieldSuccess(b *testing.B) {
validate := New() validate := New()
s := "1" s := "1"
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(&s, "len=1") _ = validate.Var(&s, "len=1")
} }
} }
func BenchmarkFieldSuccessParallel(b *testing.B) { func BenchmarkFieldSuccessParallel(b *testing.B) {
validate := New() validate := New()
s := "1" s := "1"
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(&s, "len=1") _ = validate.Var(&s, "len=1")
} }
}) })
} }
func BenchmarkFieldFailure(b *testing.B) { func BenchmarkFieldFailure(b *testing.B) {
validate := New() validate := New()
s := "12" s := "12"
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(&s, "len=1") _ = validate.Var(&s, "len=1")
} }
} }
func BenchmarkFieldFailureParallel(b *testing.B) { func BenchmarkFieldFailureParallel(b *testing.B) {
validate := New() validate := New()
s := "12" s := "12"
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(&s, "len=1") _ = validate.Var(&s, "len=1")
} }
}) })
} }
func BenchmarkFieldArrayDiveSuccess(b *testing.B) { func BenchmarkFieldArrayDiveSuccess(b *testing.B) {
validate := New() validate := New()
m := []string{"val1", "val2", "val3"} m := []string{"val1", "val2", "val3"}
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(m, "required,dive,required") _ = validate.Var(m, "required,dive,required")
} }
} }
func BenchmarkFieldArrayDiveSuccessParallel(b *testing.B) { func BenchmarkFieldArrayDiveSuccessParallel(b *testing.B) {
validate := New() validate := New()
m := []string{"val1", "val2", "val3"} m := []string{"val1", "val2", "val3"}
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(m, "required,dive,required") _ = validate.Var(m, "required,dive,required")
} }
}) })
} }
func BenchmarkFieldArrayDiveFailure(b *testing.B) { func BenchmarkFieldArrayDiveFailure(b *testing.B) {
validate := New() validate := New()
m := []string{"val1", "", "val3"} m := []string{"val1", "", "val3"}
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(m, "required,dive,required") _ = validate.Var(m, "required,dive,required")
} }
} }
func BenchmarkFieldArrayDiveFailureParallel(b *testing.B) { func BenchmarkFieldArrayDiveFailureParallel(b *testing.B) {
validate := New() validate := New()
m := []string{"val1", "", "val3"} m := []string{"val1", "", "val3"}
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(m, "required,dive,required") _ = validate.Var(m, "required,dive,required")
} }
}) })
} }
func BenchmarkFieldMapDiveSuccess(b *testing.B) { func BenchmarkFieldMapDiveSuccess(b *testing.B) {
validate := New() validate := New()
m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"} m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(m, "required,dive,required") _ = validate.Var(m, "required,dive,required")
} }
} }
func BenchmarkFieldMapDiveSuccessParallel(b *testing.B) { func BenchmarkFieldMapDiveSuccessParallel(b *testing.B) {
validate := New() validate := New()
m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"} m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(m, "required,dive,required") _ = validate.Var(m, "required,dive,required")
} }
}) })
} }
func BenchmarkFieldMapDiveFailure(b *testing.B) { func BenchmarkFieldMapDiveFailure(b *testing.B) {
validate := New() validate := New()
m := map[string]string{"": "", "val3": "val3"} m := map[string]string{"": "", "val3": "val3"}
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(m, "required,dive,required") _ = validate.Var(m, "required,dive,required")
} }
} }
func BenchmarkFieldMapDiveFailureParallel(b *testing.B) { func BenchmarkFieldMapDiveFailureParallel(b *testing.B) {
validate := New() validate := New()
m := map[string]string{"": "", "val3": "val3"} m := map[string]string{"": "", "val3": "val3"}
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(m, "required,dive,required") _ = validate.Var(m, "required,dive,required")
} }
}) })
} }
func BenchmarkFieldMapDiveWithKeysSuccess(b *testing.B) { func BenchmarkFieldMapDiveWithKeysSuccess(b *testing.B) {
validate := New() validate := New()
m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"} m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(m, "required,dive,keys,required,endkeys,required") _ = validate.Var(m, "required,dive,keys,required,endkeys,required")
} }
} }
func BenchmarkFieldMapDiveWithKeysSuccessParallel(b *testing.B) { func BenchmarkFieldMapDiveWithKeysSuccessParallel(b *testing.B) {
validate := New() validate := New()
m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"} m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(m, "required,dive,keys,required,endkeys,required") _ = validate.Var(m, "required,dive,keys,required,endkeys,required")
} }
}) })
} }
func BenchmarkFieldMapDiveWithKeysFailure(b *testing.B) { func BenchmarkFieldMapDiveWithKeysFailure(b *testing.B) {
validate := New() validate := New()
m := map[string]string{"": "", "val3": "val3"} m := map[string]string{"": "", "val3": "val3"}
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(m, "required,dive,keys,required,endkeys,required") _ = validate.Var(m, "required,dive,keys,required,endkeys,required")
} }
} }
func BenchmarkFieldMapDiveWithKeysFailureParallel(b *testing.B) { func BenchmarkFieldMapDiveWithKeysFailureParallel(b *testing.B) {
validate := New() validate := New()
m := map[string]string{"": "", "val3": "val3"} m := map[string]string{"": "", "val3": "val3"}
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(m, "required,dive,keys,required,endkeys,required") _ = validate.Var(m, "required,dive,keys,required,endkeys,required")
} }
}) })
} }
func BenchmarkFieldCustomTypeSuccess(b *testing.B) { func BenchmarkFieldCustomTypeSuccess(b *testing.B) {
validate := New() validate := New()
validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
val := valuer{ val := valuer{
Name: "1", Name: "1",
} }
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(val, "len=1") _ = validate.Var(val, "len=1")
} }
} }
func BenchmarkFieldCustomTypeSuccessParallel(b *testing.B) { func BenchmarkFieldCustomTypeSuccessParallel(b *testing.B) {
validate := New() validate := New()
validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
val := valuer{ val := valuer{
Name: "1", Name: "1",
} }
@ -245,93 +209,80 @@ func BenchmarkFieldCustomTypeSuccessParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(val, "len=1") _ = validate.Var(val, "len=1")
} }
}) })
} }
func BenchmarkFieldCustomTypeFailure(b *testing.B) { func BenchmarkFieldCustomTypeFailure(b *testing.B) {
validate := New() validate := New()
validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
val := valuer{} val := valuer{}
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(val, "len=1") _ = validate.Var(val, "len=1")
} }
} }
func BenchmarkFieldCustomTypeFailureParallel(b *testing.B) { func BenchmarkFieldCustomTypeFailureParallel(b *testing.B) {
validate := New() validate := New()
validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
val := valuer{} val := valuer{}
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(val, "len=1") _ = validate.Var(val, "len=1")
} }
}) })
} }
func BenchmarkFieldOrTagSuccess(b *testing.B) { func BenchmarkFieldOrTagSuccess(b *testing.B) {
validate := New() validate := New()
s := "rgba(0,0,0,1)" s := "rgba(0,0,0,1)"
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(s, "rgb|rgba") _ = validate.Var(s, "rgb|rgba")
} }
} }
func BenchmarkFieldOrTagSuccessParallel(b *testing.B) { func BenchmarkFieldOrTagSuccessParallel(b *testing.B) {
validate := New() validate := New()
s := "rgba(0,0,0,1)" s := "rgba(0,0,0,1)"
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(s, "rgb|rgba") _ = validate.Var(s, "rgb|rgba")
} }
}) })
} }
func BenchmarkFieldOrTagFailure(b *testing.B) { func BenchmarkFieldOrTagFailure(b *testing.B) {
validate := New() validate := New()
s := "#000" s := "#000"
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Var(s, "rgb|rgba") _ = validate.Var(s, "rgb|rgba")
} }
} }
func BenchmarkFieldOrTagFailureParallel(b *testing.B) { func BenchmarkFieldOrTagFailureParallel(b *testing.B) {
validate := New() validate := New()
s := "#000" s := "#000"
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Var(s, "rgb|rgba") _ = validate.Var(s, "rgb|rgba")
} }
}) })
} }
func BenchmarkStructLevelValidationSuccess(b *testing.B) { func BenchmarkStructLevelValidationSuccess(b *testing.B) {
validate := New() validate := New()
validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{}) validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{})
@ -341,12 +292,11 @@ func BenchmarkStructLevelValidationSuccess(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(tst) _ = validate.Struct(tst)
} }
} }
func BenchmarkStructLevelValidationSuccessParallel(b *testing.B) { func BenchmarkStructLevelValidationSuccessParallel(b *testing.B) {
validate := New() validate := New()
validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{}) validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{})
@ -357,13 +307,12 @@ func BenchmarkStructLevelValidationSuccessParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(tst) _ = validate.Struct(tst)
} }
}) })
} }
func BenchmarkStructLevelValidationFailure(b *testing.B) { func BenchmarkStructLevelValidationFailure(b *testing.B) {
validate := New() validate := New()
validate.RegisterStructValidation(StructValidationTestStruct, TestStruct{}) validate.RegisterStructValidation(StructValidationTestStruct, TestStruct{})
@ -373,12 +322,11 @@ func BenchmarkStructLevelValidationFailure(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(tst) _ = validate.Struct(tst)
} }
} }
func BenchmarkStructLevelValidationFailureParallel(b *testing.B) { func BenchmarkStructLevelValidationFailureParallel(b *testing.B) {
validate := New() validate := New()
validate.RegisterStructValidation(StructValidationTestStruct, TestStruct{}) validate.RegisterStructValidation(StructValidationTestStruct, TestStruct{})
@ -389,13 +337,12 @@ func BenchmarkStructLevelValidationFailureParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(tst) _ = validate.Struct(tst)
} }
}) })
} }
func BenchmarkStructSimpleCustomTypeSuccess(b *testing.B) { func BenchmarkStructSimpleCustomTypeSuccess(b *testing.B) {
validate := New() validate := New()
validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
@ -412,15 +359,13 @@ func BenchmarkStructSimpleCustomTypeSuccess(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(validFoo) _ = validate.Struct(validFoo)
} }
} }
func BenchmarkStructSimpleCustomTypeSuccessParallel(b *testing.B) { func BenchmarkStructSimpleCustomTypeSuccessParallel(b *testing.B) {
validate := New() validate := New()
validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
val := valuer{ val := valuer{
Name: "1", Name: "1",
} }
@ -429,19 +374,17 @@ func BenchmarkStructSimpleCustomTypeSuccessParallel(b *testing.B) {
Valuer valuer `validate:"len=1"` Valuer valuer `validate:"len=1"`
IntValue int `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"`
} }
validFoo := &Foo{Valuer: val, IntValue: 7} validFoo := &Foo{Valuer: val, IntValue: 7}
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(validFoo) _ = validate.Struct(validFoo)
} }
}) })
} }
func BenchmarkStructSimpleCustomTypeFailure(b *testing.B) { func BenchmarkStructSimpleCustomTypeFailure(b *testing.B) {
validate := New() validate := New()
validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
@ -451,17 +394,15 @@ func BenchmarkStructSimpleCustomTypeFailure(b *testing.B) {
Valuer valuer `validate:"len=1"` Valuer valuer `validate:"len=1"`
IntValue int `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"`
} }
validFoo := &Foo{Valuer: val, IntValue: 3} validFoo := &Foo{Valuer: val, IntValue: 3}
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(validFoo) _ = validate.Struct(validFoo)
} }
} }
func BenchmarkStructSimpleCustomTypeFailureParallel(b *testing.B) { func BenchmarkStructSimpleCustomTypeFailureParallel(b *testing.B) {
validate := New() validate := New()
validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{})
@ -471,19 +412,17 @@ func BenchmarkStructSimpleCustomTypeFailureParallel(b *testing.B) {
Valuer valuer `validate:"len=1"` Valuer valuer `validate:"len=1"`
IntValue int `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"`
} }
validFoo := &Foo{Valuer: val, IntValue: 3} validFoo := &Foo{Valuer: val, IntValue: 3}
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(validate.Struct(validFoo)) _ = validate.Struct(validate.Struct(validFoo))
} }
}) })
} }
func BenchmarkStructFilteredSuccess(b *testing.B) { func BenchmarkStructFilteredSuccess(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -494,21 +433,18 @@ func BenchmarkStructFilteredSuccess(b *testing.B) {
test := &Test{ test := &Test{
Name: "Joey Bloggs", Name: "Joey Bloggs",
} }
byts := []byte("Name") byts := []byte("Name")
fn := func(ns []byte) bool { fn := func(ns []byte) bool {
return !bytes.HasSuffix(ns, byts) return !bytes.HasSuffix(ns, byts)
} }
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.StructFiltered(test, fn) _ = validate.StructFiltered(test, fn)
} }
} }
func BenchmarkStructFilteredSuccessParallel(b *testing.B) { func BenchmarkStructFilteredSuccessParallel(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -519,9 +455,7 @@ func BenchmarkStructFilteredSuccessParallel(b *testing.B) {
test := &Test{ test := &Test{
Name: "Joey Bloggs", Name: "Joey Bloggs",
} }
byts := []byte("Name") byts := []byte("Name")
fn := func(ns []byte) bool { fn := func(ns []byte) bool {
return !bytes.HasSuffix(ns, byts) return !bytes.HasSuffix(ns, byts)
} }
@ -529,13 +463,12 @@ func BenchmarkStructFilteredSuccessParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.StructFiltered(test, fn) _ = validate.StructFiltered(test, fn)
} }
}) })
} }
func BenchmarkStructFilteredFailure(b *testing.B) { func BenchmarkStructFilteredFailure(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -555,12 +488,11 @@ func BenchmarkStructFilteredFailure(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.StructFiltered(test, fn) _ = validate.StructFiltered(test, fn)
} }
} }
func BenchmarkStructFilteredFailureParallel(b *testing.B) { func BenchmarkStructFilteredFailureParallel(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -571,9 +503,7 @@ func BenchmarkStructFilteredFailureParallel(b *testing.B) {
test := &Test{ test := &Test{
Name: "Joey Bloggs", Name: "Joey Bloggs",
} }
byts := []byte("NickName") byts := []byte("NickName")
fn := func(ns []byte) bool { fn := func(ns []byte) bool {
return !bytes.HasSuffix(ns, byts) return !bytes.HasSuffix(ns, byts)
} }
@ -581,13 +511,12 @@ func BenchmarkStructFilteredFailureParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.StructFiltered(test, fn) _ = validate.StructFiltered(test, fn)
} }
}) })
} }
func BenchmarkStructPartialSuccess(b *testing.B) { func BenchmarkStructPartialSuccess(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -601,12 +530,11 @@ func BenchmarkStructPartialSuccess(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.StructPartial(test, "Name") _ = validate.StructPartial(test, "Name")
} }
} }
func BenchmarkStructPartialSuccessParallel(b *testing.B) { func BenchmarkStructPartialSuccessParallel(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -621,13 +549,12 @@ func BenchmarkStructPartialSuccessParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.StructPartial(test, "Name") _ = validate.StructPartial(test, "Name")
} }
}) })
} }
func BenchmarkStructPartialFailure(b *testing.B) { func BenchmarkStructPartialFailure(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -641,12 +568,11 @@ func BenchmarkStructPartialFailure(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.StructPartial(test, "NickName") _ = validate.StructPartial(test, "NickName")
} }
} }
func BenchmarkStructPartialFailureParallel(b *testing.B) { func BenchmarkStructPartialFailureParallel(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -661,13 +587,12 @@ func BenchmarkStructPartialFailureParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.StructPartial(test, "NickName") _ = validate.StructPartial(test, "NickName")
} }
}) })
} }
func BenchmarkStructExceptSuccess(b *testing.B) { func BenchmarkStructExceptSuccess(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -681,12 +606,11 @@ func BenchmarkStructExceptSuccess(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.StructExcept(test, "Nickname") _ = validate.StructExcept(test, "Nickname")
} }
} }
func BenchmarkStructExceptSuccessParallel(b *testing.B) { func BenchmarkStructExceptSuccessParallel(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -701,13 +625,12 @@ func BenchmarkStructExceptSuccessParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.StructExcept(test, "NickName") _ = validate.StructExcept(test, "NickName")
} }
}) })
} }
func BenchmarkStructExceptFailure(b *testing.B) { func BenchmarkStructExceptFailure(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -721,12 +644,11 @@ func BenchmarkStructExceptFailure(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.StructExcept(test, "Name") _ = validate.StructExcept(test, "Name")
} }
} }
func BenchmarkStructExceptFailureParallel(b *testing.B) { func BenchmarkStructExceptFailureParallel(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -741,13 +663,12 @@ func BenchmarkStructExceptFailureParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.StructExcept(test, "Name") _ = validate.StructExcept(test, "Name")
} }
}) })
} }
func BenchmarkStructSimpleCrossFieldSuccess(b *testing.B) { func BenchmarkStructSimpleCrossFieldSuccess(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -757,7 +678,6 @@ func BenchmarkStructSimpleCrossFieldSuccess(b *testing.B) {
now := time.Now().UTC() now := time.Now().UTC()
then := now.Add(time.Hour * 5) then := now.Add(time.Hour * 5)
test := &Test{ test := &Test{
Start: now, Start: now,
End: then, End: then,
@ -765,12 +685,11 @@ func BenchmarkStructSimpleCrossFieldSuccess(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(test) _ = validate.Struct(test)
} }
} }
func BenchmarkStructSimpleCrossFieldSuccessParallel(b *testing.B) { func BenchmarkStructSimpleCrossFieldSuccessParallel(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -780,7 +699,6 @@ func BenchmarkStructSimpleCrossFieldSuccessParallel(b *testing.B) {
now := time.Now().UTC() now := time.Now().UTC()
then := now.Add(time.Hour * 5) then := now.Add(time.Hour * 5)
test := &Test{ test := &Test{
Start: now, Start: now,
End: then, End: then,
@ -789,13 +707,12 @@ func BenchmarkStructSimpleCrossFieldSuccessParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(test) _ = validate.Struct(test)
} }
}) })
} }
func BenchmarkStructSimpleCrossFieldFailure(b *testing.B) { func BenchmarkStructSimpleCrossFieldFailure(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -813,12 +730,11 @@ func BenchmarkStructSimpleCrossFieldFailure(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(test) _ = validate.Struct(test)
} }
} }
func BenchmarkStructSimpleCrossFieldFailureParallel(b *testing.B) { func BenchmarkStructSimpleCrossFieldFailureParallel(b *testing.B) {
validate := New() validate := New()
type Test struct { type Test struct {
@ -828,7 +744,6 @@ func BenchmarkStructSimpleCrossFieldFailureParallel(b *testing.B) {
now := time.Now().UTC() now := time.Now().UTC()
then := now.Add(time.Hour * -5) then := now.Add(time.Hour * -5)
test := &Test{ test := &Test{
Start: now, Start: now,
End: then, End: then,
@ -836,13 +751,12 @@ func BenchmarkStructSimpleCrossFieldFailureParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(test) _ = validate.Struct(test)
} }
}) })
} }
func BenchmarkStructSimpleCrossStructCrossFieldSuccess(b *testing.B) { func BenchmarkStructSimpleCrossStructCrossFieldSuccess(b *testing.B) {
validate := New() validate := New()
type Inner struct { type Inner struct {
@ -855,11 +769,9 @@ func BenchmarkStructSimpleCrossStructCrossFieldSuccess(b *testing.B) {
} }
now := time.Now().UTC() now := time.Now().UTC()
inner := &Inner{ inner := &Inner{
Start: now, Start: now,
} }
outer := &Outer{ outer := &Outer{
Inner: inner, Inner: inner,
CreatedAt: now, CreatedAt: now,
@ -867,12 +779,11 @@ func BenchmarkStructSimpleCrossStructCrossFieldSuccess(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(outer) _ = validate.Struct(outer)
} }
} }
func BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel(b *testing.B) { func BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel(b *testing.B) {
validate := New() validate := New()
type Inner struct { type Inner struct {
@ -885,11 +796,9 @@ func BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel(b *testing.B) {
} }
now := time.Now().UTC() now := time.Now().UTC()
inner := &Inner{ inner := &Inner{
Start: now, Start: now,
} }
outer := &Outer{ outer := &Outer{
Inner: inner, Inner: inner,
CreatedAt: now, CreatedAt: now,
@ -898,15 +807,13 @@ func BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(outer) _ = validate.Struct(outer)
} }
}) })
} }
func BenchmarkStructSimpleCrossStructCrossFieldFailure(b *testing.B) { func BenchmarkStructSimpleCrossStructCrossFieldFailure(b *testing.B) {
validate := New() validate := New()
type Inner struct { type Inner struct {
Start time.Time Start time.Time
} }
@ -930,12 +837,11 @@ func BenchmarkStructSimpleCrossStructCrossFieldFailure(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(outer) _ = validate.Struct(outer)
} }
} }
func BenchmarkStructSimpleCrossStructCrossFieldFailureParallel(b *testing.B) { func BenchmarkStructSimpleCrossStructCrossFieldFailureParallel(b *testing.B) {
validate := New() validate := New()
type Inner struct { type Inner struct {
@ -962,15 +868,13 @@ func BenchmarkStructSimpleCrossStructCrossFieldFailureParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(outer) _ = validate.Struct(outer)
} }
}) })
} }
func BenchmarkStructSimpleSuccess(b *testing.B) { func BenchmarkStructSimpleSuccess(b *testing.B) {
validate := New() validate := New()
type Foo struct { type Foo struct {
StringValue string `validate:"min=5,max=10"` StringValue string `validate:"min=5,max=10"`
IntValue int `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"`
@ -980,33 +884,28 @@ func BenchmarkStructSimpleSuccess(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(validFoo) _ = validate.Struct(validFoo)
} }
} }
func BenchmarkStructSimpleSuccessParallel(b *testing.B) { func BenchmarkStructSimpleSuccessParallel(b *testing.B) {
validate := New() validate := New()
type Foo struct { type Foo struct {
StringValue string `validate:"min=5,max=10"` StringValue string `validate:"min=5,max=10"`
IntValue int `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"`
} }
validFoo := &Foo{StringValue: "Foobar", IntValue: 7} validFoo := &Foo{StringValue: "Foobar", IntValue: 7}
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(validFoo) _ = validate.Struct(validFoo)
} }
}) })
} }
func BenchmarkStructSimpleFailure(b *testing.B) { func BenchmarkStructSimpleFailure(b *testing.B) {
validate := New() validate := New()
type Foo struct { type Foo struct {
StringValue string `validate:"min=5,max=10"` StringValue string `validate:"min=5,max=10"`
IntValue int `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"`
@ -1016,14 +915,12 @@ func BenchmarkStructSimpleFailure(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(invalidFoo) _ = validate.Struct(invalidFoo)
} }
} }
func BenchmarkStructSimpleFailureParallel(b *testing.B) { func BenchmarkStructSimpleFailureParallel(b *testing.B) {
validate := New() validate := New()
type Foo struct { type Foo struct {
StringValue string `validate:"min=5,max=10"` StringValue string `validate:"min=5,max=10"`
IntValue int `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"`
@ -1034,15 +931,13 @@ func BenchmarkStructSimpleFailureParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(invalidFoo) _ = validate.Struct(invalidFoo)
} }
}) })
} }
func BenchmarkStructComplexSuccess(b *testing.B) { func BenchmarkStructComplexSuccess(b *testing.B) {
validate := New() validate := New()
tSuccess := &TestString{ tSuccess := &TestString{
Required: "Required", Required: "Required",
Len: "length==10", Len: "length==10",
@ -1072,14 +967,12 @@ func BenchmarkStructComplexSuccess(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(tSuccess) _ = validate.Struct(tSuccess)
} }
} }
func BenchmarkStructComplexSuccessParallel(b *testing.B) { func BenchmarkStructComplexSuccessParallel(b *testing.B) {
validate := New() validate := New()
tSuccess := &TestString{ tSuccess := &TestString{
Required: "Required", Required: "Required",
Len: "length==10", Len: "length==10",
@ -1110,15 +1003,13 @@ func BenchmarkStructComplexSuccessParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(tSuccess) _ = validate.Struct(tSuccess)
} }
}) })
} }
func BenchmarkStructComplexFailure(b *testing.B) { func BenchmarkStructComplexFailure(b *testing.B) {
validate := New() validate := New()
tFail := &TestString{ tFail := &TestString{
Required: "", Required: "",
Len: "", Len: "",
@ -1145,14 +1036,12 @@ func BenchmarkStructComplexFailure(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
validate.Struct(tFail) _ = validate.Struct(tFail)
} }
} }
func BenchmarkStructComplexFailureParallel(b *testing.B) { func BenchmarkStructComplexFailureParallel(b *testing.B) {
validate := New() validate := New()
tFail := &TestString{ tFail := &TestString{
Required: "", Required: "",
Len: "", Len: "",
@ -1180,7 +1069,7 @@ func BenchmarkStructComplexFailureParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
validate.Struct(tFail) _ = validate.Struct(tFail)
} }
}) })
} }
@ -1193,7 +1082,7 @@ func BenchmarkOneof(b *testing.B) {
w := &TestOneof{Color: "green"} w := &TestOneof{Color: "green"}
val := New() val := New()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
val.Struct(w) _ = val.Struct(w)
} }
} }
@ -1204,7 +1093,7 @@ func BenchmarkOneofParallel(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
val.Struct(w) _ = val.Struct(w)
} }
}) })
} }

@ -39,9 +39,7 @@ func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) {
} }
func (sc *structCache) Set(key reflect.Type, value *cStruct) { func (sc *structCache) Set(key reflect.Type, value *cStruct) {
m := sc.m.Load().(map[reflect.Type]*cStruct) m := sc.m.Load().(map[reflect.Type]*cStruct)
nm := make(map[reflect.Type]*cStruct, len(m)+1) nm := make(map[reflect.Type]*cStruct, len(m)+1)
for k, v := range m { for k, v := range m {
nm[k] = v nm[k] = v
@ -61,9 +59,7 @@ func (tc *tagCache) Get(key string) (c *cTag, found bool) {
} }
func (tc *tagCache) Set(key string, value *cTag) { func (tc *tagCache) Set(key string, value *cTag) {
m := tc.m.Load().(map[string]*cTag) m := tc.m.Load().(map[string]*cTag)
nm := make(map[string]*cTag, len(m)+1) nm := make(map[string]*cTag, len(m)+1)
for k, v := range m { for k, v := range m {
nm[k] = v nm[k] = v
@ -87,22 +83,22 @@ type cField struct {
} }
type cTag struct { type cTag struct {
tag string tag string
aliasTag string aliasTag string
actualAliasTag string actualAliasTag string
param string param string
keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation
next *cTag next *cTag
fn FuncCtx fn FuncCtx
typeof tagType typeof tagType
hasTag bool hasTag bool
hasAlias bool hasAlias bool
hasParam bool // true if parameter used eg. eq= where the equal sign has been set hasParam bool // true if parameter used eg. eq= where the equal sign has been set
isBlockEnd bool // indicates the current tag represents the last validation in the block isBlockEnd bool // indicates the current tag represents the last validation in the block
runValidationWhenNil bool
} }
func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct {
v.structCache.lock.Lock() v.structCache.lock.Lock()
defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise! defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise!
@ -118,6 +114,7 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]} cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]}
numFields := current.NumField() numFields := current.NumField()
rules := v.rules[typ]
var ctag *cTag var ctag *cTag
var fld reflect.StructField var fld reflect.StructField
@ -132,7 +129,11 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
continue continue
} }
tag = fld.Tag.Get(v.tagName) if rtag, ok := rules[fld.Name]; ok {
tag = rtag
} else {
tag = fld.Tag.Get(v.tagName)
}
if tag == skipValidationTag { if tag == skipValidationTag {
continue continue
@ -141,9 +142,7 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
customName = fld.Name customName = fld.Name
if v.hasTagNameFunc { if v.hasTagNameFunc {
name := v.tagNameFunc(fld) name := v.tagNameFunc(fld)
if len(name) > 0 { if len(name) > 0 {
customName = name customName = name
} }
@ -168,23 +167,17 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
namesEqual: fld.Name == customName, namesEqual: fld.Name == customName,
}) })
} }
v.structCache.Set(typ, cs) v.structCache.Set(typ, cs)
return cs return cs
} }
func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) { func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) {
var t string var t string
var ok bool
noAlias := len(alias) == 0 noAlias := len(alias) == 0
tags := strings.Split(tag, tagSeparator) tags := strings.Split(tag, tagSeparator)
for i := 0; i < len(tags); i++ { for i := 0; i < len(tags); i++ {
t = tags[i] t = tags[i]
if noAlias { if noAlias {
alias = t alias = t
} }
@ -198,14 +191,13 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
current.next, current = next, curr current.next, current = next, curr
} }
continue continue
} }
var prevTag tagType var prevTag tagType
if i == 0 { if i == 0 {
current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true, typeof: typeDefault}
firstCtag = current firstCtag = current
} else { } else {
prevTag = current.typeof prevTag = current.typeof
@ -214,7 +206,6 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
} }
switch t { switch t {
case diveTag: case diveTag:
current.typeof = typeDive current.typeof = typeDive
continue continue
@ -270,18 +261,14 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
continue continue
default: default:
if t == isdefault { if t == isdefault {
current.typeof = typeIsDefault current.typeof = typeIsDefault
} }
// if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
orVals := strings.Split(t, orSeparator) orVals := strings.Split(t, orSeparator)
for j := 0; j < len(orVals); j++ { for j := 0; j < len(orVals); j++ {
vals := strings.SplitN(orVals[j], tagKeySeparator, 2) vals := strings.SplitN(orVals[j], tagKeySeparator, 2)
if noAlias { if noAlias {
alias = vals[0] alias = vals[0]
current.aliasTag = alias current.aliasTag = alias
@ -300,7 +287,10 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName))) panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName)))
} }
if current.fn, ok = v.validations[current.tag]; !ok { if wrapper, ok := v.validations[current.tag]; ok {
current.fn = wrapper.fn
current.runValidationWhenNil = wrapper.runValidatinOnNil
} else {
panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName))) panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName)))
} }

File diff suppressed because it is too large Load Diff

@ -0,0 +1,79 @@
package validator
var iso4217 = map[string]bool{
"AFN": true, "EUR": true, "ALL": true, "DZD": true, "USD": true,
"AOA": true, "XCD": true, "ARS": true, "AMD": true, "AWG": true,
"AUD": true, "AZN": true, "BSD": true, "BHD": true, "BDT": true,
"BBD": true, "BYN": true, "BZD": true, "XOF": true, "BMD": true,
"INR": true, "BTN": true, "BOB": true, "BOV": true, "BAM": true,
"BWP": true, "NOK": true, "BRL": true, "BND": true, "BGN": true,
"BIF": true, "CVE": true, "KHR": true, "XAF": true, "CAD": true,
"KYD": true, "CLP": true, "CLF": true, "CNY": true, "COP": true,
"COU": true, "KMF": true, "CDF": true, "NZD": true, "CRC": true,
"HRK": true, "CUP": true, "CUC": true, "ANG": true, "CZK": true,
"DKK": true, "DJF": true, "DOP": true, "EGP": true, "SVC": true,
"ERN": true, "SZL": true, "ETB": true, "FKP": true, "FJD": true,
"XPF": true, "GMD": true, "GEL": true, "GHS": true, "GIP": true,
"GTQ": true, "GBP": true, "GNF": true, "GYD": true, "HTG": true,
"HNL": true, "HKD": true, "HUF": true, "ISK": true, "IDR": true,
"XDR": true, "IRR": true, "IQD": true, "ILS": true, "JMD": true,
"JPY": true, "JOD": true, "KZT": true, "KES": true, "KPW": true,
"KRW": true, "KWD": true, "KGS": true, "LAK": true, "LBP": true,
"LSL": true, "ZAR": true, "LRD": true, "LYD": true, "CHF": true,
"MOP": true, "MKD": true, "MGA": true, "MWK": true, "MYR": true,
"MVR": true, "MRU": true, "MUR": true, "XUA": true, "MXN": true,
"MXV": true, "MDL": true, "MNT": true, "MAD": true, "MZN": true,
"MMK": true, "NAD": true, "NPR": true, "NIO": true, "NGN": true,
"OMR": true, "PKR": true, "PAB": true, "PGK": true, "PYG": true,
"PEN": true, "PHP": true, "PLN": true, "QAR": true, "RON": true,
"RUB": true, "RWF": true, "SHP": true, "WST": true, "STN": true,
"SAR": true, "RSD": true, "SCR": true, "SLL": true, "SGD": true,
"XSU": true, "SBD": true, "SOS": true, "SSP": true, "LKR": true,
"SDG": true, "SRD": true, "SEK": true, "CHE": true, "CHW": true,
"SYP": true, "TWD": true, "TJS": true, "TZS": true, "THB": true,
"TOP": true, "TTD": true, "TND": true, "TRY": true, "TMT": true,
"UGX": true, "UAH": true, "AED": true, "USN": true, "UYU": true,
"UYI": true, "UYW": true, "UZS": true, "VUV": true, "VES": true,
"VND": true, "YER": true, "ZMW": true, "ZWL": true, "XBA": true,
"XBB": true, "XBC": true, "XBD": true, "XTS": true, "XXX": true,
"XAU": true, "XPD": true, "XPT": true, "XAG": true,
}
var iso4217_numeric = map[int]bool{
8: true, 12: true, 32: true, 36: true, 44: true,
48: true, 50: true, 51: true, 52: true, 60: true,
64: true, 68: true, 72: true, 84: true, 90: true,
96: true, 104: true, 108: true, 116: true, 124: true,
132: true, 136: true, 144: true, 152: true, 156: true,
170: true, 174: true, 188: true, 191: true, 192: true,
203: true, 208: true, 214: true, 222: true, 230: true,
232: true, 238: true, 242: true, 262: true, 270: true,
292: true, 320: true, 324: true, 328: true, 332: true,
340: true, 344: true, 348: true, 352: true, 356: true,
360: true, 364: true, 368: true, 376: true, 388: true,
392: true, 398: true, 400: true, 404: true, 408: true,
410: true, 414: true, 417: true, 418: true, 422: true,
426: true, 430: true, 434: true, 446: true, 454: true,
458: true, 462: true, 480: true, 484: true, 496: true,
498: true, 504: true, 512: true, 516: true, 524: true,
532: true, 533: true, 548: true, 554: true, 558: true,
566: true, 578: true, 586: true, 590: true, 598: true,
600: true, 604: true, 608: true, 634: true, 643: true,
646: true, 654: true, 682: true, 690: true, 694: true,
702: true, 704: true, 706: true, 710: true, 728: true,
748: true, 752: true, 756: true, 760: true, 764: true,
776: true, 780: true, 784: true, 788: true, 800: true,
807: true, 818: true, 826: true, 834: true, 840: true,
858: true, 860: true, 882: true, 886: true, 901: true,
927: true, 928: true, 929: true, 930: true, 931: true,
932: true, 933: true, 934: true, 936: true, 938: true,
940: true, 941: true, 943: true, 944: true, 946: true,
947: true, 948: true, 949: true, 950: true, 951: true,
952: true, 953: true, 955: true, 956: true, 957: true,
958: true, 959: true, 960: true, 961: true, 962: true,
963: true, 964: true, 965: true, 967: true, 968: true,
969: true, 970: true, 971: true, 972: true, 973: true,
975: true, 976: true, 977: true, 978: true, 979: true,
980: true, 981: true, 984: true, 985: true, 986: true,
990: true, 994: true, 997: true, 999: true,
}

696
doc.go

File diff suppressed because it is too large Load Diff

@ -44,12 +44,9 @@ func (ve ValidationErrors) Error() string {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
var fe *fieldError
for i := 0; i < len(ve); i++ { for i := 0; i < len(ve); i++ {
fe = ve[i].(*fieldError) buff.WriteString(ve[i].Error())
buff.WriteString(fe.Error())
buff.WriteString("\n") buff.WriteString("\n")
} }
@ -82,7 +79,7 @@ func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslati
// FieldError contains all functions to get error details // FieldError contains all functions to get error details
type FieldError interface { type FieldError interface {
// returns the validation tag that failed. if the // Tag returns the validation tag that failed. if the
// validation was an alias, this will return the // validation was an alias, this will return the
// alias name and not the underlying tag that failed. // alias name and not the underlying tag that failed.
// //
@ -90,7 +87,7 @@ type FieldError interface {
// will return "iscolor" // will return "iscolor"
Tag() string Tag() string
// returns the validation tag that failed, even if an // ActualTag returns the validation tag that failed, even if an
// alias the actual tag within the alias will be returned. // alias the actual tag within the alias will be returned.
// If an 'or' validation fails the entire or will be returned. // If an 'or' validation fails the entire or will be returned.
// //
@ -98,8 +95,8 @@ type FieldError interface {
// will return "hexcolor|rgb|rgba|hsl|hsla" // will return "hexcolor|rgb|rgba|hsl|hsla"
ActualTag() string ActualTag() string
// returns the namespace for the field error, with the tag // Namespace returns the namespace for the field error, with the tag
// name taking precedence over the fields actual name. // name taking precedence over the field's actual name.
// //
// eg. JSON name "User.fname" // eg. JSON name "User.fname"
// //
@ -109,33 +106,33 @@ type FieldError interface {
// using validate.Field(...) as there is no way to extract it's name // using validate.Field(...) as there is no way to extract it's name
Namespace() string Namespace() string
// returns the namespace for the field error, with the fields // StructNamespace returns the namespace for the field error, with the field's
// actual name. // actual name.
// //
// eq. "User.FirstName" see Namespace for comparison // eq. "User.FirstName" see Namespace for comparison
// //
// NOTE: this field can be blank when validating a single primitive field // NOTE: this field can be blank when validating a single primitive field
// using validate.Field(...) as there is no way to extract it's name // using validate.Field(...) as there is no way to extract its name
StructNamespace() string StructNamespace() string
// returns the fields name with the tag name taking precedence over the // Field returns the fields name with the tag name taking precedence over the
// fields actual name. // field's actual name.
// //
// eq. JSON name "fname" // eq. JSON name "fname"
// see StructField for comparison // see StructField for comparison
Field() string Field() string
// returns the fields actual name from the struct, when able to determine. // StructField returns the field's actual name from the struct, when able to determine.
// //
// eq. "FirstName" // eq. "FirstName"
// see Field for comparison // see Field for comparison
StructField() string StructField() string
// returns the actual fields value in case needed for creating the error // Value returns the actual field's value in case needed for creating the error
// message // message
Value() interface{} Value() interface{}
// returns the param value, in string form for comparison; this will also // Param returns the param value, in string form for comparison; this will also
// help with generating an error message // help with generating an error message
Param() string Param() string
@ -146,15 +143,18 @@ type FieldError interface {
// Type returns the Field's reflect Type // Type returns the Field's reflect Type
// //
// // eg. time.Time's type is time.Time // eg. time.Time's type is time.Time
Type() reflect.Type Type() reflect.Type
// returns the FieldError's translated error // Translate returns the FieldError's translated error
// from the provided 'ut.Translator' and registered 'TranslationFunc' // from the provided 'ut.Translator' and registered 'TranslationFunc'
// //
// NOTE: if no registered translator can be found it returns the same as // NOTE: if no registered translator can be found it returns the same as
// calling fe.Error() // calling fe.Error()
Translate(ut ut.Translator) string Translate(ut ut.Translator) string
// Error returns the FieldError's message
Error() string
} }
// compile time interface checks // compile time interface checks
@ -190,19 +190,19 @@ func (fe *fieldError) ActualTag() string {
} }
// Namespace returns the namespace for the field error, with the tag // Namespace returns the namespace for the field error, with the tag
// name taking precedence over the fields actual name. // name taking precedence over the field's actual name.
func (fe *fieldError) Namespace() string { func (fe *fieldError) Namespace() string {
return fe.ns return fe.ns
} }
// StructNamespace returns the namespace for the field error, with the fields // StructNamespace returns the namespace for the field error, with the field's
// actual name. // actual name.
func (fe *fieldError) StructNamespace() string { func (fe *fieldError) StructNamespace() string {
return fe.structNs return fe.structNs
} }
// Field returns the fields name with the tag name taking precedence over the // Field returns the field's name with the tag name taking precedence over the
// fields actual name. // field's actual name.
func (fe *fieldError) Field() string { func (fe *fieldError) Field() string {
return fe.ns[len(fe.ns)-int(fe.fieldLen):] return fe.ns[len(fe.ns)-int(fe.fieldLen):]
@ -218,13 +218,13 @@ func (fe *fieldError) Field() string {
// return fld // return fld
} }
// returns the fields actual name from the struct, when able to determine. // StructField returns the field's actual name from the struct, when able to determine.
func (fe *fieldError) StructField() string { func (fe *fieldError) StructField() string {
// return fe.structField // return fe.structField
return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):] return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):]
} }
// Value returns the actual fields value in case needed for creating the error // Value returns the actual field's value in case needed for creating the error
// message // message
func (fe *fieldError) Value() interface{} { func (fe *fieldError) Value() interface{} {
return fe.value return fe.value
@ -254,8 +254,8 @@ func (fe *fieldError) Error() string {
// Translate returns the FieldError's translated error // Translate returns the FieldError's translated error
// from the provided 'ut.Translator' and registered 'TranslationFunc' // from the provided 'ut.Translator' and registered 'TranslationFunc'
// //
// NOTE: is not registered translation can be found it returns the same // NOTE: if no registered translation can be found, it returns the original
// as calling fe.Error() // untranslated error message.
func (fe *fieldError) Translate(ut ut.Translator) string { func (fe *fieldError) Translate(ut ut.Translator) string {
m, ok := fe.v.transTagFunc[ut] m, ok := fe.v.transTagFunc[ut]

@ -1,83 +0,0 @@
package validator_test
// import (
// "fmt"
// "gopkg.in/go-playground/validator.v8"
// )
// func ExampleValidate_new() {
// config := &validator.Config{TagName: "validate"}
// validator.New(config)
// }
// func ExampleValidate_field() {
// // This should be stored somewhere globally
// var validate *validator.Validate
// config := &validator.Config{TagName: "validate"}
// validate = validator.New(config)
// i := 0
// errs := validate.Field(i, "gt=1,lte=10")
// err := errs.(validator.ValidationErrors)[""]
// fmt.Println(err.Field)
// fmt.Println(err.Tag)
// fmt.Println(err.Kind) // NOTE: Kind and Type can be different i.e. time Kind=struct and Type=time.Time
// fmt.Println(err.Type)
// fmt.Println(err.Param)
// fmt.Println(err.Value)
// //Output:
// //
// //gt
// //int
// //int
// //1
// //0
// }
// func ExampleValidate_struct() {
// // This should be stored somewhere globally
// var validate *validator.Validate
// config := &validator.Config{TagName: "validate"}
// validate = validator.New(config)
// type ContactInformation struct {
// Phone string `validate:"required"`
// Street string `validate:"required"`
// City string `validate:"required"`
// }
// type User struct {
// Name string `validate:"required,excludesall=!@#$%^&*()_+-=:;?/0x2C"` // 0x2C = comma (,)
// Age int8 `validate:"required,gt=0,lt=150"`
// Email string `validate:"email"`
// ContactInformation []*ContactInformation
// }
// contactInfo := &ContactInformation{
// Street: "26 Here Blvd.",
// City: "Paradeso",
// }
// user := &User{
// Name: "Joey Bloggs",
// Age: 31,
// Email: "joeybloggs@gmail.com",
// ContactInformation: []*ContactInformation{contactInfo},
// }
// errs := validate.Struct(user)
// for _, v := range errs.(validator.ValidationErrors) {
// fmt.Println(v.Field) // Phone
// fmt.Println(v.Tag) // required
// //... and so forth
// //Output:
// //Phone
// //required
// }
// }

@ -6,38 +6,61 @@ import "reflect"
// to validate a field // to validate a field
type FieldLevel interface { type FieldLevel interface {
// returns the top level struct, if any // Top returns the top level struct, if any
Top() reflect.Value Top() reflect.Value
// returns the current fields parent struct, if any or // Parent returns the current fields parent struct, if any or
// the comparison value if called 'VarWithValue' // the comparison value if called 'VarWithValue'
Parent() reflect.Value Parent() reflect.Value
// returns current field for validation // Field returns current field for validation
Field() reflect.Value Field() reflect.Value
// returns the field's name with the tag // FieldName returns the field's name with the tag
// name taking precedence over the fields actual name. // name taking precedence over the fields actual name.
FieldName() string FieldName() string
// returns the struct field's name // StructFieldName returns the struct field's name
StructFieldName() string StructFieldName() string
// returns param for validation against current field // Param returns param for validation against current field
Param() string Param() string
// GetTag returns the current validations tag name
GetTag() string
// ExtractType gets the actual underlying type of field value. // ExtractType gets the actual underlying type of field value.
// It will dive into pointers, customTypes and return you the // It will dive into pointers, customTypes and return you the
// underlying value and it's kind. // underlying value and it's kind.
ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool) ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)
// traverses the parent struct to retrieve a specific field denoted by the provided namespace // GetStructFieldOK traverses the parent struct to retrieve a specific field denoted by the provided namespace
// in the param and returns the field, field kind and whether is was successful in retrieving // in the param and returns the field, field kind and whether is was successful in retrieving
// the field at all. // the field at all.
// //
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist. // could not be retrieved because it didn't exist.
//
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
GetStructFieldOK() (reflect.Value, reflect.Kind, bool) GetStructFieldOK() (reflect.Value, reflect.Kind, bool)
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
//
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool)
// GetStructFieldOK2 traverses the parent struct to retrieve a specific field denoted by the provided namespace
// in the param and returns the field, field kind, if it's a nullable type and whether is was successful in retrieving
// the field at all.
//
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist.
GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool)
// GetStructFieldOKAdvanced2 is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool)
} }
var _ FieldLevel = new(validate) var _ FieldLevel = new(validate)
@ -48,11 +71,16 @@ func (v *validate) Field() reflect.Value {
} }
// FieldName returns the field's name with the tag // FieldName returns the field's name with the tag
// name takeing precedence over the fields actual name. // name taking precedence over the fields actual name.
func (v *validate) FieldName() string { func (v *validate) FieldName() string {
return v.cf.altName return v.cf.altName
} }
// GetTag returns the current validations tag name
func (v *validate) GetTag() string {
return v.ct.tag
}
// StructFieldName returns the struct field's name // StructFieldName returns the struct field's name
func (v *validate) StructFieldName() string { func (v *validate) StructFieldName() string {
return v.cf.name return v.cf.name
@ -64,6 +92,29 @@ func (v *validate) Param() string {
} }
// GetStructFieldOK returns Param returns param for validation against current field // GetStructFieldOK returns Param returns param for validation against current field
//
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) { func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) {
current, kind, _, found := v.getStructFieldOKInternal(v.slflParent, v.ct.param)
return current, kind, found
}
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
//
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) {
current, kind, _, found := v.GetStructFieldOKAdvanced2(val, namespace)
return current, kind, found
}
// GetStructFieldOK2 returns Param returns param for validation against current field
func (v *validate) GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) {
return v.getStructFieldOKInternal(v.slflParent, v.ct.param) return v.getStructFieldOKInternal(v.slflParent, v.ct.param)
} }
// GetStructFieldOKAdvanced2 is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
func (v *validate) GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) {
return v.getStructFieldOKInternal(val, namespace)
}

@ -0,0 +1,14 @@
module github.com/go-playground/validator/v10
go 1.18
require (
github.com/go-playground/assert/v2 v2.2.0
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
github.com/leodido/go-urn v1.2.2
golang.org/x/crypto v0.7.0
golang.org/x/text v0.8.0
)
require golang.org/x/sys v0.6.0 // indirect

@ -0,0 +1,31 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4=
github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -4,7 +4,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"github.com/andreiavrammsd/validator" "github.com/go-playground/validator/v10"
) )
// NotBlank is the validation function for validating if the current field // NotBlank is the validation function for validating if the current field
@ -14,7 +14,7 @@ func NotBlank(fl validator.FieldLevel) bool {
switch field.Kind() { switch field.Kind() {
case reflect.String: case reflect.String:
return len(strings.TrimSpace(field.String())) > 0 return len(strings.Trim(strings.TrimSpace(field.String()), "\x1c\x1d\x1e\x1f")) > 0
case reflect.Chan, reflect.Map, reflect.Slice, reflect.Array: case reflect.Chan, reflect.Map, reflect.Slice, reflect.Array:
return field.Len() > 0 return field.Len() > 0
case reflect.Ptr, reflect.Interface, reflect.Func: case reflect.Ptr, reflect.Interface, reflect.Func:

@ -3,8 +3,8 @@ package validators
import ( import (
"testing" "testing"
"github.com/andreiavrammsd/validator" "github.com/go-playground/assert/v2"
"gopkg.in/go-playground/assert.v1" "github.com/go-playground/validator/v10"
) )
type test struct { type test struct {
@ -24,7 +24,7 @@ func TestNotBlank(t *testing.T) {
// Errors // Errors
var x *int var x *int
invalid := test{ invalid := test{
String: " ", String: " \x1c\x1d\x1e\x1f\r\n",
Array: []int{}, Array: []int{},
Pointer: x, Pointer: x,
Number: 0, Number: 0,

@ -0,0 +1,173 @@
package validator
import "regexp"
var postCodePatternDict = map[string]string{
"GB": `^GIR[ ]?0AA|((AB|AL|B|BA|BB|BD|BH|BL|BN|BR|BS|BT|CA|CB|CF|CH|CM|CO|CR|CT|CV|CW|DA|DD|DE|DG|DH|DL|DN|DT|DY|E|EC|EH|EN|EX|FK|FY|G|GL|GY|GU|HA|HD|HG|HP|HR|HS|HU|HX|IG|IM|IP|IV|JE|KA|KT|KW|KY|L|LA|LD|LE|LL|LN|LS|LU|M|ME|MK|ML|N|NE|NG|NN|NP|NR|NW|OL|OX|PA|PE|PH|PL|PO|PR|RG|RH|RM|S|SA|SE|SG|SK|SL|SM|SN|SO|SP|SR|SS|ST|SW|SY|TA|TD|TF|TN|TQ|TR|TS|TW|UB|W|WA|WC|WD|WF|WN|WR|WS|WV|YO|ZE)(\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}))|BFPO[ ]?\d{1,4}$`,
"JE": `^JE\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}$`,
"GG": `^GY\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}$`,
"IM": `^IM\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}$`,
"US": `^\d{5}([ \-]\d{4})?$`,
"CA": `^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJ-NPRSTV-Z][ ]?\d[ABCEGHJ-NPRSTV-Z]\d$`,
"DE": `^\d{5}$`,
"JP": `^\d{3}-\d{4}$`,
"FR": `^\d{2}[ ]?\d{3}$`,
"AU": `^\d{4}$`,
"IT": `^\d{5}$`,
"CH": `^\d{4}$`,
"AT": `^\d{4}$`,
"ES": `^\d{5}$`,
"NL": `^\d{4}[ ]?[A-Z]{2}$`,
"BE": `^\d{4}$`,
"DK": `^\d{4}$`,
"SE": `^\d{3}[ ]?\d{2}$`,
"NO": `^\d{4}$`,
"BR": `^\d{5}[\-]?\d{3}$`,
"PT": `^\d{4}([\-]\d{3})?$`,
"FI": `^\d{5}$`,
"AX": `^22\d{3}$`,
"KR": `^\d{3}[\-]\d{3}$`,
"CN": `^\d{6}$`,
"TW": `^\d{3}(\d{2})?$`,
"SG": `^\d{6}$`,
"DZ": `^\d{5}$`,
"AD": `^AD\d{3}$`,
"AR": `^([A-HJ-NP-Z])?\d{4}([A-Z]{3})?$`,
"AM": `^(37)?\d{4}$`,
"AZ": `^\d{4}$`,
"BH": `^((1[0-2]|[2-9])\d{2})?$`,
"BD": `^\d{4}$`,
"BB": `^(BB\d{5})?$`,
"BY": `^\d{6}$`,
"BM": `^[A-Z]{2}[ ]?[A-Z0-9]{2}$`,
"BA": `^\d{5}$`,
"IO": `^BBND 1ZZ$`,
"BN": `^[A-Z]{2}[ ]?\d{4}$`,
"BG": `^\d{4}$`,
"KH": `^\d{5}$`,
"CV": `^\d{4}$`,
"CL": `^\d{7}$`,
"CR": `^\d{4,5}|\d{3}-\d{4}$`,
"HR": `^\d{5}$`,
"CY": `^\d{4}$`,
"CZ": `^\d{3}[ ]?\d{2}$`,
"DO": `^\d{5}$`,
"EC": `^([A-Z]\d{4}[A-Z]|(?:[A-Z]{2})?\d{6})?$`,
"EG": `^\d{5}$`,
"EE": `^\d{5}$`,
"FO": `^\d{3}$`,
"GE": `^\d{4}$`,
"GR": `^\d{3}[ ]?\d{2}$`,
"GL": `^39\d{2}$`,
"GT": `^\d{5}$`,
"HT": `^\d{4}$`,
"HN": `^(?:\d{5})?$`,
"HU": `^\d{4}$`,
"IS": `^\d{3}$`,
"IN": `^\d{6}$`,
"ID": `^\d{5}$`,
"IL": `^\d{5}$`,
"JO": `^\d{5}$`,
"KZ": `^\d{6}$`,
"KE": `^\d{5}$`,
"KW": `^\d{5}$`,
"LA": `^\d{5}$`,
"LV": `^\d{4}$`,
"LB": `^(\d{4}([ ]?\d{4})?)?$`,
"LI": `^(948[5-9])|(949[0-7])$`,
"LT": `^\d{5}$`,
"LU": `^\d{4}$`,
"MK": `^\d{4}$`,
"MY": `^\d{5}$`,
"MV": `^\d{5}$`,
"MT": `^[A-Z]{3}[ ]?\d{2,4}$`,
"MU": `^(\d{3}[A-Z]{2}\d{3})?$`,
"MX": `^\d{5}$`,
"MD": `^\d{4}$`,
"MC": `^980\d{2}$`,
"MA": `^\d{5}$`,
"NP": `^\d{5}$`,
"NZ": `^\d{4}$`,
"NI": `^((\d{4}-)?\d{3}-\d{3}(-\d{1})?)?$`,
"NG": `^(\d{6})?$`,
"OM": `^(PC )?\d{3}$`,
"PK": `^\d{5}$`,
"PY": `^\d{4}$`,
"PH": `^\d{4}$`,
"PL": `^\d{2}-\d{3}$`,
"PR": `^00[679]\d{2}([ \-]\d{4})?$`,
"RO": `^\d{6}$`,
"RU": `^\d{6}$`,
"SM": `^4789\d$`,
"SA": `^\d{5}$`,
"SN": `^\d{5}$`,
"SK": `^\d{3}[ ]?\d{2}$`,
"SI": `^\d{4}$`,
"ZA": `^\d{4}$`,
"LK": `^\d{5}$`,
"TJ": `^\d{6}$`,
"TH": `^\d{5}$`,
"TN": `^\d{4}$`,
"TR": `^\d{5}$`,
"TM": `^\d{6}$`,
"UA": `^\d{5}$`,
"UY": `^\d{5}$`,
"UZ": `^\d{6}$`,
"VA": `^00120$`,
"VE": `^\d{4}$`,
"ZM": `^\d{5}$`,
"AS": `^96799$`,
"CC": `^6799$`,
"CK": `^\d{4}$`,
"RS": `^\d{6}$`,
"ME": `^8\d{4}$`,
"CS": `^\d{5}$`,
"YU": `^\d{5}$`,
"CX": `^6798$`,
"ET": `^\d{4}$`,
"FK": `^FIQQ 1ZZ$`,
"NF": `^2899$`,
"FM": `^(9694[1-4])([ \-]\d{4})?$`,
"GF": `^9[78]3\d{2}$`,
"GN": `^\d{3}$`,
"GP": `^9[78][01]\d{2}$`,
"GS": `^SIQQ 1ZZ$`,
"GU": `^969[123]\d([ \-]\d{4})?$`,
"GW": `^\d{4}$`,
"HM": `^\d{4}$`,
"IQ": `^\d{5}$`,
"KG": `^\d{6}$`,
"LR": `^\d{4}$`,
"LS": `^\d{3}$`,
"MG": `^\d{3}$`,
"MH": `^969[67]\d([ \-]\d{4})?$`,
"MN": `^\d{6}$`,
"MP": `^9695[012]([ \-]\d{4})?$`,
"MQ": `^9[78]2\d{2}$`,
"NC": `^988\d{2}$`,
"NE": `^\d{4}$`,
"VI": `^008(([0-4]\d)|(5[01]))([ \-]\d{4})?$`,
"VN": `^[0-9]{1,6}$`,
"PF": `^987\d{2}$`,
"PG": `^\d{3}$`,
"PM": `^9[78]5\d{2}$`,
"PN": `^PCRN 1ZZ$`,
"PW": `^96940$`,
"RE": `^9[78]4\d{2}$`,
"SH": `^(ASCN|STHL) 1ZZ$`,
"SJ": `^\d{4}$`,
"SO": `^\d{5}$`,
"SZ": `^[HLMS]\d{3}$`,
"TC": `^TKCA 1ZZ$`,
"WF": `^986\d{2}$`,
"XK": `^\d{5}$`,
"YT": `^976\d{2}$`,
}
var postCodeRegexDict = map[string]*regexp.Regexp{}
func init() {
for countryCode, pattern := range postCodePatternDict {
postCodeRegexDict[countryCode] = regexp.MustCompile(pattern)
}
}

@ -9,15 +9,17 @@ const (
alphaUnicodeNumericRegexString = "^[\\p{L}\\p{N}]+$" alphaUnicodeNumericRegexString = "^[\\p{L}\\p{N}]+$"
numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$"
numberRegexString = "^[0-9]+$" numberRegexString = "^[0-9]+$"
hexadecimalRegexString = "^[0-9a-fA-F]+$" hexadecimalRegexString = "^(0[xX])?[0-9a-fA-F]+$"
hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" hexColorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"
rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$" rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$"
rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$" hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$"
hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
e164RegexString = "^\\+[1-9]?[0-9]{7,14}$"
base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$" base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$"
base64RawURLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2,4})$"
iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$" iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$"
iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$" iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$"
uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
@ -28,24 +30,44 @@ 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}$" 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}$" 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}$" 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}$"
md4RegexString = "^[0-9a-f]{32}$"
md5RegexString = "^[0-9a-f]{32}$"
sha256RegexString = "^[0-9a-f]{64}$"
sha384RegexString = "^[0-9a-f]{96}$"
sha512RegexString = "^[0-9a-f]{128}$"
ripemd128RegexString = "^[0-9a-f]{32}$"
ripemd160RegexString = "^[0-9a-f]{40}$"
tiger128RegexString = "^[0-9a-f]{32}$"
tiger160RegexString = "^[0-9a-f]{40}$"
tiger192RegexString = "^[0-9a-f]{48}$"
aSCIIRegexString = "^[\x00-\x7F]*$" aSCIIRegexString = "^[\x00-\x7F]*$"
printableASCIIRegexString = "^[\x20-\x7E]*$" printableASCIIRegexString = "^[\x20-\x7E]*$"
multibyteRegexString = "[^\x00-\x7F]" multibyteRegexString = "[^\x00-\x7F]"
dataURIRegexString = "^data:.+\\/(.+);base64$" dataURIRegexString = `^data:((?:\w+\/(?:([^;]|;[^;]).)+)?)`
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$` sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$`
hostnameRegexStringRFC952 = `^[a-zA-Z][a-zA-Z0-9\-\.]+[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952 hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
hostnameRegexStringRFC1123 = `^[a-zA-Z0-9][a-zA-Z0-9\-\.]+[a-zA-Z0-9]$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123 hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62}){1}(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
ethAddressRegexString = `^0x[0-9a-fA-F]{40}$` ethAddressRegexString = `^0x[0-9a-fA-F]{40}$`
ethAddressUpperRegexString = `^0x[0-9A-F]{40}$` ethAddressUpperRegexString = `^0x[0-9A-F]{40}$`
ethAddressLowerRegexString = `^0x[0-9a-f]{40}$` ethAddressLowerRegexString = `^0x[0-9a-f]{40}$`
uRLEncodedRegexString = `(%[A-Fa-f0-9]{2})` uRLEncodedRegexString = `^(?:[^%]|%[0-9A-Fa-f]{2})*$`
hTMLEncodedRegexString = `&#[x]?([0-9a-fA-F]{2})|(&gt)|(&lt)|(&quot)|(&amp)+[;]?` hTMLEncodedRegexString = `&#[x]?([0-9a-fA-F]{2})|(&gt)|(&lt)|(&quot)|(&amp)+[;]?`
hTMLRegexString = `<[/]?([a-zA-Z]+).*?>` hTMLRegexString = `<[/]?([a-zA-Z]+).*?>`
jWTRegexString = "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$"
splitParamsRegexString = `'[^']*'|\S+`
bicRegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`
semverRegexString = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` // numbered capture groups https://semver.org/
dnsRegexStringRFC1035Label = "^[a-z]([-a-z0-9]*[a-z0-9]){0,62}$"
cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html
mongodbRegexString = "^[a-f\\d]{24}$"
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`
) )
var ( var (
@ -56,14 +78,16 @@ var (
numericRegex = regexp.MustCompile(numericRegexString) numericRegex = regexp.MustCompile(numericRegexString)
numberRegex = regexp.MustCompile(numberRegexString) numberRegex = regexp.MustCompile(numberRegexString)
hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString) hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString)
hexcolorRegex = regexp.MustCompile(hexcolorRegexString) hexColorRegex = regexp.MustCompile(hexColorRegexString)
rgbRegex = regexp.MustCompile(rgbRegexString) rgbRegex = regexp.MustCompile(rgbRegexString)
rgbaRegex = regexp.MustCompile(rgbaRegexString) rgbaRegex = regexp.MustCompile(rgbaRegexString)
hslRegex = regexp.MustCompile(hslRegexString) hslRegex = regexp.MustCompile(hslRegexString)
hslaRegex = regexp.MustCompile(hslaRegexString) hslaRegex = regexp.MustCompile(hslaRegexString)
e164Regex = regexp.MustCompile(e164RegexString)
emailRegex = regexp.MustCompile(emailRegexString) emailRegex = regexp.MustCompile(emailRegexString)
base64Regex = regexp.MustCompile(base64RegexString) base64Regex = regexp.MustCompile(base64RegexString)
base64URLRegex = regexp.MustCompile(base64URLRegexString) base64URLRegex = regexp.MustCompile(base64URLRegexString)
base64RawURLRegex = regexp.MustCompile(base64RawURLRegexString)
iSBN10Regex = regexp.MustCompile(iSBN10RegexString) iSBN10Regex = regexp.MustCompile(iSBN10RegexString)
iSBN13Regex = regexp.MustCompile(iSBN13RegexString) iSBN13Regex = regexp.MustCompile(iSBN13RegexString)
uUID3Regex = regexp.MustCompile(uUID3RegexString) uUID3Regex = regexp.MustCompile(uUID3RegexString)
@ -74,6 +98,17 @@ var (
uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString) uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString)
uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString) uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString)
uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString) uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString)
uLIDRegex = regexp.MustCompile(uLIDRegexString)
md4Regex = regexp.MustCompile(md4RegexString)
md5Regex = regexp.MustCompile(md5RegexString)
sha256Regex = regexp.MustCompile(sha256RegexString)
sha384Regex = regexp.MustCompile(sha384RegexString)
sha512Regex = regexp.MustCompile(sha512RegexString)
ripemd128Regex = regexp.MustCompile(ripemd128RegexString)
ripemd160Regex = regexp.MustCompile(ripemd160RegexString)
tiger128Regex = regexp.MustCompile(tiger128RegexString)
tiger160Regex = regexp.MustCompile(tiger160RegexString)
tiger192Regex = regexp.MustCompile(tiger192RegexString)
aSCIIRegex = regexp.MustCompile(aSCIIRegexString) aSCIIRegex = regexp.MustCompile(aSCIIRegexString)
printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString) printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString)
multibyteRegex = regexp.MustCompile(multibyteRegexString) multibyteRegex = regexp.MustCompile(multibyteRegexString)
@ -83,13 +118,20 @@ var (
sSNRegex = regexp.MustCompile(sSNRegexString) sSNRegex = regexp.MustCompile(sSNRegexString)
hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952) hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952)
hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123) hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123)
fqdnRegexRFC1123 = regexp.MustCompile(fqdnRegexStringRFC1123)
btcAddressRegex = regexp.MustCompile(btcAddressRegexString) btcAddressRegex = regexp.MustCompile(btcAddressRegexString)
btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32) btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32)
btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32) btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32)
ethAddressRegex = regexp.MustCompile(ethAddressRegexString) ethAddressRegex = regexp.MustCompile(ethAddressRegexString)
ethaddressRegexUpper = regexp.MustCompile(ethAddressUpperRegexString)
ethAddressRegexLower = regexp.MustCompile(ethAddressLowerRegexString)
uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString) uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString)
hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString) hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString)
hTMLRegex = regexp.MustCompile(hTMLRegexString) hTMLRegex = regexp.MustCompile(hTMLRegexString)
jWTRegex = regexp.MustCompile(jWTRegexString)
splitParamsRegex = regexp.MustCompile(splitParamsRegexString)
bicRegex = regexp.MustCompile(bicRegexString)
semverRegex = regexp.MustCompile(semverRegexString)
dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label)
cveRegex = regexp.MustCompile(cveRegexString)
mongodbRegex = regexp.MustCompile(mongodbRegexString)
cronRegex = regexp.MustCompile(cronRegexString)
) )

@ -23,18 +23,18 @@ func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx {
// to validate a struct // to validate a struct
type StructLevel interface { type StructLevel interface {
// returns the main validation object, in case one wants to call validations internally. // Validator returns the main validation object, in case one wants to call validations internally.
// this is so you don't have to use anonymous functions to get access to the validate // this is so you don't have to use anonymous functions to get access to the validate
// instance. // instance.
Validator() *Validate Validator() *Validate
// returns the top level struct, if any // Top returns the top level struct, if any
Top() reflect.Value Top() reflect.Value
// returns the current fields parent struct, if any // Parent returns the current fields parent struct, if any
Parent() reflect.Value Parent() reflect.Value
// returns the current struct. // Current returns the current struct.
Current() reflect.Value Current() reflect.Value
// ExtractType gets the actual underlying type of field value. // ExtractType gets the actual underlying type of field value.
@ -42,7 +42,7 @@ type StructLevel interface {
// underlying value and its kind. // underlying value and its kind.
ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool) ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)
// reports an error just by passing the field and tag information // ReportError reports an error just by passing the field and tag information
// //
// NOTES: // NOTES:
// //
@ -54,7 +54,7 @@ type StructLevel interface {
// and process on the flip side it's up to you. // and process on the flip side it's up to you.
ReportError(field interface{}, fieldName, structFieldName string, tag, param string) ReportError(field interface{}, fieldName, structFieldName string, tag, param string)
// reports an error just by passing ValidationErrors // ReportValidationErrors reports an error just by passing ValidationErrors
// //
// NOTES: // NOTES:
// //

File diff suppressed because it is too large Load Diff

@ -0,0 +1,695 @@
package ar
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/validator/v10"
)
func TestTranslations(t *testing.T) {
eng := english.New()
uni := ut.New(eng, eng)
trans, _ := uni.GetTranslator("en")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
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"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
JSONString string `validate:"json"`
JWTString string `validate:"jwt"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
PostCode string `validate:"postcode_iso3166_alpha2=SG"`
PostCodeCountry string
PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "يجب أن يكون IsColor لون صالح",
},
{
ns: "Test.MAC",
expected: "يجب أن يحتوي MAC على عنوان MAC صالح",
},
{
ns: "Test.IPAddr",
expected: "يجب أن يكون IPAddr عنوان IP قابل للحل",
},
{
ns: "Test.IPAddrv4",
expected: "يجب أن يكون IPAddrv4 عنوان IP قابل للحل",
},
{
ns: "Test.IPAddrv6",
expected: "يجب أن يكون IPAddrv6 عنوان IPv6 قابل للحل",
},
{
ns: "Test.UDPAddr",
expected: "يجب أن يكون UDPAddr عنوان UDP صالح",
},
{
ns: "Test.UDPAddrv4",
expected: "يجب أن يكون UDPAddrv4 عنوان IPv4 UDP صالح",
},
{
ns: "Test.UDPAddrv6",
expected: "يجب أن يكون UDPAddrv6 عنوان IPv6 UDP صالح",
},
{
ns: "Test.TCPAddr",
expected: "يجب أن يكون TCPAddr عنوان TCP صالح",
},
{
ns: "Test.TCPAddrv4",
expected: "يجب أن يكون TCPAddrv4 عنوان IPv4 TCP صالح",
},
{
ns: "Test.TCPAddrv6",
expected: "يجب أن يكون TCPAddrv6 عنوان IPv6 TCP صالح",
},
{
ns: "Test.CIDR",
expected: "يجب أن يحتوي CIDR على علامة CIDR صالحة",
},
{
ns: "Test.CIDRv4",
expected: "يجب أن يحتوي CIDRv4 على علامة CIDR صالحة لعنوان IPv4",
},
{
ns: "Test.CIDRv6",
expected: "يجب أن يحتوي CIDRv6 على علامة CIDR صالحة لعنوان IPv6",
},
{
ns: "Test.SSN",
expected: "يجب أن يكون SSN رقم SSN صالح",
},
{
ns: "Test.IP",
expected: "يجب أن يكون IP عنوان IP صالح",
},
{
ns: "Test.IPv4",
expected: "يجب أن يكون IPv4 عنوان IPv4 صالح",
},
{
ns: "Test.IPv6",
expected: "يجب أن يكون IPv6 عنوان IPv6 صالح",
},
{
ns: "Test.DataURI",
expected: "يجب أن يحتوي DataURI على URI صالح للبيانات",
},
{
ns: "Test.Latitude",
expected: "يجب أن يحتوي Latitude على إحداثيات خط عرض صالحة",
},
{
ns: "Test.Longitude",
expected: "يجب أن يحتوي Longitude على إحداثيات خط طول صالحة",
},
{
ns: "Test.MultiByte",
expected: "يجب أن يحتوي MultiByte على أحرف متعددة البايت",
},
{
ns: "Test.ASCII",
expected: "يجب أن يحتوي ASCII على أحرف ascii فقط",
},
{
ns: "Test.PrintableASCII",
expected: "يجب أن يحتوي PrintableASCII على أحرف ascii قابلة للطباعة فقط",
},
{
ns: "Test.UUID",
expected: "يجب أن يكون UUID UUID صالح",
},
{
ns: "Test.UUID3",
expected: "يجب أن يكون UUID3 UUID صالح من النسخة 3",
},
{
ns: "Test.UUID4",
expected: "يجب أن يكون UUID4 UUID صالح من النسخة 4",
},
{
ns: "Test.UUID5",
expected: "يجب أن يكون UUID5 UUID صالح من النسخة 5",
},
{
ns: "Test.ULID",
expected: "يجب أن يكون ULID ULID صالح من نسخة",
},
{
ns: "Test.ISBN",
expected: "يجب أن يكون ISBN رقم ISBN صالح",
},
{
ns: "Test.ISBN10",
expected: "يجب أن يكون ISBN10 رقم ISBN-10 صالح",
},
{
ns: "Test.ISBN13",
expected: "يجب أن يكون ISBN13 رقم ISBN-13 صالح",
},
{
ns: "Test.Excludes",
expected: "لا يمكن أن يحتوي Excludes على النص 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "لا يمكن أن يحتوي ExcludesAll على أي من الأحرف التالية '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "لا يمكن أن يحتوي ExcludesRune على التالي '☻'",
},
{
ns: "Test.ContainsAny",
expected: "يجب أن يحتوي ContainsAny على حرف واحد على الأقل من الأحرف التالية '!@#$'",
},
{
ns: "Test.Contains",
expected: "يجب أن يحتوي Contains على النص 'purpose'",
},
{
ns: "Test.Base64",
expected: "يجب أن يكون Base64 سلسلة Base64 صالحة",
},
{
ns: "Test.Email",
expected: "يجب أن يكون Email عنوان بريد إلكتروني صالح",
},
{
ns: "Test.URL",
expected: "يجب أن يكون URL رابط إنترنت صالح",
},
{
ns: "Test.URI",
expected: "يجب أن يكون URI URI صالح",
},
{
ns: "Test.RGBColorString",
expected: "يجب أن يكون RGBColorString لون RGB صالح",
},
{
ns: "Test.RGBAColorString",
expected: "يجب أن يكون RGBAColorString لون RGBA صالح",
},
{
ns: "Test.HSLColorString",
expected: "يجب أن يكون HSLColorString لون HSL صالح",
},
{
ns: "Test.HSLAColorString",
expected: "يجب أن يكون HSLAColorString لون HSLA صالح",
},
{
ns: "Test.HexadecimalString",
expected: "يجب أن يكون HexadecimalString عددًا سداسيًا عشريًا صالحاً",
},
{
ns: "Test.HexColorString",
expected: "يجب أن يكون HexColorString لون HEX صالح",
},
{
ns: "Test.NumberString",
expected: "يجب أن يكون NumberString رقم صالح",
},
{
ns: "Test.NumericString",
expected: "يجب أن يكون NumericString قيمة رقمية صالحة",
},
{
ns: "Test.AlphanumString",
expected: "يمكن أن يحتوي AlphanumString على أحرف أبجدية رقمية فقط",
},
{
ns: "Test.AlphaString",
expected: "يمكن أن يحتوي AlphaString على أحرف أبجدية فقط",
},
{
ns: "Test.LtFieldString",
expected: "يجب أن يكون LtFieldString أصغر من MaxString",
},
{
ns: "Test.LteFieldString",
expected: "يجب أن يكون LteFieldString أصغر من أو يساوي MaxString",
},
{
ns: "Test.GtFieldString",
expected: "يجب أن يكون GtFieldString أكبر من MaxString",
},
{
ns: "Test.GteFieldString",
expected: "يجب أن يكون GteFieldString أكبر من أو يساوي MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString لا يمكن أن يساوي EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "يجب أن يكون LtCSFieldString أصغر من Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "يجب أن يكون LteCSFieldString أصغر من أو يساوي Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "يجب أن يكون GtCSFieldString أكبر من Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "يجب أن يكون GteCSFieldString أكبر من أو يساوي Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString لا يمكن أن يساوي Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "يجب أن يكون EqCSFieldString مساويا ل Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "يجب أن يكون EqFieldString مساويا ل MaxString",
},
{
ns: "Test.GteString",
expected: "يجب أن يكون طول GteString على الأقل 3 أحرف",
},
{
ns: "Test.GteNumber",
expected: "GteNumber يجب أن يكون 5.56 أو أكبر",
},
{
ns: "Test.GteMultiple",
expected: "يجب أن يحتوي GteMultiple على 2 عناصر على الأقل",
},
{
ns: "Test.GteTime",
expected: "يجب أن يكون GteTime أكبر من أو يساوي التاريخ والوقت الحاليين",
},
{
ns: "Test.GtString",
expected: "يجب أن يكون طول GtString أكبر من 3 أحرف",
},
{
ns: "Test.GtNumber",
expected: "يجب أن يكون GtNumber أكبر من 5.56",
},
{
ns: "Test.GtMultiple",
expected: "يجب أن يحتوي GtMultiple على أكثر من 2 عناصر",
},
{
ns: "Test.GtTime",
expected: "يجب أن يكون GtTime أكبر من التاريخ والوقت الحاليين",
},
{
ns: "Test.LteString",
expected: "يجب أن يكون طول LteString كحد أقصى 3 أحرف",
},
{
ns: "Test.LteNumber",
expected: "LteNumber يجب أن يكون 5.56 أو اقل",
},
{
ns: "Test.LteMultiple",
expected: "يجب أن يحتوي LteMultiple على 2 عناصر كحد أقصى",
},
{
ns: "Test.LteTime",
expected: "يجب أن يكون LteTime أقل من أو يساوي التاريخ والوقت الحاليين",
},
{
ns: "Test.LtString",
expected: "يجب أن يكون طول LtString أقل من 3 أحرف",
},
{
ns: "Test.LtNumber",
expected: "يجب أن يكون LtNumber أقل من 5.56",
},
{
ns: "Test.LtMultiple",
expected: "يجب أن يحتوي LtMultiple على أقل من 2 عناصر",
},
{
ns: "Test.LtTime",
expected: "يجب أن يكون LtTime أقل من التاريخ والوقت الحاليين",
},
{
ns: "Test.NeString",
expected: "NeString يجب ألا يساوي ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber يجب ألا يساوي 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple يجب ألا يساوي 0",
},
{
ns: "Test.EqString",
expected: "EqString لا يساوي 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber لا يساوي 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple لا يساوي 7",
},
{
ns: "Test.MaxString",
expected: "يجب أن يكون طول MaxString بحد أقصى 3 أحرف",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber يجب أن يكون 1,113.00 أو اقل",
},
{
ns: "Test.MaxMultiple",
expected: "يجب أن يحتوي MaxMultiple على 7 عناصر كحد أقصى",
},
{
ns: "Test.MinString",
expected: "MinString يجب أن يكون 1 حرف أو اقل",
},
{
ns: "Test.MinNumber",
expected: "MinNumber يجب أن يكون 1,113.00 أو اقل",
},
{
ns: "Test.MinMultiple",
expected: "يجب أن يحتوي MinMultiple على 7 عناصر على الأقل",
},
{
ns: "Test.LenString",
expected: "يجب أن يكون طول LenString مساويا ل 1 حرف",
},
{
ns: "Test.LenNumber",
expected: "يجب أن يكون LenNumber مساويا ل 1,113.00",
},
{
ns: "Test.LenMultiple",
expected: "يجب أن يحتوي LenMultiple على 7 عناصر",
},
{
ns: "Test.RequiredString",
expected: "حقل RequiredString مطلوب",
},
{
ns: "Test.RequiredNumber",
expected: "حقل RequiredNumber مطلوب",
},
{
ns: "Test.RequiredMultiple",
expected: "حقل RequiredMultiple مطلوب",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen يجب أن يكون 10 أحرف أو اقل",
},
{
ns: "Test.StrPtrMaxLen",
expected: "يجب أن يكون طول StrPtrMaxLen بحد أقصى 1 حرف",
},
{
ns: "Test.StrPtrLen",
expected: "يجب أن يكون طول StrPtrLen مساويا ل 2 أحرف",
},
{
ns: "Test.StrPtrLt",
expected: "يجب أن يكون طول StrPtrLt أقل من 1 حرف",
},
{
ns: "Test.StrPtrLte",
expected: "يجب أن يكون طول StrPtrLte كحد أقصى 1 حرف",
},
{
ns: "Test.StrPtrGt",
expected: "يجب أن يكون طول StrPtrGt أكبر من 10 أحرف",
},
{
ns: "Test.StrPtrGte",
expected: "يجب أن يكون طول StrPtrGte على الأقل 10 أحرف",
},
{
ns: "Test.OneOfString",
expected: "يجب أن يكون OneOfString واحدا من [red green]",
},
{
ns: "Test.OneOfInt",
expected: "يجب أن يكون OneOfInt واحدا من [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "يجب أن يحتوي UniqueSlice على قيم فريدة",
},
{
ns: "Test.UniqueArray",
expected: "يجب أن يحتوي UniqueArray على قيم فريدة",
},
{
ns: "Test.UniqueMap",
expected: "يجب أن يحتوي UniqueMap على قيم فريدة",
},
{
ns: "Test.JSONString",
expected: "يجب أن يكون JSONString نص json صالح",
},
{
ns: "Test.JWTString",
expected: "يجب أن يكون JWTString نص jwt صالح",
},
{
ns: "Test.LowercaseString",
expected: "يجب أن يكون LowercaseString نص حروف صغيرة",
},
{
ns: "Test.UppercaseString",
expected: "يجب أن يكون UppercaseString نص حروف كبيرة",
},
{
ns: "Test.Datetime",
expected: "لا يتطابق Datetime مع تنسيق 2006-01-02",
},
{
ns: "Test.PostCode",
expected: "لا يتطابق PostCode مع تنسيق الرمز البريدي للبلد SG",
},
{
ns: "Test.PostCodeByField",
expected: "لا يتطابق PostCodeByField مع تنسيق الرمز البريدي للبلد في حقل PostCodeCountry",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

@ -10,13 +10,12 @@ import (
"github.com/go-playground/locales" "github.com/go-playground/locales"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// RegisterDefaultTranslations registers a set of default translations // RegisterDefaultTranslations registers a set of default translations
// for all built in tag's in validator; you may add your own as desired. // for all built in tag's in validator; you may add your own as desired.
func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) { func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
translations := []struct { translations := []struct {
tag string tag string
translation string translation string
@ -29,10 +28,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} is a required field", translation: "{0} is a required field",
override: false, override: false,
}, },
{
tag: "required_if",
translation: "{0} is a required field",
override: false,
},
{ {
tag: "len", tag: "len",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("len-string", "{0} must be {1} in length", false); err != nil { if err = ut.Add("len-string", "{0} must be {1} in length", false); err != nil {
return return
} }
@ -61,10 +64,8 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
} }
return return
}, },
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
var err error var err error
var t string var t string
@ -123,7 +124,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "min", tag: "min",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("min-string", "{0} must be at least {1} in length", false); err != nil { if err = ut.Add("min-string", "{0} must be at least {1} in length", false); err != nil {
return return
} }
@ -152,10 +152,8 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
} }
return return
}, },
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
var err error var err error
var t string var t string
@ -214,7 +212,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "max", tag: "max",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("max-string", "{0} must be a maximum of {1} in length", false); err != nil { if err = ut.Add("max-string", "{0} must be a maximum of {1} in length", false); err != nil {
return return
} }
@ -243,10 +240,8 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
} }
return return
}, },
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
var err error var err error
var t string var t string
@ -307,7 +302,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} is not equal to {1}", translation: "{0} is not equal to {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
fmt.Printf("warning: error translating FieldError: %#v", fe) fmt.Printf("warning: error translating FieldError: %#v", fe)
@ -322,7 +316,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} should not be equal to {1}", translation: "{0} should not be equal to {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
fmt.Printf("warning: error translating FieldError: %#v", fe) fmt.Printf("warning: error translating FieldError: %#v", fe)
@ -335,7 +328,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "lt", tag: "lt",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("lt-string", "{0} must be less than {1} in length", false); err != nil { if err = ut.Add("lt-string", "{0} must be less than {1} in length", false); err != nil {
return return
} }
@ -369,10 +361,8 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
} }
return return
}, },
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
var err error var err error
var t string var t string
var f64 float64 var f64 float64
@ -380,7 +370,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
var kind reflect.Kind var kind reflect.Kind
fn := func() (err error) { fn := func() (err error) {
if idx := strings.Index(fe.Param(), "."); idx != -1 { if idx := strings.Index(fe.Param(), "."); idx != -1 {
digits = uint64(len(fe.Param()[idx+1:])) digits = uint64(len(fe.Param()[idx+1:]))
} }
@ -456,7 +445,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "lte", tag: "lte",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("lte-string", "{0} must be at maximum {1} in length", false); err != nil { if err = ut.Add("lte-string", "{0} must be at maximum {1} in length", false); err != nil {
return return
} }
@ -492,7 +480,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
}, },
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
var err error var err error
var t string var t string
var f64 float64 var f64 float64
@ -500,7 +487,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
var kind reflect.Kind var kind reflect.Kind
fn := func() (err error) { fn := func() (err error) {
if idx := strings.Index(fe.Param(), "."); idx != -1 { if idx := strings.Index(fe.Param(), "."); idx != -1 {
digits = uint64(len(fe.Param()[idx+1:])) digits = uint64(len(fe.Param()[idx+1:]))
} }
@ -576,7 +562,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "gt", tag: "gt",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("gt-string", "{0} must be greater than {1} in length", false); err != nil { if err = ut.Add("gt-string", "{0} must be greater than {1} in length", false); err != nil {
return return
} }
@ -612,7 +597,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
}, },
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
var err error var err error
var t string var t string
var f64 float64 var f64 float64
@ -620,7 +604,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
var kind reflect.Kind var kind reflect.Kind
fn := func() (err error) { fn := func() (err error) {
if idx := strings.Index(fe.Param(), "."); idx != -1 { if idx := strings.Index(fe.Param(), "."); idx != -1 {
digits = uint64(len(fe.Param()[idx+1:])) digits = uint64(len(fe.Param()[idx+1:]))
} }
@ -696,7 +679,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "gte", tag: "gte",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("gte-string", "{0} must be at least {1} in length", false); err != nil { if err = ut.Add("gte-string", "{0} must be at least {1} in length", false); err != nil {
return return
} }
@ -732,7 +714,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
}, },
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
var err error var err error
var t string var t string
var f64 float64 var f64 float64
@ -740,7 +721,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
var kind reflect.Kind var kind reflect.Kind
fn := func() (err error) { fn := func() (err error) {
if idx := strings.Index(fe.Param(), "."); idx != -1 { if idx := strings.Index(fe.Param(), "."); idx != -1 {
digits = uint64(len(fe.Param()[idx+1:])) digits = uint64(len(fe.Param()[idx+1:]))
} }
@ -818,7 +798,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be equal to {1}", translation: "{0} must be equal to {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -833,7 +812,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be equal to {1}", translation: "{0} must be equal to {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -848,7 +826,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} cannot be equal to {1}", translation: "{0} cannot be equal to {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -863,7 +840,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be greater than {1}", translation: "{0} must be greater than {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -878,7 +854,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be greater than or equal to {1}", translation: "{0} must be greater than or equal to {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -893,7 +868,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be less than {1}", translation: "{0} must be less than {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -908,7 +882,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be less than or equal to {1}", translation: "{0} must be less than or equal to {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -923,7 +896,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} cannot be equal to {1}", translation: "{0} cannot be equal to {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -938,7 +910,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be greater than {1}", translation: "{0} must be greater than {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -953,7 +924,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be greater than or equal to {1}", translation: "{0} must be greater than or equal to {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -968,7 +938,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be less than {1}", translation: "{0} must be less than {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -983,7 +952,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be less than or equal to {1}", translation: "{0} must be less than or equal to {1}",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1043,6 +1011,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be a valid HSLA color", translation: "{0} must be a valid HSLA color",
override: false, override: false,
}, },
{
tag: "e164",
translation: "{0} must be a valid E.164 formatted phone number",
override: false,
},
{ {
tag: "email", tag: "email",
translation: "{0} must be a valid email address", translation: "{0} must be a valid email address",
@ -1068,7 +1041,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must contain the text '{1}'", translation: "{0} must contain the text '{1}'",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1083,7 +1055,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must contain at least one of the following characters '{1}'", translation: "{0} must contain at least one of the following characters '{1}'",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1098,7 +1069,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} cannot contain the text '{1}'", translation: "{0} cannot contain the text '{1}'",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1113,7 +1083,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} cannot contain any of the following characters '{1}'", translation: "{0} cannot contain any of the following characters '{1}'",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1128,7 +1097,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} cannot contain the following '{1}'", translation: "{0} cannot contain the following '{1}'",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1173,6 +1141,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be a valid version 5 UUID", translation: "{0} must be a valid version 5 UUID",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} must be a valid ULID",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} must contain only ascii characters", translation: "{0} must contain only ascii characters",
@ -1293,6 +1266,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must contain a valid MAC address", translation: "{0} must contain a valid MAC address",
override: false, override: false,
}, },
{
tag: "fqdn",
translation: "{0} must be a valid FQDN",
override: false,
},
{ {
tag: "unique", tag: "unique",
translation: "{0} must contain unique values", translation: "{0} must contain unique values",
@ -1303,6 +1281,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be a valid color", translation: "{0} must be a valid color",
override: false, override: false,
}, },
{
tag: "cron",
translation: "{0} must be a valid cron expression",
override: false,
},
{ {
tag: "oneof", tag: "oneof",
translation: "{0} must be one of [{1}]", translation: "{0} must be one of [{1}]",
@ -1316,22 +1299,88 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return s return s
}, },
}, },
{
tag: "json",
translation: "{0} must be a valid json string",
override: false,
},
{
tag: "jwt",
translation: "{0} must be a valid jwt string",
override: false,
},
{
tag: "lowercase",
translation: "{0} must be a lowercase string",
override: false,
},
{
tag: "uppercase",
translation: "{0} must be an uppercase string",
override: false,
},
{
tag: "datetime",
translation: "{0} does not match the {1} format",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe)
return fe.(error).Error()
}
return t
},
},
{
tag: "postcode_iso3166_alpha2",
translation: "{0} does not match postcode format of {1} country",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe)
return fe.(error).Error()
}
return t
},
},
{
tag: "postcode_iso3166_alpha2_field",
translation: "{0} does not match postcode format of country in {1} field",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe)
return fe.(error).Error()
}
return t
},
},
{
tag: "boolean",
translation: "{0} must be a valid boolean value",
override: false,
},
{
tag: "cve",
translation: "{0} must be a valid cve identifier",
override: false,
},
} }
for _, t := range translations { for _, t := range translations {
if t.customTransFunc != nil && t.customRegisFunc != nil { if t.customTransFunc != nil && t.customRegisFunc != nil {
err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc) err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
} else if t.customTransFunc != nil && t.customRegisFunc == nil { } else if t.customTransFunc != nil && t.customRegisFunc == nil {
err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc) err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
} else if t.customTransFunc == nil && t.customRegisFunc != nil { } else if t.customTransFunc == nil && t.customRegisFunc != nil {
err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc) err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
} else { } else {
err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc) err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
} }
@ -1345,21 +1394,16 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
} }
func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc { func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
return func(ut ut.Translator) (err error) { return func(ut ut.Translator) (err error) {
if err = ut.Add(tag, translation, override); err != nil { if err = ut.Add(tag, translation, override); err != nil {
return return
} }
return return
} }
} }
func translateFunc(ut ut.Translator, fe validator.FieldError) string { func translateFunc(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field()) t, err := ut.T(fe.Tag(), fe.Field())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)

@ -4,14 +4,13 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
english "github.com/go-playground/locales/en" english "github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "gopkg.in/go-playground/assert.v1" "github.com/go-playground/validator/v10"
"gopkg.in/go-playground/validator.v9"
) )
func TestTranslations(t *testing.T) { func TestTranslations(t *testing.T) {
eng := english.New() eng := english.New()
uni := ut.New(eng, eng) uni := ut.New(eng, eng)
trans, _ := uni.GetTranslator("en") trans, _ := uni.GetTranslator("en")
@ -28,6 +27,7 @@ func TestTranslations(t *testing.T) {
GteCSFieldString string GteCSFieldString string
LtCSFieldString string LtCSFieldString string
LteCSFieldString string LteCSFieldString string
RequiredIf string
} }
type Test struct { type Test struct {
@ -35,6 +35,7 @@ func TestTranslations(t *testing.T) {
RequiredString string `validate:"required"` RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"` RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"` RequiredMultiple []string `validate:"required"`
RequiredIf string `validate:"required_if=Inner.RequiredIf abcd"`
LenString string `validate:"len=1"` LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"` LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"` LenMultiple []string `validate:"len=7"`
@ -104,6 +105,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -128,6 +130,7 @@ func TestTranslations(t *testing.T) {
IPAddrv6 string `validate:"ip6_addr"` IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"` MAC string `validate:"mac"`
FQDN string `validate:"fqdn"`
IsColor string `validate:"iscolor"` IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"` StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"` StrPtrMaxLen *string `validate:"max=1"`
@ -141,6 +144,16 @@ func TestTranslations(t *testing.T) {
UniqueSlice []string `validate:"unique"` UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"` UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"` UniqueMap map[string]string `validate:"unique"`
JSONString string `validate:"json"`
JWTString string `validate:"jwt"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
PostCode string `validate:"postcode_iso3166_alpha2=SG"`
PostCodeCountry string
PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"`
BooleanString string `validate:"boolean"`
CveString string `validate:"cve"`
} }
var test Test var test Test
@ -183,12 +196,20 @@ func TestTranslations(t *testing.T) {
test.MultiByte = "1234feerf" test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
s := "toolong" s := "toolong"
test.StrPtrMaxLen = &s test.StrPtrMaxLen = &s
test.StrPtrLen = &s test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"} test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"} test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
test.BooleanString = "A"
test.CveString = "A"
test.Inner.RequiredIf = "abcd"
err = validate.Struct(test) err = validate.Struct(test)
NotEqual(t, err, nil) NotEqual(t, err, nil)
@ -208,6 +229,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.MAC", ns: "Test.MAC",
expected: "MAC must contain a valid MAC address", expected: "MAC must contain a valid MAC address",
}, },
{
ns: "Test.FQDN",
expected: "FQDN must be a valid FQDN",
},
{ {
ns: "Test.IPAddr", ns: "Test.IPAddr",
expected: "IPAddr must be a resolvable IP address", expected: "IPAddr must be a resolvable IP address",
@ -312,6 +337,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 must be a valid version 5 UUID", expected: "UUID5 must be a valid version 5 UUID",
}, },
{
ns: "Test.ULID",
expected: "ULID must be a valid ULID",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN must be a valid ISBN number", expected: "ISBN must be a valid ISBN number",
@ -576,6 +605,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.RequiredString", ns: "Test.RequiredString",
expected: "RequiredString is a required field", expected: "RequiredString is a required field",
}, },
{
ns: "Test.RequiredIf",
expected: "RequiredIf is a required field",
},
{ {
ns: "Test.RequiredNumber", ns: "Test.RequiredNumber",
expected: "RequiredNumber is a required field", expected: "RequiredNumber is a required field",
@ -632,6 +665,42 @@ func TestTranslations(t *testing.T) {
ns: "Test.UniqueMap", ns: "Test.UniqueMap",
expected: "UniqueMap must contain unique values", expected: "UniqueMap must contain unique values",
}, },
{
ns: "Test.JSONString",
expected: "JSONString must be a valid json string",
},
{
ns: "Test.JWTString",
expected: "JWTString must be a valid jwt string",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString must be a lowercase string",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString must be an uppercase string",
},
{
ns: "Test.Datetime",
expected: "Datetime does not match the 2006-01-02 format",
},
{
ns: "Test.PostCode",
expected: "PostCode does not match postcode format of SG country",
},
{
ns: "Test.PostCodeByField",
expected: "PostCodeByField does not match postcode format of country in PostCodeCountry field",
},
{
ns: "Test.BooleanString",
expected: "BooleanString must be a valid boolean value",
},
{
ns: "Test.CveString",
expected: "CveString must be a valid cve identifier",
},
} }
for _, tt := range tests { for _, tt := range tests {
@ -648,5 +717,4 @@ func TestTranslations(t *testing.T) {
NotEqual(t, fe, nil) NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans)) Equal(t, tt.expected, fe.Translate(trans))
} }
} }

File diff suppressed because it is too large Load Diff

@ -0,0 +1,658 @@
package es
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/validator/v10"
)
func TestTranslations(t *testing.T) {
spa := spanish.New()
uni := ut.New(spa, spa)
trans, _ := uni.GetTranslator("es")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
RequiredIf string `validate:"required_if=Inner.RequiredIf abcd"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
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"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor debe ser un color válido",
},
{
ns: "Test.MAC",
expected: "MAC debe contener una dirección MAC válida",
},
{
ns: "Test.IPAddr",
expected: "IPAddr debe ser una dirección IP resoluble",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 debe ser una dirección IPv4 resoluble",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 debe ser una dirección IPv6 resoluble",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr debe ser una dirección UDP válida",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 debe ser una dirección IPv4 UDP válida",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 debe ser una dirección IPv6 UDP válida",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr debe ser una dirección TCP válida",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 debe ser una dirección IPv4 TCP válida",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 debe ser una dirección IPv6 TCP válida",
},
{
ns: "Test.CIDR",
expected: "CIDR debe contener una anotación válida del CIDR",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 debe contener una notación CIDR válida para una dirección IPv4",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 debe contener una notación CIDR válida para una dirección IPv6",
},
{
ns: "Test.SSN",
expected: "SSN debe ser un número válido de SSN",
},
{
ns: "Test.IP",
expected: "IP debe ser una dirección IP válida",
},
{
ns: "Test.IPv4",
expected: "IPv4 debe ser una dirección IPv4 válida",
},
{
ns: "Test.IPv6",
expected: "IPv6 debe ser una dirección IPv6 válida",
},
{
ns: "Test.DataURI",
expected: "DataURI debe contener un URI de datos válido",
},
{
ns: "Test.Latitude",
expected: "Latitude debe contener coordenadas de latitud válidas",
},
{
ns: "Test.Longitude",
expected: "Longitude debe contener unas coordenadas de longitud válidas",
},
{
ns: "Test.MultiByte",
expected: "MultiByte debe contener caracteres multibyte",
},
{
ns: "Test.ASCII",
expected: "ASCII debe contener sólo caracteres ascii",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII debe contener sólo caracteres ASCII imprimibles",
},
{
ns: "Test.UUID",
expected: "UUID debe ser un UUID válido",
},
{
ns: "Test.UUID3",
expected: "UUID3 debe ser una versión válida 3 UUID",
},
{
ns: "Test.UUID4",
expected: "UUID4 debe ser una versión válida 4 UUID",
},
{
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",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 debe ser un número ISBN-10 válido",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 debe ser un número ISBN-13 válido",
},
{
ns: "Test.Excludes",
expected: "Excludes no puede contener el texto 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll no puede contener ninguno de los siguientes caracteres '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune no puede contener lo siguiente '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny debe contener al menos uno de los siguientes caracteres '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains debe contener el texto 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 debe ser una cadena de Base64 válida",
},
{
ns: "Test.Email",
expected: "Email debe ser una dirección de correo electrónico válida",
},
{
ns: "Test.URL",
expected: "URL debe ser un URL válido",
},
{
ns: "Test.URI",
expected: "URI debe ser una URI válida",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString debe ser un color RGB válido",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString debe ser un color RGBA válido",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString debe ser un color HSL válido",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString debe ser un color HSL válido",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString debe ser un hexadecimal válido",
},
{
ns: "Test.HexColorString",
expected: "HexColorString debe ser un color HEX válido",
},
{
ns: "Test.NumberString",
expected: "NumberString debe ser un número válido",
},
{
ns: "Test.NumericString",
expected: "NumericString debe ser un valor numérico válido",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString sólo puede contener caracteres alfanuméricos",
},
{
ns: "Test.AlphaString",
expected: "AlphaString sólo puede contener caracteres alfabéticos",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString debe ser menor que MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString debe ser menor o igual a MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString debe ser mayor que MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString debe ser mayor o igual a MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString no puede ser igual a EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString debe ser menor que Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString debe ser menor o igual a Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString debe ser mayor que Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString debe ser mayor o igual a Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString no puede ser igual a Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString debe ser igual a Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString debe ser igual a MaxString",
},
{
ns: "Test.GteString",
expected: "GteString debe tener al menos 3 caracteres de longitud",
},
{
ns: "Test.GteNumber",
expected: "GteNumber debe ser 5,56 o mayor",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple debe contener al menos 2 elementos",
},
{
ns: "Test.GteTime",
expected: "GteTime debe ser después o durante la fecha y hora actuales",
},
{
ns: "Test.GtString",
expected: "GtString debe ser mayor que 3 caracteres en longitud",
},
{
ns: "Test.GtNumber",
expected: "GtNumber debe ser mayor que 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple debe contener más de 2 elementos",
},
{
ns: "Test.GtTime",
expected: "GtTime debe ser después de la fecha y hora actual",
},
{
ns: "Test.LteString",
expected: "LteString debe tener un máximo de 3 caracteres de longitud",
},
{
ns: "Test.LteNumber",
expected: "LteNumber debe ser 5,56 o menos",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple debe contener como máximo 2 elementos",
},
{
ns: "Test.LteTime",
expected: "LteTime debe ser antes o durante la fecha y hora actual",
},
{
ns: "Test.LtString",
expected: "LtString debe tener menos de 3 caracteres de longitud",
},
{
ns: "Test.LtNumber",
expected: "LtNumber debe ser menos de 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple debe contener menos de 2 elementos",
},
{
ns: "Test.LtTime",
expected: "LtTime debe ser antes de la fecha y hora actual",
},
{
ns: "Test.NeString",
expected: "NeString no debería ser igual a ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber no debería ser igual a 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple no debería ser igual a 0",
},
{
ns: "Test.EqString",
expected: "EqString no es igual a 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber no es igual a 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple no es igual a 7",
},
{
ns: "Test.MaxString",
expected: "MaxString debe tener un máximo de 3 caracteres de longitud",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber debe ser 1.113,00 o menos",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple debe contener como máximo 7 elementos",
},
{
ns: "Test.MinString",
expected: "MinString debe tener al menos 1 carácter de longitud",
},
{
ns: "Test.MinNumber",
expected: "MinNumber debe ser 1.113,00 o más",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple debe contener al menos 7 elementos",
},
{
ns: "Test.LenString",
expected: "LenString debe tener 1 carácter de longitud",
},
{
ns: "Test.LenNumber",
expected: "LenNumber debe ser igual a 1.113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple debe contener 7 elementos",
},
{
ns: "Test.RequiredString",
expected: "RequiredString es un campo requerido",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber es un campo requerido",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple es un campo requerido",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen debe tener al menos 10 caracteres de longitud",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen debe tener un máximo de 1 carácter de longitud",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen debe tener 2 caracteres de longitud",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt debe tener menos de 1 carácter de longitud",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte debe tener un máximo de 1 carácter de longitud",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt debe ser mayor que 10 caracteres en longitud",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte debe tener al menos 10 caracteres de longitud",
},
{
ns: "Test.OneOfString",
expected: "OneOfString debe ser uno de [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt debe ser uno de [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice debe contener valores únicos",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray debe contener valores únicos",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap debe contener valores únicos",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,690 @@
package fa
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/validator/v10"
)
func TestTranslations(t *testing.T) {
eng := english.New()
uni := ut.New(eng, eng)
trans, _ := uni.GetTranslator("en")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
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"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
JSONString string `validate:"json"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
PostCode string `validate:"postcode_iso3166_alpha2=SG"`
PostCodeCountry string
PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor باید یک رنگ معتبر باشد",
},
{
ns: "Test.MAC",
expected: "MAC باید یک مکآدرس معتبر باشد",
},
{
ns: "Test.IPAddr",
expected: "IPAddr باید یک آدرس آیپی قابل دسترس باشد",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 باید یک آدرس آیپی IPv4 قابل دسترس باشد",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 باید یک آدرس آیپی IPv6 قابل دسترس باشد",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr باید یک آدرس UDP معتبر باشد",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 باید یک آدرس UDP IPv4 معتبر باشد",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 باید یک آدرس UDP IPv6 معتبر باشد",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr باید یک آدرس TCP معتبر باشد",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 باید یک آدرس TCP IPv4 معتبر باشد",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 باید یک آدرس TCP IPv6 معتبر باشد",
},
{
ns: "Test.CIDR",
expected: "CIDR باید یک نشانهگذاری CIDR معتبر باشد",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 باید یک نشانهگذاری CIDR معتبر برای آدرس آیپی IPv4 باشد",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 باید یک نشانهگذاری CIDR معتبر برای آدرس آیپی IPv6 باشد",
},
{
ns: "Test.SSN",
expected: "SSN باید یک شماره SSN معتبر باشد",
},
{
ns: "Test.IP",
expected: "IP باید یک آدرس آیپی معتبر باشد",
},
{
ns: "Test.IPv4",
expected: "IPv4 باید یک آدرس آیپی IPv4 معتبر باشد",
},
{
ns: "Test.IPv6",
expected: "IPv6 باید یک آدرس آیپی IPv6 معتبر باشد",
},
{
ns: "Test.DataURI",
expected: "DataURI باید یک Data URI معتبر باشد",
},
{
ns: "Test.Latitude",
expected: "Latitude باید یک عرض جغرافیایی معتبر باشد",
},
{
ns: "Test.Longitude",
expected: "Longitude باید یک طول جغرافیایی معتبر باشد",
},
{
ns: "Test.MultiByte",
expected: "MultiByte باید شامل کاراکترهای چندبایته باشد",
},
{
ns: "Test.ASCII",
expected: "ASCII باید فقط شامل کاراکترهای اسکی باشد",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII باید فقط شامل کاراکترهای اسکی قابل چاپ باشد",
},
{
ns: "Test.UUID",
expected: "UUID باید یک UUID معتبر باشد",
},
{
ns: "Test.UUID3",
expected: "UUID3 باید یک UUID نسخه 3 معتبر باشد",
},
{
ns: "Test.UUID4",
expected: "UUID4 باید یک UUID نسخه 4 معتبر باشد",
},
{
ns: "Test.UUID5",
expected: "UUID5 باید یک UUID نسخه 5 معتبر باشد",
},
{
ns: "Test.ULID",
expected: "ULID باید یک ULID معتبر باشد",
},
{
ns: "Test.ISBN",
expected: "ISBN باید یک شابک معتبر باشد",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 باید یک شابک(ISBN-10) معتبر باشد",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 باید یک شابک(ISBN-13) معتبر باشد",
},
{
ns: "Test.Excludes",
expected: "Excludes نمیتواند شامل 'text' باشد",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll نمیتواند شامل کاراکترهای '!@#$' باشد",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune نمیتواند شامل '☻' باشد",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny باید شامل کاراکترهای '!@#$' باشد",
},
{
ns: "Test.Contains",
expected: "Contains باید شامل 'purpose' باشد",
},
{
ns: "Test.Base64",
expected: "Base64 باید یک متن درمبنای64 معتبر باشد",
},
{
ns: "Test.Email",
expected: "Email باید یک ایمیل معتبر باشد",
},
{
ns: "Test.URL",
expected: "URL باید یک آدرس اینترنتی معتبر باشد",
},
{
ns: "Test.URI",
expected: "URI باید یک URI معتبر باشد",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString باید یک کد رنگ RGB باشد",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString باید یک کد رنگ RGBA باشد",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString باید یک کد رنگ HSL باشد",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString باید یک کد رنگ HSLA باشد",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString باید یک عدد درمبنای16 باشد",
},
{
ns: "Test.HexColorString",
expected: "HexColorString باید یک کد رنگ HEX باشد",
},
{
ns: "Test.NumberString",
expected: "NumberString باید یک عدد معتبر باشد",
},
{
ns: "Test.NumericString",
expected: "NumericString باید یک عدد معتبر باشد",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString میتواند فقط شامل حروف و اعداد باشد",
},
{
ns: "Test.AlphaString",
expected: "AlphaString میتواند فقط شامل حروف باشد",
},
{
ns: "Test.LtFieldString",
expected: "طول LtFieldString باید کمتر از MaxString باشد",
},
{
ns: "Test.LteFieldString",
expected: "طول LteFieldString باید کمتر یا برابر MaxString باشد",
},
{
ns: "Test.GtFieldString",
expected: "طول GtFieldString باید بیشتر از MaxString باشد",
},
{
ns: "Test.GteFieldString",
expected: "طول GteFieldString باید بیشتر یا برابر MaxString باشد",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString نمیتواند برابر EqFieldString باشد",
},
{
ns: "Test.LtCSFieldString",
expected: "طول LtCSFieldString باید کمتر از Inner.LtCSFieldString باشد",
},
{
ns: "Test.LteCSFieldString",
expected: "طول LteCSFieldString باید کمتر یا برابر Inner.LteCSFieldString باشد",
},
{
ns: "Test.GtCSFieldString",
expected: "طول GtCSFieldString باید بیشتر از Inner.GtCSFieldString باشد",
},
{
ns: "Test.GteCSFieldString",
expected: "طول GteCSFieldString باید بیشتر یا برابر Inner.GteCSFieldString باشد",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString نمیتواند برابر Inner.NeCSFieldString باشد",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString باید برابر Inner.EqCSFieldString باشد",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString باید برابر MaxString باشد",
},
{
ns: "Test.GteString",
expected: "طول GteString باید حداقل 3 کاراکتر باشد",
},
{
ns: "Test.GteNumber",
expected: "GteNumber باید بیشتر یا برابر 5.56 باشد",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple باید شامل حداقل 2 آیتم باشد",
},
{
ns: "Test.GteTime",
expected: "GteTime باید بعد یا برابر تاریخ و زمان کنونی باشد",
},
{
ns: "Test.GtString",
expected: "طول GtString باید بیشتر از 3 کاراکتر باشد",
},
{
ns: "Test.GtNumber",
expected: "GtNumber باید بیشتر از 5.56 باشد",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple باید دارای بیشتر از 2 آیتم باشد",
},
{
ns: "Test.GtTime",
expected: "GtTime باید بعد از تاریخ و زمان کنونی باشد",
},
{
ns: "Test.LteString",
expected: "طول LteString باید حداکثر 3 کاراکتر باشد",
},
{
ns: "Test.LteNumber",
expected: "LteNumber باید کمتر یا برابر 5.56 باشد",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple باید حداکثر شامل 2 آیتم باشد",
},
{
ns: "Test.LteTime",
expected: "LteTime باید قبل یا برابر تاریخ و زمان کنونی باشد",
},
{
ns: "Test.LtString",
expected: "طول LtString باید کمتر از 3 کاراکتر باشد",
},
{
ns: "Test.LtNumber",
expected: "LtNumber باید کمتر از 5.56 باشد",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple باید دارای کمتر از 2 آیتم باشد",
},
{
ns: "Test.LtTime",
expected: "LtTime باید قبل از تاریخ و زمان کنونی باشد",
},
{
ns: "Test.NeString",
expected: "NeString نباید برابر باشد",
},
{
ns: "Test.NeNumber",
expected: "NeNumber نباید برابر 0.00 باشد",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple نباید برابر 0 باشد",
},
{
ns: "Test.EqString",
expected: "EqString برابر 3 نمیباشد",
},
{
ns: "Test.EqNumber",
expected: "EqNumber برابر 2.33 نمیباشد",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple برابر 7 نمیباشد",
},
{
ns: "Test.MaxString",
expected: "طول MaxString باید حداکثر 3 کاراکتر باشد",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber باید کمتر یا برابر 1,113.00 باشد",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple باید شامل حداکثر 7 آیتم باشد",
},
{
ns: "Test.MinString",
expected: "طول MinString باید حداقل 1 کاراکتر باشد",
},
{
ns: "Test.MinNumber",
expected: "MinNumber باید بزرگتر یا برابر 1,113.00 باشد",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple باید شامل حداقل 7 آیتم باشد",
},
{
ns: "Test.LenString",
expected: "طول LenString باید 1 کاراکتر باشد",
},
{
ns: "Test.LenNumber",
expected: "طول LenNumber باید برابر 1,113.00 باشد",
},
{
ns: "Test.LenMultiple",
expected: "تعداد LenMultiple باید برابر 7 آیتم باشد",
},
{
ns: "Test.RequiredString",
expected: "فیلد RequiredString اجباری میباشد",
},
{
ns: "Test.RequiredNumber",
expected: "فیلد RequiredNumber اجباری میباشد",
},
{
ns: "Test.RequiredMultiple",
expected: "فیلد RequiredMultiple اجباری میباشد",
},
{
ns: "Test.StrPtrMinLen",
expected: "طول StrPtrMinLen باید حداقل 10 کاراکتر باشد",
},
{
ns: "Test.StrPtrMaxLen",
expected: "طول StrPtrMaxLen باید حداکثر 1 کاراکتر باشد",
},
{
ns: "Test.StrPtrLen",
expected: "طول StrPtrLen باید 2 کاراکتر باشد",
},
{
ns: "Test.StrPtrLt",
expected: "طول StrPtrLt باید کمتر از 1 کاراکتر باشد",
},
{
ns: "Test.StrPtrLte",
expected: "طول StrPtrLte باید حداکثر 1 کاراکتر باشد",
},
{
ns: "Test.StrPtrGt",
expected: "طول StrPtrGt باید بیشتر از 10 کاراکتر باشد",
},
{
ns: "Test.StrPtrGte",
expected: "طول StrPtrGte باید حداقل 10 کاراکتر باشد",
},
{
ns: "Test.OneOfString",
expected: "OneOfString باید یکی از مقادیر [red green] باشد",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt باید یکی از مقادیر [5 63] باشد",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice باید شامل مقادیر منحصربفرد باشد",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray باید شامل مقادیر منحصربفرد باشد",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap باید شامل مقادیر منحصربفرد باشد",
},
{
ns: "Test.JSONString",
expected: "JSONString باید یک json معتبر باشد",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString باید یک متن با حروف کوچک باشد",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString باید یک متن با حروف بزرگ باشد",
},
{
ns: "Test.Datetime",
expected: "فرمت Datetime با 2006-01-02 سازگار نیست",
},
{
ns: "Test.PostCode",
expected: "PostCode یک کدپستی معتبر کشور SG نیست",
},
{
ns: "Test.PostCodeByField",
expected: "PostCodeByField یک کدپستی معتبر کشور فیلد PostCodeCountry نیست",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

@ -10,7 +10,7 @@ import (
"github.com/go-playground/locales" "github.com/go-playground/locales"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// RegisterDefaultTranslations registers a set of default translations // RegisterDefaultTranslations registers a set of default translations
@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} doit être un UUID version 5 valid", translation: "{0} doit être un UUID version 5 valid",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} doit être une ULID valide",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} ne doit contenir que des caractères ascii", translation: "{0} ne doit contenir que des caractères ascii",

@ -4,10 +4,10 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
french "github.com/go-playground/locales/fr" french "github.com/go-playground/locales/fr"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "gopkg.in/go-playground/assert.v1" "github.com/go-playground/validator/v10"
"gopkg.in/go-playground/validator.v9"
) )
func TestTranslations(t *testing.T) { func TestTranslations(t *testing.T) {
@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 doit être un UUID version 5 valid", expected: "UUID5 doit être un UUID version 5 valid",
}, },
{
ns: "Test.ULID",
expected: "ULID doit être une ULID valide",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN doit être un numéro ISBN valid", expected: "ISBN doit être un numéro ISBN valid",
@ -536,7 +541,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.MaxNumber", ns: "Test.MaxNumber",
expected: "MaxNumber doit être égal à 1 113,00 ou moins", expected: "MaxNumber doit être égal à 1113,00 ou moins",
}, },
{ {
ns: "Test.MaxMultiple", ns: "Test.MaxMultiple",
@ -548,7 +553,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.MinNumber", ns: "Test.MinNumber",
expected: "MinNumber doit être égal à 1 113,00 ou plus", expected: "MinNumber doit être égal à 1113,00 ou plus",
}, },
{ {
ns: "Test.MinMultiple", ns: "Test.MinMultiple",
@ -560,7 +565,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.LenNumber", ns: "Test.LenNumber",
expected: "LenNumber doit être égal à 1 113,00", expected: "LenNumber doit être égal à 1113,00",
}, },
{ {
ns: "Test.LenMultiple", ns: "Test.LenMultiple",

@ -10,7 +10,7 @@ import (
"github.com/go-playground/locales" "github.com/go-playground/locales"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// RegisterDefaultTranslations registers a set of default translations // RegisterDefaultTranslations registers a set of default translations
@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} harus berupa UUID versi 5 yang valid", translation: "{0} harus berupa UUID versi 5 yang valid",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} harus berupa ULID yang valid",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} hanya boleh berisi karakter ascii", translation: "{0} hanya boleh berisi karakter ascii",

@ -4,10 +4,10 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
indonesia "github.com/go-playground/locales/id" indonesia "github.com/go-playground/locales/id"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "gopkg.in/go-playground/assert.v1" "github.com/go-playground/validator/v10"
"gopkg.in/go-playground/validator.v9"
) )
func TestTranslations(t *testing.T) { func TestTranslations(t *testing.T) {
@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 harus berupa UUID versi 5 yang valid", expected: "UUID5 harus berupa UUID versi 5 yang valid",
}, },
{
ns: "Test.ULID",
expected: "ULID harus berupa ULID yang valid",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN harus berupa nomor ISBN yang valid", expected: "ISBN harus berupa nomor ISBN yang valid",

File diff suppressed because it is too large Load Diff

@ -0,0 +1,725 @@
package it
import (
"testing"
"time"
. "github.com/go-playground/assert/v2"
italian "github.com/go-playground/locales/it"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
)
func TestTranslations(t *testing.T) {
ita := italian.New()
uni := ut.New(ita, ita)
trans, _ := uni.GetTranslator("it")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
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"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
BooleanString string `validate:"boolean"`
JSONString string `validate:"json"`
JWTString string `validate:"jwt"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
StartsWithString string `validate:"startswith=foo"`
StartsNotWithString string `validate:"startsnotwith=foo"`
EndsWithString string `validate:"endswith=foo"`
EndsNotWithString string `validate:"endsnotwith=foo"`
Datetime string `validate:"datetime=2006-01-02"`
PostCode string `validate:"postcode_iso3166_alpha2=SG"`
PostCodeCountry string
PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
test.StartsWithString = "hello"
test.StartsNotWithString = "foo-hello"
test.EndsWithString = "hello"
test.EndsNotWithString = "hello-foo"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor deve essere un colore valido",
},
{
ns: "Test.MAC",
expected: "MAC deve contenere un indirizzo MAC valido",
},
{
ns: "Test.IPAddr",
expected: "IPAddr deve essere un indirizzo IP risolvibile",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 deve essere un indirizzo IPv4 risolvibile",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 deve essere un indirizzo IPv6 risolvibile",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr deve essere un indirizzo UDP valido",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 deve essere un indirizzo IPv4 UDP valido",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 deve essere un indirizzo IPv6 UDP valido",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr deve essere un indirizzo TCP valido",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 deve essere un indirizzo IPv4 TCP valido",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 deve essere un indirizzo IPv6 TCP valido",
},
{
ns: "Test.CIDR",
expected: "CIDR deve contenere una notazione CIDR valida",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 deve contenere una notazione CIDR per un indirizzo IPv4 valida",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 deve contenere una notazione CIDR per un indirizzo IPv6 valida",
},
{
ns: "Test.SSN",
expected: "SSN deve essere un numero SSN valido",
},
{
ns: "Test.IP",
expected: "IP deve essere un indirizzo IP valido",
},
{
ns: "Test.IPv4",
expected: "IPv4 deve essere un indirizzo IPv4 valido",
},
{
ns: "Test.IPv6",
expected: "IPv6 deve essere un indirizzo IPv6 valido",
},
{
ns: "Test.DataURI",
expected: "DataURI deve contenere un Data URI valido",
},
{
ns: "Test.Latitude",
expected: "Latitude deve contenere una latitudine valida",
},
{
ns: "Test.Longitude",
expected: "Longitude deve contenere una longitudine valida",
},
{
ns: "Test.MultiByte",
expected: "MultiByte deve contenere caratteri multibyte",
},
{
ns: "Test.ASCII",
expected: "ASCII deve contenere solo caratteri ascii",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII deve contenere solo caratteri ascii stampabili",
},
{
ns: "Test.UUID",
expected: "UUID deve essere un UUID valido",
},
{
ns: "Test.UUID3",
expected: "UUID3 deve essere un UUID versione 3 valido",
},
{
ns: "Test.UUID4",
expected: "UUID4 deve essere un UUID versione 4 valido",
},
{
ns: "Test.UUID5",
expected: "UUID5 deve essere un UUID versione 5 valido",
},
{
ns: "Test.ULID",
expected: "ULID deve essere un ULID valido",
},
{
ns: "Test.ISBN",
expected: "ISBN deve essere un numero ISBN valido",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 deve essere un numero ISBN-10 valido",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 deve essere un numero ISBN-13 valido",
},
{
ns: "Test.Excludes",
expected: "Excludes non deve contenere il testo 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll non deve contenere alcuno dei seguenti caratteri '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune non deve contenere '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny deve contenere almeno uno dei seguenti caratteri '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains deve contenere il testo 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 deve essere una stringa Base64 valida",
},
{
ns: "Test.Email",
expected: "Email deve essere un indirizzo email valido",
},
{
ns: "Test.URL",
expected: "URL deve essere un URL valido",
},
{
ns: "Test.URI",
expected: "URI deve essere un URI valido",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString deve essere un colore RGB valido",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString deve essere un colore RGBA valido",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString deve essere un colore HSL valido",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString deve essere un colore HSLA valido",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString deve essere un esadecimale valido",
},
{
ns: "Test.HexColorString",
expected: "HexColorString deve essere un colore HEX valido",
},
{
ns: "Test.NumberString",
expected: "NumberString deve essere un numero valido",
},
{
ns: "Test.NumericString",
expected: "NumericString deve essere un valore numerico valido",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString può contenere solo caratteri alfanumerici",
},
{
ns: "Test.AlphaString",
expected: "AlphaString può contenere solo caratteri alfabetici",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString deve essere minore di MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString deve essere minore o uguale a MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString deve essere maggiore di MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString deve essere maggiore o uguale a MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString deve essere diverso da EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString deve essere minore di Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString deve essere minore o uguale a Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString deve essere maggiore di Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString deve essere maggiore o uguale a Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString deve essere diverso da Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString deve essere uguale a Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString deve essere uguale a MaxString",
},
{
ns: "Test.GteString",
expected: "GteString deve essere lungo almeno 3 caratteri",
},
{
ns: "Test.GteNumber",
expected: "GteNumber deve essere maggiore o uguale a 5,56",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple deve contenere almeno 2 elementi",
},
{
ns: "Test.GteTime",
expected: "GteTime deve essere uguale o successivo alla Data/Ora corrente",
},
{
ns: "Test.GtString",
expected: "GtString deve essere lungo più di 3 caratteri",
},
{
ns: "Test.GtNumber",
expected: "GtNumber deve essere maggiore di 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple deve contenere più di 2 elementi",
},
{
ns: "Test.GtTime",
expected: "GtTime deve essere successivo alla Data/Ora corrente",
},
{
ns: "Test.LteString",
expected: "LteString deve essere lungo al massimo 3 caratteri",
},
{
ns: "Test.LteNumber",
expected: "LteNumber deve essere minore o uguale a 5,56",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple deve contenere al massimo 2 elementi",
},
{
ns: "Test.LteTime",
expected: "LteTime deve essere uguale o precedente alla Data/Ora corrente",
},
{
ns: "Test.LtString",
expected: "LtString deve essere lungo meno di 3 caratteri",
},
{
ns: "Test.LtNumber",
expected: "LtNumber deve essere minore di 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple deve contenere meno di 2 elementi",
},
{
ns: "Test.LtTime",
expected: "LtTime deve essere precedente alla Data/Ora corrente",
},
{
ns: "Test.NeString",
expected: "NeString deve essere diverso da ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber deve essere diverso da 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple deve essere diverso da 0",
},
{
ns: "Test.EqString",
expected: "EqString non è uguale a 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber non è uguale a 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple non è uguale a 7",
},
{
ns: "Test.MaxString",
expected: "MaxString deve essere lungo al massimo 3 caratteri",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber deve essere minore o uguale a 1.113,00",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple deve contenere al massimo 7 elementi",
},
{
ns: "Test.MinString",
expected: "MinString deve essere lungo almeno 1 carattere",
},
{
ns: "Test.MinNumber",
expected: "MinNumber deve essere maggiore o uguale a 1.113,00",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple deve contenere almeno 7 elementi",
},
{
ns: "Test.LenString",
expected: "LenString deve essere lungo 1 carattere",
},
{
ns: "Test.LenNumber",
expected: "LenNumber deve essere uguale a 1.113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple deve contenere 7 elementi",
},
{
ns: "Test.RequiredString",
expected: "RequiredString è un campo obbligatorio",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber è un campo obbligatorio",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple è un campo obbligatorio",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen deve essere lungo almeno 10 caratteri",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen deve essere lungo al massimo 1 carattere",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen deve essere lungo 2 caratteri",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt deve essere lungo meno di 1 carattere",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte deve essere lungo al massimo 1 carattere",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt deve essere lungo più di 10 caratteri",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte deve essere lungo almeno 10 caratteri",
},
{
ns: "Test.OneOfString",
expected: "OneOfString deve essere uno di [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt deve essere uno di [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice deve contenere valori unici",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray deve contenere valori unici",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap deve contenere valori unici",
},
{
ns: "Test.BooleanString",
expected: "BooleanString deve rappresentare un valore booleano",
},
{
ns: "Test.JSONString",
expected: "JSONString deve essere una stringa json valida",
},
{
ns: "Test.JWTString",
expected: "JWTString deve essere una stringa jwt valida",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString deve essere una stringa minuscola",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString deve essere una stringa maiuscola",
},
{
ns: "Test.StartsWithString",
expected: "StartsWithString deve iniziare con foo",
},
{
ns: "Test.StartsNotWithString",
expected: "StartsNotWithString non deve iniziare con foo",
},
{
ns: "Test.EndsWithString",
expected: "EndsWithString deve terminare con foo",
},
{
ns: "Test.EndsNotWithString",
expected: "EndsNotWithString non deve terminare con foo",
},
{
ns: "Test.Datetime",
expected: "Datetime non corrisponde al formato 2006-01-02",
},
{
ns: "Test.PostCode",
expected: "PostCode non corrisponde al formato del codice postale dello stato SG",
},
{
ns: "Test.PostCodeByField",
expected: "PostCodeByField non corrisponde al formato del codice postale dello stato nel campo PostCodeCountry",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

@ -10,13 +10,12 @@ import (
"github.com/go-playground/locales" "github.com/go-playground/locales"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// RegisterDefaultTranslations registers a set of default translations // RegisterDefaultTranslations registers a set of default translations
// for all built in tag's in validator; you may add your own as desired. // for all built in tag's in validator; you may add your own as desired.
func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) { func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) {
translations := []struct { translations := []struct {
tag string tag string
translation string translation string
@ -29,10 +28,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は必須フィールドです", translation: "{0}は必須フィールドです",
override: false, override: false,
}, },
{
tag: "required_if",
translation: "{0}は必須フィールドです",
override: false,
},
{ {
tag: "len", tag: "len",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("len-string", "{0}の長さは{1}でなければなりません", false); err != nil { if err = ut.Add("len-string", "{0}の長さは{1}でなければなりません", false); err != nil {
return return
} }
@ -64,7 +67,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
}, },
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
var err error var err error
var t string var t string
@ -123,7 +125,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "min", tag: "min",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("min-string", "{0}の長さは少なくとも{1}はなければなりません", false); err != nil { if err = ut.Add("min-string", "{0}の長さは少なくとも{1}はなければなりません", false); err != nil {
return return
} }
@ -136,7 +137,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
} }
if err = ut.Add("min-number", "{0}は{1}かより大きくなければなりません", false); err != nil { if err = ut.Add("min-number", "{0}は{1}以上でなければなりません", false); err != nil {
return return
} }
@ -155,7 +156,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
}, },
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
var err error var err error
var t string var t string
@ -214,7 +214,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "max", tag: "max",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("max-string", "{0}の長さは最大でも{1}でなければなりません", false); err != nil { if err = ut.Add("max-string", "{0}の長さは最大でも{1}でなければなりません", false); err != nil {
return return
} }
@ -227,7 +226,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
} }
if err = ut.Add("max-number", "{0}は{1}かより小さくなければなりません", false); err != nil { if err = ut.Add("max-number", "{0}は{1}以下でなければなりません", false); err != nil {
return return
} }
@ -307,7 +306,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}と等しくありません", translation: "{0}は{1}と等しくありません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
fmt.Printf("warning: error translating FieldError: %#v", fe) fmt.Printf("warning: error translating FieldError: %#v", fe)
@ -374,9 +372,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
if err != nil { if err != nil {
goto END goto END
} }
t, err = ut.T("ne-items", fe.Field(), c) t, err = ut.T("ne-items", fe.Field(), c)
break
default: default:
t, err = ut.T("ne", fe.Field(), fe.Param()) t, err = ut.T("ne", fe.Field(), fe.Param())
} }
@ -393,7 +389,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "lt", tag: "lt",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("lt-string", "{0}の長さは{1}よりも少なくなければなりません", false); err != nil { if err = ut.Add("lt-string", "{0}の長さは{1}よりも少なくなければなりません", false); err != nil {
return return
} }
@ -514,7 +509,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "lte", tag: "lte",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("lte-string", "{0}の長さは最大でも{1}でなければなりません", false); err != nil { if err = ut.Add("lte-string", "{0}の長さは最大でも{1}でなければなりません", false); err != nil {
return return
} }
@ -527,7 +521,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
} }
if err = ut.Add("lte-number", "{0}は{1}かより小さくなければなりません", false); err != nil { if err = ut.Add("lte-number", "{0}は{1}以下でなければなりません", false); err != nil {
return return
} }
@ -634,7 +628,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "gt", tag: "gt",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("gt-string", "{0}の長さは{1}よりも多くなければなりません", false); err != nil { if err = ut.Add("gt-string", "{0}の長さは{1}よりも多くなければなりません", false); err != nil {
return return
} }
@ -754,7 +747,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{ {
tag: "gte", tag: "gte",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("gte-string", "{0}の長さは少なくとも{1}以上はなければなりません", false); err != nil { if err = ut.Add("gte-string", "{0}の長さは少なくとも{1}以上はなければなりません", false); err != nil {
return return
} }
@ -767,7 +759,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
} }
if err = ut.Add("gte-number", "{0}は{1}かより大きくなければなりません", false); err != nil { if err = ut.Add("gte-number", "{0}は{1}以上でなければなりません", false); err != nil {
return return
} }
@ -876,7 +868,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}と等しくなければなりません", translation: "{0}は{1}と等しくなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -891,7 +882,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}と等しくなければなりません", translation: "{0}は{1}と等しくなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -906,7 +896,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}とは異ならなければなりません", translation: "{0}は{1}とは異ならなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -921,7 +910,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}よりも大きくなければなりません", translation: "{0}は{1}よりも大きくなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -936,7 +924,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}以上でなければなりません", translation: "{0}は{1}以上でなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -951,7 +938,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}よりも小さくなければなりません", translation: "{0}は{1}よりも小さくなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -966,7 +952,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}以下でなければなりません", translation: "{0}は{1}以下でなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -981,7 +966,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}とは異ならなければなりません", translation: "{0}は{1}とは異ならなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -996,7 +980,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}よりも大きくなければなりません", translation: "{0}は{1}よりも大きくなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1011,7 +994,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}以上でなければなりません", translation: "{0}は{1}以上でなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1026,7 +1008,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}よりも小さくなければなりません", translation: "{0}は{1}よりも小さくなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1041,7 +1022,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は{1}以下でなければなりません", translation: "{0}は{1}以下でなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1126,7 +1106,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は'{1}'を含まなければなりません", translation: "{0}は'{1}'を含まなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1141,7 +1120,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は'{1}'の少なくとも1つを含まなければなりません", translation: "{0}は'{1}'の少なくとも1つを含まなければなりません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1156,7 +1134,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}には'{1}'というテキストを含むことはできません", translation: "{0}には'{1}'というテキストを含むことはできません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1171,7 +1148,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}には'{1}'のどれも含めることはできません", translation: "{0}には'{1}'のどれも含めることはできません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1186,7 +1162,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}には'{1}'を含めることはできません", translation: "{0}には'{1}'を含めることはできません",
override: false, override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)
@ -1228,7 +1203,12 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
}, },
{ {
tag: "uuid5", tag: "uuid5",
translation: "{0}はバージョンが4の正しいUUIDでなければなりません", translation: "{0}はバージョンが5の正しいUUIDでなければなりません",
override: false,
},
{
tag: "ulid",
translation: "{0}は正しいULIDでなければなりません",
override: false, override: false,
}, },
{ {
@ -1351,6 +1331,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}は正しいMACアドレスを含まなければなりません", translation: "{0}は正しいMACアドレスを含まなければなりません",
override: false, override: false,
}, },
{
tag: "unique",
translation: "{0}は一意な値のみを含まなければなりません",
override: false,
},
{ {
tag: "iscolor", tag: "iscolor",
translation: "{0}は正しい色でなければなりません", translation: "{0}は正しい色でなければなりません",
@ -1369,22 +1354,83 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return s return s
}, },
}, },
{
tag: "json",
translation: "{0}は正しいJSON文字列でなければなりません",
override: false,
},
{
tag: "jwt",
translation: "{0}は正しいJWT文字列でなければなりません",
override: false,
},
{
tag: "lowercase",
translation: "{0}は小文字でなければなりません",
override: false,
},
{
tag: "uppercase",
translation: "{0}は大文字でなければなりません",
override: false,
},
{
tag: "datetime",
translation: "{0}は{1}の書式と一致しません",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe)
return fe.(error).Error()
}
return t
},
},
{
tag: "postcode_iso3166_alpha2",
translation: "{0}は国名コード{1}の郵便番号形式と一致しません",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe)
return fe.(error).Error()
}
return t
},
},
{
tag: "postcode_iso3166_alpha2_field",
translation: "{0}は{1}フィールドで指定された国名コードの郵便番号形式と一致しません",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe)
return fe.(error).Error()
}
return t
},
},
{
tag: "boolean",
translation: "{0}は正しいブール値でなければなりません",
override: false,
},
} }
for _, t := range translations { for _, t := range translations {
if t.customTransFunc != nil && t.customRegisFunc != nil { if t.customTransFunc != nil && t.customRegisFunc != nil {
err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc) err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
} else if t.customTransFunc != nil && t.customRegisFunc == nil { } else if t.customTransFunc != nil && t.customRegisFunc == nil {
err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc) err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc)
} else if t.customTransFunc == nil && t.customRegisFunc != nil { } else if t.customTransFunc == nil && t.customRegisFunc != nil {
err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc) err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc)
} else { } else {
err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc) err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc)
} }
@ -1398,9 +1444,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
} }
func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc { func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc {
return func(ut ut.Translator) (err error) { return func(ut ut.Translator) (err error) {
if err = ut.Add(tag, translation, override); err != nil { if err = ut.Add(tag, translation, override); err != nil {
return return
} }
@ -1412,7 +1456,6 @@ func registrationFunc(tag string, translation string, override bool) validator.R
} }
func translateFunc(ut ut.Translator, fe validator.FieldError) string { func translateFunc(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field()) t, err := ut.T(fe.Tag(), fe.Field())
if err != nil { if err != nil {
log.Printf("warning: error translating FieldError: %#v", fe) log.Printf("warning: error translating FieldError: %#v", fe)

@ -4,14 +4,13 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
ja_locale "github.com/go-playground/locales/ja" ja_locale "github.com/go-playground/locales/ja"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "gopkg.in/go-playground/assert.v1" "github.com/go-playground/validator/v10"
"gopkg.in/go-playground/validator.v9"
) )
func TestTranslations(t *testing.T) { func TestTranslations(t *testing.T) {
japanese := ja_locale.New() japanese := ja_locale.New()
uni := ut.New(japanese, japanese) uni := ut.New(japanese, japanese)
trans, _ := uni.GetTranslator("ja") trans, _ := uni.GetTranslator("ja")
@ -28,116 +27,131 @@ func TestTranslations(t *testing.T) {
GteCSFieldString string GteCSFieldString string
LtCSFieldString string LtCSFieldString string
LteCSFieldString string LteCSFieldString string
RequiredIf string
} }
type Test struct { type Test struct {
Inner Inner Inner Inner
RequiredString string `validate:"required"` RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"` RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"` RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"` RequiredIf string `validate:"required_if=Inner.RequiredIf abcd"`
LenNumber float64 `validate:"len=1113.00"` LenString string `validate:"len=1"`
LenMultiple []string `validate:"len=7"` LenNumber float64 `validate:"len=1113.00"`
MinString string `validate:"min=1"` LenMultiple []string `validate:"len=7"`
MinNumber float64 `validate:"min=1113.00"` MinString string `validate:"min=1"`
MinMultiple []string `validate:"min=7"` MinNumber float64 `validate:"min=1113.00"`
MaxString string `validate:"max=3"` MinMultiple []string `validate:"min=7"`
MaxNumber float64 `validate:"max=1113.00"` MaxString string `validate:"max=3"`
MaxMultiple []string `validate:"max=7"` MaxNumber float64 `validate:"max=1113.00"`
EqString string `validate:"eq=3"` MaxMultiple []string `validate:"max=7"`
EqNumber float64 `validate:"eq=2.33"` EqString string `validate:"eq=3"`
EqMultiple []string `validate:"eq=7"` EqNumber float64 `validate:"eq=2.33"`
NeString string `validate:"ne="` EqMultiple []string `validate:"eq=7"`
NeNumber float64 `validate:"ne=0.00"` NeString string `validate:"ne="`
NeMultiple []string `validate:"ne=0"` NeNumber float64 `validate:"ne=0.00"`
LtString string `validate:"lt=3"` NeMultiple []string `validate:"ne=0"`
LtNumber float64 `validate:"lt=5.56"` LtString string `validate:"lt=3"`
LtMultiple []string `validate:"lt=2"` LtNumber float64 `validate:"lt=5.56"`
LtTime time.Time `validate:"lt"` LtMultiple []string `validate:"lt=2"`
LteString string `validate:"lte=3"` LtTime time.Time `validate:"lt"`
LteNumber float64 `validate:"lte=5.56"` LteString string `validate:"lte=3"`
LteMultiple []string `validate:"lte=2"` LteNumber float64 `validate:"lte=5.56"`
LteTime time.Time `validate:"lte"` LteMultiple []string `validate:"lte=2"`
GtString string `validate:"gt=3"` LteTime time.Time `validate:"lte"`
GtNumber float64 `validate:"gt=5.56"` GtString string `validate:"gt=3"`
GtMultiple []string `validate:"gt=2"` GtNumber float64 `validate:"gt=5.56"`
GtTime time.Time `validate:"gt"` GtMultiple []string `validate:"gt=2"`
GteString string `validate:"gte=3"` GtTime time.Time `validate:"gt"`
GteNumber float64 `validate:"gte=5.56"` GteString string `validate:"gte=3"`
GteMultiple []string `validate:"gte=2"` GteNumber float64 `validate:"gte=5.56"`
GteTime time.Time `validate:"gte"` GteMultiple []string `validate:"gte=2"`
EqFieldString string `validate:"eqfield=MaxString"` GteTime time.Time `validate:"gte"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"` EqFieldString string `validate:"eqfield=MaxString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"` EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"` NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"` GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"` GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"` LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"` LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
GtFieldString string `validate:"gtfield=MaxString"` NeFieldString string `validate:"nefield=EqFieldString"`
GteFieldString string `validate:"gtefield=MaxString"` GtFieldString string `validate:"gtfield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"` GteFieldString string `validate:"gtefield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"` LtFieldString string `validate:"ltfield=MaxString"`
AlphaString string `validate:"alpha"` LteFieldString string `validate:"ltefield=MaxString"`
AlphanumString string `validate:"alphanum"` AlphaString string `validate:"alpha"`
NumericString string `validate:"numeric"` AlphanumString string `validate:"alphanum"`
NumberString string `validate:"number"` NumericString string `validate:"numeric"`
HexadecimalString string `validate:"hexadecimal"` NumberString string `validate:"number"`
HexColorString string `validate:"hexcolor"` HexadecimalString string `validate:"hexadecimal"`
RGBColorString string `validate:"rgb"` HexColorString string `validate:"hexcolor"`
RGBAColorString string `validate:"rgba"` RGBColorString string `validate:"rgb"`
HSLColorString string `validate:"hsl"` RGBAColorString string `validate:"rgba"`
HSLAColorString string `validate:"hsla"` HSLColorString string `validate:"hsl"`
Email string `validate:"email"` HSLAColorString string `validate:"hsla"`
URL string `validate:"url"` Email string `validate:"email"`
URI string `validate:"uri"` URL string `validate:"url"`
Base64 string `validate:"base64"` URI string `validate:"uri"`
Contains string `validate:"contains=purpose"` Base64 string `validate:"base64"`
ContainsAny string `validate:"containsany=!@#$"` Contains string `validate:"contains=purpose"`
Excludes string `validate:"excludes=text"` ContainsAny string `validate:"containsany=!@#$"`
ExcludesAll string `validate:"excludesall=!@#$"` Excludes string `validate:"excludes=text"`
ExcludesRune string `validate:"excludesrune=☻"` ExcludesAll string `validate:"excludesall=!@#$"`
ISBN string `validate:"isbn"` ExcludesRune string `validate:"excludesrune=☻"`
ISBN10 string `validate:"isbn10"` ISBN string `validate:"isbn"`
ISBN13 string `validate:"isbn13"` ISBN10 string `validate:"isbn10"`
UUID string `validate:"uuid"` ISBN13 string `validate:"isbn13"`
UUID3 string `validate:"uuid3"` UUID string `validate:"uuid"`
UUID4 string `validate:"uuid4"` UUID3 string `validate:"uuid3"`
UUID5 string `validate:"uuid5"` UUID4 string `validate:"uuid4"`
ASCII string `validate:"ascii"` UUID5 string `validate:"uuid5"`
PrintableASCII string `validate:"printascii"` ULID string `validate:"ulid"`
MultiByte string `validate:"multibyte"` ASCII string `validate:"ascii"`
DataURI string `validate:"datauri"` PrintableASCII string `validate:"printascii"`
Latitude string `validate:"latitude"` MultiByte string `validate:"multibyte"`
Longitude string `validate:"longitude"` DataURI string `validate:"datauri"`
SSN string `validate:"ssn"` Latitude string `validate:"latitude"`
IP string `validate:"ip"` Longitude string `validate:"longitude"`
IPv4 string `validate:"ipv4"` SSN string `validate:"ssn"`
IPv6 string `validate:"ipv6"` IP string `validate:"ip"`
CIDR string `validate:"cidr"` IPv4 string `validate:"ipv4"`
CIDRv4 string `validate:"cidrv4"` IPv6 string `validate:"ipv6"`
CIDRv6 string `validate:"cidrv6"` CIDR string `validate:"cidr"`
TCPAddr string `validate:"tcp_addr"` CIDRv4 string `validate:"cidrv4"`
TCPAddrv4 string `validate:"tcp4_addr"` CIDRv6 string `validate:"cidrv6"`
TCPAddrv6 string `validate:"tcp6_addr"` TCPAddr string `validate:"tcp_addr"`
UDPAddr string `validate:"udp_addr"` TCPAddrv4 string `validate:"tcp4_addr"`
UDPAddrv4 string `validate:"udp4_addr"` TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddrv6 string `validate:"udp6_addr"` UDPAddr string `validate:"udp_addr"`
IPAddr string `validate:"ip_addr"` UDPAddrv4 string `validate:"udp4_addr"`
IPAddrv4 string `validate:"ip4_addr"` UDPAddrv6 string `validate:"udp6_addr"`
IPAddrv6 string `validate:"ip6_addr"` IPAddr string `validate:"ip_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future IPAddrv4 string `validate:"ip4_addr"`
MAC string `validate:"mac"` IPAddrv6 string `validate:"ip6_addr"`
IsColor string `validate:"iscolor"` UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
StrPtrMinLen *string `validate:"min=10"` MAC string `validate:"mac"`
StrPtrMaxLen *string `validate:"max=1"` IsColor string `validate:"iscolor"`
StrPtrLen *string `validate:"len=2"` StrPtrMinLen *string `validate:"min=10"`
StrPtrLt *string `validate:"lt=1"` StrPtrMaxLen *string `validate:"max=1"`
StrPtrLte *string `validate:"lte=1"` StrPtrLen *string `validate:"len=2"`
StrPtrGt *string `validate:"gt=10"` StrPtrLt *string `validate:"lt=1"`
StrPtrGte *string `validate:"gte=10"` StrPtrLte *string `validate:"lte=1"`
OneOfString string `validate:"oneof=red green"` StrPtrGt *string `validate:"gt=10"`
OneOfInt int `validate:"oneof=5 63"` StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
JSONString string `validate:"json"`
JWTString string `validate:"jwt"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
PostCode string `validate:"postcode_iso3166_alpha2=SG"`
PostCodeCountry string
PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"`
BooleanString string `validate:"boolean"`
} }
var test Test var test Test
@ -180,10 +194,20 @@ func TestTranslations(t *testing.T) {
test.MultiByte = "1234feerf" test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
s := "toolong" s := "toolong"
test.StrPtrMaxLen = &s test.StrPtrMaxLen = &s
test.StrPtrLen = &s test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
test.BooleanString = "A"
test.Inner.RequiredIf = "abcd"
err = validate.Struct(test) err = validate.Struct(test)
NotEqual(t, err, nil) NotEqual(t, err, nil)
@ -304,7 +328,11 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5はバージョンが4の正しいUUIDでなければなりません", expected: "UUID5はバージョンが5の正しいUUIDでなければなりません",
},
{
ns: "Test.ULID",
expected: "ULIDは正しいULIDでなければなりません",
}, },
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
@ -448,7 +476,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.GteNumber", ns: "Test.GteNumber",
expected: "GteNumberは5.56かより大きくなければなりません", expected: "GteNumberは5.56以上でなければなりません",
}, },
{ {
ns: "Test.GteMultiple", ns: "Test.GteMultiple",
@ -480,7 +508,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.LteNumber", ns: "Test.LteNumber",
expected: "LteNumberは5.56かより小さくなければなりません", expected: "LteNumberは5.56以下でなければなりません",
}, },
{ {
ns: "Test.LteMultiple", ns: "Test.LteMultiple",
@ -536,7 +564,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.MaxNumber", ns: "Test.MaxNumber",
expected: "MaxNumberは1,113.00かより小さくなければなりません", expected: "MaxNumberは1,113.00以下でなければなりません",
}, },
{ {
ns: "Test.MaxMultiple", ns: "Test.MaxMultiple",
@ -548,7 +576,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.MinNumber", ns: "Test.MinNumber",
expected: "MinNumberは1,113.00かより大きくなければなりません", expected: "MinNumberは1,113.00以上でなければなりません",
}, },
{ {
ns: "Test.MinMultiple", ns: "Test.MinMultiple",
@ -570,6 +598,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.RequiredString", ns: "Test.RequiredString",
expected: "RequiredStringは必須フィールドです", expected: "RequiredStringは必須フィールドです",
}, },
{
ns: "Test.RequiredIf",
expected: "RequiredIfは必須フィールドです",
},
{ {
ns: "Test.RequiredNumber", ns: "Test.RequiredNumber",
expected: "RequiredNumberは必須フィールドです", expected: "RequiredNumberは必須フィールドです",
@ -614,6 +646,50 @@ func TestTranslations(t *testing.T) {
ns: "Test.OneOfInt", ns: "Test.OneOfInt",
expected: "OneOfIntは[5 63]のうちのいずれかでなければなりません", expected: "OneOfIntは[5 63]のうちのいずれかでなければなりません",
}, },
{
ns: "Test.UniqueSlice",
expected: "UniqueSliceは一意な値のみを含まなければなりません",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArrayは一意な値のみを含まなければなりません",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMapは一意な値のみを含まなければなりません",
},
{
ns: "Test.JSONString",
expected: "JSONStringは正しいJSON文字列でなければなりません",
},
{
ns: "Test.JWTString",
expected: "JWTStringは正しいJWT文字列でなければなりません",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseStringは小文字でなければなりません",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseStringは大文字でなければなりません",
},
{
ns: "Test.Datetime",
expected: "Datetimeは2006-01-02の書式と一致しません",
},
{
ns: "Test.PostCode",
expected: "PostCodeは国名コードSGの郵便番号形式と一致しません",
},
{
ns: "Test.PostCodeByField",
expected: "PostCodeByFieldはPostCodeCountryフィールドで指定された国名コードの郵便番号形式と一致しません",
},
{
ns: "Test.BooleanString",
expected: "BooleanStringは正しいブール値でなければなりません",
},
} }
for _, tt := range tests { for _, tt := range tests {

File diff suppressed because it is too large Load Diff

@ -0,0 +1,709 @@
package lv
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/validator/v10"
)
func TestTranslations(t *testing.T) {
eng := english.New()
uni := ut.New(eng, eng)
trans, _ := uni.GetTranslator("en")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
RequiredIf string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
RequiredIf string `validate:"required_if=Inner.RequiredIf abcd"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
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"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
JSONString string `validate:"json"`
JWTString string `validate:"jwt"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
PostCode string `validate:"postcode_iso3166_alpha2=SG"`
PostCodeCountry string
PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"`
BooleanString string `validate:"boolean"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
test.BooleanString = "A"
test.Inner.RequiredIf = "abcd"
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor jābūt derīgai krāsai",
},
{
ns: "Test.MAC",
expected: "MAC jābūt derīgai MAC adresei",
},
{
ns: "Test.IPAddr",
expected: "IPAddr jābūt atrisināmai IP adresei",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 jābūt atrisināmai IPv4 adresei",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 jābūt atrisināmai IPv6 adresei",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr jābūt derīgai UDP adresei",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 jābūt derīgai IPv4 UDP adresei",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 jābūt derīgai IPv6 UDP adresei",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr jābūt derīgai TCP adresei",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 jābūt derīgai IPv4 TCP adresei",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 jābūt derīgai IPv6 TCP adresei",
},
{
ns: "Test.CIDR",
expected: "CIDR jāsatur derīgu CIDR notāciju",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 jāsatur derīgu CIDR notāciju IPv4 adresei",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 jāsatur derīgu CIDR notāciju IPv6 adresei",
},
{
ns: "Test.SSN",
expected: "SSN jābūt derīgam SSN numuram",
},
{
ns: "Test.IP",
expected: "IP jābūt derīgai IP adresei",
},
{
ns: "Test.IPv4",
expected: "IPv4 jābūt derīgai IPv4 adresei",
},
{
ns: "Test.IPv6",
expected: "IPv6 jābūt derīgai IPv6 adresei",
},
{
ns: "Test.DataURI",
expected: "DataURI jāsatur derīgs Data URI",
},
{
ns: "Test.Latitude",
expected: "Latitude jāsatur derīgus platuma grādus",
},
{
ns: "Test.Longitude",
expected: "Longitude jāsatur derīgus garuma grādus",
},
{
ns: "Test.MultiByte",
expected: "MultiByte jāsatur multibyte rakstu zīmes",
},
{
ns: "Test.ASCII",
expected: "ASCII jāsatur tikai ascii rakstu zīmes",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII jāsatur tikai drukājamas ascii rakstu zīmes",
},
{
ns: "Test.UUID",
expected: "UUID jābūt derīgam UUID",
},
{
ns: "Test.UUID3",
expected: "UUID3 jābūt derīgam 3. versijas UUID",
},
{
ns: "Test.UUID4",
expected: "UUID4 jābūt derīgam 4. versijas UUID",
},
{
ns: "Test.UUID5",
expected: "UUID5 jābūt derīgam 5. versijas UUID",
},
{
ns: "Test.ULID",
expected: "ULID jābūt derīgam ULID",
},
{
ns: "Test.ISBN",
expected: "ISBN jābūt derīgam ISBN numuram",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 jābūt derīgam ISBN-10 numuram",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 jābūt derīgam ISBN-13 numuram",
},
{
ns: "Test.Excludes",
expected: "Excludes nedrīkst saturēt tekstu 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll nedrīkst saturēt nevienu no sekojošām rakstu zīmēm '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune nedrīkst saturēt sekojošo '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny jāsatur minimums 1 no rakstu zīmēm '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains jāsatur teksts 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 jābūt derīgai Base64 virknei",
},
{
ns: "Test.Email",
expected: "Email jābūt derīgai e-pasta adresei",
},
{
ns: "Test.URL",
expected: "URL jābūt derīgam URL",
},
{
ns: "Test.URI",
expected: "URI jābūt derīgam URI",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString jābūt derīgai RGB krāsai",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString jābūt derīgai RGBA krāsai",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString jābūt derīgai HSL krāsai",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString jābūt derīgai HSLA krāsai",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString jābūt heksadecimālam skaitlim",
},
{
ns: "Test.HexColorString",
expected: "HexColorString jābūt derīgai HEX krāsai",
},
{
ns: "Test.NumberString",
expected: "NumberString jāsatur derīgs skaitlis",
},
{
ns: "Test.NumericString",
expected: "NumericString jāsatur tikai cipari",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString jāsatur tikai simboli no alfabēta vai cipari (Alphanumeric)",
},
{
ns: "Test.AlphaString",
expected: "AlphaString jāsatur tikai simboli no alfabēta",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString jābūt mazākam par MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString jābūt mazākam par MaxString vai vienādam",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString jābūt lielākam par MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString jābūt lielākam par MaxString vai vienādam",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString nedrīkst būt vienāds ar EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString jābūt mazākam par Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString jābūt mazākam par Inner.LteCSFieldString vai vienādam",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString jābūt lielākam par Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString jābūt lielākam par Inner.GteCSFieldString vai vienādam",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString nedrīkst būt vienāds ar Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString jābūt vienādam ar Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString jābūt vienādam ar MaxString",
},
{
ns: "Test.GteString",
expected: "GteString garumam jābūt minimums 3 rakstu zīmes",
},
{
ns: "Test.GteNumber",
expected: "GteNumber jābūt 5.56 vai lielākam",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple jāsatur minimums 2 elementi",
},
{
ns: "Test.GteTime",
expected: "GteTime jābūt lielākam par šī brīža Datumu un laiku vai vienādam",
},
{
ns: "Test.GtString",
expected: "GtString ir jābūt garākam par 3 rakstu zīmēm",
},
{
ns: "Test.GtNumber",
expected: "GtNumber jābūt lielākam par 5.56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple jāsatur vairāk par 2 elementiem",
},
{
ns: "Test.GtTime",
expected: "GtTime jābūt lielākam par šī brīža Datumu un laiku",
},
{
ns: "Test.LteString",
expected: "LteString garumam jābūt maksimums 3 rakstu zīmes",
},
{
ns: "Test.LteNumber",
expected: "LteNumber jābūt 5.56 vai mazākam",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple jāsatur maksimums 2 elementi",
},
{
ns: "Test.LteTime",
expected: "LteTime jābūt mazākam par šī brīža Datumu un laiku vai vienādam",
},
{
ns: "Test.LtString",
expected: "LtString garumam jābūt mazākam par 3 rakstu zīmēm",
},
{
ns: "Test.LtNumber",
expected: "LtNumber jābūt mazākam par 5.56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple jāsatur mazāk par 2 elementiem",
},
{
ns: "Test.LtTime",
expected: "LtTime jābūt mazākam par šī brīža Datumu un laiku",
},
{
ns: "Test.NeString",
expected: "NeString nedrīkst būt vienāds ar ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber nedrīkst būt vienāds ar 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple nedrīkst būt vienāds ar 0",
},
{
ns: "Test.EqString",
expected: "EqString nav vienāds ar 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber nav vienāds ar 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple nav vienāds ar 7",
},
{
ns: "Test.MaxString",
expected: "MaxString vērtība pārsniedz maksimālo garumu 3 rakstu zīmes",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber vērtībai jābūt 1,113.00 vai mazākai",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple jāsatur maksimums 7 elementi",
},
{
ns: "Test.MinString",
expected: "MinString garumam jābūt minimums 1 rakstu zīme",
},
{
ns: "Test.MinNumber",
expected: "MinNumber vērtībai jābūt 1,113.00 vai lielākai",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple jāsatur minimums 7 elementi",
},
{
ns: "Test.LenString",
expected: "LenString garumam jābūt 1 rakstu zīme",
},
{
ns: "Test.LenNumber",
expected: "LenNumber vērtībai jābūt 1,113.00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple vērtībai jāsatur 7 elementi",
},
{
ns: "Test.RequiredString",
expected: "RequiredString ir obligāts lauks",
},
{
ns: "Test.RequiredIf",
expected: "RequiredIf ir obligāts lauks",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber ir obligāts lauks",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple ir obligāts lauks",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen garumam jābūt minimums 10 rakstu zīmes",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen vērtība pārsniedz maksimālo garumu 1 rakstu zīme",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen garumam jābūt 2 rakstu zīmes",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt garumam jābūt mazākam par 1 rakstu zīmi",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte garumam jābūt maksimums 1 rakstu zīme",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt ir jābūt garākam par 10 rakstu zīmēm",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte garumam jābūt minimums 10 rakstu zīmes",
},
{
ns: "Test.OneOfString",
expected: "OneOfString jābūt vienam no [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt jābūt vienam no [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice jāsatur unikālas vērtības",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray jāsatur unikālas vērtības",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap jāsatur unikālas vērtības",
},
{
ns: "Test.JSONString",
expected: "JSONString jābūt derīgai json virknei",
},
{
ns: "Test.JWTString",
expected: "JWTString jābūt derīgai jwt virknei",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString jābūt mazo burtu virknei",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString jābūt lielo burtu virknei",
},
{
ns: "Test.Datetime",
expected: "Datetime neatbilst formātam 2006-01-02",
},
{
ns: "Test.PostCode",
expected: "PostCode neatbilst pasta indeksa formātam valstī SG",
},
{
ns: "Test.PostCodeByField",
expected: "PostCodeByField neatbilst pasta indeksa formātam valstī, kura norādīta laukā PostCodeCountry",
},
{
ns: "Test.BooleanString",
expected: "BooleanString jābūt derīgai boolean vērtībai",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

@ -10,7 +10,7 @@ import (
"github.com/go-playground/locales" "github.com/go-playground/locales"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// RegisterDefaultTranslations registers a set of default translations // RegisterDefaultTranslations registers a set of default translations
@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} moet een geldige versie 5 UUID zijn", translation: "{0} moet een geldige versie 5 UUID zijn",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} moet een geldige ULID zijn",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} mag alleen ascii karakters bevatten", translation: "{0} mag alleen ascii karakters bevatten",

@ -4,10 +4,10 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
english "github.com/go-playground/locales/en" english "github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "gopkg.in/go-playground/assert.v1" "github.com/go-playground/validator/v10"
"gopkg.in/go-playground/validator.v9"
) )
func TestTranslations(t *testing.T) { func TestTranslations(t *testing.T) {
@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 moet een geldige versie 5 UUID zijn", expected: "UUID5 moet een geldige versie 5 UUID zijn",
}, },
{
ns: "Test.ULID",
expected: "ULID moet een geldige ULID zijn",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN moet een geldig ISBN nummer zijn", expected: "ISBN moet een geldig ISBN nummer zijn",

File diff suppressed because it is too large Load Diff

@ -0,0 +1,682 @@
package pt
import (
"testing"
"time"
. "github.com/go-playground/assert/v2"
"github.com/go-playground/locales/pt"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
)
func TestTranslations(t *testing.T) {
pt := pt.New()
uni := ut.New(pt, pt)
trans, _ := uni.GetTranslator("pt")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
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"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
JSONString string `validate:"json"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor deve ser uma cor válida",
},
{
ns: "Test.MAC",
expected: "MAC deve conter um endereço MAC válido",
},
{
ns: "Test.IPAddr",
expected: "IPAddr deve ser um endereço IP resolvível",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 deve ser um endereço IPv4 resolvível",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 deve ser um endereço IPv6 resolvível",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr deve ser um endereço UDP válido",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 deve ser um endereço UDP IPv4 válido",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 deve ser um endereço UDP IPv6 válido",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr deve ser um endereço TCP válido",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 deve ser um endereço TCP IPv4 válido",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 deve ser um endereço TCP IPv6 válido",
},
{
ns: "Test.CIDR",
expected: "CIDR não respeita a notação CIDR",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 não respeita a notação CIDR para um endereço IPv4",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 não respeita a notação CIDR para um endereço IPv6",
},
{
ns: "Test.SSN",
expected: "SSN deve ser um número SSN válido",
},
{
ns: "Test.IP",
expected: "IP deve ser um endereço IP válido",
},
{
ns: "Test.IPv4",
expected: "IPv4 deve ser um endereço IPv4 válido",
},
{
ns: "Test.IPv6",
expected: "IPv6 deve ser um endereço IPv6 válido",
},
{
ns: "Test.DataURI",
expected: "DataURI deve conter um Data URI válido",
},
{
ns: "Test.Latitude",
expected: "Latitude deve conter uma coordenada de latitude válida",
},
{
ns: "Test.Longitude",
expected: "Longitude deve conter uma coordenada de longitude válida",
},
{
ns: "Test.MultiByte",
expected: "MultiByte deve conter caracteres multibyte",
},
{
ns: "Test.ASCII",
expected: "ASCII deve conter apenas caracteres ascii",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII deve conter apenas caracteres ascii imprimíveis",
},
{
ns: "Test.UUID",
expected: "UUID deve ser um UUID válido",
},
{
ns: "Test.UUID3",
expected: "UUID3 deve ser um UUID versão 3 válido",
},
{
ns: "Test.UUID4",
expected: "UUID4 deve ser um UUID versão 4 válido",
},
{
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",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 deve ser um número ISBN-10 válido",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 deve ser um número ISBN-13 válido",
},
{
ns: "Test.Excludes",
expected: "Excludes não deve conter o texto 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll não deve conter os seguintes caracteres '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune não pode conter o seguinte '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny deve conter pelo menos um dos seguintes caracteres '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains deve conter o texto 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 deve ser uma string Base64 válida",
},
{
ns: "Test.Email",
expected: "Email deve ser um endereço de e-mail válido",
},
{
ns: "Test.URL",
expected: "URL deve ser um URL válido",
},
{
ns: "Test.URI",
expected: "URI deve ser um URI válido",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString deve ser uma cor RGB válida",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString deve ser uma cor RGBA válida",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString deve ser uma cor HSL válida",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString deve ser uma cor HSLA válida",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString deve ser um hexadecimal válido",
},
{
ns: "Test.HexColorString",
expected: "HexColorString deve ser uma cor HEX válida",
},
{
ns: "Test.NumberString",
expected: "NumberString deve ser um número válido",
},
{
ns: "Test.NumericString",
expected: "NumericString deve ser um valor numérico válido",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString deve conter apenas caracteres alfanuméricos",
},
{
ns: "Test.AlphaString",
expected: "AlphaString deve conter apenas caracteres alfabéticos",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString deve ser menor que MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString deve ser menor ou igual que MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString deve ser maior que MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString deve ser maior ou igual que MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString não deve ser igual a EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString deve ser menor que Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString deve ser menor ou igual que Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString deve ser maior que Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString deve ser maior ou igual que Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString não deve ser igual a Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString deve ser igual a Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString deve ser igual a MaxString",
},
{
ns: "Test.GteString",
expected: "GteString deve ter pelo menos 3 caracteres",
},
{
ns: "Test.GteNumber",
expected: "GteNumber deve ser maior ou igual a 5,56",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple deve conter pelo menos 2 items",
},
{
ns: "Test.GteTime",
expected: "GteTime deve ser posterior ou igual à data/hora atual",
},
{
ns: "Test.GtString",
expected: "GtString deve conter mais de 3 caracteres",
},
{
ns: "Test.GtNumber",
expected: "GtNumber deve ser maior que 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple deve conter mais de 2 items",
},
{
ns: "Test.GtTime",
expected: "GtTime deve ser posterior à data/hora atual",
},
{
ns: "Test.LteString",
expected: "LteString deve ter no máximo 3 caracteres",
},
{
ns: "Test.LteNumber",
expected: "LteNumber deve ser menor ou igual a 5,56",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple deve conter no máximo 2 items",
},
{
ns: "Test.LteTime",
expected: "LteTime deve ser anterior ou igual à data/hora atual",
},
{
ns: "Test.LtString",
expected: "LtString deve ter menos de 3 caracteres",
},
{
ns: "Test.LtNumber",
expected: "LtNumber deve ser menor que 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple deve conter menos de 2 items",
},
{
ns: "Test.LtTime",
expected: "LtTime deve ser anterior à data / hora atual",
},
{
ns: "Test.NeString",
expected: "NeString não deve ser igual a ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber não deve ser igual a 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple não deve ser igual a 0",
},
{
ns: "Test.EqString",
expected: "EqString não é igual a 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber não é igual a 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple não é igual a 7",
},
{
ns: "Test.MaxString",
expected: "MaxString deve ter no máximo 3 caracteres",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber deve ser 1.113,00 ou menos",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple deve conter no máximo 7 items",
},
{
ns: "Test.MinString",
expected: "MinString deve ter pelo menos 1 caractere",
},
{
ns: "Test.MinNumber",
expected: "MinNumber deve ser 1.113,00 ou superior",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple deve conter pelo menos 7 items",
},
{
ns: "Test.LenString",
expected: "LenString deve ter 1 caractere",
},
{
ns: "Test.LenNumber",
expected: "LenNumber deve ser igual a 1.113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple deve conter 7 items",
},
{
ns: "Test.RequiredString",
expected: "RequiredString é obrigatório",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber é obrigatório",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple é obrigatório",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen deve ter pelo menos 10 caracteres",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen deve ter no máximo 1 caractere",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen deve ter 2 caracteres",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt deve ter menos de 1 caractere",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte deve ter no máximo 1 caractere",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt deve conter mais de 10 caracteres",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte deve ter pelo menos 10 caracteres",
},
{
ns: "Test.OneOfString",
expected: "OneOfString deve ser um de [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt deve ser um de [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice deve conter valores únicos",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray deve conter valores únicos",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap deve conter valores únicos",
},
{
ns: "Test.JSONString",
expected: "JSONString deve ser uma string json válida",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString deve estar em minuscúlas",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString deve estar em maiúsculas",
},
{
ns: "Test.Datetime",
expected: "Datetime não está no formato 2006-01-02",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

@ -10,7 +10,7 @@ import (
"github.com/go-playground/locales" "github.com/go-playground/locales"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// RegisterDefaultTranslations registers a set of default translations // RegisterDefaultTranslations registers a set of default translations
@ -26,7 +26,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
}{ }{
{ {
tag: "required", tag: "required",
translation: "{0} é um campo requerido", translation: "{0} é um campo obrigatório",
override: false, override: false,
}, },
{ {
@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} deve ser um UUID versão 5 válido", translation: "{0} deve ser um UUID versão 5 válido",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} deve ser uma ULID válida",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} deve conter apenas caracteres ascii", translation: "{0} deve conter apenas caracteres ascii",
@ -1311,6 +1316,16 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return s return s
}, },
}, },
{
tag: "boolean",
translation: "{0} deve ser um valor booleano válido",
override: false,
},
{
tag: "cve",
translation: "{0} deve ser um identificador cve válido",
override: false,
},
} }
for _, t := range translations { for _, t := range translations {

@ -4,10 +4,10 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
brazilian_portuguese "github.com/go-playground/locales/pt_BR" brazilian_portuguese "github.com/go-playground/locales/pt_BR"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
. "gopkg.in/go-playground/assert.v1"
) )
func TestTranslations(t *testing.T) { func TestTranslations(t *testing.T) {
@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -138,6 +139,8 @@ func TestTranslations(t *testing.T) {
StrPtrGte *string `validate:"gte=10"` StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"` OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"` OneOfInt int `validate:"oneof=5 63"`
BooleanString string `validate:"boolean"`
CveString string `validate:"cve"`
} }
var test Test var test Test
@ -170,6 +173,8 @@ func TestTranslations(t *testing.T) {
test.AlphanumString = "abc3!" test.AlphanumString = "abc3!"
test.NumericString = "12E.00" test.NumericString = "12E.00"
test.NumberString = "12E" test.NumberString = "12E"
test.BooleanString = "A"
test.CveString = "A"
test.Excludes = "este é um texto de teste" test.Excludes = "este é um texto de teste"
test.ExcludesAll = "Isso é Ótimo!" test.ExcludesAll = "Isso é Ótimo!"
@ -306,6 +311,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 deve ser um UUID versão 5 válido", 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", ns: "Test.ISBN",
expected: "ISBN deve ser um número ISBN válido", expected: "ISBN deve ser um número ISBN válido",
@ -568,15 +577,15 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.RequiredString", ns: "Test.RequiredString",
expected: "RequiredString é um campo requerido", expected: "RequiredString é um campo obrigatório",
}, },
{ {
ns: "Test.RequiredNumber", ns: "Test.RequiredNumber",
expected: "RequiredNumber é um campo requerido", expected: "RequiredNumber é um campo obrigatório",
}, },
{ {
ns: "Test.RequiredMultiple", ns: "Test.RequiredMultiple",
expected: "RequiredMultiple é um campo requerido", expected: "RequiredMultiple é um campo obrigatório",
}, },
{ {
ns: "Test.StrPtrMinLen", ns: "Test.StrPtrMinLen",
@ -614,6 +623,14 @@ func TestTranslations(t *testing.T) {
ns: "Test.OneOfInt", ns: "Test.OneOfInt",
expected: "OneOfInt deve ser um de [5 63]", expected: "OneOfInt deve ser um de [5 63]",
}, },
{
ns: "Test.BooleanString",
expected: "BooleanString deve ser um valor booleano válido",
},
{
ns: "Test.CveString",
expected: "CveString deve ser um identificador cve válido",
},
} }
for _, tt := range tests { for _, tt := range tests {

File diff suppressed because it is too large Load Diff

@ -0,0 +1,767 @@
package ru
import (
"log"
//"github.com/rustery/validator"
"testing"
"time"
. "github.com/go-playground/assert/v2"
russian "github.com/go-playground/locales/ru"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
)
func TestTranslations(t *testing.T) {
ru := russian.New()
uni := ut.New(ru, ru)
trans, _ := uni.GetTranslator("ru")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
LenMultipleSecond []string `validate:"len=2"`
MinString string `validate:"min=1"`
MinStringMultiple string `validate:"min=2"`
MinStringMultipleSecond string `validate:"min=7"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MinMultipleSecond []string `validate:"min=2"`
MaxString string `validate:"max=3"`
MaxStringSecond string `validate:"max=7"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
MaxMultipleSecond []string `validate:"max=2"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtStringSecond string `validate:"lt=7"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtMultipleSecond []string `validate:"lt=7"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteStringSecond string `validate:"lte=7"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteMultipleSecond []string `validate:"lte=7"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtStringSecond string `validate:"gt=7"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtMultipleSecond []string `validate:"gt=7"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteStringSecond string `validate:"gte=7"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteMultipleSecond []string `validate:"gte=7"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
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"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMinLenSecond *string `validate:"min=2"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLenSecond *string `validate:"len=7"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrLteMultiple *string `validate:"lte=2"`
StrPtrLteMultipleSecond *string `validate:"lte=7"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
StrPtrGtSecond *string `validate:"gt=2"`
StrPtrGteSecond *string `validate:"gte=2"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxStringSecond = "12345678"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.MaxMultipleSecond = make([]string, 3)
test.LtString = "1234"
test.LtStringSecond = "12345678"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtMultipleSecond = make([]string, 8)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteStringSecond = "12345678"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteMultipleSecond = make([]string, 8)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor должен быть цветом",
},
{
ns: "Test.MAC",
expected: "MAC должен содержать MAC адрес",
},
{
ns: "Test.IPAddr",
expected: "IPAddr должен быть распознаваемым IP адресом",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 должен быть распознаваемым IPv4 адресом",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 должен быть распознаваемым IPv6 адресом",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr должен быть UDP адресом",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 должен быть IPv4 UDP адресом",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 должен быть IPv6 UDP адресом",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr должен быть TCP адресом",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 должен быть IPv4 TCP адресом",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 должен быть IPv6 TCP адресом",
},
{
ns: "Test.CIDR",
expected: "CIDR должен содержать CIDR обозначения",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 должен содержать CIDR обозначения для IPv4 адреса",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 должен содержать CIDR обозначения для IPv6 адреса",
},
{
ns: "Test.SSN",
expected: "SSN должен быть SSN номером",
},
{
ns: "Test.IP",
expected: "IP должен быть IP адресом",
},
{
ns: "Test.IPv4",
expected: "IPv4 должен быть IPv4 адресом",
},
{
ns: "Test.IPv6",
expected: "IPv6 должен быть IPv6 адресом",
},
{
ns: "Test.DataURI",
expected: "DataURI должен содержать Data URI",
},
{
ns: "Test.Latitude",
expected: "Latitude должен содержать координаты широты",
},
{
ns: "Test.Longitude",
expected: "Longitude должен содержать координаты долготы",
},
{
ns: "Test.MultiByte",
expected: "MultiByte должен содержать мультибайтные символы",
},
{
ns: "Test.ASCII",
expected: "ASCII должен содержать только ascii символы",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII должен содержать только доступные для печати ascii символы",
},
{
ns: "Test.UUID",
expected: "UUID должен быть UUID",
},
{
ns: "Test.UUID3",
expected: "UUID3 должен быть UUID 3 версии",
},
{
ns: "Test.UUID4",
expected: "UUID4 должен быть UUID 4 версии",
},
{
ns: "Test.UUID5",
expected: "UUID5 должен быть UUID 5 версии",
},
{
ns: "Test.ULID",
expected: "ULID должен быть ULID",
},
{
ns: "Test.ISBN",
expected: "ISBN должен быть ISBN номером",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 должен быть ISBN-10 номером",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 должен быть ISBN-13 номером",
},
{
ns: "Test.Excludes",
expected: "Excludes не должен содержать текст 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll не должен содержать символы '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune не должен содержать '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny должен содержать минимум один из символов '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains должен содержать текст 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 должен быть Base64 строкой",
},
{
ns: "Test.Email",
expected: "Email должен быть email адресом",
},
{
ns: "Test.URL",
expected: "URL должен быть URL",
},
{
ns: "Test.URI",
expected: "URI должен быть URI",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString должен быть RGB цветом",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString должен быть RGBA цветом",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString должен быть HSL цветом",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString должен быть HSLA цветом",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString должен быть шестнадцатеричной строкой",
},
{
ns: "Test.HexColorString",
expected: "HexColorString должен быть HEX цветом",
},
{
ns: "Test.NumberString",
expected: "NumberString должен быть цифрой",
},
{
ns: "Test.NumericString",
expected: "NumericString должен быть цифровым значением",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString должен содержать только буквы и цифры",
},
{
ns: "Test.AlphaString",
expected: "AlphaString должен содержать только буквы",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString должен быть менее MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString должен быть менее или равен MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString должен быть больше MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString должен быть больше или равен MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString не должен быть равен EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString должен быть менее Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString должен быть менее или равен Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString должен быть больше Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString должен быть больше или равен Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString не должен быть равен Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString должен быть равен Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString должен быть равен MaxString",
},
{
ns: "Test.GteString",
expected: "GteString должен содержать минимум 3 символа",
},
{
ns: "Test.GteStringSecond",
expected: "GteStringSecond должен содержать минимум 7 символов",
},
{
ns: "Test.GteNumber",
expected: "GteNumber должен быть больше или равно 5,56",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple должен содержать минимум 2 элемента",
},
{
ns: "Test.GteMultipleSecond",
expected: "GteMultipleSecond должен содержать минимум 7 элементов",
},
{
ns: "Test.GteTime",
expected: "GteTime должна быть позже или равна текущему моменту",
},
{
ns: "Test.GtString",
expected: "GtString должен быть длиннее 3 символов",
},
{
ns: "Test.GtStringSecond",
expected: "GtStringSecond должен быть длиннее 7 символов",
},
{
ns: "Test.GtNumber",
expected: "GtNumber должен быть больше 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple должен содержать более 2 элементов",
},
{
ns: "Test.GtMultipleSecond",
expected: "GtMultipleSecond должен содержать более 7 элементов",
},
{
ns: "Test.GtTime",
expected: "GtTime должна быть позже текущего момента",
},
{
ns: "Test.LteString",
expected: "LteString должен содержать максимум 3 символа",
},
{
ns: "Test.LteStringSecond",
expected: "LteStringSecond должен содержать максимум 7 символов",
},
{
ns: "Test.LteNumber",
expected: "LteNumber должен быть менее или равен 5,56",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple должен содержать максимум 2 элемента",
},
{
ns: "Test.LteMultipleSecond",
expected: "LteMultipleSecond должен содержать максимум 7 элементов",
},
{
ns: "Test.LteTime",
expected: "LteTime must be less than or equal to the current Date & Time",
},
{
ns: "Test.LtString",
expected: "LtString должен иметь менее 3 символов",
},
{
ns: "Test.LtStringSecond",
expected: "LtStringSecond должен иметь менее 7 символов",
},
{
ns: "Test.LtNumber",
expected: "LtNumber должен быть менее 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple должен содержать менее 2 элементов",
},
{
ns: "Test.LtMultipleSecond",
expected: "LtMultipleSecond должен содержать менее 7 элементов",
},
{
ns: "Test.LtTime",
expected: "LtTime must be less than the current Date & Time",
},
{
ns: "Test.NeString",
expected: "NeString должен быть не равен ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber должен быть не равен 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple должен быть не равен 0",
},
{
ns: "Test.EqString",
expected: "EqString не равен 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber не равен 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple не равен 7",
},
{
ns: "Test.MaxString",
expected: "MaxString должен содержать максимум 3 символа",
},
{
ns: "Test.MaxStringSecond",
expected: "MaxStringSecond должен содержать максимум 7 символов",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber должен быть меньше или равно 1 113,00",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple должен содержать максимум 7 элементов",
},
{
ns: "Test.MaxMultipleSecond",
expected: "MaxMultipleSecond должен содержать максимум 2 элемента",
},
{
ns: "Test.MinString",
expected: "MinString должен содержать минимум 1 символ",
},
{
ns: "Test.MinStringMultiple",
expected: "MinStringMultiple должен содержать минимум 2 символа",
},
{
ns: "Test.MinStringMultipleSecond",
expected: "MinStringMultipleSecond должен содержать минимум 7 символов",
},
{
ns: "Test.MinNumber",
expected: "MinNumber должен быть больше или равно 1 113,00",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple должен содержать минимум 7 элементов",
},
{
ns: "Test.MinMultipleSecond",
expected: "MinMultipleSecond должен содержать минимум 2 элемента",
},
{
ns: "Test.LenString",
expected: "LenString должен быть длиной в 1 символ",
},
{
ns: "Test.LenNumber",
expected: "LenNumber должен быть равен 1 113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple должен содержать 7 элементов",
},
{
ns: "Test.LenMultipleSecond",
expected: "LenMultipleSecond должен содержать 2 элемента",
},
{
ns: "Test.RequiredString",
expected: "RequiredString обязательное поле",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber обязательное поле",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple обязательное поле",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen должен содержать минимум 10 символов",
},
{
ns: "Test.StrPtrMinLenSecond",
expected: "StrPtrMinLenSecond должен содержать минимум 2 символа",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen должен содержать максимум 1 символ",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen должен быть длиной в 2 символа",
},
{
ns: "Test.StrPtrLenSecond",
expected: "StrPtrLenSecond должен быть длиной в 7 символов",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt должен иметь менее 1 символ",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte должен содержать максимум 1 символ",
},
{
ns: "Test.StrPtrLteMultiple",
expected: "StrPtrLteMultiple должен содержать максимум 2 символа",
},
{
ns: "Test.StrPtrLteMultipleSecond",
expected: "StrPtrLteMultipleSecond должен содержать максимум 7 символов",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt должен быть длиннее 10 символов",
},
{
ns: "Test.StrPtrGtSecond",
expected: "StrPtrGtSecond должен быть длиннее 2 символов",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte должен содержать минимум 10 символов",
},
{
ns: "Test.StrPtrGteSecond",
expected: "StrPtrGteSecond должен содержать минимум 2 символа",
},
{
ns: "Test.OneOfString",
expected: "OneOfString должен быть одним из [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt должен быть одним из [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice должен содержать уникальные значения",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray должен содержать уникальные значения",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap должен содержать уникальные значения",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
log.Println(fe)
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,657 @@
package tr
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/validator/v10"
)
func TestTranslations(t *testing.T) {
tr := turkish.New()
uni := ut.New(tr, tr)
trans, _ := uni.GetTranslator("tr")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
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"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor geçerli bir renk olmalıdır",
},
{
ns: "Test.MAC",
expected: "MAC geçerli bir MAC adresi içermelidir",
},
{
ns: "Test.IPAddr",
expected: "IPAddr çözülebilir bir IP adresi olmalıdır",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 çözülebilir bir IPv4 adresi olmalıdır",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 çözülebilir bir IPv6 adresi olmalıdır",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr geçerli bir UDP adresi olmalıdır",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 geçerli bir IPv4 UDP adresi olmalıdır",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 geçerli bir IPv6 UDP adresi olmalıdır",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr geçerli bir TCP adresi olmalıdır",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 geçerli bir IPv4 TCP adresi olmalıdır",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 geçerli bir IPv6 TCP adresi olmalıdır",
},
{
ns: "Test.CIDR",
expected: "CIDR geçerli bir CIDR gösterimi içermelidir",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 bir IPv4 adresi için geçerli bir CIDR gösterimi içermelidir",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 bir IPv6 adresi için geçerli bir CIDR gösterimi içermelidir",
},
{
ns: "Test.SSN",
expected: "SSN geçerli bir SSN numarası olmalıdır",
},
{
ns: "Test.IP",
expected: "IP geçerli bir IP adresi olmalıdır",
},
{
ns: "Test.IPv4",
expected: "IPv4 geçerli bir IPv4 adresi olmalıdır",
},
{
ns: "Test.IPv6",
expected: "IPv6 geçerli bir IPv6 adresi olmalıdır",
},
{
ns: "Test.DataURI",
expected: "DataURI geçerli bir Veri URI içermelidir",
},
{
ns: "Test.Latitude",
expected: "Latitude geçerli bir enlem koordinatı içermelidir",
},
{
ns: "Test.Longitude",
expected: "Longitude geçerli bir boylam koordinatı içermelidir",
},
{
ns: "Test.MultiByte",
expected: "MultiByte çok baytlı karakterler içermelidir",
},
{
ns: "Test.ASCII",
expected: "ASCII yalnızca ascii karakterler içermelidir",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII yalnızca yazdırılabilir ascii karakterleri içermelidir",
},
{
ns: "Test.UUID",
expected: "UUID geçerli bir UUID olmalıdır",
},
{
ns: "Test.UUID3",
expected: "UUID3 geçerli bir sürüm 3 UUID olmalıdır",
},
{
ns: "Test.UUID4",
expected: "UUID4 geçerli bir sürüm 4 UUID olmalıdır",
},
{
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",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 geçerli bir ISBN-10 numarası olmalıdır",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 geçerli bir ISBN-13 numarası olmalıdır",
},
{
ns: "Test.Excludes",
expected: "Excludes, 'text' metnini içeremez",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll, '!@#$' karakterlerinden hiçbirini içeremez",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune, '☻' ifadesini içeremez",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny, '!@#$' karakterlerinden en az birini içermelidir",
},
{
ns: "Test.Contains",
expected: "Contains, 'purpose' metnini içermelidir",
},
{
ns: "Test.Base64",
expected: "Base64 geçerli bir Base64 karakter dizesi olmalıdır",
},
{
ns: "Test.Email",
expected: "Email geçerli bir e-posta adresi olmalıdır",
},
{
ns: "Test.URL",
expected: "URL geçerli bir URL olmalıdır",
},
{
ns: "Test.URI",
expected: "URI geçerli bir URI olmalıdır",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString geçerli bir RGB rengi olmalıdır",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString geçerli bir RGBA rengi olmalıdır",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString geçerli bir HSL rengi olmalıdır",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString geçerli bir HSLA rengi olmalıdır",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString geçerli bir onaltılık olmalıdır",
},
{
ns: "Test.HexColorString",
expected: "HexColorString geçerli bir HEX rengi olmalıdır",
},
{
ns: "Test.NumberString",
expected: "NumberString geçerli bir sayı olmalıdır",
},
{
ns: "Test.NumericString",
expected: "NumericString geçerli bir sayısal değer olmalıdır",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString yalnızca alfanümerik karakterler içerebilir",
},
{
ns: "Test.AlphaString",
expected: "AlphaString yalnızca alfabetik karakterler içerebilir",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString, MaxString değerinden küçük olmalıdır",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString, MaxString değerinden küçük veya ona eşit olmalıdır",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString, MaxString değerinden büyük olmalıdır",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString, MaxString değerinden büyük veya ona eşit olmalıdır",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString, EqFieldString değerine eşit olmamalıdır",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString, Inner.LtCSFieldString değerinden küçük olmalıdır",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString, Inner.LteCSFieldString değerinden küçük veya ona eşit olmalıdır",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString, Inner.GtCSFieldString değerinden büyük olmalıdır",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString, Inner.GteCSFieldString değerinden küçük veya ona eşit olmalıdır",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString, Inner.NeCSFieldString değerine eşit olmamalıdır",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString, Inner.EqCSFieldString değerine eşit olmalıdır",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString, MaxString değerine eşit olmalıdır",
},
{
ns: "Test.GteString",
expected: "GteString en az 3 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.GteNumber",
expected: "GteNumber, 5,56 veya daha büyük olmalıdır",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple en az 2 öğe içermelidir",
},
{
ns: "Test.GteTime",
expected: "GteTime geçerli Tarih ve Saatten büyük veya ona eşit olmalıdır",
},
{
ns: "Test.GtString",
expected: "GtString, 3 karakter uzunluğundan fazla olmalıdır",
},
{
ns: "Test.GtNumber",
expected: "GtNumber, 5,56 değerinden büyük olmalıdır",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple, 2 öğeden daha fazla içermelidir",
},
{
ns: "Test.GtTime",
expected: "GtTime geçerli Tarih ve Saatten büyük olmalıdır",
},
{
ns: "Test.LteString",
expected: "LteString en fazla 3 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.LteNumber",
expected: "LteNumber, 5,56 veya daha az olmalıdır",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple, maksimum 2 öğe içermelidir",
},
{
ns: "Test.LteTime",
expected: "LteTime geçerli Tarih ve Saate eşit veya daha küçük olmalıdır",
},
{
ns: "Test.LtString",
expected: "LtString, 3 karakter uzunluğundan daha az olmalıdır",
},
{
ns: "Test.LtNumber",
expected: "LtNumber, 5,56 değerinden küçük olmalıdır",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple, 2 öğeden daha az içermelidir",
},
{
ns: "Test.LtTime",
expected: "LtTime geçerli Tarih ve Saatten daha az olmalıdır",
},
{
ns: "Test.NeString",
expected: "NeString, değerine eşit olmamalıdır",
},
{
ns: "Test.NeNumber",
expected: "NeNumber, 0.00 değerine eşit olmamalıdır",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple, 0 değerine eşit olmamalıdır",
},
{
ns: "Test.EqString",
expected: "EqString, 3 değerine eşit değil",
},
{
ns: "Test.EqNumber",
expected: "EqNumber, 2.33 değerine eşit değil",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple, 7 değerine eşit değil",
},
{
ns: "Test.MaxString",
expected: "MaxString uzunluğu en fazla 3 karakter olmalıdır",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber, 1.113,00 veya daha az olmalıdır",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple maksimum 7 öğe içermelidir",
},
{
ns: "Test.MinString",
expected: "MinString en az 1 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.MinNumber",
expected: "MinNumber, 1.113,00 veya daha büyük olmalıdır",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple en az 7 öğe içermelidir",
},
{
ns: "Test.LenString",
expected: "LenString uzunluğu 1 karakter olmalıdır",
},
{
ns: "Test.LenNumber",
expected: "LenNumber, 1.113,00 değerine eşit olmalıdır",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple, 7 öğe içermelidir",
},
{
ns: "Test.RequiredString",
expected: "RequiredString zorunlu bir alandır",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber zorunlu bir alandır",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple zorunlu bir alandır",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen en az 10 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen uzunluğu en fazla 1 karakter olmalıdır",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen uzunluğu 2 karakter olmalıdır",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt, 1 karakter uzunluğundan daha az olmalıdır",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte en fazla 1 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt, 10 karakter uzunluğundan fazla olmalıdır",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte en az 10 karakter uzunluğunda olmalıdır",
},
{
ns: "Test.OneOfString",
expected: "OneOfString, [red green]'dan biri olmalıdır",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt, [5 63]'dan biri olmalıdır",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice benzersiz değerler içermelidir",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray benzersiz değerler içermelidir",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap benzersiz değerler içermelidir",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,690 @@
package vi
import (
"testing"
"time"
. "github.com/go-playground/assert/v2"
vietnamese "github.com/go-playground/locales/vi"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
)
func TestTranslations(t *testing.T) {
vie := vietnamese.New()
uni := ut.New(vie, vie)
trans, _ := uni.GetTranslator("vi")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
JSONString string `validate:"json"`
JWTString string `validate:"jwt"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
PostCode string `validate:"postcode_iso3166_alpha2=SG"`
PostCodeCountry string
PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor phải là màu sắc hợp lệ",
},
{
ns: "Test.MAC",
expected: "MAC chỉ được chứa địa chỉ MAC",
},
{
ns: "Test.IPAddr",
expected: "IPAddr phải là địa chỉ IP có thể phân giải",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 phải là địa chỉ IPv4 có thể phân giải",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 phải là địa chỉ IPv6 có thể phân giải",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr phải là địa chỉ UDP",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 phải là địa chỉ IPv4 UDP",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 phải là địa chỉ IPv6 UDP",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr phải là địa chỉ TCP",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 phải là địa chỉ IPv4 TCP",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 phải là địa chỉ IPv6 TCP",
},
{
ns: "Test.CIDR",
expected: "CIDR chỉ được chứa CIDR notation",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 chỉ được chứa CIDR notation của một địa chỉ IPv4",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 chỉ được chứa CIDR notation của một địa chỉ IPv6",
},
{
ns: "Test.SSN",
expected: "SSN phải là SSN number",
},
{
ns: "Test.IP",
expected: "IP phải là địa chỉ IP",
},
{
ns: "Test.IPv4",
expected: "IPv4 phải là địa chỉ IPv4",
},
{
ns: "Test.IPv6",
expected: "IPv6 phải là địa chỉ IPv6",
},
{
ns: "Test.DataURI",
expected: "DataURI chỉ được chứa Data URI",
},
{
ns: "Test.Latitude",
expected: "Latitude chỉ được chứa latitude (vỹ độ)",
},
{
ns: "Test.Longitude",
expected: "Longitude chỉ được chứa longitude (kinh độ)",
},
{
ns: "Test.MultiByte",
expected: "MultiByte chỉ được chứa ký tự multibyte",
},
{
ns: "Test.ASCII",
expected: "ASCII chỉ được chứa ký tự ASCII",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII chỉ được chứa ký tự ASCII có thể in ấn",
},
{
ns: "Test.UUID",
expected: "UUID phải là giá trị UUID",
},
{
ns: "Test.UUID3",
expected: "UUID3 phải là giá trị UUID phiên bản 3",
},
{
ns: "Test.UUID4",
expected: "UUID4 phải là giá trị UUID phiên bản 4",
},
{
ns: "Test.UUID5",
expected: "UUID5 phải là giá trị UUID phiên bản 5",
},
{
ns: "Test.ISBN",
expected: "ISBN phải là số ISBN",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 phải là số ISBN-10",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 phải là số ISBN-13",
},
{
ns: "Test.Excludes",
expected: "Excludes không được chứa chuỗi 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll không được chứa bất kỳ ký tự nào trong nhóm ký tự '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune không được chứa '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny phải chứa ít nhất 1 trong cách ký tự sau '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains phải chứa chuỗi 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 phải là giá trị chuỗi Base64",
},
{
ns: "Test.Email",
expected: "Email phải là giá trị email address",
},
{
ns: "Test.URL",
expected: "URL phải là giá trị URL",
},
{
ns: "Test.URI",
expected: "URI phải là giá trị URI",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString phải là giá trị RGB color",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString phải là giá trị RGBA color",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString phải là giá trị HSL color",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString phải là giá trị HSLA color",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString phải là giá trị hexadecimal",
},
{
ns: "Test.HexColorString",
expected: "HexColorString phải là giá trị HEX color",
},
{
ns: "Test.NumberString",
expected: "NumberString chỉ được chứa giá trị số",
},
{
ns: "Test.NumericString",
expected: "NumericString chỉ được chứa giá trị số hoặc số dưới dạng chữ",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString chỉ được chứa ký tự dạng alphanumeric",
},
{
ns: "Test.AlphaString",
expected: "AlphaString chỉ được chứa ký tự dạng alphabetic",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString chỉ được nhỏ hơn MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString chỉ được nhỏ hơn hoặc bằng MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString phải lớn hơn MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString phải lớn hơn hoặc bằng MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString không được phép bằng EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString chỉ được nhỏ hơn Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString chỉ được nhỏ hơn hoặc bằng Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString phải lớn hơn Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString phải lớn hơn hoặc bằng Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString không được phép bằng Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString phải bằng Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString phải bằng MaxString",
},
{
ns: "Test.GteString",
expected: "GteString phải có độ dài ít nhất 3 ký tự",
},
{
ns: "Test.GteNumber",
expected: "GteNumber phải là 5,56 hoặc lớn hơn",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple phải chứa ít nhất 2 phần tử",
},
{
ns: "Test.GteTime",
expected: "GteTime phải lớn hơn hoặc bằng Ngày & Giờ hiện tại",
},
{
ns: "Test.GtString",
expected: "GtString phải có độ dài lớn hơn 3 ký tự",
},
{
ns: "Test.GtNumber",
expected: "GtNumber phải lớn hơn 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple phải chứa nhiều hơn 2 phần tử",
},
{
ns: "Test.GtTime",
expected: "GtTime phải lớn hơn Ngày & Giờ hiện tại",
},
{
ns: "Test.LteString",
expected: "LteString chỉ được có độ dài tối đa là 3 ký tự",
},
{
ns: "Test.LteNumber",
expected: "LteNumber phải là 5,56 hoặc nhỏ hơn",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple chỉ được chứa nhiều nhất 2 phần tử",
},
{
ns: "Test.LteTime",
expected: "LteTime chỉ được nhỏ hơn hoặc bằng Ngày & Giờ hiện tại",
},
{
ns: "Test.LtString",
expected: "LtString phải có độ dài nhỏ hơn 3 ký tự",
},
{
ns: "Test.LtNumber",
expected: "LtNumber phải nhỏ hơn 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple chỉ được chứa ít hơn 2 phần tử",
},
{
ns: "Test.LtTime",
expected: "LtTime phải nhỏ hơn Ngày & Giờ hiện tại",
},
{
ns: "Test.NeString",
expected: "NeString không được bằng ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber không được bằng 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple không được bằng 0",
},
{
ns: "Test.EqString",
expected: "EqString không bằng 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber không bằng 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple không bằng 7",
},
{
ns: "Test.MaxString",
expected: "MaxString chỉ được chứa tối đa 3 ký tự",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber phải là 1.113,00 hoặc nhỏ hơn",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple chỉ được chứa tối đa 7 phần tử",
},
{
ns: "Test.MinString",
expected: "MinString phải chứa ít nhất 1 ký tự",
},
{
ns: "Test.MinNumber",
expected: "MinNumber phải bằng 1.113,00 hoặc lớn hơn",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple phải chứa ít nhất 7 phần tử",
},
{
ns: "Test.LenString",
expected: "LenString phải có độ dài là 1 ký tự",
},
{
ns: "Test.LenNumber",
expected: "LenNumber phải bằng 1.113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple phải chứa 7 phần tử",
},
{
ns: "Test.RequiredString",
expected: "RequiredString không được bỏ trống",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber không được bỏ trống",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple không được bỏ trống",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen phải chứa ít nhất 10 ký tự",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen chỉ được chứa tối đa 1 ký tự",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen phải có độ dài là 2 ký tự",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt phải có độ dài nhỏ hơn 1 ký tự",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte chỉ được có độ dài tối đa là 1 ký tự",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt phải có độ dài lớn hơn 10 ký tự",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte phải có độ dài ít nhất 10 ký tự",
},
{
ns: "Test.OneOfString",
expected: "OneOfString phải là trong những giá trị [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt phải là trong những giá trị [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice chỉ được chứa những giá trị không trùng lặp",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray chỉ được chứa những giá trị không trùng lặp",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap chỉ được chứa những giá trị không trùng lặp",
},
{
ns: "Test.JSONString",
expected: "JSONString phải là một chuỗi json hợp lệ",
},
{
ns: "Test.JWTString",
expected: "JWTString phải là một chuỗi jwt hợp lệ",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString phải được viết thường",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString phải được viết hoa",
},
{
ns: "Test.Datetime",
expected: "Datetime không trùng định dạng ngày tháng 2006-01-02",
},
{
ns: "Test.PostCode",
expected: "PostCode sai định dạng postcode của quốc gia SG",
},
{
ns: "Test.PostCodeByField",
expected: "PostCodeByField sai định dạng postcode của quốc gia tương ứng thuộc trường PostCodeCountry",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

@ -9,8 +9,8 @@ import (
"time" "time"
"github.com/go-playground/locales" "github.com/go-playground/locales"
"github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// RegisterDefaultTranslations registers a set of default translations // RegisterDefaultTranslations registers a set of default translations
@ -29,6 +29,36 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}为必填字段", translation: "{0}为必填字段",
override: false, override: false,
}, },
{
tag: "required_if",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_unless",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_with",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_with_all",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_without",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_without_all",
translation: "{0}为必填字段",
override: false,
},
{ {
tag: "len", tag: "len",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
@ -430,10 +460,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
case reflect.Struct: case reflect.Struct:
if fe.Type() != reflect.TypeOf(time.Time{}) { if fe.Type() != reflect.TypeOf(time.Time{}) {
err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag()) err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag())
} else {
t, err = ut.T("lt-datetime", fe.Field())
} }
t, err = ut.T("lt-datetime", fe.Field())
default: default:
err = fn() err = fn()
if err != nil { if err != nil {
@ -549,10 +579,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
case reflect.Struct: case reflect.Struct:
if fe.Type() != reflect.TypeOf(time.Time{}) { if fe.Type() != reflect.TypeOf(time.Time{}) {
err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag()) err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag())
} else {
t, err = ut.T("lte-datetime", fe.Field())
} }
t, err = ut.T("lte-datetime", fe.Field())
default: default:
err = fn() err = fn()
if err != nil { if err != nil {
@ -668,9 +698,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
case reflect.Struct: case reflect.Struct:
if fe.Type() != reflect.TypeOf(time.Time{}) { if fe.Type() != reflect.TypeOf(time.Time{}) {
err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag()) err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag())
} } else {
t, err = ut.T("gt-datetime", fe.Field()) t, err = ut.T("gt-datetime", fe.Field())
}
default: default:
err = fn() err = fn()
@ -787,10 +818,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
case reflect.Struct: case reflect.Struct:
if fe.Type() != reflect.TypeOf(time.Time{}) { if fe.Type() != reflect.TypeOf(time.Time{}) {
err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag()) err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag())
} else {
t, err = ut.T("gte-datetime", fe.Field())
} }
t, err = ut.T("gte-datetime", fe.Field())
default: default:
err = fn() err = fn()
if err != nil { if err != nil {
@ -999,6 +1030,16 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}只能包含字母和数字", translation: "{0}只能包含字母和数字",
override: false, override: false,
}, },
{
tag: "alphanumunicode",
translation: "{0}只能包含字母数字和Unicode字符",
override: false,
},
{
tag: "alphaunicode",
translation: "{0}只能包含字母和Unicode字符",
override: false,
},
{ {
tag: "numeric", tag: "numeric",
translation: "{0}必须是一个有效的数值", translation: "{0}必须是一个有效的数值",
@ -1089,6 +1130,21 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return t return t
}, },
}, },
{
tag: "containsrune",
translation: "{0}必须包含字符'{1}'",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("警告: 翻译字段错误: %#v", fe)
return fe.(error).Error()
}
return t
},
},
{ {
tag: "excludes", tag: "excludes",
translation: "{0}不能包含文本'{1}'", translation: "{0}不能包含文本'{1}'",
@ -1134,6 +1190,36 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return t return t
}, },
}, },
{
tag: "endswith",
translation: "{0}必须以文本'{1}'结尾",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("警告: 翻译字段错误: %#v", fe)
return fe.(error).Error()
}
return t
},
},
{
tag: "startswith",
translation: "{0}必须以文本'{1}'开头",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("警告: 翻译字段错误: %#v", fe)
return fe.(error).Error()
}
return t
},
},
{ {
tag: "isbn", tag: "isbn",
translation: "{0}必须是一个有效的ISBN编号", translation: "{0}必须是一个有效的ISBN编号",
@ -1169,6 +1255,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}必须是一个有效的V5 UUID", translation: "{0}必须是一个有效的V5 UUID",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0}必须是一个有效的ULID",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0}必须只包含ascii字符", translation: "{0}必须只包含ascii字符",
@ -1307,6 +1398,36 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return s return s
}, },
}, },
{
tag: "json",
translation: "{0}必须是一个JSON字符串",
override: false,
},
{
tag: "lowercase",
translation: "{0}必须是小写字母",
override: false,
},
{
tag: "uppercase",
translation: "{0}必须是大写字母",
override: false,
},
{
tag: "datetime",
translation: "{0}的格式必须是{1}",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("警告: 翻译字段错误: %#v", fe)
return fe.(error).Error()
}
return t
},
},
} }
for _, t := range translations { for _, t := range translations {

@ -4,17 +4,17 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
zhongwen "github.com/go-playground/locales/zh" zhongwen "github.com/go-playground/locales/zh"
"github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "gopkg.in/go-playground/assert.v1" "github.com/go-playground/validator/v10"
"gopkg.in/go-playground/validator.v9"
) )
func TestTranslations(t *testing.T) { func TestTranslations(t *testing.T) {
zh := zhongwen.New() zh := zhongwen.New()
uni := ut.New(zh, zh) uni := ut.New(zh, zh)
trans, ok := uni.GetTranslator("zh") trans, _ := uni.GetTranslator("zh")
validate := validator.New() validate := validator.New()
@ -31,113 +31,123 @@ func TestTranslations(t *testing.T) {
} }
type Test struct { type Test struct {
Inner Inner Inner Inner
RequiredString string `validate:"required"` RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"` RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"` RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"` LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"` LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"` LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"` MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"` MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"` MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"` MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"` MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"` MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"` EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"` EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"` EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="` NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"` NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"` NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"` LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"` LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"` LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"` LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"` LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"` LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"` LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"` LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"` GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"` GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"` GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"` GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"` GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"` GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"` GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"` GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"` EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"` EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"` NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"` GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"` GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"` LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"` LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"` NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"` GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"` GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"` LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"` LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"` AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"` AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"` AlphanumUnicodeString string `validate:"alphanumunicode"`
NumberString string `validate:"number"` AlphaUnicodeString string `validate:"alphaunicode"`
HexadecimalString string `validate:"hexadecimal"` NumericString string `validate:"numeric"`
HexColorString string `validate:"hexcolor"` NumberString string `validate:"number"`
RGBColorString string `validate:"rgb"` HexadecimalString string `validate:"hexadecimal"`
RGBAColorString string `validate:"rgba"` HexColorString string `validate:"hexcolor"`
HSLColorString string `validate:"hsl"` RGBColorString string `validate:"rgb"`
HSLAColorString string `validate:"hsla"` RGBAColorString string `validate:"rgba"`
Email string `validate:"email"` HSLColorString string `validate:"hsl"`
URL string `validate:"url"` HSLAColorString string `validate:"hsla"`
URI string `validate:"uri"` Email string `validate:"email"`
Base64 string `validate:"base64"` URL string `validate:"url"`
Contains string `validate:"contains=purpose"` URI string `validate:"uri"`
ContainsAny string `validate:"containsany=!@#$"` Base64 string `validate:"base64"`
Excludes string `validate:"excludes=text"` Contains string `validate:"contains=purpose"`
ExcludesAll string `validate:"excludesall=!@#$"` ContainsAny string `validate:"containsany=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"` ContainsRune string `validate:"containsrune=☻"`
ISBN string `validate:"isbn"` Excludes string `validate:"excludes=text"`
ISBN10 string `validate:"isbn10"` ExcludesAll string `validate:"excludesall=!@#$"`
ISBN13 string `validate:"isbn13"` ExcludesRune string `validate:"excludesrune=☻"`
UUID string `validate:"uuid"` EndsWith string `validate:"endswith=end"`
UUID3 string `validate:"uuid3"` StartsWith string `validate:"startswith=start"`
UUID4 string `validate:"uuid4"` ISBN string `validate:"isbn"`
UUID5 string `validate:"uuid5"` ISBN10 string `validate:"isbn10"`
ASCII string `validate:"ascii"` ISBN13 string `validate:"isbn13"`
PrintableASCII string `validate:"printascii"` UUID string `validate:"uuid"`
MultiByte string `validate:"multibyte"` UUID3 string `validate:"uuid3"`
DataURI string `validate:"datauri"` UUID4 string `validate:"uuid4"`
Latitude string `validate:"latitude"` UUID5 string `validate:"uuid5"`
Longitude string `validate:"longitude"` ULID string `validate:"ulid"`
SSN string `validate:"ssn"` ASCII string `validate:"ascii"`
IP string `validate:"ip"` PrintableASCII string `validate:"printascii"`
IPv4 string `validate:"ipv4"` MultiByte string `validate:"multibyte"`
IPv6 string `validate:"ipv6"` DataURI string `validate:"datauri"`
CIDR string `validate:"cidr"` Latitude string `validate:"latitude"`
CIDRv4 string `validate:"cidrv4"` Longitude string `validate:"longitude"`
CIDRv6 string `validate:"cidrv6"` SSN string `validate:"ssn"`
TCPAddr string `validate:"tcp_addr"` IP string `validate:"ip"`
TCPAddrv4 string `validate:"tcp4_addr"` IPv4 string `validate:"ipv4"`
TCPAddrv6 string `validate:"tcp6_addr"` IPv6 string `validate:"ipv6"`
UDPAddr string `validate:"udp_addr"` CIDR string `validate:"cidr"`
UDPAddrv4 string `validate:"udp4_addr"` CIDRv4 string `validate:"cidrv4"`
UDPAddrv6 string `validate:"udp6_addr"` CIDRv6 string `validate:"cidrv6"`
IPAddr string `validate:"ip_addr"` TCPAddr string `validate:"tcp_addr"`
IPAddrv4 string `validate:"ip4_addr"` TCPAddrv4 string `validate:"tcp4_addr"`
IPAddrv6 string `validate:"ip6_addr"` TCPAddrv6 string `validate:"tcp6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future UDPAddr string `validate:"udp_addr"`
MAC string `validate:"mac"` UDPAddrv4 string `validate:"udp4_addr"`
IsColor string `validate:"iscolor"` UDPAddrv6 string `validate:"udp6_addr"`
StrPtrMinLen *string `validate:"min=10"` IPAddr string `validate:"ip_addr"`
StrPtrMaxLen *string `validate:"max=1"` IPAddrv4 string `validate:"ip4_addr"`
StrPtrLen *string `validate:"len=2"` IPAddrv6 string `validate:"ip6_addr"`
StrPtrLt *string `validate:"lt=1"` UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
StrPtrLte *string `validate:"lte=1"` MAC string `validate:"mac"`
StrPtrGt *string `validate:"gt=10"` IsColor string `validate:"iscolor"`
StrPtrGte *string `validate:"gte=10"` StrPtrMinLen *string `validate:"min=10"`
OneOfString string `validate:"oneof=red green"` StrPtrMaxLen *string `validate:"max=1"`
OneOfInt int `validate:"oneof=5 63"` StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
JsonString string `validate:"json"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
Datetime string `validate:"datetime=2006-01-02"`
} }
var test Test var test Test
@ -168,6 +178,8 @@ func TestTranslations(t *testing.T) {
test.AlphaString = "abc3" test.AlphaString = "abc3"
test.AlphanumString = "abc3!" test.AlphanumString = "abc3!"
test.AlphanumUnicodeString = "abc3啊!"
test.AlphaUnicodeString = "abc3啊"
test.NumericString = "12E.00" test.NumericString = "12E.00"
test.NumberString = "12E" test.NumberString = "12E"
@ -175,6 +187,9 @@ func TestTranslations(t *testing.T) {
test.ExcludesAll = "This is Great!" test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻" test.ExcludesRune = "Love it ☻"
test.EndsWith = "this is some test text"
test.StartsWith = "this is some test text"
test.ASCII = "カタカナ" test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ" test.PrintableASCII = "カタカナ"
@ -184,6 +199,13 @@ func TestTranslations(t *testing.T) {
test.StrPtrMaxLen = &s test.StrPtrMaxLen = &s
test.StrPtrLen = &s test.StrPtrLen = &s
test.JsonString = "{\"foo\":\"bar\",}"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
test.Datetime = "20060102"
err = validate.Struct(test) err = validate.Struct(test)
NotEqual(t, err, nil) NotEqual(t, err, nil)
@ -306,6 +328,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5必须是一个有效的V5 UUID", expected: "UUID5必须是一个有效的V5 UUID",
}, },
{
ns: "Test.ULID",
expected: "ULID必须是一个有效的ULID",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN必须是一个有效的ISBN编号", expected: "ISBN必须是一个有效的ISBN编号",
@ -318,6 +344,14 @@ func TestTranslations(t *testing.T) {
ns: "Test.ISBN13", ns: "Test.ISBN13",
expected: "ISBN13必须是一个有效的ISBN-13编号", expected: "ISBN13必须是一个有效的ISBN-13编号",
}, },
{
ns: "Test.EndsWith",
expected: "EndsWith必须以文本'end'结尾",
},
{
ns: "Test.StartsWith",
expected: "StartsWith必须以文本'start'开头",
},
{ {
ns: "Test.Excludes", ns: "Test.Excludes",
expected: "Excludes不能包含文本'text'", expected: "Excludes不能包含文本'text'",
@ -330,6 +364,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.ExcludesRune", ns: "Test.ExcludesRune",
expected: "ExcludesRune不能包含'☻'", expected: "ExcludesRune不能包含'☻'",
}, },
{
ns: "Test.ContainsRune",
expected: "ContainsRune必须包含字符'☻'",
},
{ {
ns: "Test.ContainsAny", ns: "Test.ContainsAny",
expected: "ContainsAny必须包含至少一个以下字符'!@#$'", expected: "ContainsAny必须包含至少一个以下字符'!@#$'",
@ -386,6 +424,14 @@ func TestTranslations(t *testing.T) {
ns: "Test.NumericString", ns: "Test.NumericString",
expected: "NumericString必须是一个有效的数值", expected: "NumericString必须是一个有效的数值",
}, },
{
ns: "Test.AlphaUnicodeString",
expected: "AlphaUnicodeString只能包含字母和Unicode字符",
},
{
ns: "Test.AlphanumUnicodeString",
expected: "AlphanumUnicodeString只能包含字母数字和Unicode字符",
},
{ {
ns: "Test.AlphanumString", ns: "Test.AlphanumString",
expected: "AlphanumString只能包含字母和数字", expected: "AlphanumString只能包含字母和数字",
@ -614,6 +660,22 @@ func TestTranslations(t *testing.T) {
ns: "Test.OneOfInt", ns: "Test.OneOfInt",
expected: "OneOfInt必须是[5 63]中的一个", expected: "OneOfInt必须是[5 63]中的一个",
}, },
{
ns: "Test.JsonString",
expected: "JsonString必须是一个JSON字符串",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString必须是小写字母",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString必须是大写字母",
},
{
ns: "Test.Datetime",
expected: "Datetime的格式必须是2006-01-02",
},
} }
for _, tt := range tests { for _, tt := range tests {

@ -9,8 +9,8 @@ import (
"time" "time"
"github.com/go-playground/locales" "github.com/go-playground/locales"
"github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9" "github.com/go-playground/validator/v10"
) )
// RegisterDefaultTranslations registers a set of default translations // RegisterDefaultTranslations registers a set of default translations
@ -430,10 +430,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
case reflect.Struct: case reflect.Struct:
if fe.Type() != reflect.TypeOf(time.Time{}) { if fe.Type() != reflect.TypeOf(time.Time{}) {
err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag()) err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag())
} else {
t, err = ut.T("lt-datetime", fe.Field())
} }
t, err = ut.T("lt-datetime", fe.Field())
default: default:
err = fn() err = fn()
if err != nil { if err != nil {
@ -549,10 +549,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
case reflect.Struct: case reflect.Struct:
if fe.Type() != reflect.TypeOf(time.Time{}) { if fe.Type() != reflect.TypeOf(time.Time{}) {
err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag()) err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag())
} else {
t, err = ut.T("lte-datetime", fe.Field())
} }
t, err = ut.T("lte-datetime", fe.Field())
default: default:
err = fn() err = fn()
if err != nil { if err != nil {
@ -618,13 +618,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
var kind reflect.Kind var kind reflect.Kind
fn := func() (err error) { fn := func() (err error) {
if idx := strings.Index(fe.Param(), "."); idx != -1 { if idx := strings.Index(fe.Param(), "."); idx != -1 {
digits = uint64(len(fe.Param()[idx+1:])) digits = uint64(len(fe.Param()[idx+1:]))
} }
f64, err = strconv.ParseFloat(fe.Param(), 64) f64, err = strconv.ParseFloat(fe.Param(), 64)
return return
} }
@ -668,10 +665,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
case reflect.Struct: case reflect.Struct:
if fe.Type() != reflect.TypeOf(time.Time{}) { if fe.Type() != reflect.TypeOf(time.Time{}) {
err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag()) err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag())
} else {
t, err = ut.T("gt-datetime", fe.Field())
} }
t, err = ut.T("gt-datetime", fe.Field())
default: default:
err = fn() err = fn()
if err != nil { if err != nil {
@ -787,10 +784,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
case reflect.Struct: case reflect.Struct:
if fe.Type() != reflect.TypeOf(time.Time{}) { if fe.Type() != reflect.TypeOf(time.Time{}) {
err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag()) err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag())
} else {
t, err = ut.T("gte-datetime", fe.Field())
} }
t, err = ut.T("gte-datetime", fe.Field())
default: default:
err = fn() err = fn()
if err != nil { if err != nil {
@ -1169,6 +1166,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}必須是一個有效的V5 UUID", translation: "{0}必須是一個有效的V5 UUID",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0}必須是一個有效的ULID",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0}必須只包含ascii字元", translation: "{0}必須只包含ascii字元",
@ -1307,6 +1309,21 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return s return s
}, },
}, },
{
tag: "datetime",
translation: "{0}與{1}格式不匹配",
override: false,
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
if err != nil {
log.Printf("警告: 翻譯欄位錯誤: %#v", fe)
return fe.(error).Error()
}
return t
},
},
} }
for _, t := range translations { for _, t := range translations {

@ -4,17 +4,17 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
zhongwen "github.com/go-playground/locales/zh_Hant_TW" zhongwen "github.com/go-playground/locales/zh_Hant_TW"
"github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "gopkg.in/go-playground/assert.v1" "github.com/go-playground/validator/v10"
"gopkg.in/go-playground/validator.v9"
) )
func TestTranslations(t *testing.T) { func TestTranslations(t *testing.T) {
zh := zhongwen.New() zh := zhongwen.New()
uni := ut.New(zh, zh) uni := ut.New(zh, zh)
trans, ok := uni.GetTranslator("zh") trans, _ := uni.GetTranslator("zh")
validate := validator.New() validate := validator.New()
@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -138,6 +139,7 @@ func TestTranslations(t *testing.T) {
StrPtrGte *string `validate:"gte=10"` StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"` OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"` OneOfInt int `validate:"oneof=5 63"`
Datetime string `validate:"datetime=2006-01-02"`
} }
var test Test var test Test
@ -184,6 +186,8 @@ func TestTranslations(t *testing.T) {
test.StrPtrMaxLen = &s test.StrPtrMaxLen = &s
test.StrPtrLen = &s test.StrPtrLen = &s
test.Datetime = "2008-Feb-01"
err = validate.Struct(test) err = validate.Struct(test)
NotEqual(t, err, nil) NotEqual(t, err, nil)
@ -306,6 +310,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5必須是一個有效的V5 UUID", expected: "UUID5必須是一個有效的V5 UUID",
}, },
{
ns: "Test.ULID",
expected: "ULID必須是一個有效的ULID",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN必須是一個有效的ISBN編號", expected: "ISBN必須是一個有效的ISBN編號",
@ -614,6 +622,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.OneOfInt", ns: "Test.OneOfInt",
expected: "OneOfInt必須是[5 63]中的一個", expected: "OneOfInt必須是[5 63]中的一個",
}, },
{
ns: "Test.Datetime",
expected: "Datetime與2006-01-02格式不匹配",
},
} }
for _, tt := range tests { for _, tt := range tests {

@ -4,6 +4,7 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"time"
) )
// extractTypeInternal gets the actual underlying type of field value. // extractTypeInternal gets the actual underlying type of field value.
@ -57,11 +58,10 @@ BEGIN:
// //
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist. // could not be retrieved because it didn't exist.
func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, found bool) { func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) {
BEGIN: BEGIN:
current, kind, _ = v.ExtractType(val) current, kind, nullable = v.ExtractType(val)
if kind == reflect.Invalid { if kind == reflect.Invalid {
return return
} }
@ -82,7 +82,7 @@ BEGIN:
fld := namespace fld := namespace
var ns string var ns string
if typ != timeType { if !typ.ConvertibleTo(timeType) {
idx := strings.Index(namespace, namespaceSeparator) idx := strings.Index(namespace, namespaceSeparator)
@ -112,7 +112,7 @@ BEGIN:
arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2]) arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2])
if arrIdx >= current.Len() { if arrIdx >= current.Len() {
return current, kind, false return
} }
startIdx := idx2 + 1 startIdx := idx2 + 1
@ -223,13 +223,34 @@ BEGIN:
// asInt returns the parameter as a int64 // asInt returns the parameter as a int64
// or panics if it can't convert // or panics if it can't convert
func asInt(param string) int64 { func asInt(param string) int64 {
i, err := strconv.ParseInt(param, 0, 64) i, err := strconv.ParseInt(param, 0, 64)
panicIf(err) panicIf(err)
return i return i
} }
// asIntFromTimeDuration parses param as time.Duration and returns it as int64
// or panics on error.
func asIntFromTimeDuration(param string) int64 {
d, err := time.ParseDuration(param)
if err != nil {
// attempt parsing as an an integer assuming nanosecond precision
return asInt(param)
}
return int64(d)
}
// asIntFromType calls the proper function to parse param as int64,
// given a field's Type t.
func asIntFromType(t reflect.Type, param string) int64 {
switch t {
case timeDurationType:
return asIntFromTimeDuration(param)
default:
return asInt(param)
}
}
// asUint returns the parameter as a uint64 // asUint returns the parameter as a uint64
// or panics if it can't convert // or panics if it can't convert
func asUint(param string) uint64 { func asUint(param string) uint64 {
@ -250,6 +271,16 @@ func asFloat(param string) float64 {
return i return i
} }
// asBool returns the parameter as a bool
// or panics if it can't convert
func asBool(param string) bool {
i, err := strconv.ParseBool(param)
panicIf(err)
return i
}
func panicIf(err error) { func panicIf(err error) {
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())

@ -7,7 +7,7 @@ import (
"strconv" "strconv"
) )
// per validate contruct // per validate construct
type validate struct { type validate struct {
v *Validate v *Validate
top reflect.Value top reflect.Value
@ -74,7 +74,7 @@ func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, cur
} }
} }
v.traverseField(ctx, parent, current.Field(f.idx), ns, structNs, f, f.cTags) v.traverseField(ctx, current, current.Field(f.idx), ns, structNs, f, f.cTags)
} }
} }
@ -94,7 +94,6 @@ func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, cur
// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options // traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) { func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
var typ reflect.Type var typ reflect.Type
var kind reflect.Kind var kind reflect.Kind
@ -112,16 +111,36 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
} }
if ct.hasTag { if ct.hasTag {
if kind == reflect.Invalid {
v.str1 = string(append(ns, cf.altName...))
if v.v.hasTagNameFunc {
v.str2 = string(append(structNs, cf.name...))
} else {
v.str2 = v.str1
}
v.errs = append(v.errs,
&fieldError{
v: v.v,
tag: ct.aliasTag,
actualTag: ct.tag,
ns: v.str1,
structNs: v.str2,
fieldLen: uint8(len(cf.altName)),
structfieldLen: uint8(len(cf.name)),
param: ct.param,
kind: kind,
},
)
return
}
v.str1 = string(append(ns, cf.altName...)) v.str1 = string(append(ns, cf.altName...))
if v.v.hasTagNameFunc { if v.v.hasTagNameFunc {
v.str2 = string(append(structNs, cf.name...)) v.str2 = string(append(structNs, cf.name...))
} else { } else {
v.str2 = v.str1 v.str2 = v.str1
} }
if !ct.runValidationWhenNil {
if kind == reflect.Invalid {
v.errs = append(v.errs, v.errs = append(v.errs,
&fieldError{ &fieldError{
v: v.v, v: v.v,
@ -131,38 +150,21 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
structNs: v.str2, structNs: v.str2,
fieldLen: uint8(len(cf.altName)), fieldLen: uint8(len(cf.altName)),
structfieldLen: uint8(len(cf.name)), structfieldLen: uint8(len(cf.name)),
value: current.Interface(),
param: ct.param, param: ct.param,
kind: kind, kind: kind,
typ: current.Type(),
}, },
) )
return return
} }
v.errs = append(v.errs,
&fieldError{
v: v.v,
tag: ct.aliasTag,
actualTag: ct.tag,
ns: v.str1,
structNs: v.str2,
fieldLen: uint8(len(cf.altName)),
structfieldLen: uint8(len(cf.name)),
value: current.Interface(),
param: ct.param,
kind: kind,
typ: current.Type(),
},
)
return
} }
case reflect.Struct: case reflect.Struct:
typ = current.Type() typ = current.Type()
if typ != timeType { if !typ.ConvertibleTo(timeType) {
if ct != nil { if ct != nil {
@ -220,12 +222,12 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
structNs = append(append(structNs, cf.name...), '.') structNs = append(append(structNs, cf.name...), '.')
} }
v.validateStruct(ctx, current, current, typ, ns, structNs, ct) v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
return return
} }
} }
if !ct.hasTag { if ct == nil || !ct.hasTag {
return return
} }
@ -247,7 +249,7 @@ OUTER:
v.cf = cf v.cf = cf
v.ct = ct v.ct = ct
if !v.fldIsPointer && !hasValue(v) { if !hasValue(v) {
return return
} }
@ -353,6 +355,10 @@ OUTER:
v.ct = ct v.ct = ct
if ct.fn(ctx, v) { if ct.fn(ctx, v) {
if ct.isBlockEnd {
ct = ct.next
continue OUTER
}
// drain rest of the 'or' values, then continue or leave // drain rest of the 'or' values, then continue or leave
for { for {
@ -366,6 +372,11 @@ OUTER:
if ct.typeof != typeOr { if ct.typeof != typeOr {
continue OUTER continue OUTER
} }
if ct.isBlockEnd {
ct = ct.next
continue OUTER
}
} }
} }
@ -441,7 +452,6 @@ OUTER:
v.ct = ct v.ct = ct
if !ct.fn(ctx, v) { if !ct.fn(ctx, v) {
v.str1 = string(append(ns, cf.altName...)) v.str1 = string(append(ns, cf.altName...))
if v.v.hasTagNameFunc { if v.v.hasTagNameFunc {

@ -13,31 +13,45 @@ import (
) )
const ( const (
defaultTagName = "validate" defaultTagName = "validate"
utf8HexComma = "0x2C" utf8HexComma = "0x2C"
utf8Pipe = "0x7C" utf8Pipe = "0x7C"
tagSeparator = "," tagSeparator = ","
orSeparator = "|" orSeparator = "|"
tagKeySeparator = "=" tagKeySeparator = "="
structOnlyTag = "structonly" structOnlyTag = "structonly"
noStructLevelTag = "nostructlevel" noStructLevelTag = "nostructlevel"
omitempty = "omitempty" omitempty = "omitempty"
isdefault = "isdefault" isdefault = "isdefault"
skipValidationTag = "-" requiredWithoutAllTag = "required_without_all"
diveTag = "dive" requiredWithoutTag = "required_without"
keysTag = "keys" requiredWithTag = "required_with"
endKeysTag = "endkeys" requiredWithAllTag = "required_with_all"
requiredTag = "required" requiredIfTag = "required_if"
namespaceSeparator = "." requiredUnlessTag = "required_unless"
leftBracket = "[" excludedWithoutAllTag = "excluded_without_all"
rightBracket = "]" excludedWithoutTag = "excluded_without"
restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}" excludedWithTag = "excluded_with"
restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" excludedWithAllTag = "excluded_with_all"
restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" excludedIfTag = "excluded_if"
excludedUnlessTag = "excluded_unless"
skipValidationTag = "-"
diveTag = "dive"
keysTag = "keys"
endKeysTag = "endkeys"
requiredTag = "required"
namespaceSeparator = "."
leftBracket = "["
rightBracket = "]"
restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}"
restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
) )
var ( var (
timeType = reflect.TypeOf(time.Time{}) timeDurationType = reflect.TypeOf(time.Duration(0))
timeType = reflect.TypeOf(time.Time{})
defaultCField = &cField{namesEqual: true} defaultCField = &cField{namesEqual: true}
) )
@ -55,6 +69,11 @@ type CustomTypeFunc func(field reflect.Value) interface{}
// TagNameFunc allows for adding of a custom tag name parser // TagNameFunc allows for adding of a custom tag name parser
type TagNameFunc func(field reflect.StructField) string type TagNameFunc func(field reflect.StructField) string
type internalValidationFuncWrapper struct {
fn FuncCtx
runValidatinOnNil bool
}
// Validate contains the validator settings and cache // Validate contains the validator settings and cache
type Validate struct { type Validate struct {
tagName string tagName string
@ -65,13 +84,18 @@ type Validate struct {
structLevelFuncs map[reflect.Type]StructLevelFuncCtx structLevelFuncs map[reflect.Type]StructLevelFuncCtx
customFuncs map[reflect.Type]CustomTypeFunc customFuncs map[reflect.Type]CustomTypeFunc
aliases map[string]string aliases map[string]string
validations map[string]FuncCtx validations map[string]internalValidationFuncWrapper
transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
rules map[reflect.Type]map[string]string
tagCache *tagCache tagCache *tagCache
structCache *structCache structCache *structCache
} }
// New returns a new instance of 'validate' with sane defaults. // New returns a new instance of 'validate' with sane defaults.
// Validate is designed to be thread-safe and used as a singleton instance.
// It caches information about your struct and validations,
// in essence only parsing your validation tags once per struct type.
// Using multiple instances neglects the benefit of caching.
func New() *Validate { func New() *Validate {
tc := new(tagCache) tc := new(tagCache)
@ -83,7 +107,7 @@ func New() *Validate {
v := &Validate{ v := &Validate{
tagName: defaultTagName, tagName: defaultTagName,
aliases: make(map[string]string, len(bakedInAliases)), aliases: make(map[string]string, len(bakedInAliases)),
validations: make(map[string]FuncCtx, len(bakedInValidators)), validations: make(map[string]internalValidationFuncWrapper, len(bakedInValidators)),
tagCache: tc, tagCache: tc,
structCache: sc, structCache: sc,
} }
@ -96,8 +120,15 @@ func New() *Validate {
// must copy validators for separate validations to be used in each instance // must copy validators for separate validations to be used in each instance
for k, val := range bakedInValidators { for k, val := range bakedInValidators {
// no need to error check here, baked in will always be valid switch k {
_ = v.registerValidation(k, wrapFunc(val), true) // these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag,
excludedIfTag, excludedUnlessTag, excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag:
_ = v.registerValidation(k, wrapFunc(val), true, true)
default:
// no need to error check here, baked in will always be valid
_ = v.registerValidation(k, wrapFunc(val), true, false)
}
} }
v.pool = &sync.Pool{ v.pool = &sync.Pool{
@ -119,17 +150,54 @@ func (v *Validate) SetTagName(name string) {
v.tagName = name v.tagName = name
} }
// ValidateMapCtx validates a map using a map of validation rules and allows passing of contextual
// validation validation information via context.Context.
func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
errs := make(map[string]interface{})
for field, rule := range rules {
if ruleObj, ok := rule.(map[string]interface{}); ok {
if dataObj, ok := data[field].(map[string]interface{}); ok {
err := v.ValidateMapCtx(ctx, dataObj, ruleObj)
if len(err) > 0 {
errs[field] = err
}
} else if dataObjs, ok := data[field].([]map[string]interface{}); ok {
for _, obj := range dataObjs {
err := v.ValidateMapCtx(ctx, obj, ruleObj)
if len(err) > 0 {
errs[field] = err
}
}
} else {
errs[field] = errors.New("The field: '" + field + "' is not a map to dive")
}
} else if ruleStr, ok := rule.(string); ok {
err := v.VarCtx(ctx, data[field], ruleStr)
if err != nil {
errs[field] = err
}
}
}
return errs
}
// ValidateMap validates map data from a map of tags
func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
return v.ValidateMapCtx(context.Background(), data, rules)
}
// RegisterTagNameFunc registers a function to get alternate names for StructFields. // RegisterTagNameFunc registers a function to get alternate names for StructFields.
// //
// eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names: // eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names:
// //
// validate.RegisterTagNameFunc(func(fld reflect.StructField) string { // validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
// name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] // name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
// if name == "-" { // // skip if tag key says it should be ignored
// return "" // if name == "-" {
// } // return ""
// return name // }
// }) // return name
// })
func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) { func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) {
v.tagNameFunc = fn v.tagNameFunc = fn
v.hasTagNameFunc = true v.hasTagNameFunc = true
@ -140,34 +208,34 @@ func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) {
// NOTES: // NOTES:
// - if the key already exists, the previous validation function will be replaced. // - if the key already exists, the previous validation function will be replaced.
// - this method is not thread-safe it is intended that these all be registered prior to any validation // - this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterValidation(tag string, fn Func) error { func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error {
return v.RegisterValidationCtx(tag, wrapFunc(fn)) return v.RegisterValidationCtx(tag, wrapFunc(fn), callValidationEvenIfNull...)
} }
// RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation // RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation
// allowing context.Context validation support. // allowing context.Context validation support.
func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx) error { func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationEvenIfNull ...bool) error {
return v.registerValidation(tag, fn, false) var nilCheckable bool
if len(callValidationEvenIfNull) > 0 {
nilCheckable = callValidationEvenIfNull[0]
}
return v.registerValidation(tag, fn, false, nilCheckable)
} }
func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool) error { func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
if len(tag) == 0 { if len(tag) == 0 {
return errors.New("Function Key cannot be empty") return errors.New("function Key cannot be empty")
} }
if fn == nil { if fn == nil {
return errors.New("Function cannot be empty") return errors.New("function cannot be empty")
} }
_, ok := restrictedTags[tag] _, ok := restrictedTags[tag]
if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) { if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
panic(fmt.Sprintf(restrictedTagErr, tag)) panic(fmt.Sprintf(restrictedTagErr, tag))
} }
v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidatinOnNil: nilCheckable}
v.validations[tag] = fn
return nil return nil
} }
@ -216,6 +284,34 @@ func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...i
} }
} }
// RegisterStructValidationMapRules registers validate map rules.
// Be aware that map validation rules supersede those defined on a/the struct if present.
//
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, types ...interface{}) {
if v.rules == nil {
v.rules = make(map[reflect.Type]map[string]string)
}
deepCopyRules := make(map[string]string)
for i, rule := range rules {
deepCopyRules[i] = rule
}
for _, t := range types {
typ := reflect.TypeOf(t)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() != reflect.Struct {
continue
}
v.rules[typ] = deepCopyRules
}
}
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types // RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
// //
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation // NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
@ -276,7 +372,7 @@ func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
val = val.Elem() val = val.Elem()
} }
if val.Kind() != reflect.Struct || val.Type() == timeType { if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)} return &InvalidValidationError{Type: reflect.TypeOf(s)}
} }
@ -321,7 +417,7 @@ func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn Filt
val = val.Elem() val = val.Elem()
} }
if val.Kind() != reflect.Struct || val.Type() == timeType { if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)} return &InvalidValidationError{Type: reflect.TypeOf(s)}
} }
@ -369,7 +465,7 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
val = val.Elem() val = val.Elem()
} }
if val.Kind() != reflect.Struct || val.Type() == timeType { if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)} return &InvalidValidationError{Type: reflect.TypeOf(s)}
} }
@ -390,7 +486,10 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
if len(flds) > 0 { if len(flds) > 0 {
vd.misc = append(vd.misc[0:0], name...) vd.misc = append(vd.misc[0:0], name...)
vd.misc = append(vd.misc, '.') // Don't append empty name for unnamed structs
if len(vd.misc) != 0 {
vd.misc = append(vd.misc, '.')
}
for _, s := range flds { for _, s := range flds {
@ -456,7 +555,7 @@ func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ..
val = val.Elem() val = val.Elem()
} }
if val.Kind() != reflect.Struct || val.Type() == timeType { if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)} return &InvalidValidationError{Type: reflect.TypeOf(s)}
} }
@ -514,7 +613,7 @@ func (v *Validate) Var(field interface{}, tag string) error {
} }
// VarCtx validates a single variable using tag style validation and allows passing of contextual // VarCtx validates a single variable using tag style validation and allows passing of contextual
// validation validation information via context.Context. // validation information via context.Context.
// eg. // eg.
// var i int // var i int
// validate.Var(i, "gt=1,lt=10") // validate.Var(i, "gt=1,lt=10")
@ -533,6 +632,7 @@ func (v *Validate) VarCtx(ctx context.Context, field interface{}, tag string) (e
} }
ctag := v.fetchCacheTag(tag) ctag := v.fetchCacheTag(tag)
val := reflect.ValueOf(field) val := reflect.ValueOf(field)
vd := v.pool.Get().(*validate) vd := v.pool.Get().(*validate)
vd.top = val vd.top = val

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save