Laptop and coffee on a wooden table

EmDashで読了時間が全部「2 min」になる問題

EmDash でブログを書き始めて気づいたのですが、記事の読了時間がすべて「2 min」と表示されます。短い記事も長い記事も全部2分。
全部2分でハードコードされてたりして〜と思って調べてみたら、意外と勉強になったので2分で読めるようにまとめてみます。

原因を探す

読了時間の計算ロジックはソースコードのどこかにあるはず。

Claudeにも手伝ってもらって、src/utils/reading-time.ts というファイルと判明。

const WORDS_PER_MINUTE = 200;
const WHITESPACE_REGEX = /\s+/;

export function getReadingTime(content: PortableTextBlock[] | undefined): number {
    const text = extractText(content);
    const wordCount = text.split(WHITESPACE_REGEX).filter(Boolean).length;
    const minutes = Math.ceil(wordCount / WORDS_PER_MINUTE);
    return Math.max(1, minutes);
}

原因は text.split(WHITESPACE_REGEX)   スペースで区切って「単語数」を数えていました!

英語なら This is a pen は4単語としてカウントされますが、日本語の これはペンです だとスペースがないので、文章全体で「1単語」扱いとなり、うまくカウントできていない模様。

CJKという概念

こういった同様のケースを調べていく中で「CJK」という言葉を知りました。
Chinese, Japanese, Korean の頭文字で、スペースで単語を区切らない言語群の文脈で使われていました。
そんな分類をされるぐらいスペースを使わない言語はレアなんですかね。

とりあえず修正

CJK文字が含まれている場合は「単語数」ではなく「文字数」で読了時間を計算します。
Hugo(Go製の静的サイトジェネレーター)にも同様のCJK対応が入っていて、それを参考に500文字/分とします。
英語部分は従来どおり単語数ベースで計算し、両方を合算します。

んじゃ、Claude Codeさんよろしく〜っとこんな感じに修正となりました。

const WORDS_PER_MINUTE = 200;
const CJK_CHARS_PER_MINUTE = 500;
const WHITESPACE_REGEX = /\s+/;
const CJK_REGEX = /[\u3000-\u9fff\uf900-\ufaff]/;

export function getReadingTime(content: PortableTextBlock[] | undefined): number {
    const text = extractText(content);

    // CUSTOM: CJK text uses character count instead of word count
    if (CJK_REGEX.test(text)) {
        const cjkCount = (text.match(/[\u3000-\u9fff\uf900-\ufaff]/g) || []).length;
        const nonCjkText = text.replace(/[\u3000-\u9fff\uf900-\ufaff]/g, " ");
        const wordCount = nonCjkText.split(WHITESPACE_REGEX).filter(Boolean).length;
        const minutes = Math.ceil(cjkCount / CJK_CHARS_PER_MINUTE + wordCount / WORDS_PER_MINUTE);
        return Math.max(1, minutes);
    }

    const wordCount = text.split(WHITESPACE_REGEX).filter(Boolean).length;
    const minutes = Math.ceil(wordCount / WORDS_PER_MINUTE);
    return Math.max(1, minutes);
}

修正後、ビルドし直したら無事それっぽい読了時間が表示されるようになりました。

おまけ:Issue を出してみた

せっかくなので、EmDash の GitHub リポジトリ(emdash-cms/emdash)に Bug Report として Issue を出してみました。
大昔にメールでバグレポートを送ったことはありますが初のOSS Issue 投稿でドキドキです。

出してから7時間後には、別の方が PR を出してくれて、テスト付きの本格的な修正として対応されていました。
自分の修正とほぼ同じ内容(CJK文字を500chars/minでカウント)に加え、さらにひらがな・カタカナ・ハングルの個別検出やテストケースの追加、Cloudflareテンプレートとデモへの同期まで含めた丁寧な内容でした。
地味機能のニッチ言語向け修正でしたが活きの良いOSSのスピード感に感謝🙏

No comments yet