# ADR-003: MessageProvider Pattern for i18n **Status:** Accepted **Date:** 2026-03-18 ## Context Validation error messages shown to end users must be human-readable. Applications targeting different locales need messages in different languages. Hardcoding English messages inside the `Validator` implementation would make internationalization impossible without forking the package. At the same time, the majority of applications use a single language throughout their lifetime. Requiring every caller to configure a message provider would be boilerplate-heavy for the common case. ## Decision A `MessageProvider` interface is defined: ```go type MessageProvider interface { Message(field, tag, param string) string } ``` `Message` receives the failing field name, the rule tag, and the rule parameter (e.g., `"5"` for `min=5`, or `""` if none), and returns a human-readable string. Two built-in implementations are provided as package-level variables: - `DefaultMessages` — English, used automatically when no option is passed to `New()`. - `SpanishMessages` — Spanish, available as an opt-in via `WithMessageProvider(valid.SpanishMessages)`. Custom implementations are supported by passing any value that satisfies `MessageProvider` to `WithMessageProvider`. The `New()` constructor defaults to `DefaultMessages` and applies options via a `config` struct, following the functional options pattern. This means zero boilerplate for the common (English) case and a single option call for overrides. ## Consequences - **Positive**: English is the zero-configuration default — `valid.New()` requires no arguments. - **Positive**: Spanish is available without any external dependency — just `valid.SpanishMessages`. - **Positive**: Applications can supply their own `MessageProvider` for any other language or for message formats that include the failing value, link to docs, etc. - **Negative**: The built-in providers handle only four tags (`required`, `email`, `min`, `max`) explicitly; all others fall through to a generic fallback message. Applications using many custom tags should supply a custom provider. - **Note**: Message formatting uses the struct field name as returned by `go-playground/validator` (the Go field name, e.g. `"Email"`), not a JSON tag. If user-facing messages must show the JSON key name, a custom `MessageProvider` combined with a registered tag name function on the playground validator would be needed.