Локализация ошибок

Валидация данных в Ajv сопровождается формированием структурированного массива ошибок, каждая из которых содержит техническую информацию о несоответствии JSON-схеме. По умолчанию сообщения об ошибках представлены на английском языке и ориентированы на разработчика, а не на конечного пользователя. Локализация в этом контексте означает преобразование этих технических сообщений в человеко-читаемый формат на нужном языке с учётом контекста приложения.

В основе системы ошибок Ajv лежит массив errors, формируемый после вызова validate(). Каждый элемент массива содержит поля:

  • keyword — имя нарушенного ключевого правила (например, type, required, minLength)
  • instancePath — путь к данным, где обнаружена ошибка
  • schemaPath — путь к правилу схемы
  • params — параметры ошибки
  • message — стандартное текстовое сообщение

Именно поле message чаще всего подвергается локализации, однако для полноценного перевода требуется учитывать и остальные поля.


Структура ошибок и влияние на локализацию

Типичная ошибка Ajv выглядит следующим образом:

{
  "instancePath": "/user/age",
  "schemaPath": "#/properties/user/properties/age/minimum",
  "keyword": "minimum",
  "params": {
    "comparison": ">=",
    "limit": 18
  },
  "message": "must be >= 18"
}

Проблема локализации заключается в том, что поле message не является самостоятельным источником истины — оно генерируется на основе keyword и params. Поэтому простая замена строки недостаточна, требуется переопределение генерации сообщений.


Отключение стандартных сообщений Ajv

В Ajv можно управлять генерацией текстов ошибок через опцию:

const ajv = new Ajv({
  messages: false
});

При отключении стандартных сообщений поле message может отсутствовать или быть минимальным, что позволяет полностью взять контроль над локализацией.


Использование кастомных сообщений через errorMessage

Одним из основных механизмов локализации является ключ схемы errorMessage.

const schema = {
  type: "object",
  properties: {
    age: {
      type: "number",
      minimum: 18,
      errorMessage: {
        minimum: "Возраст должен быть не меньше 18 лет",
        type: "Возраст должен быть числом"
      }
    }
  }
};

При использовании этого подхода Ajv заменяет стандартные сообщения на пользовательские, привязанные к конкретным keywords.

Особенность заключается в том, что errorMessage работает на уровне схемы и позволяет локализовать ошибки без постобработки.


Глобальная локализация через форматирование ошибок

При необходимости централизованной обработки используется преобразование массива errors после валидации:

const translateError = (error) => {
  switch (error.keyword) {
    case "type":
      return `Неверный тип данных в ${error.instancePath}`;
    case "minimum":
      return `Значение меньше допустимого минимума`;
    default:
      return `Ошибка валидации`;
  }
};

const localizedErrors = validate.errors.map(translateError);

Этот подход позволяет отделить логику схемы от языковой логики приложения.


Использование ajv-i18n для перевода ошибок

Существует специализированный пакет для локализации — ajv-i18n. Он предоставляет готовые функции перевода массива ошибок.

Пример использования:

import localize fr om "ajv-i18n";

validate(data);

if (validate.errors) {
  localize.ru(validate.errors);
}

После вызова функции ошибки модифицируются прямо в массиве errors, а поле message заменяется на локализованное значение.

Поддерживаются различные языки:

  • en
  • ru
  • es
  • de
  • fr

Механизм работает через сопоставление keyword и params с заранее подготовленными шаблонами.


Локализация через пользовательские keyword-обработчики

Ajv позволяет создавать собственные keywords через addKeyword, что открывает возможность полной кастомизации сообщений.

ajv.addKeyword({
  keyword: "positiveNumber",
  validate: (schema, data) => typeof data === "number" && data > 0,
  errors: true,
  metaSchema: {
    type: "boolean"
  }
});

Для локализации ошибок в таком случае необходимо вручную задавать сообщение:

validate.errors.push({
  keyword: "positiveNumber",
  message: "Число должно быть положительным",
  instancePath: "/amount"
});

