feat(core): initial implementation — launcher, logz, xerrors, valid
Introduces `code.nochebuena.dev/einherjar/core` — the foundational implementation module of the Einherjar framework. Provides four sub-packages that together cover every service's baseline needs: lifecycle management, structured logging, typed errors, and struct validation. - launcher: Launcher interface — three-phase managed lifecycle (OnInit → BeforeStart hooks → OnStart → OS signal wait → OnStop in reverse). Accepts lifecycle.Component and logging.Logger from contracts. Prints an ASCII art banner at startup (EINHERJAR_BANNER=off to suppress). Banner includes core version via runtime/debug.ReadBuildInfo() and a loaded-module list for every registered component that implements observability.Identifiable. Config struct with EINHERJAR_COMPONENT_STOP_TIMEOUT env tag (caarlos0/env syntax, default 15s). - logz: Logger implementation backed by log/slog. Returns contracts/logging.Logger. Detects errs.CodedError and errs.ContextualError (from contracts/errs) to enrich log records automatically — replaces the private duck-typed bridge from micro-lib. Context helpers: WithRequestID, WithField, WithFields, GetRequestID. Config struct with EINHERJAR_LOG_LEVEL (default INFO) and EINHERJAR_LOG_JSON (default false) env tags (caarlos0/env syntax); programmatic-only fields StaticArgs and Writer carry no tags. - xerrors: Typed error codes with context enrichment. Complete gRPC canonical set (16 codes) plus HTTP 410 ErrGone. Adds ErrOutOfRange, ErrAborted, ErrDataLoss over micro-lib. One convenience constructor per code. *Err declares compile-time satisfaction of errs.CodedError and errs.ContextualError. - valid: Struct validation wrapping go-playground/validator/v10. Validator interface + MessageProvider interface with full built-in tag coverage (~150 tags) in both DefaultMessages (English) and SpanishMessages (Spanish). Backend fully hidden; returns *xerrors.Err with ErrInvalidInput or ErrInternal. FieldLevel interface abstracts the backend's field-level access for custom validators. WithCustomValidator registers custom validation tags at construction time; OverrideProvider chains a tag→handler map with a fallback MessageProvider for custom tag messages without re-implementing built-ins. Compliance test enforces CT-6 (at most one exported TypeSpec per file via AST) and verifies behavioural correctness of all four sub-packages, including custom validator registration and OverrideProvider composition. Compile-time var _ assertions prove interface satisfaction. docs: ADR-001 (core module composition), ADR-002 (logz contracts/errs adoption), ADR-003 (Config naming convention and caarlos0/env tag standard)
This commit is contained in:
750
valid/message_provider.go
Normal file
750
valid/message_provider.go
Normal file
@@ -0,0 +1,750 @@
|
||||
package valid
|
||||
|
||||
import "fmt"
|
||||
|
||||
// MessageProvider maps a validation failure to a human-readable message.
|
||||
//
|
||||
// - field: the struct field name (e.g. "Email")
|
||||
// - tag: the failing validation rule (e.g. "required", "email", "min")
|
||||
// - param: the rule parameter if any (e.g. "18" for min=18), or "" if none
|
||||
type MessageProvider interface {
|
||||
Message(field, tag, param string) string
|
||||
}
|
||||
|
||||
// DefaultMessages is the built-in English message provider.
|
||||
// Used automatically when no WithMessageProvider option is given.
|
||||
var DefaultMessages MessageProvider = defaultMessages{}
|
||||
|
||||
// SpanishMessages is an opt-in Spanish message provider.
|
||||
var SpanishMessages MessageProvider = spanishMessages{}
|
||||
|
||||
// OverrideProvider returns a MessageProvider that resolves messages from handlers
|
||||
// for specific tags, delegating to base for any tag not in handlers.
|
||||
//
|
||||
// Use it to supply messages for custom validators or to override individual tags:
|
||||
//
|
||||
// p := valid.OverrideProvider(
|
||||
// map[string]func(field, param string) string{
|
||||
// "strongpassword": func(field, _ string) string {
|
||||
// return fmt.Sprintf("field '%s' must contain a letter, digit, and symbol", field)
|
||||
// },
|
||||
// },
|
||||
// valid.DefaultMessages,
|
||||
// )
|
||||
func OverrideProvider(handlers map[string]func(field, param string) string, base MessageProvider) MessageProvider {
|
||||
return &overrideProvider{handlers: handlers, base: base}
|
||||
}
|
||||
|
||||
type overrideProvider struct {
|
||||
handlers map[string]func(field, param string) string
|
||||
base MessageProvider
|
||||
}
|
||||
|
||||
func (p *overrideProvider) Message(field, tag, param string) string {
|
||||
if fn, ok := p.handlers[tag]; ok {
|
||||
return fn(field, param)
|
||||
}
|
||||
return p.base.Message(field, tag, param)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// English (default)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
type defaultMessages struct{}
|
||||
|
||||
func (defaultMessages) Message(field, tag, param string) string {
|
||||
switch tag {
|
||||
// Required
|
||||
case "required":
|
||||
return fmt.Sprintf("field '%s' is required", field)
|
||||
case "required_if", "required_unless":
|
||||
return fmt.Sprintf("field '%s' is required", field)
|
||||
case "required_with":
|
||||
return fmt.Sprintf("field '%s' is required when %s is present", field, param)
|
||||
case "required_with_all":
|
||||
return fmt.Sprintf("field '%s' is required when all of [%s] are present", field, param)
|
||||
case "required_without":
|
||||
return fmt.Sprintf("field '%s' is required when %s is absent", field, param)
|
||||
case "required_without_all":
|
||||
return fmt.Sprintf("field '%s' is required when all of [%s] are absent", field, param)
|
||||
// Excluded
|
||||
case "excluded_if", "excluded_unless":
|
||||
return fmt.Sprintf("field '%s' must not be set in this context", field)
|
||||
case "excluded_with":
|
||||
return fmt.Sprintf("field '%s' must not be set when %s is present", field, param)
|
||||
case "excluded_with_all":
|
||||
return fmt.Sprintf("field '%s' must not be set when all of [%s] are present", field, param)
|
||||
case "excluded_without":
|
||||
return fmt.Sprintf("field '%s' must not be set when %s is absent", field, param)
|
||||
case "excluded_without_all":
|
||||
return fmt.Sprintf("field '%s' must not be set when all of [%s] are absent", field, param)
|
||||
// Size / value
|
||||
case "len":
|
||||
return fmt.Sprintf("field '%s' must be exactly %s characters long", field, param)
|
||||
case "min":
|
||||
return fmt.Sprintf("field '%s' is too short (minimum %s)", field, param)
|
||||
case "max":
|
||||
return fmt.Sprintf("field '%s' is too long (maximum %s)", field, param)
|
||||
case "eq":
|
||||
return fmt.Sprintf("field '%s' must equal %s", field, param)
|
||||
case "eq_ignore_case":
|
||||
return fmt.Sprintf("field '%s' must equal %s (case-insensitive)", field, param)
|
||||
case "ne":
|
||||
return fmt.Sprintf("field '%s' must not equal %s", field, param)
|
||||
case "ne_ignore_case":
|
||||
return fmt.Sprintf("field '%s' must not equal %s (case-insensitive)", field, param)
|
||||
case "gt":
|
||||
return fmt.Sprintf("field '%s' must be greater than %s", field, param)
|
||||
case "gte":
|
||||
return fmt.Sprintf("field '%s' must be greater than or equal to %s", field, param)
|
||||
case "lt":
|
||||
return fmt.Sprintf("field '%s' must be less than %s", field, param)
|
||||
case "lte":
|
||||
return fmt.Sprintf("field '%s' must be less than or equal to %s", field, param)
|
||||
case "oneof":
|
||||
return fmt.Sprintf("field '%s' must be one of [%s]", field, param)
|
||||
case "noneof":
|
||||
return fmt.Sprintf("field '%s' must not be any of [%s]", field, param)
|
||||
case "unique":
|
||||
return fmt.Sprintf("field '%s' must contain unique values", field)
|
||||
case "isdefault":
|
||||
return fmt.Sprintf("field '%s' must be the default (zero) value", field)
|
||||
case "validateFn":
|
||||
return fmt.Sprintf("field '%s' failed custom validation", field)
|
||||
// Cross-field comparisons
|
||||
case "eqfield", "eqcsfield":
|
||||
return fmt.Sprintf("field '%s' must equal %s", field, param)
|
||||
case "nefield", "necsfield":
|
||||
return fmt.Sprintf("field '%s' must not equal %s", field, param)
|
||||
case "gtfield", "gtcsfield":
|
||||
return fmt.Sprintf("field '%s' must be greater than %s", field, param)
|
||||
case "gtefield", "gtecsfield":
|
||||
return fmt.Sprintf("field '%s' must be greater than or equal to %s", field, param)
|
||||
case "ltfield", "ltcsfield":
|
||||
return fmt.Sprintf("field '%s' must be less than %s", field, param)
|
||||
case "ltefield", "ltecsfield":
|
||||
return fmt.Sprintf("field '%s' must be less than or equal to %s", field, param)
|
||||
case "fieldcontains":
|
||||
return fmt.Sprintf("field '%s' must contain '%s'", field, param)
|
||||
case "fieldexcludes":
|
||||
return fmt.Sprintf("field '%s' must not contain '%s'", field, param)
|
||||
// String format
|
||||
case "alpha":
|
||||
return fmt.Sprintf("field '%s' must contain only letters", field)
|
||||
case "alphaspace":
|
||||
return fmt.Sprintf("field '%s' must contain only letters and spaces", field)
|
||||
case "alphanum":
|
||||
return fmt.Sprintf("field '%s' must contain only letters and digits", field)
|
||||
case "alphanumspace":
|
||||
return fmt.Sprintf("field '%s' must contain only letters, digits, and spaces", field)
|
||||
case "alphanumunicode":
|
||||
return fmt.Sprintf("field '%s' must contain only unicode letters and digits", field)
|
||||
case "alphaunicode":
|
||||
return fmt.Sprintf("field '%s' must contain only unicode letters", field)
|
||||
case "ascii":
|
||||
return fmt.Sprintf("field '%s' must contain only ASCII characters", field)
|
||||
case "printascii":
|
||||
return fmt.Sprintf("field '%s' must contain only printable ASCII characters", field)
|
||||
case "multibyte":
|
||||
return fmt.Sprintf("field '%s' must contain multi-byte characters", field)
|
||||
case "boolean":
|
||||
return fmt.Sprintf("field '%s' must be a boolean value", field)
|
||||
case "number":
|
||||
return fmt.Sprintf("field '%s' must be a number", field)
|
||||
case "numeric":
|
||||
return fmt.Sprintf("field '%s' must be a numeric string", field)
|
||||
case "lowercase":
|
||||
return fmt.Sprintf("field '%s' must be lowercase", field)
|
||||
case "uppercase":
|
||||
return fmt.Sprintf("field '%s' must be uppercase", field)
|
||||
// String content
|
||||
case "contains":
|
||||
return fmt.Sprintf("field '%s' must contain '%s'", field, param)
|
||||
case "containsany":
|
||||
return fmt.Sprintf("field '%s' must contain at least one of '%s'", field, param)
|
||||
case "containsrune":
|
||||
return fmt.Sprintf("field '%s' must contain the character '%s'", field, param)
|
||||
case "excludes":
|
||||
return fmt.Sprintf("field '%s' must not contain '%s'", field, param)
|
||||
case "excludesall":
|
||||
return fmt.Sprintf("field '%s' must not contain any of '%s'", field, param)
|
||||
case "excludesrune":
|
||||
return fmt.Sprintf("field '%s' must not contain the character '%s'", field, param)
|
||||
case "startswith":
|
||||
return fmt.Sprintf("field '%s' must start with '%s'", field, param)
|
||||
case "startsnotwith":
|
||||
return fmt.Sprintf("field '%s' must not start with '%s'", field, param)
|
||||
case "endswith":
|
||||
return fmt.Sprintf("field '%s' must end with '%s'", field, param)
|
||||
case "endsnotwith":
|
||||
return fmt.Sprintf("field '%s' must not end with '%s'", field, param)
|
||||
// Network
|
||||
case "ip", "ip_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid IP address", field)
|
||||
case "ipv4", "ip4_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid IPv4 address", field)
|
||||
case "ipv6", "ip6_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid IPv6 address", field)
|
||||
case "cidr":
|
||||
return fmt.Sprintf("field '%s' must be a valid CIDR notation address", field)
|
||||
case "cidrv4":
|
||||
return fmt.Sprintf("field '%s' must be a valid IPv4 CIDR notation address", field)
|
||||
case "cidrv6":
|
||||
return fmt.Sprintf("field '%s' must be a valid IPv6 CIDR notation address", field)
|
||||
case "mac":
|
||||
return fmt.Sprintf("field '%s' must be a valid MAC address", field)
|
||||
case "hostname":
|
||||
return fmt.Sprintf("field '%s' must be a valid RFC 952 hostname", field)
|
||||
case "hostname_rfc1123":
|
||||
return fmt.Sprintf("field '%s' must be a valid RFC 1123 hostname", field)
|
||||
case "hostname_port":
|
||||
return fmt.Sprintf("field '%s' must be a valid host:port pair", field)
|
||||
case "port":
|
||||
return fmt.Sprintf("field '%s' must be a valid port number", field)
|
||||
case "fqdn":
|
||||
return fmt.Sprintf("field '%s' must be a fully qualified domain name", field)
|
||||
case "tcp4_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid TCPv4 address", field)
|
||||
case "tcp6_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid TCPv6 address", field)
|
||||
case "tcp_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid TCP address", field)
|
||||
case "udp4_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid UDPv4 address", field)
|
||||
case "udp6_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid UDPv6 address", field)
|
||||
case "udp_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid UDP address", field)
|
||||
case "unix_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid Unix domain socket address", field)
|
||||
case "uds_exists":
|
||||
return fmt.Sprintf("field '%s' must be an existing Unix domain socket", field)
|
||||
case "uri":
|
||||
return fmt.Sprintf("field '%s' must be a valid URI", field)
|
||||
case "url":
|
||||
return fmt.Sprintf("field '%s' must be a valid URL", field)
|
||||
case "http_url":
|
||||
return fmt.Sprintf("field '%s' must be a valid HTTP or HTTPS URL", field)
|
||||
case "https_url":
|
||||
return fmt.Sprintf("field '%s' must be a valid HTTPS URL", field)
|
||||
case "url_encoded":
|
||||
return fmt.Sprintf("field '%s' must be a valid URL-encoded string", field)
|
||||
case "origin":
|
||||
return fmt.Sprintf("field '%s' must be a valid web origin", field)
|
||||
case "urn_rfc2141":
|
||||
return fmt.Sprintf("field '%s' must be a valid URN (RFC 2141)", field)
|
||||
case "datauri":
|
||||
return fmt.Sprintf("field '%s' must be a valid data URI", field)
|
||||
// Format / identity
|
||||
case "email":
|
||||
return fmt.Sprintf("field '%s' must be a valid email address", field)
|
||||
case "uuid":
|
||||
return fmt.Sprintf("field '%s' must be a valid UUID", field)
|
||||
case "uuid3":
|
||||
return fmt.Sprintf("field '%s' must be a valid version 3 UUID", field)
|
||||
case "uuid3_rfc4122":
|
||||
return fmt.Sprintf("field '%s' must be a valid RFC 4122 version 3 UUID", field)
|
||||
case "uuid4":
|
||||
return fmt.Sprintf("field '%s' must be a valid version 4 UUID", field)
|
||||
case "uuid4_rfc4122":
|
||||
return fmt.Sprintf("field '%s' must be a valid RFC 4122 version 4 UUID", field)
|
||||
case "uuid5":
|
||||
return fmt.Sprintf("field '%s' must be a valid version 5 UUID", field)
|
||||
case "uuid5_rfc4122":
|
||||
return fmt.Sprintf("field '%s' must be a valid RFC 4122 version 5 UUID", field)
|
||||
case "uuid_rfc4122":
|
||||
return fmt.Sprintf("field '%s' must be a valid RFC 4122 UUID", field)
|
||||
case "ulid":
|
||||
return fmt.Sprintf("field '%s' must be a valid ULID", field)
|
||||
case "e164":
|
||||
return fmt.Sprintf("field '%s' must be a valid E.164 phone number", field)
|
||||
case "isbn":
|
||||
return fmt.Sprintf("field '%s' must be a valid ISBN", field)
|
||||
case "isbn10":
|
||||
return fmt.Sprintf("field '%s' must be a valid ISBN-10", field)
|
||||
case "isbn13":
|
||||
return fmt.Sprintf("field '%s' must be a valid ISBN-13", field)
|
||||
case "issn":
|
||||
return fmt.Sprintf("field '%s' must be a valid ISSN", field)
|
||||
case "credit_card":
|
||||
return fmt.Sprintf("field '%s' must be a valid credit card number", field)
|
||||
case "ssn":
|
||||
return fmt.Sprintf("field '%s' must be a valid Social Security Number", field)
|
||||
case "ein":
|
||||
return fmt.Sprintf("field '%s' must be a valid EIN", field)
|
||||
case "latitude":
|
||||
return fmt.Sprintf("field '%s' must be a valid latitude", field)
|
||||
case "longitude":
|
||||
return fmt.Sprintf("field '%s' must be a valid longitude", field)
|
||||
case "timezone":
|
||||
return fmt.Sprintf("field '%s' must be a valid IANA timezone", field)
|
||||
case "datetime":
|
||||
return fmt.Sprintf("field '%s' must match the datetime format '%s'", field, param)
|
||||
case "json":
|
||||
return fmt.Sprintf("field '%s' must be valid JSON", field)
|
||||
case "jwt":
|
||||
return fmt.Sprintf("field '%s' must be a valid JWT", field)
|
||||
case "semver":
|
||||
return fmt.Sprintf("field '%s' must be a valid semantic version", field)
|
||||
case "cve":
|
||||
return fmt.Sprintf("field '%s' must be a valid CVE identifier", field)
|
||||
case "cron":
|
||||
return fmt.Sprintf("field '%s' must be a valid cron expression", field)
|
||||
case "spicedb":
|
||||
return fmt.Sprintf("field '%s' must be a valid SpiceDB identifier", field)
|
||||
// Encoding / hash
|
||||
case "base64":
|
||||
return fmt.Sprintf("field '%s' must be a valid base64-encoded string", field)
|
||||
case "base64url":
|
||||
return fmt.Sprintf("field '%s' must be a valid base64url-encoded string", field)
|
||||
case "base64rawurl":
|
||||
return fmt.Sprintf("field '%s' must be a valid base64rawurl-encoded string", field)
|
||||
case "hexadecimal":
|
||||
return fmt.Sprintf("field '%s' must be a valid hexadecimal string", field)
|
||||
case "md4":
|
||||
return fmt.Sprintf("field '%s' must be a valid MD4 hash", field)
|
||||
case "md5":
|
||||
return fmt.Sprintf("field '%s' must be a valid MD5 hash", field)
|
||||
case "sha256":
|
||||
return fmt.Sprintf("field '%s' must be a valid SHA-256 hash", field)
|
||||
case "sha384":
|
||||
return fmt.Sprintf("field '%s' must be a valid SHA-384 hash", field)
|
||||
case "sha512":
|
||||
return fmt.Sprintf("field '%s' must be a valid SHA-512 hash", field)
|
||||
case "ripemd128":
|
||||
return fmt.Sprintf("field '%s' must be a valid RIPEMD-128 hash", field)
|
||||
case "ripemd160":
|
||||
return fmt.Sprintf("field '%s' must be a valid RIPEMD-160 hash", field)
|
||||
case "tiger128":
|
||||
return fmt.Sprintf("field '%s' must be a valid TIGER-128 hash", field)
|
||||
case "tiger160":
|
||||
return fmt.Sprintf("field '%s' must be a valid TIGER-160 hash", field)
|
||||
case "tiger192":
|
||||
return fmt.Sprintf("field '%s' must be a valid TIGER-192 hash", field)
|
||||
case "luhn_checksum":
|
||||
return fmt.Sprintf("field '%s' must pass the Luhn checksum", field)
|
||||
// Color
|
||||
case "hexcolor":
|
||||
return fmt.Sprintf("field '%s' must be a valid hexadecimal color code", field)
|
||||
case "rgb":
|
||||
return fmt.Sprintf("field '%s' must be a valid RGB color", field)
|
||||
case "rgba":
|
||||
return fmt.Sprintf("field '%s' must be a valid RGBA color", field)
|
||||
case "hsl":
|
||||
return fmt.Sprintf("field '%s' must be a valid HSL color", field)
|
||||
case "hsla":
|
||||
return fmt.Sprintf("field '%s' must be a valid HSLA color", field)
|
||||
case "cmyk":
|
||||
return fmt.Sprintf("field '%s' must be a valid CMYK color", field)
|
||||
case "iscolor":
|
||||
return fmt.Sprintf("field '%s' must be a valid color (hex, RGB, RGBA, HSL, HSLA, or CMYK)", field)
|
||||
// HTML
|
||||
case "html":
|
||||
return fmt.Sprintf("field '%s' must contain valid HTML", field)
|
||||
case "html_encoded":
|
||||
return fmt.Sprintf("field '%s' must be HTML-encoded", field)
|
||||
// International standards
|
||||
case "iso3166_1_alpha2":
|
||||
return fmt.Sprintf("field '%s' must be a valid ISO 3166-1 alpha-2 country code", field)
|
||||
case "iso3166_1_alpha3":
|
||||
return fmt.Sprintf("field '%s' must be a valid ISO 3166-1 alpha-3 country code", field)
|
||||
case "iso3166_1_alpha_numeric":
|
||||
return fmt.Sprintf("field '%s' must be a valid ISO 3166-1 numeric country code", field)
|
||||
case "iso3166_2":
|
||||
return fmt.Sprintf("field '%s' must be a valid ISO 3166-2 subdivision code", field)
|
||||
case "iso4217":
|
||||
return fmt.Sprintf("field '%s' must be a valid ISO 4217 currency code", field)
|
||||
case "country_code":
|
||||
return fmt.Sprintf("field '%s' must be a valid ISO 3166 country code", field)
|
||||
case "bcp47_language_tag":
|
||||
return fmt.Sprintf("field '%s' must be a valid BCP 47 language tag", field)
|
||||
case "bcp47_strict_language_tag":
|
||||
return fmt.Sprintf("field '%s' must be a valid BCP 47 language tag (RFC 5646)", field)
|
||||
case "postcode_iso3166_alpha2", "postcode_iso3166_alpha2_field":
|
||||
return fmt.Sprintf("field '%s' must be a valid postcode", field)
|
||||
// Crypto / blockchain
|
||||
case "btc_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid Bitcoin address", field)
|
||||
case "btc_addr_bech32":
|
||||
return fmt.Sprintf("field '%s' must be a valid Bitcoin Bech32 address", field)
|
||||
case "eth_addr":
|
||||
return fmt.Sprintf("field '%s' must be a valid Ethereum address", field)
|
||||
// Database
|
||||
case "mongodb":
|
||||
return fmt.Sprintf("field '%s' must be a valid MongoDB ObjectID", field)
|
||||
case "mongodb_connection_string":
|
||||
return fmt.Sprintf("field '%s' must be a valid MongoDB connection string", field)
|
||||
// Business identity
|
||||
case "bic_iso_9362_2014":
|
||||
return fmt.Sprintf("field '%s' must be a valid BIC (ISO 9362:2014)", field)
|
||||
case "bic":
|
||||
return fmt.Sprintf("field '%s' must be a valid BIC (ISO 9362:2022)", field)
|
||||
// File system
|
||||
case "dir":
|
||||
return fmt.Sprintf("field '%s' must be an existing directory", field)
|
||||
case "dirpath":
|
||||
return fmt.Sprintf("field '%s' must be a valid directory path", field)
|
||||
case "file":
|
||||
return fmt.Sprintf("field '%s' must be an existing file", field)
|
||||
case "filepath":
|
||||
return fmt.Sprintf("field '%s' must be a valid file path", field)
|
||||
case "image":
|
||||
return fmt.Sprintf("field '%s' must be a valid image file", field)
|
||||
case "mimetype":
|
||||
return fmt.Sprintf("field '%s' must have MIME type '%s'", field, param)
|
||||
default:
|
||||
return fmt.Sprintf("field '%s' failed validation rule '%s'", field, tag)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Spanish (opt-in)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
type spanishMessages struct{}
|
||||
|
||||
func (spanishMessages) Message(field, tag, param string) string {
|
||||
switch tag {
|
||||
// Required
|
||||
case "required":
|
||||
return fmt.Sprintf("El campo '%s' es obligatorio", field)
|
||||
case "required_if", "required_unless":
|
||||
return fmt.Sprintf("El campo '%s' es obligatorio", field)
|
||||
case "required_with":
|
||||
return fmt.Sprintf("El campo '%s' es obligatorio cuando %s está presente", field, param)
|
||||
case "required_with_all":
|
||||
return fmt.Sprintf("El campo '%s' es obligatorio cuando todos los [%s] están presentes", field, param)
|
||||
case "required_without":
|
||||
return fmt.Sprintf("El campo '%s' es obligatorio cuando %s está ausente", field, param)
|
||||
case "required_without_all":
|
||||
return fmt.Sprintf("El campo '%s' es obligatorio cuando todos los [%s] están ausentes", field, param)
|
||||
// Excluded
|
||||
case "excluded_if", "excluded_unless":
|
||||
return fmt.Sprintf("El campo '%s' no debe estar definido en este contexto", field)
|
||||
case "excluded_with":
|
||||
return fmt.Sprintf("El campo '%s' no debe estar definido cuando %s está presente", field, param)
|
||||
case "excluded_with_all":
|
||||
return fmt.Sprintf("El campo '%s' no debe estar definido cuando todos los [%s] están presentes", field, param)
|
||||
case "excluded_without":
|
||||
return fmt.Sprintf("El campo '%s' no debe estar definido cuando %s está ausente", field, param)
|
||||
case "excluded_without_all":
|
||||
return fmt.Sprintf("El campo '%s' no debe estar definido cuando todos los [%s] están ausentes", field, param)
|
||||
// Size / value
|
||||
case "len":
|
||||
return fmt.Sprintf("El campo '%s' debe tener exactamente %s caracteres", field, param)
|
||||
case "min":
|
||||
return fmt.Sprintf("El campo '%s' es demasiado corto (mínimo %s)", field, param)
|
||||
case "max":
|
||||
return fmt.Sprintf("El campo '%s' es demasiado largo (máximo %s)", field, param)
|
||||
case "eq":
|
||||
return fmt.Sprintf("El campo '%s' debe ser igual a %s", field, param)
|
||||
case "eq_ignore_case":
|
||||
return fmt.Sprintf("El campo '%s' debe ser igual a %s (sin distinción de mayúsculas)", field, param)
|
||||
case "ne":
|
||||
return fmt.Sprintf("El campo '%s' no debe ser igual a %s", field, param)
|
||||
case "ne_ignore_case":
|
||||
return fmt.Sprintf("El campo '%s' no debe ser igual a %s (sin distinción de mayúsculas)", field, param)
|
||||
case "gt":
|
||||
return fmt.Sprintf("El campo '%s' debe ser mayor que %s", field, param)
|
||||
case "gte":
|
||||
return fmt.Sprintf("El campo '%s' debe ser mayor o igual a %s", field, param)
|
||||
case "lt":
|
||||
return fmt.Sprintf("El campo '%s' debe ser menor que %s", field, param)
|
||||
case "lte":
|
||||
return fmt.Sprintf("El campo '%s' debe ser menor o igual a %s", field, param)
|
||||
case "oneof":
|
||||
return fmt.Sprintf("El campo '%s' debe ser uno de [%s]", field, param)
|
||||
case "noneof":
|
||||
return fmt.Sprintf("El campo '%s' no debe ser ninguno de [%s]", field, param)
|
||||
case "unique":
|
||||
return fmt.Sprintf("El campo '%s' debe contener valores únicos", field)
|
||||
case "isdefault":
|
||||
return fmt.Sprintf("El campo '%s' debe ser el valor predeterminado", field)
|
||||
case "validateFn":
|
||||
return fmt.Sprintf("El campo '%s' no superó la validación personalizada", field)
|
||||
// Cross-field comparisons
|
||||
case "eqfield", "eqcsfield":
|
||||
return fmt.Sprintf("El campo '%s' debe ser igual a %s", field, param)
|
||||
case "nefield", "necsfield":
|
||||
return fmt.Sprintf("El campo '%s' no debe ser igual a %s", field, param)
|
||||
case "gtfield", "gtcsfield":
|
||||
return fmt.Sprintf("El campo '%s' debe ser mayor que %s", field, param)
|
||||
case "gtefield", "gtecsfield":
|
||||
return fmt.Sprintf("El campo '%s' debe ser mayor o igual a %s", field, param)
|
||||
case "ltfield", "ltcsfield":
|
||||
return fmt.Sprintf("El campo '%s' debe ser menor que %s", field, param)
|
||||
case "ltefield", "ltecsfield":
|
||||
return fmt.Sprintf("El campo '%s' debe ser menor o igual a %s", field, param)
|
||||
case "fieldcontains":
|
||||
return fmt.Sprintf("El campo '%s' debe contener '%s'", field, param)
|
||||
case "fieldexcludes":
|
||||
return fmt.Sprintf("El campo '%s' no debe contener '%s'", field, param)
|
||||
// String format
|
||||
case "alpha":
|
||||
return fmt.Sprintf("El campo '%s' solo debe contener letras", field)
|
||||
case "alphaspace":
|
||||
return fmt.Sprintf("El campo '%s' solo debe contener letras y espacios", field)
|
||||
case "alphanum":
|
||||
return fmt.Sprintf("El campo '%s' solo debe contener letras y dígitos", field)
|
||||
case "alphanumspace":
|
||||
return fmt.Sprintf("El campo '%s' solo debe contener letras, dígitos y espacios", field)
|
||||
case "alphanumunicode":
|
||||
return fmt.Sprintf("El campo '%s' solo debe contener letras y dígitos Unicode", field)
|
||||
case "alphaunicode":
|
||||
return fmt.Sprintf("El campo '%s' solo debe contener letras Unicode", field)
|
||||
case "ascii":
|
||||
return fmt.Sprintf("El campo '%s' solo debe contener caracteres ASCII", field)
|
||||
case "printascii":
|
||||
return fmt.Sprintf("El campo '%s' solo debe contener caracteres ASCII imprimibles", field)
|
||||
case "multibyte":
|
||||
return fmt.Sprintf("El campo '%s' debe contener caracteres multibyte", field)
|
||||
case "boolean":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un valor booleano", field)
|
||||
case "number":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un número", field)
|
||||
case "numeric":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una cadena numérica", field)
|
||||
case "lowercase":
|
||||
return fmt.Sprintf("El campo '%s' debe estar en minúsculas", field)
|
||||
case "uppercase":
|
||||
return fmt.Sprintf("El campo '%s' debe estar en mayúsculas", field)
|
||||
// String content
|
||||
case "contains":
|
||||
return fmt.Sprintf("El campo '%s' debe contener '%s'", field, param)
|
||||
case "containsany":
|
||||
return fmt.Sprintf("El campo '%s' debe contener al menos uno de '%s'", field, param)
|
||||
case "containsrune":
|
||||
return fmt.Sprintf("El campo '%s' debe contener el carácter '%s'", field, param)
|
||||
case "excludes":
|
||||
return fmt.Sprintf("El campo '%s' no debe contener '%s'", field, param)
|
||||
case "excludesall":
|
||||
return fmt.Sprintf("El campo '%s' no debe contener ninguno de '%s'", field, param)
|
||||
case "excludesrune":
|
||||
return fmt.Sprintf("El campo '%s' no debe contener el carácter '%s'", field, param)
|
||||
case "startswith":
|
||||
return fmt.Sprintf("El campo '%s' debe comenzar con '%s'", field, param)
|
||||
case "startsnotwith":
|
||||
return fmt.Sprintf("El campo '%s' no debe comenzar con '%s'", field, param)
|
||||
case "endswith":
|
||||
return fmt.Sprintf("El campo '%s' debe terminar con '%s'", field, param)
|
||||
case "endsnotwith":
|
||||
return fmt.Sprintf("El campo '%s' no debe terminar con '%s'", field, param)
|
||||
// Network
|
||||
case "ip", "ip_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección IP válida", field)
|
||||
case "ipv4", "ip4_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección IPv4 válida", field)
|
||||
case "ipv6", "ip6_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección IPv6 válida", field)
|
||||
case "cidr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección CIDR válida", field)
|
||||
case "cidrv4":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección CIDR IPv4 válida", field)
|
||||
case "cidrv6":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección CIDR IPv6 válida", field)
|
||||
case "mac":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección MAC válida", field)
|
||||
case "hostname":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un nombre de host válido (RFC 952)", field)
|
||||
case "hostname_rfc1123":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un nombre de host válido (RFC 1123)", field)
|
||||
case "hostname_port":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un par host:puerto válido", field)
|
||||
case "port":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un número de puerto válido", field)
|
||||
case "fqdn":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un nombre de dominio completamente calificado", field)
|
||||
case "tcp4_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección TCPv4 válida", field)
|
||||
case "tcp6_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección TCPv6 válida", field)
|
||||
case "tcp_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección TCP válida", field)
|
||||
case "udp4_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección UDPv4 válida", field)
|
||||
case "udp6_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección UDPv6 válida", field)
|
||||
case "udp_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección UDP válida", field)
|
||||
case "unix_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección de socket Unix válida", field)
|
||||
case "uds_exists":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un socket de dominio Unix existente", field)
|
||||
case "uri":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un URI válido", field)
|
||||
case "url":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una URL válida", field)
|
||||
case "http_url":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una URL HTTP o HTTPS válida", field)
|
||||
case "https_url":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una URL HTTPS válida", field)
|
||||
case "url_encoded":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una cadena codificada en URL válida", field)
|
||||
case "origin":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un origen web válido", field)
|
||||
case "urn_rfc2141":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un URN válido (RFC 2141)", field)
|
||||
case "datauri":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un URI de datos válido", field)
|
||||
// Format / identity
|
||||
case "email":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un correo electrónico válido", field)
|
||||
case "uuid":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un UUID válido", field)
|
||||
case "uuid3":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un UUID versión 3 válido", field)
|
||||
case "uuid3_rfc4122":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un UUID RFC 4122 versión 3 válido", field)
|
||||
case "uuid4":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un UUID versión 4 válido", field)
|
||||
case "uuid4_rfc4122":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un UUID RFC 4122 versión 4 válido", field)
|
||||
case "uuid5":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un UUID versión 5 válido", field)
|
||||
case "uuid5_rfc4122":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un UUID RFC 4122 versión 5 válido", field)
|
||||
case "uuid_rfc4122":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un UUID RFC 4122 válido", field)
|
||||
case "ulid":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un ULID válido", field)
|
||||
case "e164":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un número de teléfono E.164 válido", field)
|
||||
case "isbn":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un ISBN válido", field)
|
||||
case "isbn10":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un ISBN-10 válido", field)
|
||||
case "isbn13":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un ISBN-13 válido", field)
|
||||
case "issn":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un ISSN válido", field)
|
||||
case "credit_card":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un número de tarjeta de crédito válido", field)
|
||||
case "ssn":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un número de seguro social válido", field)
|
||||
case "ein":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un EIN válido", field)
|
||||
case "latitude":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una latitud válida", field)
|
||||
case "longitude":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una longitud válida", field)
|
||||
case "timezone":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una zona horaria IANA válida", field)
|
||||
case "datetime":
|
||||
return fmt.Sprintf("El campo '%s' debe coincidir con el formato de fecha '%s'", field, param)
|
||||
case "json":
|
||||
return fmt.Sprintf("El campo '%s' debe ser JSON válido", field)
|
||||
case "jwt":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un JWT válido", field)
|
||||
case "semver":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una versión semántica válida", field)
|
||||
case "cve":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un identificador CVE válido", field)
|
||||
case "cron":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una expresión cron válida", field)
|
||||
case "spicedb":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un identificador SpiceDB válido", field)
|
||||
// Encoding / hash
|
||||
case "base64":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una cadena base64 válida", field)
|
||||
case "base64url":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una cadena base64url válida", field)
|
||||
case "base64rawurl":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una cadena base64rawurl válida", field)
|
||||
case "hexadecimal":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una cadena hexadecimal válida", field)
|
||||
case "md4":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un hash MD4 válido", field)
|
||||
case "md5":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un hash MD5 válido", field)
|
||||
case "sha256":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un hash SHA-256 válido", field)
|
||||
case "sha384":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un hash SHA-384 válido", field)
|
||||
case "sha512":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un hash SHA-512 válido", field)
|
||||
case "ripemd128":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un hash RIPEMD-128 válido", field)
|
||||
case "ripemd160":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un hash RIPEMD-160 válido", field)
|
||||
case "tiger128":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un hash TIGER-128 válido", field)
|
||||
case "tiger160":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un hash TIGER-160 válido", field)
|
||||
case "tiger192":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un hash TIGER-192 válido", field)
|
||||
case "luhn_checksum":
|
||||
return fmt.Sprintf("El campo '%s' debe pasar la suma de verificación Luhn", field)
|
||||
// Color
|
||||
case "hexcolor":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un código de color hexadecimal válido", field)
|
||||
case "rgb":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un color RGB válido", field)
|
||||
case "rgba":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un color RGBA válido", field)
|
||||
case "hsl":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un color HSL válido", field)
|
||||
case "hsla":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un color HSLA válido", field)
|
||||
case "cmyk":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un color CMYK válido", field)
|
||||
case "iscolor":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un color válido (hex, RGB, RGBA, HSL, HSLA o CMYK)", field)
|
||||
// HTML
|
||||
case "html":
|
||||
return fmt.Sprintf("El campo '%s' debe contener HTML válido", field)
|
||||
case "html_encoded":
|
||||
return fmt.Sprintf("El campo '%s' debe estar codificado en HTML", field)
|
||||
// International standards
|
||||
case "iso3166_1_alpha2":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un código de país ISO 3166-1 alpha-2 válido", field)
|
||||
case "iso3166_1_alpha3":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un código de país ISO 3166-1 alpha-3 válido", field)
|
||||
case "iso3166_1_alpha_numeric":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un código de país ISO 3166-1 numérico válido", field)
|
||||
case "iso3166_2":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un código de subdivisión ISO 3166-2 válido", field)
|
||||
case "iso4217":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un código de moneda ISO 4217 válido", field)
|
||||
case "country_code":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un código de país ISO 3166 válido", field)
|
||||
case "bcp47_language_tag":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una etiqueta de idioma BCP 47 válida", field)
|
||||
case "bcp47_strict_language_tag":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una etiqueta de idioma BCP 47 válida (RFC 5646)", field)
|
||||
case "postcode_iso3166_alpha2", "postcode_iso3166_alpha2_field":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un código postal válido", field)
|
||||
// Crypto / blockchain
|
||||
case "btc_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección Bitcoin válida", field)
|
||||
case "btc_addr_bech32":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección Bitcoin Bech32 válida", field)
|
||||
case "eth_addr":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una dirección Ethereum válida", field)
|
||||
// Database
|
||||
case "mongodb":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un ObjectID de MongoDB válido", field)
|
||||
case "mongodb_connection_string":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una cadena de conexión de MongoDB válida", field)
|
||||
// Business identity
|
||||
case "bic_iso_9362_2014":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un BIC válido (ISO 9362:2014)", field)
|
||||
case "bic":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un BIC válido (ISO 9362:2022)", field)
|
||||
// File system
|
||||
case "dir":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un directorio existente", field)
|
||||
case "dirpath":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una ruta de directorio válida", field)
|
||||
case "file":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un archivo existente", field)
|
||||
case "filepath":
|
||||
return fmt.Sprintf("El campo '%s' debe ser una ruta de archivo válida", field)
|
||||
case "image":
|
||||
return fmt.Sprintf("El campo '%s' debe ser un archivo de imagen válido", field)
|
||||
case "mimetype":
|
||||
return fmt.Sprintf("El campo '%s' debe tener el tipo MIME '%s'", field, param)
|
||||
default:
|
||||
return fmt.Sprintf("Error en el campo '%s': regla '%s' no cumplida", field, tag)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user