Работа с формами в JavaScript-экосистеме часто строится вокруг
валидации данных и отображения ошибок пользователю. В связке с
react-hook-form одним из наиболее распространённых решений
является YupResolver, который позволяет использовать схему
валидации на базе библиотеки Yup. Ключевой аспект при построении
прикладных интерфейсов — корректная локализация сообщений об ошибках,
поскольку пользовательские интерфейсы редко ограничиваются одним
языком.
Понимание локализации невозможно без осознания того, где именно формируются сообщения:
react-hook-form.Следовательно, локализация может быть реализована на нескольких уровнях:
Yup предоставляет встроенный механизм глобальной локализации через
setLocale. Он позволяет задать сообщения по умолчанию для
всех типов валидаторов.
import * as Yup from 'yup';
Yup.setLocale({
mixed: {
required: 'Поле обязательно для заполнения',
default: 'Некорректное значение',
},
string: {
min: 'Минимальная длина — ${min} символов',
max: 'Максимальная длина — ${max} символов',
email: 'Некорректный email адрес',
},
number: {
min: 'Значение должно быть не меньше ${min}',
max: 'Значение должно быть не больше ${max}',
},
});
Особенность этого подхода заключается в том, что сообщения становятся глобальными. Это удобно для небольших приложений, но создаёт ограничения при работе с мультиязычностью.
Более гибкий способ — определение сообщений непосредственно в схеме Yup. Это позволяет контролировать тексты локально и избегать глобальных побочных эффектов.
import * as Yup from 'yup';
const schema = Yup.object({
email: Yup.string()
.email('Введите корректный email')
.required('Email обязателен'),
password: Yup.string()
.min(8, 'Пароль должен содержать минимум 8 символов')
.required('Пароль обязателен'),
});
Такой подход предпочтителен, когда:
YupResolver служит мостом между схемой Yup и
react-hook-form. Он не занимается локализацией напрямую, а
лишь передаёт уже сформированные сообщения.
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
const form = useForm({
resolver: yupResolver(schema),
});
Важный момент: любые изменения в локализации должны происходить до
передачи схемы в yupResolver. Resolver не трансформирует
тексты ошибок.
В реальных приложениях часто используется i18n-библиотека (например,
i18next). В этом случае возникает задача синхронизации
языка Yup с текущей локалью приложения.
Один из подходов — пересоздание схемы при смене языка.
import * as Yup from 'yup';
const createSchema = (t) =>
Yup.object({
email: Yup.string()
.email(t('validation.emailInvalid'))
.required(t('validation.emailRequired')),
password: Yup.string()
.min(8, t('validation.passwordMin'))
.required(t('validation.passwordRequired')),
});
Далее схема пересоздаётся при изменении языка:
const schema = useMemo(() => createSchema(t), [t]);
Такой подход обеспечивает:
При масштабных приложениях часто используется централизованная фабрика сообщений:
const messages = {
email: {
invalid: (t) => t('validation.emailInvalid'),
required: (t) => t('validation.emailRequired'),
},
};
И затем схема строится на основе этих функций:
const createSchema = (t) =>
Yup.object({
email: Yup.string()
.email(messages.email.invalid(t))
.required(messages.email.required(t)),
});
Такой подход позволяет:
Помимо базовых типов (string, number,
mixed), Yup поддерживает сложные структуры:
Yup.array()
.of(Yup.string().required('Каждый элемент обязателен'))
.min(1, 'Добавьте хотя бы один элемент');
Yup.object({
profile: Yup.object({
firstName: Yup.string().required('Имя обязательно'),
lastName: Yup.string().required('Фамилия обязательна'),
}),
});
При локализации вложенных объектов важно сохранять единообразие ключей перевода, иначе поддержка становится затруднительной.
Yup поддерживает подстановку параметров в строках:
${min}${max}${path}${value}Пример:
Yup.string()
.min(5, 'Минимум ${min} символов')
.max(20, 'Максимум ${max} символов');
При локализации важно учитывать, что порядок слов в разных языках может отличаться, поэтому простая строковая интерполяция иногда оказывается недостаточной.
YupResolver возвращает ошибки в формате:
{
email: {
type: "required",
message: "Email обязателен"
}
}
Это означает, что локализация может быть реализована и на уровне отображения, если хранить ключи сообщений вместо готового текста:
{
email: {
type: "required",
messageKey: "validation.emailRequired"
}
}
И уже UI слой переводит ключ в текст:
t(error.messageKey)
Такой подход отделяет валидацию от представления.
В крупных приложениях используется комбинированная стратегия:
Пример схемы:
Yup.string().required('validation.emailRequired');
UI:
const message = t(error.message);
Yup.setLocale может неожиданно перекрывать динамические
переводы, если используется в мульти-язычных приложениях.
Если схема создаётся вне React-цикла, она может «запомнить» старый язык.
При использовании кастомных резолверов структура ошибок может отличаться, что усложняет унификацию перевода.
Выбор метода локализации зависит от масштаба:
Yup.setLocale;Некоторые реализации позволяют передавать контекст в
yupResolver:
yupResolver(schema, { context: { lang: 'ru' } });
Хотя Yup не использует этот контекст напрямую, его можно применять при построении схем через фабрику:
const createSchema = (lang) => {
const t = translations[lang];
return Yup.object({
email: Yup.string().required(t.required),
});
};
Локализация в связке Yup + YupResolver строится как многоуровневая система:
Такая архитектура позволяет масштабировать систему валидации без привязки к конкретному языку и обеспечивает независимость логики и представления.