При работе с автодополнением на основе Awesomplete в среде с многоалфавитными наборами данных основная сложность возникает не в рендеринге списка, а в нормализации и сравнении строк. Разные системы письма обладают различной степенью формальной и фонетической совместимости, а стандартное посимвольное сравнение JavaScript не учитывает ни диакритические знаки, ни особенности регистровой чувствительности, ни варианты представления символов Unicode.
Любая реализация поиска в Awesomplete, работающая с несколькими алфавитами, должна начинаться с приведения строк к единой нормальной форме Unicode. В противном случае визуально идентичные строки могут иметь разные бинарные представления.
На практике используется NFC или NFD, но
для поисковых сценариев чаще применяется NFKD, так как он
дополнительно разлагает совместимые символы:
function normalize(str) {
return str
.normalize('NFKD')
.toLowerCase();
}
Такой слой позволяет корректно обрабатывать случаи с диакритикой:
é → e + ́, что упрощает последующее
сравнение.
Однако одной нормализации недостаточно, поскольку разные языки требуют специфических правил фильтрации.
По умолчанию Awesomplete использует простое сравнение подстроки. В многоалфавитной среде это приводит к некорректному поведению: латиница с диакритикой перестаёт совпадать с базовой формой, кириллица и латиница обрабатываются одинаково грубо, а арабские и ивритские строки требуют иной логики направления и сегментации.
Расширение фильтра реализуется через опцию filter:
Awesomplete.prototype.filter = function (text, input) {
const nText = normalize(text);
const nInput = normalize(input);
return nText.includes(nInput);
};
Такой подход унифицирует сравнение, но остаётся поверхностным, поскольку не учитывает транслитерацию и фонетические соответствия.
При работе с данными, где одновременно присутствуют кириллица и
латиница, возникает необходимость поиска по фонетическому эквиваленту.
Например, ввод kazan должен находить
Казань.
Решение заключается в предварительном преобразовании строки в транслитерированную форму:
const map = {
'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g',
'д': 'd', 'е': 'e', 'ё': 'e', 'ж': 'zh',
'з': 'z', 'и': 'i', 'й': 'y', 'к': 'k'
};
function transliterate(str) {
return str
.toLowerCase()
.split('')
.map(ch => map[ch] || ch)
.join('');
}
И затем комбинированный фильтр:
Awesomplete.prototype.filter = function (text, input) {
const t1 = transliterate(normalize(text));
const t2 = transliterate(normalize(input));
return t1.includes(t2);
};
Такой слой позволяет объединить разные системы письма в единое поисковое пространство.
При использовании арабского или иврита возникает проблема не только сравнения, но и визуального отображения. Awesomplete по умолчанию не управляет направлением текста, поэтому список может отображаться корректно, но ввод и подсветка совпадений требуют CSS-адаптации.
Ключевой параметр — direction:
.awesomplete {
direction: rtl;
text-align: right;
}
Однако этого недостаточно, если данные смешанные. В таких случаях направление задаётся на уровне элемента:
function detectRTL(text) {
return /[\u0591-\u07FF]/.test(text);
}
И применяется динамически:
input.addEventListener('input', function () {
input.dir = detectRTL(input.value) ? 'rtl' : 'ltr';
});
Алгоритм сортировки по умолчанию в Awesomplete не учитывает языковую
специфику. Для корректной работы с Unicode требуется использование
localeCompare.
Awesomplete.prototype.sort = function (a, b) {
return a.label.localeCompare(b.label, 'ru');
};
Для мультиалфавитных данных предпочтительно использовать нейтральный режим:
Awesomplete.prototype.sort = function (a, b) {
return a.label.localeCompare(b.label, undefined, {
sensitivity: 'base'
});
};
Это снижает влияние диакритики и различий регистра.
Стандартная подсветка Awesomplete опирается на индекс подстроки. В Unicode-контексте это может приводить к разрыву символов в комбинированных графемах.
Более устойчивый вариант — подсветка по нормализованной строке с сохранением исходного текста:
Awesomplete.prototype.item = function (text, input) {
const html = text.label.replace(
new RegExp(input, 'i'),
match => `<mark>${match}</mark>`
);
const li = document.createElement('li');
li.innerHTML = html;
return li;
};
Однако в многоалфавитной среде необходимо использовать нормализованный input:
const safeInput = normalize(input);
И сопоставлять уже через промежуточные индексы, иначе визуальное выделение может расходиться с логикой фильтра.
JavaScript-строки оперируют UTF-16 кодовыми единицами, а не графемами. Это приводит к ошибкам при работе с эмодзи и сложными символами (например, индийские слоги или комбинированные диакритики).
Для корректного разбиения используется
Intl.Segmenter:
const segmenter = new Intl.Segmenter('und', { granularity: 'grapheme' });
function splitGraphemes(str) {
return [...segmenter.segment(str)].map(s => s.segment);
}
Это позволяет избегать разрыва символов при подсветке и сравнении.
При работе с разными алфавитами важно стандартизировать структуру входных данных. Awesomplete допускает как строки, так и объекты:
const list = [
{ label: 'Москва', value: 'moscow' },
{ label: 'München', value: 'munich' },
{ label: 'القاهرة', value: 'cairo' }
];
В таких случаях нормализация должна применяться только к
label, сохраняя value без изменений.
function prepareItem(item) {
return {
label: item.label,
value: item.value,
norm: normalize(transliterate(item.label))
};
}
И фильтр работает уже по norm:
Awesomplete.prototype.filter = function (text, input) {
const nInput = normalize(transliterate(input));
return text.norm.includes(nInput);
};
Сценарий, где пользователь вводит комбинации латиницы и кириллицы,
требует отдельной стратегии. Простое includes становится
недостаточным, поскольку токены принадлежат разным системам письма.
Решением является токенизация с разделением по Unicode-блокам:
function tokenize(str) {
return str.match(/[\p{L}\p{N}]+/gu) || [];
}
Далее поиск осуществляется по пересечению токенов:
function match(item, input) {
const iTokens = tokenize(normalize(input));
const tTokens = tokenize(item.norm);
return iTokens.every(t =>
tTokens.some(x => x.includes(t))
);
}
В стабильной конфигурации Awesomplete в среде Unicode опирается на несколько последовательных уровней обработки:
localeCompareТакая композиция позволяет Awesomplete работать не как простое автодополнение по подстроке, а как адаптивный поисковый слой, устойчивый к различиям письменных систем и кодировочных представлений.