ねこものがたり

いちにちいっぽ

JSで日本語文字列の「何文字目」を扱う時はIntl.Segmenterが便利

この記事のきっかけ

仕事中、Next.jsで描画する画面で「ある文字列の20文字目までは表示したい、21文字目以降は '...' で省略したい」という仕様がありました。 こういう要件は度々あって、すでにプロジェクト内には String.prototype.substring()を利用したそれ用の関数が用意されています。しかし、String.prototype.substring()ってどんな文字で期待通りに動くのかなとふと疑問になって調べていくうちに、やはりうまく動かない場合があること、Intl.Segmenterがに合わせた処理ができることを知ったので書き留めておきます。

String.prototype.substring()

String.prototype.substring()の詳しいことはMDNに任せるとして、例えばこんなふうに使うことができます。

const str = "こんにちは"
// UTF-16のcode unit単位で、strの0番目から1番目までを表示する
console.log(str.substring(0, 1))
// => 'こ'

String.prototype.substring() で困ったこと

基準がUTF-16のcode unitなので、1文字で2ユニット分ある文字だと人間の直感的な期待値と戻り値が変わってきます。以下のように、ベースとなる文字とは異なる文字が戻り値に含まれていたり、文字化けのようになったりします。

const str = "𩸽は美味しい"
console.log(str.substring(0, 1)) // => �
console.log(str.substring(0, 2)) // => 𩸽

絵文字でも同じことが起こります。(絵文字がコードブロック等では文字化けしてしまうのでスクショ貼っておきます)

Intl.Segmenterを利用する

この問題を解決するにはIntl.Segmenterがあるようです。

MDNでのIntl.Segmenter() constructorの説明に

The Intl.Segmenter object enables locale-sensitive text segmentation, enabling you to get meaningful items (graphemes, words or sentences) from a string.

とあるように、オプションでロケールを渡すことができて、そのロケールに合わせて文字列をセグメントに分割したオブジェクトを生成してくれるのがIntl.Segmenterです。

インスタンスメソッドIntl.Segmenter.prototype.segment()に処理したい文字列を渡すと、分割した結果を得ることができます。

const str = "𩸽は美味しい"

const segmenter = new Intl.Segmenter('Ja-jp', { granularity: 'grapheme' }); // 人間から見た1文字ずつで処理
console.table(Array.from(segmenter.segment(str)))

出力は次のようになります。

(index ) segment index input
0 𩸽 0 𩸽は美味しい
1 2 𩸽は美味しい
2 3 𩸽は美味しい
3 4 𩸽は美味しい
4 5 𩸽は美味しい
5 6 𩸽は美味しい

このテーブルの(index)がArrayとしての順番で、Intl.Segmenterが持っている方のindexはコードユニットでカウントした時に何番目かという値になっているため、「人間が見たときの2文字目」を取得したい場合はArray.from(segmenter.segment(str))[1]で取得できました!

また絵文字も問題なく動きそうです。(👨‍👩‍👧‍👧がコードブロック等では文字化けしてしまうのでスクショ貼っておきます)

その他

本題から逸れるのでこのエントリからは書いてないんですが、MDNのIntlのページを読んでいると、色々な工夫や「そんなふうに使うといいのか」というものがあって読んでいて楽しいです。(別件で、西暦を和暦に変換したい時にもIntlにお世話になりました)

『行動を変えるデザイン』を読みました

平日始業前に読書をする - ねこものがたりで少し触れた、『行動を変えるデザイン ―心理学と行動経済学をプロダクトデザインに活用する』を読み終えました。

www.oreilly.co.jp

この本を読んだ動機・きっかけ

仕事でプロダクト開発している時も、最近やっているるびまの活動でも、それを受け取ってくれる人からのフィードバックをもらった時はすごく嬉しいです。 フィードバックがなかったとしても、人々に何か少しでも良い影響を生み出していたら、幸せなことだなと思います。

しかし同時に、現状私の中でそれはすごく感覚的なものでしかないため、特に仕事においては「もっと狙って、勝ちに行って、ユーザーに良い価値を届けたい」というようなことを課題として抱いていました。

そんな中たまたま見かけて「これは面白そうだ」と思って手を取ってみました。

感想

一番印象的だったのは、生み出したい成果の設定についてです。 正直な話、私は「行動変容」と聞いて「変わることそれ自体」とが目的な認識があったなと、この本を読んで自覚しました。

例えば、健康管理をアプリを開発していてユーザーが運動習慣を身につけることを目指したい成果は、 私の元々の感覚だと「ユーザーが毎日何かしらの運動をするようになる」くらいのイメージで 本に書いてある内容だと「ユーザーが1日1万歩歩くようになる」のような感じです。

具体的で、数値に表せる成果、心理的なものではなくて実際のアウトプットが伴うものを成果にするのがポイントです。

「運動が楽しいと思ってほしい」のような方向に流れがちな自分としては、「行動変容」の観点でも成果の設定はそのような考え方で望むものなんだなと学べたのがよかったです。

他に考えたとことしては、プロダクト全体のデザインがこの本のテーマ・内容だったのですが、プロダクトの部分的な改善をしたい時にも参考にできそうだということです。 会社で運用しているプロダクトでも、「本当はこういう操作をしてほしいが、そうしているユーザーは少ない」のような課題があってそこに行動変容をもたらしたい場合があります。 そうした、小規模な場合でも、この本に書いてある考え方をフレームワークとしてみると、手探りでやる場合とは違うことができるのではないかと感じています。

最後に

最初から書いてある通りにうまくやる・うまくいくことは難しいかもしれませんが、課題整理、思考整理の際の指標の1つにしていきたい、実践することで行動変更について理解を深めたり経験値を貯めていきたいと思える本でした。