Работа с диакритикой в интерфейсах выбора напрямую влияет на качество поиска и удобство пользователей, использующих языки с акцентами, умляутами, тильдами и другими модификаторами символов. В контексте Slim Select ключевая задача заключается в корректной нормализации строк перед сравнением, чтобы поисковый алгоритм не различал символы с диакритикой и без неё.
В основе поведения поиска Slim Select лежит строковое сопоставление
значений опций с пользовательским вводом. Без дополнительной обработки
символы é, è, ê, ë
будут восприниматься как отличные от e, что приводит к
снижению релевантности результатов.
В Unicode каждый символ с диакритикой может быть представлен либо как единый кодовый пункт (precomposed form), либо как комбинация базового символа и модификатора (decomposed form). Например:
é (U+00E9)e + ́ (U+0065 + U+0301)Для алгоритма поиска это два разных набора байтов, хотя визуально они эквивалентны.
В Slim Select, если не применять нормализацию, поиск по строке:
resume
не найдет:
résumé
что критично для мультиязычных интерфейсов.
Основной механизм унификации строк —
String.prototype.normalize().
На практике используется форма NFD (Canonical Decomposition) или NFKD (Compatibility Decomposition).
function normalizeString(str) {
return str
.normalize('NFD')
.replace(/\p{Diacritic}/gu, '');
}
Здесь выполняются два ключевых шага:
\p{Diacritic}.Результат:
résumé → resume
Slim Select позволяет переопределять поведение поиска через
конфигурацию searchText или кастомные обработчики
фильтрации.
Стандартная модель поиска основана на приведении строк к нижнему регистру и проверке вхождения подстроки. Для корректной работы с диакритикой необходимо расширить этот слой.
new SlimSelect({
select: '#select',
searchText: (search, text) => {
const normalize = (str) =>
str.toLowerCase()
.normalize('NFD')
.replace(/\p{Diacritic}/gu, '');
return normalize(text).includes(normalize(search));
}
});
Такая реализация обеспечивает:
Разные языки демонстрируют различную плотность диакритики:
é, è, ê, ëä, ö, ü, ßñ, á, éğ, ı, şПосле нормализации поведение поиска становится унифицированным:
| Ввод | Оригинал | Совпадение |
|---|---|---|
| cafe | café | да |
| koln | köln | да |
| ano | año | да |
| gross | groß | да |
Нормализация строк — операция не нулевая по стоимости. При большом
количестве опций (тысячи и десятки тысяч элементов) прямое выполнение
.normalize() на каждый ввод может стать узким местом.
Оптимизация заключается в предвычислении нормализованных значений.
const cache = new Map();
function normalizeCached(str) {
if (cache.has(str)) return cache.get(str);
const normalized = str
.toLowerCase()
.normalize('NFD')
.replace(/\p{Diacritic}/gu, '');
cache.set(str, normalized);
return normalized;
}
Применение в Slim Select:
new SlimSelect({
select: '#select',
searchText: (search, text) => {
return normalizeCached(text)
.includes(normalizeCached(search));
}
});
Более эффективный подход — нормализовать данные один раз при инициализации списка.
const data = [
{ text: 'Café', value: '1' },
{ text: 'Résumé', value: '2' }
];
const preparedData = data.map(item => ({
...item,
normalizedText: item.text
.toLowerCase()
.normalize('NFD')
.replace(/\p{Diacritic}/gu, '')
}));
Далее поиск работает только с normalizedText:
new SlimSelect({
select: '#select',
data: preparedData,
searchText: (search, text, data) => {
const normalizedSearch = normalizeCached(search);
return data.normalizedText.includes(normalizedSearch);
}
});
Некоторые языки используют комбинируемые диакритические знаки, которые могут быть:
Например:
ẞ → ss (в некоторых нормализациях)
Форма NFKD дополнительно учитывает совместимость
символов, что может быть важно для немецкого и некоторых
восточноевропейских языков.
Использование Unicode property escapes (\p{Diacritic})
требует поддержки ECMAScript 2018+. В старых окружениях браузеров это
может приводить к ошибкам.
Альтернативная реализация через диапазоны Unicode:
function removeDiacritics(str) {
return str
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '');
}
Диапазон \u0300-\u036f охватывает большинство
комбинируемых диакритических знаков.
Slim Select по умолчанию ориентирован на подстрочное совпадение. После нормализации это поведение становится более предсказуемым, но может привести к расширению результатов.
Например:
input: "res"
matches:
- résumé
- reseda
- response
Для ограничения результата используется дополнительная логика:
return normalize(text).startsWith(normalize(search));
Совмещение двух преобразований:
toLowerCase()создает единый слой сравнения, близкий к «фонетическому» поиску. Это особенно важно для интерфейсов автодополнения.
Порядок операций имеет значение:
В некоторых интерфейсах требуется комбинировать несколько стратегий:
Пример токенизированной фильтрации:
function tokenize(str) {
return normalize(str).split(/\s+/);
}
function matchTokens(search, text) {
const searchTokens = tokenize(search);
const textTokens = tokenize(text);
return searchTokens.every(token =>
textTokens.some(t => t.includes(token))
);
}
В многоязычных системах строки могут содержать одновременно:
Удаление диакритики применяется только к блокам Unicode, относящимся к комбинируемым знакам, не затрагивая остальные скрипты.
Это позволяет избежать повреждения строк вида:
Москва → остается без изменений
São Paulo → Sao Paulo
После внедрения нормализации поиск становится менее чувствительным к ошибкам ввода:
Это приводит к более устойчивому поведению автодополнения и снижает когнитивную нагрузку при поиске элементов в списках Slim Select.