LoginSignup
5
1

Firefox だけ未実装シリーズ:Intl.Segmenter の状況

Posted at

はじめに

この記事は 2023 年の MDN 翻訳 Advent Calendar 向けに作成したものです。

こんにちは。debiru です。連日 MDN 翻訳のアドベントカレンダーを書いていますが、私は Firefox 開発者でもなければ、MDN のコアメンテナでもありません。ただのいちコントリビューターです。

Firefox 開発者ではないので詳しいことは分からず、正確性に欠ける情報をお伝えしてしまうかもしれませんが、今日は私が知り得た Intl.Segmenter の Firefox の実装状況についてお伝えしたいと思います。

JavaScript で文字列を逆順にする関数を書いて下さい

最初に考えるのは、Stringreverse 的なメソッドがないかということです。ありそうなものですが、実は JavaScript には String.prototype.reverse はありません。

ということで、これを実現するには、String を配列的に扱って逆順にする処理を施すということになります。

配列化して逆順にする

function reverse(str) {
  return str.split('').reverse().join('');
}

通常の文字列ならこれでも十分に題意を満たせるのですが、Unicode 文字には色々と種類があって、合字(複数のコードポイントの組み合わせで 1 つの文字を表す文字)が含まれている場合には、これだとうまくいかないケースがあるのです。

考慮すべき文字について

うまくいかないケースとなる文字は以下の Examples にいくつか示されています。

// plain latin alphabet - nothing spectacular
splitter.splitGraphemes('abcd'); // returns ["a", "b", "c", "d"]

// two-char emojis and six-char combined emoji
splitter.splitGraphemes('🌷🎁💩😜👍🏳️‍🌈'); // returns ["🌷","🎁","💩","😜","👍","🏳️‍🌈"]

// diacritics as combining marks, 10 JavaScript chars
splitter.splitGraphemes('Ĺo͂řȩm̅'); // returns ["Ĺ","o͂","ř","ȩ","m̅"]

// individual Korean characters (Jamo), 4 JavaScript chars
splitter.splitGraphemes('뎌쉐'); // returns ["뎌","쉐"]

// Hindi text with combining marks, 8 JavaScript chars
splitter.splitGraphemes('अनुच्छेद'); // returns ["अ","नु","च्","छे","द"]

// demonic multiple combining marks, 75 JavaScript chars
splitter.splitGraphemes('Z͑ͫ̓ͪ̂ͫ̽͏̴̙̤̞͉͚̯̞̠͍A̴̵̜̰͔ͫ͗͢L̠ͨͧͩ͘G̴̻͈͍͔̹̑͗̎̅͛́Ǫ̵̹̻̝̳͂̌̌͘!͖̬̰̙̗̿̋ͥͥ̂ͣ̐́́͜͞'); // returns ["Z͑ͫ̓ͪ̂ͫ̽͏̴̙̤̞͉͚̯̞̠͍","A̴̵̜̰͔ͫ͗͢","L̠ͨͧͩ͘","G̴̻͈͍͔̹̑͗̎̅͛́","Ǫ̵̹̻̝̳͂̌̌͘","!͖̬̰̙̗̿̋ͥͥ̂ͣ̐́́͜͞"]

その他、関連する記事を示しておきます。

考えられる色々な reverse 関数の実装方法

const functions = [
  function reverse_1(str) {
    return str.split('').reverse().join('');
  },
  function reverse_2(str) {
    return [...str].reverse().join('');
  },
  function reverse_3(str) {
    const rev = s => {
      const m = s.match(/^(.)(.*)(.)$/u);
      return m ? m[3] + rev(m[2]) + m[1] : s;
    }
    return rev(str);
  },
  function reverse_4(str) {
    let ret = '';
    for (const segment of new Intl.Segmenter().segment(str)) {
      ret = segment.segment + ret;
    }
    return ret;
  },
];

なお、これらの実装を考える上で、次のツイートを参考にさせていただきました。

これらのコードを CodePen に上げてみました。

上記 CodePen を Chrome で表示した結果です

reverse_1, reverse_2, reverse_3 は合字の絵文字(絵文字に限らないですが)が含まれると reverse 処理に失敗しています。

reverse_4 では Intl.Segmenter の力を借りることで、見事に全てのテストケースで reverse 処理を成功できました。

余談:Intl.Segmenter を使わずに成功させる方法

合字を含めた Unicode 文字をうまいこと 1 文字ずつ分離するためのライブラリが存在しています。

上記のライブラリでは Intl.Segmenter を使わずに実装されています。ではどうしているのかというと、上記のリンクで示している通り、コードポイントごとに条件分岐を地獄のように書いて処理しています。考えられる最もナイーブな実装ということでしょうか。

Intl.Segmenter を使うと、地獄のような実装をせずともやりたいことが実現できるようになります。

Intl.Segmenter とは

をお読みいただだければ概要が分かるでしょう。入力文字列に対して、形態素解析をしたり、文字を解析したりすることができるものです。

Intl.Segmenter の歴史については、次の記事が参考になります。

そんな便利な Intl.Segmenter は、Chrome, Safari, Edge, Opera では 2020 年 11 月から 2021 年 4 月にかけてサポートされ、もちろん Vivaldi でも使えるものの、Firefox だけサポートされていないという状況でした。

ブラウザー互換性テーブル。Chrome, Edge, Opera, Safari がサポートする中、Firefox だけがサポートされていない。

Bugzilla の Intl.Segmenter の導入を扱うレポートも、2020 年頃から実装の見込みがない様子で止まっていました。

それが、なんと 2023 年の 5 月頃から動きが見られ始めました。そして 12 月現在ではなんだかコメントがいっぱいついて、実装が進んでいるような雰囲気が窺えます。

まだ、リリースの目処は立っていないようですが、近々、Firefox でも Intl.Segmenter が使える日がやってきそうに思います。……というお話でした。

さいごに

ど、どうしよう、そろそろ MDN のネタが尽きてきました。2023 年の MDN 翻訳 Advent Calendar は、まだこんなに空いているというのに。もしこの記事を読んでくれている MDN 関係者さんがいたら今からでも全然遅くないので、ぜひアドベントカレンダーに登録して 1 つくらい記事を書いてみてもいいんじゃないでしょうか><

本当はこの記事、もうちょっと正確な実装状況を調べてご報告するつもりでしたが、関連するレポートやら何やらを読んでも実装状況が私には理解できなくてご紹介できませんでした……。

少しでも面白いなと思ってくれたら嬉しいです。というわけで、Intl.Segmenter のお話でした。

おわり。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1