-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidator.go
More file actions
254 lines (214 loc) · 7 KB
/
Copy pathvalidator.go
File metadata and controls
254 lines (214 loc) · 7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
package validator
import (
"strings"
"github.com/go-playground/validator/v10"
"github.com/go-universal/i18n"
)
// Validator defines an interface for localized validation functionality.
type Validator interface {
// AddValidation registers a custom validation rule with a custom validation function.
AddValidation(rule string, f validator.Func)
// AddTranslation adds a translation message for a validation rule in a specified locale.
AddTranslation(locale, rule, message string, options ...i18n.PluralOption)
// Struct validates an entire struct based on its defined validation rules.
// Returns validation errors in the provided locale, defaulting to the translator locale if empty.
Struct(locale string, value any) ValidationError
// StructExpect validates a struct while ignoring specified fields.
// Returns validation errors in the provided locale, defaulting to the translator locale if empty.
StructExpect(locale string, value any, fields ...string) ValidationError
// StructPartial validates only specified fields of a struct.
// Returns validation errors in the provided locale, defaulting to the translator locale if empty.
StructPartial(locale string, value any, fields ...string) ValidationError
// Var validates a single variable against a rule with optional custom messages.
Var(locale, name string, value any, rules string) ValidationError
// VarWithValue validates a variable against another using a rule with custom error messages.
VarWithValue(locale, name string, value any, other any, rules string) ValidationError
}
type i18nValidator struct {
prefix string
translator i18n.Translator
validator *validator.Validate
}
// NewValidator creates a new Validator instance with optional configurations.
// Initializes the I18nValidator with the provided translator and base validator, and applies any options.
func NewValidator(validator *validator.Validate, options ...Options) Validator {
// Initialize the I18nValidator with the provided validator
v := &i18nValidator{
translator: nil,
validator: validator,
}
// Apply any additional options
for _, opt := range options {
opt(v)
}
// Return the configured validator instance
return v
}
func (v *i18nValidator) AddValidation(rule string, f validator.Func) {
rule = strings.TrimSpace(rule)
if rule == "" {
return
}
v.validator.RegisterValidation(rule, f)
}
func (v *i18nValidator) AddTranslation(locale, rule, message string, options ...i18n.PluralOption) {
rule = strings.TrimSpace(rule)
if rule == "" || v.translator == nil {
return
}
if v.prefix == "" {
v.translator.AddMessage(locale, rule, message, options...)
} else {
v.translator.AddMessage(locale, v.prefix+"."+rule, message, options...)
}
}
func (v *i18nValidator) Struct(locale string, value any) ValidationError {
return v.parseStructErrors(
locale,
value,
v.validator.Struct(value),
)
}
func (v *i18nValidator) StructExpect(locale string, value any, fields ...string) ValidationError {
return v.parseStructErrors(
locale,
value,
v.validator.StructExcept(value, fields...),
)
}
func (v *i18nValidator) StructPartial(locale string, value any, fields ...string) ValidationError {
return v.parseStructErrors(
locale,
value,
v.validator.StructPartial(value, fields...),
)
}
func (v *i18nValidator) Var(locale, name string, value any, rules string) ValidationError {
return v.parseVariableErrors(
locale,
name,
value,
v.validator.Var(value, rules),
)
}
func (v *i18nValidator) VarWithValue(locale, name string, value any, other any, rules string) ValidationError {
return v.parseVariableErrors(
locale,
name,
value,
v.validator.VarWithValue(value, other, rules),
)
}
// translate generates a localized error message based on the provided value, field, and parameters.
func (v *i18nValidator) translate(locale, name, rule, field string, param, value any, count int) string {
// Return empty string if translator not passed to I18nValidator
if v.translator == nil {
return ""
}
// Try resolving error translation using the Translatable interface
if t, ok := value.(Translatable); ok {
if res := t.TranslateError(locale, rule, field); res != "" {
return res
}
}
// Use the main translator to generate the message with pluralization support
// If a prefix is set, prepend it to the rule
if v.prefix != "" {
rule = v.prefix + "." + rule
}
// Next, attempt to translate the field name using TranslatableField interface
if t, ok := value.(TranslatableField); ok {
if n := t.TranslateTitle(locale, field); n != "" {
name = n
}
}
return v.translator.Plural(locale, rule, count, map[string]any{
"field": name,
"param": param,
})
}
// parseStructErrors processes and translates validation errors
// based on the provided locale and value for struct.
func (v *i18nValidator) parseStructErrors(locale string, value any, err error) ValidationError {
// Skip nil error
if err == nil {
return NewEmptyError()
}
// Assert the error as validator.ValidationErrors
errs, ok := err.(validator.ValidationErrors)
if !ok {
return NewError(err)
}
// Initialize the result validation error
res := NewEmptyError()
// Iterate over each validation error and process
for _, field := range errs {
// Parse the field parameter and handle its type (int or float)
var count int
var param any = field.Param()
if i, f := parseNumeric(field.Param()); i != nil {
param = *i
count = int(*i)
} else if f != nil {
param = *f
count = int(*f)
}
// Add the translated error if translator available or raw error to the result
if v.translator == nil {
res.AddError(field.Field(), field.Tag(), field.Error())
} else {
res.AddError(
field.Field(),
field.Tag(),
v.translate(
locale, field.Field(), field.Tag(),
field.StructField(), param, value, count,
),
)
}
}
// Return the aggregated validation errors
return res
}
// parseVariableErrors processes and translates validation errors based on the provided locale and value for variable.
func (v *i18nValidator) parseVariableErrors(locale, name string, value any, err error) ValidationError {
// Skip nil error
if err == nil {
return NewEmptyError()
}
// Assert the error as validator.ValidationErrors
errs, ok := err.(validator.ValidationErrors)
if !ok {
return NewError(err)
}
// Initialize the result validation error
res := NewEmptyError()
// Iterate over each validation error and process
for _, field := range errs {
// Parse the field parameter and handle its type (int or float)
var count int
var param any = field.Param()
if i, f := parseNumeric(field.Param()); i != nil {
param = *i
count = int(*i)
} else if f != nil {
param = *f
count = int(*f)
}
// Add the translated error if translator available or raw error to the result
if v.translator == nil {
res.AddError(field.Field(), field.Tag(), field.Error())
} else {
res.AddError(
name,
field.Tag(),
v.translate(
locale, name, field.Tag(), name,
param, value, count,
),
)
}
}
// Return the aggregated validation errors
return res
}