根据用户语言环境过滤字符串列表

11
当我在使用AngularJS 1.6进行JavaScript项目开发时,我有一个字符串列表需要过滤。例如,假设我的列表包含árbolcigüeñanidotubo
在对西班牙语字符串进行过滤时,如果我筛选“u”,我希望cigüeñatubo都会出现,这对于西班牙人来说是最自然的结果。但是,在德语中不是这种情况-u和ü是不同的字母,因此德国人不想在列表中看到cigüeña。因此,我正在寻找一种使我的列表过滤意识到用户所在地区的方法。
我碰巧有一个包含许多变音符的对象,例如:
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方法的参考并尝试了使用和敏感度选项,但它们似乎在这里没有什么帮助。
我该如何调整我的代码使其起作用?是否有任何库可以正确处理这个问题?
2个回答

4

我会通过 navigator (src) 直接从浏览器获取用户的语言环境,
其中 navigator 是代表用户代理的对象。

var language = navigator.language;

这将会为language赋值用户浏览器的语言代码,例如我的是en-US。我发现this site有助于查找测试其他地区的语言代码。
我的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.Collatorsrc)的那一行。该行将变音符与规范化字母进行比较,并检查给定语言的字母表中的位置差异。因此:
/* 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函数中所看到的,只有当Intl.Collator的结果为0时,它才返回true,这表示该语言字母表中没有字母的位置差异,这意味着可以安全地进行替换。
有了这个,下面是对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

1
我认为你最后的代码片段中有一个小错误。我认为“location”变量应该被称为“language”。除此之外,它是完美的! - unpollito
1
@unpollito 哎呀,现在已经修好了 - Cole

1
你可能正在寻找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

在填充UI小部件之前,您可以将列表按正确顺序排序。


很遗憾,这不起作用。这与我在localeCompare()部分提到的基本相同:当比较“u”和“ü”时,在西班牙语和德语中结果是相同的。这在Collator和localeCompare()中都会发生,这并不奇怪,因为它们是同一API的一部分。 - unpollito
阅读Cole的答案后,我意识到我和你的解决方案中都缺少了同时使用“search”和“base”敏感度的方法。一旦我们这样做,排序器就可以意识到西班牙语和德语中“u”和“ü”的区别,如果我们省略“usage”,这种区别将不会发生。 - unpollito

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接