Перевод системных сообщений

Система локализации в Tom Select строится вокруг набора текстовых строк, которые используются внутри компонента для отображения пользовательских сообщений: состояния загрузки, отсутствия результатов, подсказок поиска, ошибок и служебных уведомлений. Эти строки не являются частью бизнес-логики, но напрямую влияют на восприятие интерфейса, особенно в многоязычных приложениях и системах с динамически изменяемым языком.

Внутренние тексты компонента сгруппированы в объект конфигурации render/plugins/settings, но ключевой механизм локализации реализован через объект render и переопределяемые строки конфигурации. В стандартной поставке используется набор значений по умолчанию:

  • сообщение об отсутствии результатов поиска
  • сообщение о загрузке данных
  • подсказка для ввода
  • текст удаления выбранного элемента
  • текст создания нового элемента (в режиме creatable)

Каждое из этих сообщений может быть заменено на пользовательское значение через конфигурацию при инициализации.

Конфигурационный объект i18n-строк

Основной механизм переопределения системных сообщений заключается в передаче текстов через параметры инициализации:

new TomSelect("#select", {
  render: {
    no_results: function(data, escape) {
      return `Ничего не найдено для "${escape(data.input)}"`;
    },
    loading: function(data, escape) {
      return "Загрузка данных...";
    }
  }
});

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

Контекстные параметры сообщений

Функции рендера получают объект состояния компонента. Наиболее часто используемые поля:

  • input — текущий текст в поле поиска
  • options — доступные элементы
  • items — выбранные элементы

Это позволяет формировать динамические сообщения, адаптирующиеся к действиям пользователя.

Переопределение стандартных текстов интерфейса

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

Пример изменения текста удаления выбранного элемента:

new TomSelect("#select", {
  plugins: ["remove_button"],
  render: {
    item_remove: function(data, escape) {
      return `<div class="remove-hint">Удалить</div>`;
    }
  }
});

Хотя в большинстве случаев используется плагин remove_button, текст кнопки может быть стилизован или заменён через кастомный рендер.

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

Наиболее важной частью системы является обработка сообщений поиска. При отсутствии совпадений отображается сообщение no_results. Его поведение критично для UX, особенно в больших списках.

Типичный вариант локализации:

new TomSelect("#select", {
  render: {
    no_results: function(data, escape) {
      if (!data.input.length) {
        return "Введите запрос для поиска";
      }
      return `По запросу "${escape(data.input)}" ничего не найдено`;
    }
  }
});

Здесь реализована логика двух состояний:

  • пустой ввод
  • ввод без совпадений

Такой подход позволяет снизить когнитивную нагрузку и направить пользователя.

Поддержка многоязычности через внешние словари

Для масштабных приложений строки выносятся в отдельные словари:

const i18n = {
  ru: {
    no_results: "Ничего не найдено",
    loading: "Загрузка...",
    create: (input) => `Создать "${input}"`
  },
  en: {
    no_results: "No results found",
    loading: "Loading...",
    create: (input) => `Create "${input}"`
  }
};

Инициализация компонента с выбором языка:

const lang = "ru";

new TomSelect("#select", {
  render: {
    no_results: () => i18n[lang].no_results,
    loading: () => i18n[lang].loading,
    option_create: (data, escape) => i18n[lang].create(escape(data.input))
  }
});

Такой подход позволяет отделить UI-слой от логики локализации и упростить поддержку нескольких языков.

Динамическая смена языка без пересоздания компонента

В ряде сценариев требуется смена языка без полной реинициализации селекта. Для этого обновляются функции render и вызывается принудительное обновление списка.

function updateLanguage(ts, lang) {
  ts.settings.render.no_results = function(data, escape) {
    return i18n[lang].no_results;
  };

  ts.settings.render.loading = function() {
    return i18n[lang].loading;
  };

  ts.refreshOptions(false);
}

Метод refreshOptions используется для перерисовки выпадающего списка без потери текущего состояния ввода.

Обработка сообщений создания новых элементов

В режиме create компонент позволяет добавлять новые элементы, если они отсутствуют в списке. Соответствующее сообщение также подлежит локализации.

new TomSelect("#select", {
  create: true,
  render: {
    option_create: function(data, escape) {
      return `
        <div class="create-option">
          Добавить: <strong>${escape(data.input)}</strong>
        </div>
      `;
    }
  }
});

Важно учитывать, что пользовательский ввод должен быть экранирован для предотвращения XSS.

Использование escape-функции в системных сообщениях

Функция escape передаётся во все рендер-функции и предназначена для безопасного вывода пользовательских данных. Она обязательна при включении input в строки сообщений.

Типичная ошибка — прямое использование data.input без экранирования:

// небезопасно
return `Ничего не найдено для ${data.input}`;

Корректный вариант:

return `Ничего не найдено для "${escape(data.input)}"`;

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

Расширенная кастомизация текстов через плагины

Некоторые сообщения формируются внутри плагинов и требуют отдельной переопределяемой логики. Например, плагин множественного выбора может добавлять собственные подсказки.

new TomSelect("#select", {
  plugins: ["remove_button"],
  render: {
    item: function(data, escape) {
      return `<div class="item">${escape(data.text)}</div>`;
    },
    item_remove: function() {
      return "×";
    }
  }
});

Здесь даже минимальные символы интерфейса могут быть локализованы или заменены в зависимости от языка или дизайна системы.

Централизованная система сообщений

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

function createTomSelectI18n(lang) {
  return {
    no_results: (data, escape) => i18n[lang].no_results,
    loading: () => i18n[lang].loading,
    create: (input) => i18n[lang].create(input),
    placeholder: i18n[lang].placeholder
  };
}

Инициализация:

new TomSelect("#select", {
  render: createTomSelectI18n("ru")
});

Такой подход упрощает масштабирование интерфейса и снижает дублирование логики.

Поведение сообщений в асинхронных источниках данных

При использовании remote data источников сообщения становятся частью UX обработки сети. В частности:

  • loading отображается при каждом запросе
  • no_results появляется после завершения поиска
  • промежуточные состояния могут требовать дополнительных сообщений
new TomSelect("#select", {
  load: function(query, callback) {
    fetch(`/api/search?q=${query}`)
      .then(res => res.json())
      .then(data => callback(data))
      .catch(() => callback());
  },
  render: {
    loading: () => "Поиск...",
    no_results: () => "Результаты отсутствуют"
  }
});

Здесь текстовые сообщения становятся частью асинхронного взаимодействия и должны учитывать задержки сети и ошибки API.

Контекстная адаптация системных строк

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

render: {
  no_results: function(data, escape) {
    return user.isAdmin
      ? "Элементы не найдены в базе данных"
      : "Ничего не найдено";
  }
}

Такой подход позволяет адаптировать интерфейс без изменения логики компонента, сохраняя единый механизм локализации.