Messages and i18n
Every error a form shows is a message: a field's required message, a rule's message, or Valibot's own. Below you will learn where each one comes from, and how to translate them.
Where messages come from
- The required message is the field's own, from
"required": { "message": "..." }, or else the builder'smessages.required, which defaults to"Required". - A rule's message is its
message. A rule without one reports Valibot's own message, like "Invalid length: Expected >=10 but received 5". - A form rule's message is its
message, which it always has.
Translating your messages
resolveMessage is called with every message of the definition, every label and every description, and the required message of every required field without its own. Return the text to use, or undefined to keep the text as written. Write keys in the definition, and translate them here:
const translations: Record<string, string> = {
'form.required': 'Obavezno polje',
'signup.email': 'E-pošta',
'signup.password.short': 'Najmanje 10 znakova',
}
const builder = createSchemaBuilder({
messages: { required: 'form.required' },
// Called with every message, label and description of the definition.
resolveMessage: (key) => translations[key],
})The schema then reports the translated messages:
const schema = builder.build({
version: '1',
fields: [
{ name: 'email', type: 'email', label: 'signup.email', required: true },
{
name: 'password',
type: 'password',
required: true,
rules: [
{ rule: 'minLength', value: 10, message: 'signup.password.short' },
],
},
],
})
const result = v.safeParse(schema, { email: '', password: 'short' })
// The issues' messages: 'Obavezno polje' for email, 'Najmanje 10 znakova' for password.The second argument of resolveMessage is a MessageContext: the field the text belongs to, the rule for a rule's message, and the path of the field in the definition. Build one builder per language, or read the current language inside resolveMessage. A builder resolves the messages when it builds the schema, so build the form again when the language changes.
Translating Valibot's messages
A rule without a message reports Valibot's own, so Valibot's i18n package translates it, with no setup here. Import the languages you need and set the language with Valibot's setGlobalConfig.
The object schema of a list of fields has a message of its own, which gives a required field whose key is missing its required message. Its other errors, like an input that is no object, get the message you set with setSpecificMessage(v.object, ...), setSchemaMessage or setGlobalMessage, else Valibot's own. A message in the config of a single parse, like v.parse(schema, input, { message }), doesn't reach them, because Valibot uses a schema's own message first. It still reaches the errors of the fields.
Labels and text in components
resolveMessage changes what goes into the schema: messages, and the label and description the schema carries as metadata. Your components get each field as the definition has it, so translate field.label, field.placeholder, the text of display elements, and the labels of options in your components, with the same function.