Files
valid/docs/adr/ADR-003-message-provider-i18n.md

39 lines
2.4 KiB
Markdown
Raw Normal View History

# 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.