はきくま!制作日記:カクヨムの記法を落とし込む

投稿日

カクヨムの記法で書いた小説の本文を、そのまま自作サイトの本文として表示するための変換処理の記録。


その1:本文はテキストエリア+独自記法

本文をリッチエディタにしなかったのは、カクヨムとの原稿の行き来のため。プレーンテキストのまま書いて、表示用の変換は formatLine 一か所で行う。順番が重要。

export function formatLine(line: string) {
  let html = escapeHtml(line); // 最初に本文の < & をエスケープ

  // 縦中横:タグを入れる前に処理する(後だと class 属性の英字まで拾う)
  html = html.replace(/(?<![0-9,.])([0-9]{2,3})(?![0-9,.])/g, '<span class="tcy">$1</span>');
  html = html.replace(/(?<![A-Za-z&;])([A-Za-z])(?![A-Za-z;])/g, '<span class="tcy">$1</span>');

  html = html.replace(/\{\{(.+?)\}\}/g, '<span lang="zh-HK" class="u-ff_cantonese">$1</span>');
  html = html.replace(/《《(.+?)》》/g, '<em class="bouten">$1</em>'); // ルビより先
  html = html.replace(/[||]([^||《》]+?)《(.+?)》/g, "<ruby>$1<rt>$2</rt></ruby>");
  html = html.replace(/([\p{Script=Han}々〆ヶ]+)《(.+?)》/gu, "<ruby>$1<rt>$2</rt></ruby>");
  html = html.replace(/([?!]+)/g, '<span class="mark">$1</span>');
  return html;
}
  • 最初にエスケープしてから、自分で決めた記法だけを HTML にするので、set:html でも安全
  • 傍点 《《》》 はルビ 《》 の二重なので、ルビより先に処理
  • 縦中横の数字は、カンマや小数点に隣接するものを除外(1,000,000 が3桁ずつ分割されるのを防ぐ)
  • 小説の中の広東語は {{ }} で囲んで書く。lang="zh-HK" と広東語専用フォントを当てるクラスを付けた span に変換して、囲んだ部分だけ広東語用のフォントで表示される
  • ルビの親文字判定は \p{Script=Han} にして、広東語の字にも振れるように
  • 自動ルビは直前の漢字の並びだけが親文字になる。「黄金の果実」のように かな を挟む時は | で範囲を指定する(カクヨムと同じ書き方)
  • テキストエリアの値が空のフィールドは、API のレスポンスに項目ごと出てこない。(text ?? "").split("\n") で受ける