Skip to content

Translated validation errors: i18n Kit + Form Schema

Form Schema's built-in validators (required, minLength, email, and others) return English messages by default. Most of them take an optional msg parameter to override the text — that's enough to wire up i18n Kit and get a message in the user's current locale, without rewriting validation from scratch.

Locale files

locales/ru.json:

json
{
  "validation": {
    "required": "Обязательное поле",
    "minLength": "Минимум {n} символов",
    "email": "Введите корректный email"
  }
}

locales/en.json:

json
{
  "validation": {
    "required": "This field is required",
    "minLength": "At least {n} characters",
    "email": "Enter a valid email"
  }
}

The schema

ts
import { useT } from 'vue-i18n-kit'
import { required, minLength, email } from '@macrulez/vue-form-schema'
import type { FieldDefinition } from '@macrulez/vue-form-schema'

const { t } = useT()

const schema: FieldDefinition[] = [
  {
    type: 'text',
    name: 'name',
    label: 'Name',
    validators: [required(t('validation.required'))],
  },
  {
    type: 'text',
    name: 'bio',
    label: 'Bio',
    validators: [minLength(10, t('validation.minLength', { n: 10 }))],
  },
  {
    type: 'email',
    name: 'email',
    label: 'Email',
    validators: [email(t('validation.email'))],
  },
]

The catch: messages are locked in when the schema is built

t(...) here runs once, when the schema array is constructed — if the locale switches after the form has already rendered, already-shown error text won't update on its own until the validator runs again. For a form that should re-render already-visible errors instantly on a language switch, wrap schema in a computed() and rebuild it when the active locale changes — useLocale() from i18n Kit exposes it reactively.

What's next

  • For messages needing ICU pluralization ("1 file" / "2 files" / "5 files"), tm() instead of t(), same idea. See i18n Kit — overview.
  • The same idea works for custom (non-built-in) validators too — a ValidatorFn just returns t('validation.myRule') instead of a static string.