Фильтрация списка строк на основе пользовательской локали

При работе над проектом JavaScript с AngularJS 1.6 у меня есть список строк, которые я хотел бы фильтровать. Например, предположим, что мой список содержит раббол, сиганю, нидо и тубо.

При фильтрации строк на испанском языке, если бы я отфильтровал "u", я ожидал появления как сигаью, так и тубо, что было бы самым естественным результатом для испанца. Однако это не так на немецком языке - u и ü - разные буквы, и, таким образом, немецкий не захочет видеть сигареты в списке. Поэтому я ищу способ настроить фильтрацию списка в пользовательской локали.

У меня есть объект, содержащий множество диакритических знаков, например:

diacritics["á"] = "a";
diacritics["ü"] = "u";
// and so on...

Вот как выглядит мой код фильтрации:

function matches(word, search) {
    var cleanWord = removeDiacritics(word.toLowerCase());
    var cleanSearch = removeDiacritics(search.toLowerCase());
    return cleanWord.indexOf(cleanSearch) > -1;
}

function removeDiacritics(word) {
    function match(a) {
        return diacritics[a] || a;
    }
    return text.replace(/[^\u0000-\u007E]/g, match);
}

Вышеприведенный код просто удаляет все диакритики, поэтому я думал, чтобы он знал о локали пользователя. Таким образом, я изменил функцию match() на это:

function match(a) {
    if (diacritics[a] && a.localeCompare(diacritics[a] === 0) {
        return diacritics[a];
    }
    return a;
}

К сожалению, это не работает. Функция localeCompare возвращает те же значения при сравнении "u" и "ü" с немецкими и испанскими локалями, так что это не был ответ здесь. Я просмотрел ссылку для метода localeCompare и попробовал варианты использования и чувствительности, но они, похоже, не помогают здесь.

Как я могу настроить мой код для этого? Есть ли библиотека, которая может справиться с этим правильно для меня?

Ответ 1

Я хотел бы получить локализацию пользователя непосредственно из браузера через navigator (src), объект, представляющий пользовательский агент

var language = navigator.language;

Это присвоит language код языковой версии браузера пользователя, в моем случае en-US. Я нашел этот сайт полезен для поиска кода локали для тестирования других регионов мира.

Моя функция strFromLocale сравнима с вашей функцией removeDiacritics:

function strFromLocale(str) {
    function match(letter) {
        function letterMatch(letter, normalizedLetter) {
            var location = new Intl.Collator(language, {usage: 'search', sensitivity: 'base' }).compare(letter, normalizedLetter);
            return (location == 0)
        }
        normalizedLetter = letter.normalize('NFD').replace(/[\u0300-\u036f]/gi, "")
        if ( letterMatch(letter, normalizedLetter) ) {
            return normalizedLetter;
        } else {
            return letter;
        }
    }
    return str.replace(/[^\u0000-\u007E]/g, match);
}

Обратите внимание на строку с Intl.Collator (src). Эта линия сравнивает диакритику с нормированной буквой диакритики и проверяет данный алфавит языка для позиционных различий. Поэтому:

/* English */
new Intl.Collator('en-US', {usage: 'search', sensitivity: 'base' }).compare('u', 'ü');
>>> 0

/* Swedish */
new Intl.Collator('sv', {usage: 'search', sensitivity: 'base' }).compare('u', 'ü');
>>> -1

/* German */
new Intl.Collator('de', {usage: 'search', sensitivity: 'base' }).compare('u', 'ü');
>>> -1

Как вы можете видеть в функции letterMatch, она возвращает true тогда и только тогда, когда результат Intl.Collator равен 0, что указывает на отсутствие позиционных отличий буквы в алфавите этого языка, что означает безопасно заменять.

С этим, вот некоторые тесты функции strFromLocale:

var language = navigator.language; // en-US
strFromLocale("cigüeña");
>>> ciguena

var language = 'sv' // Swedish
strFromLocale("cigüeña");
>>> cigüena

var language = 'de' // German
strFromLocale("cigüeña");
>>> cigüena

var language = 'es-mx' // Spanish - Mexico
strFromLocale("cigüeña");
>>> cigueña

Ответ 2

Вероятно, вы ищете библиотеку ECMA 6 Intl. Это позволит вам настроить порядок сортировки на основе языка, например:

// in German, ä sorts with a
console.log(new Intl.Collator('de').compare('ä', 'z'));
// → a negative value

// in Swedish, ä sorts after z
console.log(new Intl.Collator('sv').compare('ä', 'z'));
// → a positive value

Опция sensitivity: 'base' будет автоматически сортироваться с/без диакритики.

// in German, ä has a as the base letter
console.log(new Intl.Collator('de', { sensitivity: 'base' }).compare('ä', 'a'));
// → 0

// in Swedish, ä and a are separate base letters
console.log(new Intl.Collator('sv', { sensitivity: 'base' }).compare('ä', 'a'));
// → a positive value

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