大家好,这里是大家的林语冰。
免责声明
本文属于是语冰的直男翻译了属于是,仅供粉丝参考,英文原味版请临幸 Polypane 官方博客 。
浏览器的 Intl
API 有一大坨关于编辑和格式化文本与数字的功能。在填写 HTML 现状调查 时,我邂逅了一个前所未闻的知识盲区:Segmenter
API。在研究它时,我发现它允许你根据文本语言将文本碎片化为片段。
这抓住了我的眼球,因为我最近实现了一个 文本测量功能,当时我十分抓狂,我殚精竭虑也只能支持西方语言使用拉丁字符和空格来分隔单词。
为了支持其他语言,我花了大量时间在 npm 包和正则等方案海量试错,虽然但是,最终我还是骑猫难下,两手一摊。
开始我们的表演。
Polypane 中 segmenter API 的正确打开方式
每当你在 Polypane 中选择文本并右键单击打开上下文菜单时,我们都会显示已选文本的字符、单词和 emoji。
这样做可以快速检查你是否为推文编写了正确的字符数,或者你是否为博客文章编写了正确的字数。
设置 Segmenter API
首先分享一个噩耗。Segmenter
API 目前能且仅能支持 Safari@">= 14.1" 和 Chromium@">= 87",但在 FireFox 中尚不可用。这里有一个臃肿的 polyfill(功能补丁)。
为了使用 Segmenter
API,首先我们得创建一个实例。这需要两个参数,即一种语言和一组选项。
js
const segmenter = new Intl.Segmenter('en', {
granularity: 'word'
})
选择对象有两个可选属性:
localeMatcher
属性的值可以是'lookup'
或'best fit'
(默认值),其中'lookup'
使用特定的匹配算法(BCP 47),'best fit'
使用更宽松的算法,该算法尝试尽可能近似匹配浏览器或 OS(操作系统)通用的语言。granularity
属性的值可以是'grapheme'
(默认值)、'word'
或'sentence'
。这决定了返回哪些类型的segment
(片段)。grapheme
(字母)是默认值,它们是你所见即所得的字符。
然后,你传递想要切片的文本调用 segmenter.segment()
,这会返回一个迭代器,你可以使用 Array.from()
将其转化为数组:
js
const segments = segmenter.segment('This has four words!')
Array.from(segments).map(segment => segment.segment)
// ['This', ' ', 'has', ' ', 'four', ' ', 'words', '!']
迭代器中的每一项都是一个对象,每个对象根据 granularity
(粒度)有不同的值,但至少:
segment
属性是segment
的实际文本。index
属性是原始文本中segment
的索引,比如它的起始位置。input
属性是原始文本。
若 granularity
属性的值是 word
,它还有:
isWordLike
属性是一个布尔值,若segment
是单词或类似单词,则其值为true
。
你可能瞄一眼数组然后说,"拜托,那些空白字符并不是单词,对不?"你可能说得对。为了获取单词,我们需要过滤数组:
js
const segments = segmenter.segment('This has four words!')
Array.from(segments)
.filter(segment => segment.isWordLike)
.map(segment => segment.segment)
// ['This', 'has', 'four', 'words']
现在我们知道这句话实际有多少个单词了。
除了像上述例子中的单词,你还可以要求它切片句子,它知道哪些句点结束句子,哪些句点用于数字,比如千位分隔符:
js
const segmenter = new Intl.Segmenter('en', {
granularity: 'sentence'
})
const segments = segmenter.segment(
'This is a sentence with the value 100.000 in it. This is another sentence.'
)
Array.from(segments).map(segment => segment.segment)
// [
// 'This is a sentence with the value 100.000 in it.',
// 'This is another sentence.'
// ]
虽然没有 isSentenceLike
和 isGraphemeLike
属性,但你可以直接计算项目的数量。
支持其他语言
真正的强大之处在于,Intl.Segment()
API 不仅能且仅能根据空格或句点分割字符串,而且为每种语言使用专属算法。这意味着该 API 还可以切片没有空格的语言的单词,比如日语:
js
const segmenter = new Intl.Segmenter('ja', {
granularity: 'word'
})
const segments =
segmenter.segment('これは日本語のテキストです')
Array.from(segments).map(segment => segment.segment)
// ['これ', 'は', '日本語', 'の', 'テキスト', 'です']
现在平心而论,我不懂日语所以我不知道这种切片正确与否,但它看起来令人信服。
对比字符串分隔或正则,这要准确得多,而且可以正确处理非拉丁字符和标点符号,需要更少的代码,并且不需要额外的检查和测试。浏览器可以理解一大坨语言,所以你大可不必为此担心。
深入了解 Intl API
Intl
API 提供了一大坨有用的工具,用于创建自然语言结构,包括但不限于列表、日期、金钱、数字等。总而言之,它不难用,但它使用了一大坨陌生的精确术语(举个栗子,"grapheme(字母)" 而不是 "character(字符)",因为后者太模糊了),所以可能入门就成为国家退堂鼓一级表演大师。如果你从未使用过它,请临幸 MDN 的 Intl API。
至于 Polypane,你可以相信下个版本将具有跨语言的文本测量选项,还可以告诉你已选文本中的句子数量。
学废了的小伙伴可以点赞给语冰打 call,欢迎关注最新动态和订阅前沿资讯。谢谢大家的彼芯,掰掰~