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

Библиотека Validator.js предоставляет набор функций для проверки строковых значений (email, URL, числа, длина строки и др.), однако не содержит встроенного механизма интернационализации. Все функции возвращают либо true, либо false, не формируя текстовые сообщения об ошибках. Это означает, что локализация реализуется на уровне приложения, а не внутри библиотеки.

Ключевая особенность архитектуры Validator.js заключается в том, что она отделяет проверку данных от формирования пользовательских сообщений. Такой подход даёт полный контроль над тем, как и на каком языке будут отображаться ошибки.


Базовый принцип локализации

Локализация валидационных сообщений строится вокруг трёх компонентов:

  • набор правил проверки (Validator.js);
  • набор шаблонов сообщений на разных языках;
  • механизм выбора языка и подстановки параметров.

Validator.js используется только как слой проверки:

import validator from 'validator';

validator.isEmail('test@example.com'); // true
validator.isEmail('invalid-email');    // false

Сообщения формируются отдельно:

const messages = {
  ru: {
    email: 'Некорректный email адрес',
  },
  en: {
    email: 'Invalid email address',
  }
};

Обёртка над функциями Validator.js

На практике создаётся слой абстракции, который связывает результат проверки с текстом ошибки.

import validator from 'validator';

const locale = 'ru';

const dictionary = {
  ru: {
    email: 'Некорректный email адрес',
    required: 'Поле обязательно для заполнения'
  },
  en: {
    email: 'Invalid email address',
    required: 'This field is required'
  }
};

function validateEmail(value) {
  if (!value) {
    return dictionary[locale].required;
  }

  if (!validator.isEmail(value)) {
    return dictionary[locale].email;
  }

  return null;
}

Такой подход масштабируется на любое количество правил.


Универсальная система сообщений

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

import validator from 'validator';

const locale = 'ru';

const messages = {
  ru: {
    required: 'Поле обязательно',
    email: 'Некорректный email',
    minLength: (n) => `Минимальная длина: ${n}`
  }
};

const rules = {
  email: (value) => validator.isEmail(value),
  required: (value) => value && value.trim().length > 0,
  minLength: (value, n) => value.length >= n
};

function validate(value, ruleName, param) {
  const rule = rules[ruleName];

  const isValid = rule(value, param);

  if (isValid) return null;

  const message = messages[locale][ruleName];

  return typeof message === 'function' ? message(param) : message;
}

Подстановка параметров в сообщения

Часто требуется динамическая подстановка значений (например, минимальная длина строки). Для этого используются функции или шаблоны.

Вариант с функциями

minLength: (n) => `Минимальная длина ${n} символов`

Вариант с шаблонами

function interpolate(template, params) {
  return template.replace(/\{(\w+)\}/g, (_, key) => params[key]);
}

const messages = {
  ru: {
    minLength: 'Минимальная длина {n} символов'
  }
};

interpolate(messages.ru.minLength, { n: 8 });

Централизованный валидатор форм

При работе с формами удобнее использовать единый механизм проверки набора полей.

import validator from 'validator';

const locale = 'ru';

const messages = {
  ru: {
    email: 'Некорректный email',
    required: 'Поле обязательно',
    minLength: 'Минимум {n} символов'
  }
};

function validateField(value, rules) {
  for (const rule of rules) {
    if (rule.type === 'required' && !value) {
      return messages[locale].required;
    }

    if (rule.type === 'email' && !validator.isEmail(value)) {
      return messages[locale].email;
    }

    if (rule.type === 'minLength' && value.length < rule.value) {
      return messages[locale].minLength.replace('{n}', rule.value);
    }
  }

  return null;
}

Интеграция с системами i18n

В реальных приложениях локализация часто делегируется специализированным библиотекам, например i18next. Validator.js при этом остаётся только механизмом проверки.

import validator from 'validator';
import i18next from 'i18next';

function validateEmail(value) {
  if (!validator.isEmail(value)) {
    return i18next.t('validation.email');
  }

  return null;
}

Структура переводов:

{
  "validation": {
    "email": "Некорректный email",
    "required": "Обязательное поле"
  }
}

Такой подход позволяет:

  • централизовать переводы;
  • динамически переключать язык;
  • поддерживать сложные языковые структуры.

Разделение логики правил и сообщений

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

Плохо

if (!validator.isEmail(value)) {
  return 'Invalid email';
}

Лучше

const isValid = validator.isEmail(value);

if (!isValid) {
  return getMessage('email');
}

Структура словаря сообщений

При масштабировании проекта используется многоуровневая структура:

const messages = {
  ru: {
    validation: {
      email: 'Некорректный email',
      password: {
        minLength: 'Пароль слишком короткий',
        weak: 'Слишком простой пароль'
      }
    }
  },
  en: {
    validation: {
      email: 'Invalid email',
      password: {
        minLength: 'Password too short',
        weak: 'Password is too weak'
      }
    }
  }
};

Доступ к сообщениям реализуется через вспомогательную функцию:

function t(path) {
  return path.split('.').reduce((acc, key) => acc?.[key], messages[locale]);
}

Локализация сложных правил

Некоторые правила требуют контекста (например, сравнение дат или диапазонов чисел).

import validator from 'validator';

function validateAge(value, min, max) {
  if (!validator.isInt(value + '')) {
    return 'Возраст должен быть числом';
  }

  const age = parseInt(value, 10);

  if (age < min || age > max) {
    return `Возраст должен быть от ${min} до ${max}`;
  }

  return null;
}

При локализации такие строки также выносятся в словарь с параметризацией.


Поддержка нескольких языков в рантайме

Переключение языка обычно реализуется через глобальный контекст:

let currentLocale = 'ru';

function setLocale(locale) {
  currentLocale = locale;
}

function getLocale() {
  return currentLocale;
}

Использование:

function getMessage(key) {
  return messages[getLocale()][key];
}

Архитектурный подход для крупных приложений

В больших системах применяется модульная схема:

  • validator.js — только проверка;
  • validation layer — связывает правила и ошибки;
  • i18n layer — хранит переводы;
  • form layer — управляет состоянием формы.

Пример структуры:

/validation
  rules.js
  validator.js
  messages/
    ru.js
    en.js
  index.js

Такое разделение упрощает:

  • добавление новых языков;
  • расширение правил;
  • тестирование логики без привязки к UI.

Унификация сообщений для разных типов данных

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

const error = {
  field: 'email',
  rule: 'email',
  message: 'Некорректный email'
};

Это упрощает интеграцию с фронтенд-фреймворками и API.


Обработка массивов ошибок

При валидации формы часто требуется возвращать несколько ошибок одновременно:

function validateForm(data) {
  const errors = {};

  if (!validator.isEmail(data.email || '')) {
    errors.email = messages.ru.email;
  }

  if (!data.password || data.password.length < 8) {
    errors.password = 'Слишком короткий пароль';
  }

  return errors;
}

Гибкость подхода Validator.js в локализации

Отсутствие встроенной локализации в Validator.js является архитектурным преимуществом: библиотека остаётся минималистичной и независимой от UI-логики. Это позволяет строить любую систему локализации поверх неё — от простых словарей до сложных многоязычных платформ с динамическими правилами и контекстными сообщениями.