Этот подход используется при сложной бизнес-логике, где стандартные keywords недостаточны.


Форматы ошибок и их влияние на перевод

Ajv различает несколько типов ошибок:

  • ошибки типов (type)
  • ошибки структуры (required, additionalProperties)
  • ошибки диапазона (minimum, maximum)
  • ошибки строк (minLength, pattern)
  • ошибки форматов (format)

Каждый тип требует собственного шаблона перевода. Например:

type

Ожидался тип number

required

Отсутствует обязательное поле: user.name

pattern

Строка не соответствует требуемому формату

Унификация перевода без учёта keyword приводит к потере точности сообщений.


Работа с instancePath при локализации

Поле instancePath критично для формирования понятного сообщения. Оно указывает, где именно возникла ошибка в объекте данных.

Пример:

"/user/profile/email"

При локализации часто требуется преобразование пути в читаемый формат:

const formatPath = (path) =>
  path.replace("/", "").replace(/\//g, ".");

Результат:

user.profile.email

Это позволяет интегрировать путь в локализованные сообщения:

Поле user.profile.email содержит ошибку формата

Множественные ошибки и агрегация сообщений

Ajv по умолчанию может возвращать несколько ошибок для одного объекта. Это влияет на локализацию, так как требуется объединение сообщений.

const messages = validate.errors.map(e => translateError(e)).join("\n");

При сложных схемах важно группировать ошибки по instancePath:

const grouped = validate.errors.reduce((acc, err) => {
  acc[err.instancePath] = acc[err.instancePath] || [];
  acc[err.instancePath].push(err);
  return acc;
}, {});

Это позволяет формировать структурированные локализованные отчёты.


Локализация через схемы JSON и словари

Распространённый подход — использование внешних словарей:

const dictionary = {
  required: "Поле обязательно для заполнения",
  type: "Неверный тип данных",
  minimum: "Значение слишком маленькое"
};

И последующее сопоставление:

const message = dictionary[error.keyword] || "Ошибка валидации";

Такой подход упрощает поддержку мультиязычности, так как перевод отделяется от логики схем.


Особенности версии Ajv v8 и влияние на локализацию

В Ajv v8 изменилась структура некоторых ошибок и поведение сообщений:

  • улучшена поддержка errorMessage
  • изменена обработка allErrors
  • обновлён формат некоторых params

Это влияет на стабильность локализаторов, которые зависят от структуры errors. При обновлении версии требуется проверка совместимости переводов.


Интеграция локализации в pipeline валидации

Типичный pipeline с локализацией ошибок:

  1. Валидация данных через validate(data)
  2. Проверка validate.errors
  3. Преобразование ошибок через локализатор
  4. Группировка и форматирование результата
  5. Передача пользователю или в UI

Пример:

if (!validate(data)) {
  const localized = validate.errors.map(localizeError);
  return {
    success: false,
    errors: localized
  };
}

Сопоставление keyword с языковыми шаблонами

Базовая модель локализации строится вокруг ключа keyword:

keyword шаблон
type Неверный тип
required Отсутствует поле
minimum Значение меньше допустимого
pattern Неверный формат строки

Расширение этой таблицы позволяет поддерживать сложные схемы без изменения логики валидации.


Контекстная локализация сообщений

Некоторые ошибки требуют учёта контекста схемы:

message: ({ instancePath, params }) =>
  `Ошибка в ${instancePath}: значение должно быть >= ${params.lim it}`

Это позволяет генерировать динамические локализованные сообщения, зависящие от входных данных схемы.


Ограничения и особенности подходов локализации

При работе с локализацией в Ajv важно учитывать:

  • сообщения генерируются после валидации, а не в процессе
  • структура errors не предназначена для UI напрямую
  • разные версии Ajv могут менять формат params
  • кастомные keywords требуют ручной локализации
  • глубокие схемы усложняют перевод instancePath

Эти особенности делают локализацию не побочным эффектом, а отдельным слоем архитектуры валидации данных.