Closed1

Mastodonの文字数測定は何を使っているか

eaieai

Rails側

https://github.com/mastodon/mastodon/blob/11f5a8e54b240d15424bd69deabbaf5742a37723/app/validators/status_length_validator.rb

def countable_length(str)
  str.mb_chars.grapheme_length
end

grapheme_lengthを使っている

grapheme_length

Returns the number of grapheme clusters in the string.
https://api.rubyonrails.org/v6.0.2.2/classes/ActiveSupport/Multibyte/Chars.html#method-i-grapheme_length

'क्षि'.mb_chars.length   # => 4
'क्षि'.mb_chars.grapheme_length # => 3

というわけで書記素クラスタを考慮した感じでやっているらしい

Web側

https://github.com/mastodon/mastodon/blob/11f5a8e54b240d15424bd69deabbaf5742a37723/app/javascript/mastodon/features/compose/components/character_counter.jsx

stringz というライブラリを使っている

stringz

Javascript has a serious problem with unicode. Even ES6 can’t solve the problem entirely since some characters like the new colored emojis are three bytes instead of two bytes. Sometimes even more! "👍🏽".length returns 4 which is totally wrong (hint: it should be 1!). ES6's Array.from tried to solve this, but that even fails: Array.from("👍🏽") returns ["👍", "🏽"] which is incorrect. This library tries to tackle all these problems with a mega RegExp.
https://www.npmjs.com/package/stringz

[..."string"]でうまくバラせないやつもバラせるやつらしい

mega RegExp と書いてあるが、stringzが正規表現を持ってるわけではなく、char-regex を使っている。
つまり、stringzはそのラッパーである。

最近のJS環境なら組み込みのIntl.Segmenterで書記素クラスタを考慮した感じでバラすことができる

このように:

function getLengthWithIntlSegmenter(str) {
  const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
  let count = 0;
  for (const segment of segmenter.segment(str)) {
    count++;
  }
  return count;
}

しかしstringzのベンチマークに組み込んでみたところめちゃくちゃ遅かった

ネイティブだから早いというものでもないらしい

余談だが、sindresorhus[1]による同様のことをするstring-lengthというライブラリは5.xではchar-regexを使っていて、6.xではIntl.Segmenterを使うように書き換えられた。

脚注
  1. https://github.com/sindresorhus node関係でたくさんライブラリを作ってて、それらがたくさんダウンロードされてるすごい人 ↩︎

このスクラップは2023/08/08にクローズされました