Поддержка Unicode

Работа автодополнения в Awesomplete напрямую связана с тем, как библиотека сравнивает строки, фильтрует список и отображает результаты. При переходе от латинских наборов данных к многоязычным источникам основная сложность возникает из-за особенностей Unicode: различий в нормализации, наличии диакритических знаков, разных системах письма и неоднозначного сравнения символов.


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

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

  • «café» может быть записано как единый символ é
  • или как комбинация e + ́ (акцент)

Без нормализации такие строки не будут равны при прямом сравнении.


Нормализация строк и её значение

Для корректной работы поиска в Awesomplete необходимо привести все строки к единому виду. Чаще всего используется Unicode Normalization Form C (NFC), которая объединяет составные символы.

function normalize(str) {
    return str.normalize("NFC");
}

Однако в задачах поиска часто требуется не только нормализация, но и устранение диакритических знаков:

function stripDiacritics(str) {
    return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}

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

  • «café»
  • «cafe»

как эквивалентные строки.


Проблема регистров и локалей

Обычное toLowerCase() недостаточно в многоязычных системах. В Unicode существуют языки, где регистровая трансформация зависит от локали.

Например:

  • Турецкий язык различает I и İ
  • Немецкий может использовать ß, который в некоторых контекстах заменяется на ss

Более корректный подход:

function normalizeCase(str) {
    return str.toLocaleLowerCase("en");
}

Для универсальных решений часто комбинируют:

function prepare(str) {
    return stripDiacritics(
        str.normalize("NFC").toLocaleLowerCase("en")
    );
}

Встраивание Unicode-обработки в фильтрацию

В Awesomplete фильтрация списка выполняется через функцию filter. По умолчанию используется простое сравнение начала строки, но для Unicode это поведение часто требуется заменить.

Базовая настройка:

new Awesomplete(input, {
    list: [...],
    filter: Awesomplete.FILTER_CONTAINS
});

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

function unicodeFilter(text, input) {
    const t = prepare(text);
    const i = prepare(input);

    return t.indexOf(i) !== -1;
}

Сравнение строк через localeCompare

Для более точной поддержки различных языков используется localeCompare. Он учитывает правила конкретной локали:

function unicodeCompare(a, b) {
    return a.localeCompare(b, "en", { sensitivity: "base" });
}

Параметр sensitivity: "base" позволяет игнорировать регистр и диакритические знаки, что особенно важно при поиске в глобальных каталогах.


Unicode и поведение автодополнения

Внутренний механизм Awesomplete обычно работает с предположением, что строки уже приведены к сопоставимому виду. Однако при подключении внешних API или динамических источников данных это предположение нарушается.

Типичные проблемы:

  • смешение NFC и NFD строк
  • несогласованность регистра
  • разные алфавиты в одном списке
  • эмодзи как часть данных
  • комбинированные символы (например, математические или фонетические знаки)

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

Unicode включает эмодзи, которые могут занимать несколько кодовых точек. Это влияет на:

  • длину строки
  • позицию совпадения
  • разбиение на символы

Пример:

"?".length // 2 или больше в зависимости от кодировки

Для корректной работы поиска важно не полагаться на .length, а использовать посимвольную итерацию:

for (const char of str) {
    console.log(char);
}

Поддержка кириллицы и смешанных алфавитов

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

Проблемы возникают при смешанных данных:

  • латиница + кириллица
  • транслитерация
  • разные кодировки источников

Для унификации используется нормализация + приведение к единому алфавитному представлению:

function normalizeMultilang(str) {
    return stripDiacritics(
        str.normalize("NFC")
    ).toLocaleLowerCase("en");
}

Кастомизация источников данных с Unicode

При работе с динамическими источниками важно нормализовать данные до передачи в Awesomplete:

fetch("/api/cities")
    .then(r => r.json())
    .then(data => {
        awesomplete.list = data.map(item => ({
            label: item.name,
            value: normalizeMultilang(item.name)
        }));
    });

Это предотвращает расхождение между отображаемым значением и поисковым индексом.


Устойчивый Unicode-пайплайн

Корректная схема обработки данных обычно включает несколько этапов:

  1. NFC-нормализация
  2. удаление диакритики
  3. приведение регистра
  4. унификация локали
  5. применение фильтра поиска
function pipeline(str) {
    return str
        .normalize("NFC")
        .toLocaleLowerCase("en")
        .replace(/[\u0300-\u036f]/g, "");
}

Особенности сравнения префиксов

Стандартное автодополнение часто опирается на проверку начала строки. В Unicode это может давать неожиданные результаты при комбинированных символах.

Корректный подход:

function startsWithUnicode(text, input) {
    return pipeline(text).startsWith(pipeline(input));
}

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


Итоговая модель поведения Unicode в автодополнении

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