Динамическая смена языка

В библиотеках кастомных селектов, построенных поверх нативного <select>, текстовые элементы интерфейса обычно выносятся в конфигурацию экземпляра. Slim Select относится к таким решениям: строки интерфейса не «зашиты» в DOM, а формируются через набор параметров, что позволяет реализовать динамическую смену языка без модификации разметки.

Типовая структура текстов интерфейса включает:

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

Именно эти поля становятся точками локализации при переключении языка.


Конфигурационные текстовые параметры

При инициализации Slim Select набор строк задаётся через объект конфигурации:

new SlimSelect({
  select: '#example',
  settings: {
    placeholderText: 'Select value',
    searchPlaceholder: 'Search...',
    searchText: 'No results',
    searchingText: 'Searching...',
    searchHighlight: true,
    closeOnSelect: true
  }
});

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

Основные локализуемые свойства:

  • placeholderText — текст до выбора значения
  • searchPlaceholder — плейсхолдер строки поиска
  • searchText — сообщение «нет результатов»
  • searchingText — сообщение загрузки/поиска
  • addableText (если используется расширенный режим) — подсказка добавления нового значения

Архитектура динамической смены языка

Смена языка в Slim Select обычно реализуется одним из трёх подходов:

  1. Полное пересоздание экземпляра
  2. Обновление конфигурации через destroy + reinit
  3. Имитация обновления через DOM-перезапись и синхронизацию состояния

Наиболее стабильной моделью считается пересоздание экземпляра, поскольку внутреннее состояние компонента слабо рассчитано на горячую замену всех текстов.


Пересоздание экземпляра при смене локали

Базовый паттерн смены языка заключается в уничтожении текущего экземпляра и создании нового с обновлёнными строками:

let slim = new SlimSelect({
  select: '#example',
  settings: {
    placeholderText: i18n['en'].placeholderText,
    searchPlaceholder: i18n['en'].searchPlaceholder,
    searchText: i18n['en'].searchText,
    searchingText: i18n['en'].searchingText
  }
});

function changeLanguage(lang) {
  slim.destroy();

  slim = new SlimSelect({
    select: '#example',
    settings: {
      placeholderText: i18n[lang].placeholderText,
      searchPlaceholder: i18n[lang].searchPlaceholder,
      searchText: i18n[lang].searchText,
      searchingText: i18n[lang].searchingText
    }
  });
}

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


Централизованное хранилище переводов

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

const i18n = {
  en: {
    placeholderText: 'Select value',
    searchPlaceholder: 'Search...',
    searchText: 'No results found',
    searchingText: 'Searching...'
  },
  ru: {
    placeholderText: 'Выберите значение',
    searchPlaceholder: 'Поиск...',
    searchText: 'Ничего не найдено',
    searchingText: 'Поиск...'
  },
  kz: {
    placeholderText: 'Мәнді таңдаңыз',
    searchPlaceholder: 'Іздеу...',
    searchText: 'Ештеңе табылмады',
    searchingText: 'Ізделуде...'
  }
};

Такая структура позволяет синхронизировать UI с глобальной системой i18n без привязки к конкретной библиотеке.


Динамическое обновление без пересоздания (ограниченный подход)

В некоторых сценариях применяется частичное обновление DOM без уничтожения экземпляра. Этот способ зависит от внутренней реализации Slim Select и может быть нестабилен, но используется для минимизации пересборки интерфейса.

Подход основан на ручном обновлении текстовых узлов:

function updateTexts(lang) {
  const root = document.querySelector('.ss-main');

  const placeholder = root.querySelector('.ss-placeholder');
  if (placeholder) {
    placeholder.textContent = i18n[lang].placeholderText;
  }

  const searchInput = root.querySelector('.ss-search input');
  if (searchInput) {
    searchInput.placeholder = i18n[lang].searchPlaceholder;
  }

  const noResult = root.querySelector('.ss-no-results');
  if (noResult) {
    noResult.textContent = i18n[lang].searchText;
  }
}

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


Синхронизация языка с глобальным состоянием приложения

В SPA-приложениях смена языка обычно управляется глобальным стором. Slim Select интегрируется через подписку на изменение языка:

let currentLang = 'en';

function setLanguage(lang) {
  currentLang = lang;
  changeLanguage(lang);
}

document.querySelector('#lang-switch').addEventListener('change', (e) => {
  setLanguage(e.target.value);
});

При использовании state manager (Redux, Zustand, Pinia-подобные модели) смена языка становится реактивной, а Slim Select выступает как визуальный слой, пересобираемый при изменении состояния.


Локализация данных и опций

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

const options = {
  en: [
    { text: 'Apple', value: 'apple' },
    { text: 'Orange', value: 'orange' }
  ],
  ru: [
    { text: 'Яблоко', value: 'apple' },
    { text: 'Апельсин', value: 'orange' }
  ]
};

function rebuildOptions(lang) {
  slim.destroy();

  slim = new SlimSelect({
    select: '#example',
    data: options[lang],
    settings: {
      placeholderText: i18n[lang].placeholderText,
      searchPlaceholder: i18n[lang].searchPlaceholder
    }
  });
}

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


Обновление без потери выбранного значения

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

let selectedValue = null;

function rebuildWithState(lang) {
  const select = document.querySelector('#example');
  selectedValue = select.value;

  slim.destroy();

  slim = new SlimSelect({
    select: '#example',
    data: options[lang],
    settings: i18n[lang]
  });

  slim.set(selectedValue);
}

Метод set используется для восстановления состояния после пересборки.


Асинхронная загрузка переводов

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

async function loadLanguage(lang) {
  const res = await fetch(`/i18n/${lang}.json`);
  const dict = await res.json();

  i18n[lang] = dict;

  slim.destroy();

  slim = new SlimSelect({
    select: '#example',
    settings: i18n[lang]
  });
}

Этот подход позволяет масштабировать систему переводов независимо от фронтенд-кода.


Обработка RTL-языков при смене локали

Для языков с направлением письма справа налево требуется дополнительная синхронизация с DOM:

function applyDirection(lang) {
  const rtlLanguages = ['ar', 'he', 'fa'];

  document.documentElement.setAttribute(
    'dir',
    rtlLanguages.includes(lang) ? 'rtl' : 'ltr'
  );
}

Slim Select автоматически наследует направление текста от родительского контейнера, поэтому достаточно управлять глобальным атрибутом dir.


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

При переключении локали необходимо сбрасывать фильтр поиска, иначе остаётся старый результат:

function resetSearch() {
  const input = document.querySelector('.ss-search input');
  if (input) {
    input.value = '';
    input.dispatchEvent(new Event('input'));
  }
}

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


Комбинированная стратегия обновления

На практике применяется объединённый подход:

  • сохранение выбранного значения
  • сброс поиска
  • пересоздание экземпляра
  • восстановление состояния
  • установка направления текста
function switchLang(lang) {
  const value = document.querySelector('#example').value;

  applyDirection(lang);

  slim.destroy();

  slim = new SlimSelect({
    select: '#example',
    data: options[lang],
    settings: i18n[lang]
  });

  slim.set(value);
  resetSearch();
}

Такой сценарий обеспечивает целостность интерфейса при любом изменении локали.