【译】你不知道的 Intl.Segmenter API

大家好,这里是大家的林语冰。

免责声明

本文属于是语冰的直男翻译了属于是,仅供粉丝参考,英文原味版请临幸 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.'
// ]

虽然没有 isSentenceLikeisGraphemeLike 属性,但你可以直接计算项目的数量。

支持其他语言

真正的强大之处在于,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,欢迎关注最新动态和订阅前沿资讯。谢谢大家的彼芯,掰掰~

相关推荐
小马哥编程2 小时前
Function.prototype和Object.prototype 的区别
javascript
王小王和他的小伙伴2 小时前
解决 vue3 中 echarts图表在el-dialog中显示问题
javascript·vue.js·echarts
学前端的小朱2 小时前
处理字体图标、js、html及其他资源
开发语言·javascript·webpack·html·打包工具
outstanding木槿2 小时前
react+antd的Table组件编辑单元格
前端·javascript·react.js·前端框架
好名字08213 小时前
前端取Content-Disposition中的filename字段与解码(vue)
前端·javascript·vue.js·前端框架
摇光933 小时前
js高阶-async与事件循环
开发语言·javascript·事件循环·宏任务·微任务
胡西风_foxww4 小时前
【ES6复习笔记】Class类(15)
javascript·笔记·es6·继承··class·静态成员
布兰妮甜4 小时前
使用 WebRTC 进行实时通信
javascript·webrtc·实时通信
艾斯特_4 小时前
JavaScript甘特图 dhtmlx-gantt
前端·javascript·甘特图
飞翔的渴望4 小时前
react18与react17有哪些区别
前端·javascript·react.js