api-qobiltu-dev / space /validation /validation.go
lifedebugger's picture
Deploy files from GitHub repository
602f6ba
package validation
import (
"errors"
"fmt"
"strings"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/id"
ut "github.com/go-playground/universal-translator"
v10 "github.com/go-playground/validator/v10"
entranslations "github.com/go-playground/validator/v10/translations/en"
idtranslations "github.com/go-playground/validator/v10/translations/id"
)
// Constants for supported locales
const (
LocaleID = "id"
LocaleEN = "en"
)
// ErrorMessage represents a validation error message
type ErrorMessage struct {
Field string `json:"field"`
Message string `json:"-"`
}
type validator struct {
validate *v10.Validate
translator ut.Translator
}
// validatorInstance adalah instance global dari validator.
var validatorInstance *validator
// New creates a new validation instance with the specified locale
// dan menginisialisasi instance global validatorInstance.
func New(locale string) error {
v := &validator{}
parsedLocale := parseLocale(locale)
uni := ut.New(en.New(), id.New(), en.New())
translator, found := uni.GetTranslator(parsedLocale)
if !found {
return fmt.Errorf("translator not found for locale: %s", parsedLocale)
}
validate := v10.New()
if err := setupValidations(validate); err != nil {
return fmt.Errorf("failed to setup validations: %w", err)
}
if err := setupTranslations(validate, translator, parsedLocale); err != nil {
return fmt.Errorf("failed to setup translations for locale %s: %w", parsedLocale, err)
}
v.validate = validate
v.translator = translator
validatorInstance = v // Inisialisasi instance global
return nil
}
func parseLocale(locale string) string {
switch strings.ToLower(locale) {
case "id":
return LocaleID
case "en":
return LocaleEN
default:
return LocaleID // Default to Indonesian
}
}
// setupValidations configures custom validation rules.
func setupValidations(validate *v10.Validate) error {
rules := NewValidatorRules(&InMemoryOptionSource{})
if err := rules.RegisterAllCustomRules(validate); err != nil {
return err
}
return nil
}
// setupTranslations configures translations for validation messages.
func setupTranslations(validate *v10.Validate, translator ut.Translator, locale string) error {
// Register default translations based on locale
if err := registerDefaultTranslations(validate, translator, locale); err != nil {
return fmt.Errorf("failed to register default translations for locale %s: %w", locale, err)
}
// Register custom password validation translation
err := validate.RegisterTranslation("password", translator,
func(ut ut.Translator) error {
return ut.Add("password", "harus mengandung minimal 8 karakter, huruf besar, huruf kecil, dan angka.", true)
},
func(ut ut.Translator, fe v10.FieldError) string {
translated, err := ut.T(fe.Tag(), fe.Field())
if err != nil {
return fe.Field() + " is invalid"
}
return translated
},
)
if err != nil {
return fmt.Errorf("failed to register password translation: %w", err)
}
return nil
}
// registerDefaultTranslations sets up default translations for the specified locale.
func registerDefaultTranslations(validate *v10.Validate, translator ut.Translator, locale string) error {
switch locale {
case LocaleID:
return idtranslations.RegisterDefaultTranslations(validate, translator)
case LocaleEN:
return entranslations.RegisterDefaultTranslations(validate, translator)
default:
// Fallback to English if the locale is not supported
return entranslations.RegisterDefaultTranslations(validate, translator)
}
}
// Validate validates a struct using the global validator instance
// and returns a slice of ErrorMessage.
func Validate(s any) []ErrorMessage {
if validatorInstance == nil {
return []ErrorMessage{{Field: "", Message: "Validator belum diinisialisasi. Panggil validation.New() terlebih dahulu."}}
}
err := validatorInstance.validate.Struct(s)
if err != nil {
return TranslateError(err)
}
return nil
}
// TranslateError takes a validation error and translates it using the global translator.
func TranslateError(err error) []ErrorMessage {
if validatorInstance == nil {
return nil
}
var validationErrors v10.ValidationErrors
if !errors.As(err, &validationErrors) {
return nil
}
errorMessages := make([]ErrorMessage, 0, len(validationErrors))
for _, e := range validationErrors {
fieldLabel := e.Field()
msg, err := validatorInstance.translator.T(e.Tag(), fieldLabel)
if err != nil {
msg = fieldLabel + " is Invalid"
}
errorMessages = append(errorMessages, ErrorMessage{
Field: e.Tag(),
Message: msg,
})
}
return errorMessages
}