Skip to content

Сообщения и локализация ​

У каждой ошибки есть код (например, minLength) и сообщение. Сообщение можно задать для конкретного правила, для всего приложения или подключить готовую локализацию.

Сообщение для конкретного правила ​

Последний аргумент любой схемы и правила — сообщение:

ts
string('Введите имя')
pipe(string(), minLength(8, 'Пароль слишком короткий'))

Для схем (string(), object()…) сообщение заменяет все их ошибки — и «обязательное поле», и «неверный тип».

Параметры в тексте ​

В тексте можно использовать параметры правила в фигурных скобках:

ts
minLength(3, 'Минимум {min} символа')   // → «Минимум 3 символа»
oneOf(['a', 'b'], 'Выберите: {options}') // → «Выберите: a, b»

Если нужна логика (например, склонение), передайте функцию — она получит параметры:

ts
const plural = (n: number) => (n % 10 === 1 && n % 100 !== 11 ? 'символ' : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 'символа' : 'символов');

minLength(5, (p) => `Минимум ${p.min} ${plural(p.min as number)}`) // → «Минимум 5 символов»

Склонения: «3 символа», «5 символов» ​

Форма слова, зависящая от числа, пишется в фигурных скобках через |: сначала параметр, потом формы. Форма выбирается по правилам языка (Intl.PluralRules), язык берётся из ключа $locale в lang-файле:

ts
setMessages({
  minLength: 'Минимум {min} {min|символ|символа|символов}',
});
// 1 символ, 2 символа, 5 символов, 21 символ
ЯзыкСколько формПример
Русский, украинский3: одна, несколько, много{n|файл|файла|файлов}
Английский, немецкий2: одна, много{n|file|files}
Китайский, японский1{n|个} или просто текст без форм

Встроенные русская и английская локали уже используют склонения. Если форм указано меньше, чем нужно языку, берётся последняя.

Сообщения для всего приложения ​

setMessages меняет тексты по умолчанию. Вызовите один раз при старте приложения:

ts
import { setMessages } from 'tinyval';
import ru from 'tinyval/locales/ru';

setMessages(ru);                             // всё на русском
setMessages({ required: 'Заполните поле' });  // переопределить отдельные тексты

Язык для одного вызова ​

setMessages действует на всё приложение. На сервере, где у каждого запроса свой язык, передайте сообщения в сам вызов — они не затронут другие запросы:

ts
import ru from 'tinyval/locales/ru';
import en from 'tinyval/locales/en';

const locales = { ru, en };

app.post('/users', (req, res) => {
  const lang = req.acceptsLanguages('ru', 'en') || 'en';
  const result = validate(CreateUser, req.body, { messages: locales[lang] });
});

Сообщения вызова можно передать частично — недостающие возьмутся из setMessages.

Название поля — {label} ​

В любом сообщении доступен параметр {label} — как :attribute в Laravel. По умолчанию это имя поля (email), а своё название задаёт label или опция labels:

ts
const Signup = object({
  email: label(pipe(string(), email()), 'Почта'),
  password: label(pipe(string(), minLength(8)), 'Пароль'),
});

setMessages({ required: 'Поле «{label}» обязательно', minLength: '{label}: минимум {min} символов' });

validate(Signup, { password: '123' }).errors;
// { email: 'Поле «Почта» обязательно', password: 'Пароль: минимум 8 символов' }

Перевод на клиенте — params ​

Если сервер не знает язык пользователя, можно отдать клиенту коды и параметры ошибок, а текст собрать на фронтенде:

ts
validate(Signup, body).issues;
// [{ code: 'minLength', params: { min: 8 }, path: ['password'], message: '...' }]

Локализации ​

ИмпортЯзык
tinyval/locales/enАнглийский (по умолчанию)
tinyval/locales/ruРусский
tinyval/locales/zhКитайский (упрощённый)

setMessages(en) возвращает английские тексты.

Чтобы поменять тексты под себя, скопируйте lang-файл в проект командой npx tinyval lang ru — см. Свой lang-файл.

Все коды ошибок ​

КодГде возникаетПо умолчанию (en)В tinyval/locales/ru
invalidcheck, checkAsyncInvalid valueНекорректное значение
requiredпустое значение в любой схемеRequiredОбязательное поле
stringstringMust be a stringДолжно быть строкой
numbernumberMust be a numberДолжно быть числом
booleanbooleanMust be a booleanДолжно быть логическим значением
datedateMust be a valid dateНекорректная дата
objectobjectMust be an objectДолжно быть объектом
arrayarrayMust be an arrayДолжно быть списком
literalliteralMust be {expected}Должно быть {expected}
oneOfoneOf, enumOfMust be one of: {options}Допустимые значения: {options}
unionunionDoes not match any allowed typeЗначение не подходит ни под один из вариантов
variantvariantUnknown type, expected one of: {options}Неизвестный тип, допустимые: {options}
depthlazyToo deeply nested (max {max})Слишком глубокая вложенность (максимум {max})
unknownKeyobject с unknown: 'strict'Unknown fieldНеизвестное поле
minLengthminLength у строкиMust be at least {min} {min|character|characters} longМинимум {min} {min|символ|символа|символов}
maxLengthmaxLength у строкиMust be at most {max} {max|character|characters} longМаксимум {max} {max|символ|символа|символов}
lengthlength у строкиMust be exactly {length} {length|character|characters} longДолжно быть {length} {length|символ|символа|символов}
minminMust be at least {min}Не меньше {min}
maxmaxMust be at most {max}Не больше {max}
integerintegerMust be an integerДолжно быть целым числом
patternpatternInvalid formatНеверный формат
emailemailInvalid emailНекорректный email
minItemsminLength у массиваMust have at least {min} {min|item|items}Минимум {min} {min|элемент|элемента|элементов}
maxItemsmaxLength у массиваMust have at most {max} {max|item|items}Максимум {max} {max|элемент|элемента|элементов}
itemslength у массива, tupleMust have exactly {length} {length|item|items}Должно быть {length} {length|элемент|элемента|элементов}
betweenbetweenMust be between {min} and {max}От {min} до {max}
urlurlInvalid URLНекорректная ссылка
uuiduuidInvalid UUIDНекорректный UUID
startsWithstartsWithMust start with "{value}"Должно начинаться с «{value}»
endsWithendsWithMust end with "{value}"Должно заканчиваться на «{value}»
includesincludesMust contain "{value}"Должно содержать «{value}»
uniqueuniqueMust not contain duplicatesЗначения не должны повторяться
minDateminDateMust be on or after {date}Не раньше {date}
maxDatemaxDateMust be on or before {date}Не позже {date}
afterafterMust be after {field}Должно быть позже, чем {field}
beforebeforeMust be before {field}Должно быть раньше, чем {field}
inninnInvalid INNНекорректный ИНН
snilssnilsInvalid SNILSНекорректный СНИЛС
luhnluhnInvalid card numberНекорректный номер карты
prohibitedprohibitedIfMust be emptyПоле должно быть пустым
filefileMust be a fileВыберите файл
fileSizefile — maxSizeFile must be at most {max}Файл не больше {max}
fileTypefile — typesFile type must be one of: {types}Допустимые типы файлов: {types}
sameAssameAsMust match {field}Значения не совпадают

Своё сообщение с параметрами, которых нет в таблице, тоже работает — неизвестный {параметр} остаётся в тексте как есть.

См. также ​