Edited at

各種小説投稿サイトのルビ記法をJavaScriptで実現する

GitHubでWeb小説書いてる8amjpと申します。

原稿は、基本的にMarkdownで書いてるんですが、厄介なのがルビの扱い。

Markdownでルビを表現することは、今のところできません。かと言って、わざわざruby要素を記述するのも面倒です。

なので、執筆時は各種小説投稿サイトで採用されているルビ記法で記述し、表示するときにJavaScriptでruby要素に変換する、という手法を取ることにしました。

その際に調べたことを書いておきます。


はじめに

HTMLでは、ルビを下記のように表記します。

<ruby>瀬尾絵子<rt>せのおえこ</rt></ruby>

ブラウザでは瀬尾絵子せのおえこという風に表示されます。

で、「瀬尾絵子」の部分をベーステキスト、「せのおえこ」の部分をルビテキストと呼びます。

各種小説投稿サイトでは、この表記を簡略化するために、



  • <ruby>に代わるベーステキストの開始記号


  • <rt></rt>に代わるルビテキストの範囲指定記号

を定義しているわけですね。


各種小説投稿サイトのルビ記法

それでは、各サイトのルールを見ていきましょう。


青空文庫

青空文庫作業マニュアル【入力編】

「4. 青空文庫注記形式」では、ルビの入力方法について以下のように定義されています。


  • 開始記号は全角の縦棒「|」とする。

  • 範囲指定記号は《》(二重山括弧)を使用できる。

  • ただし、ベーステキストが漢字の連続である場合は、開始記号を省略できる。

例: 全方位弾《ブロードキャストパケット》 |PM《プロジェクトマネージャー》

自由度は少ないですが、その分ルールはシンプルです。

ちなみに、《》(二重山括弧)をそのまま表示させることは、青空文庫ではできないらしいです。


小説家になろう/ノベラボ

ルビの振り方

小説家になろうでは、以下のように定義されています。


  • 開始記号は全角の縦棒「|」または半角の縦棒「|」とする。

  • 範囲指定記号は《》(二重山括弧)、()(全角丸括弧)、()(半角丸括弧)を使用できる。

  • ただし、ベーステキストが漢字の連続である場合は、開始記号を省略できる。


    • その際、範囲指定記号に()(全角丸括弧)または()(半角丸括弧)を使用した場合は、ルビテキストはカタカナまたはひらがなである必要がある。



  • 括弧内の文字をルビ扱いにしたくない場合は、括弧の直前に開始記号を付ける。

例: 強制帰還(ループバック)

だそうです。自由な分、ややこしいですね。

なおノベラボも同じルールです。縦書きなのにエライ。


カクヨム/エブリスタ/NOVEL DAYS

ルビや傍点を付ける(カクヨム記法を使う)

カクヨム記法では以下のとおり。


  • 開始記号は全角の縦棒「|」または半角の縦棒「|」とする。

  • 範囲指定記号は《》(二重山括弧)を使用できる。

  • ただし、ベーステキストが漢字の連続である場合は、開始記号を省略できる。

  • 括弧内の文字をルビ扱いにしたくない場合は、括弧の直前に開始記号を付ける。

例: |《翠玉女帝》エクシエラ

ルビのルールは青空文庫とほぼ同じですが、例のように記述すると《》(二重山括弧)をそのまま表示させることもできます。

エブリスタNOVEL DAYSも同じルールです。


ハーメルン

ルビの振り方


  • 開始記号は半角の縦棒「|」とする。省略不可。

  • 範囲指定記号は《》(二重山括弧)を使用できる。

とってもシンプルです。潔い。


マグネット


  • 開始記号は全角の縦棒「|」または半角の縦棒「|」とする。省略不可。

  • 範囲指定記号は《》(二重山括弧)を使用できる。

マグネットのサイトのどこにも記法は明記されていませんが、エディタで試してみた限りではこうでした。


アルファポリス

ヘルプより引用:


※ルビの振り方

サンプル:#宇宙__そら__#

上記の例の場合、「宇宙」と書かれた部分が対象のテキストで、「そら」と書かれた部分がルビになります。


……なんでこんなルールにしたの?とりあえず無視。


魔法のiらんど/野いちご

魔法のiらんど野いちごなんかは、ルビ記法は実装されていない代わりに、一部のHTML要素(タグ)が使えるそうです。

ひょっとしたら、直接ruby要素を記述することで実現できるかもしれません。(未確認)

ケータイ小説、けっこう骨太。


taskey

taskeyのルビ機能。もはや記法じゃないです。これは無理。


ルビ非対応のサイト

note時空モノガタリcomicoノベルmonogatary.comなどはルビそのものが非対応みたいです。残念。


JavaScriptでルビ機能を実装する

さて、上記に挙げた記法のうち、青空文庫/小説家になろう/カクヨムの記法通りにルビを振るJavaScript(jQuery)を作成してみます。

なお、文書全体を対象にする必要もないので、対象はarticle要素内の文字列だけにしています。

$(function(){

$('article').each(function() {
$(this).html(
$(this).html()
/* 半角または全角の縦棒以降の文字をベーステキスト、括弧内の文字をルビテキストとします。 */
.replace(/[\|](.+?)(.+?)》/g, '<ruby>$1<rt>$2</rt></ruby>')
.replace(/[\|](.+?)(.+?))/g, '<ruby>$1<rt>$2</rt></ruby>')
.replace(/[\|](.+?)\((.+?)\)/g, '<ruby>$1<rt>$2</rt></ruby>')
/* 漢字の連続の後に括弧が存在した場合、一連の漢字をベーステキスト、括弧内の文字をルビテキストとします。 */
.replace(/([一-龠]+)(.+?)》/g, '<ruby>$1<rt>$2</rt></ruby>')
/* ただし丸括弧内の文字はひらがなかカタカナのみを指定できます。 */
.replace(/([一-龠]+)([ぁ-んァ-ヶ]+?))/g, '<ruby>$1<rt>$2</rt></ruby>')
.replace(/([一-龠]+)\(([ぁ-んァ-ヶ]+?)\)/g, '<ruby>$1<rt>$2</rt></ruby>')
/* 括弧を括弧のまま表示したい場合は、括弧の直前に縦棒を入力します。 */
.replace(/[\|](.+?)》/g, '《$1》')
.replace(/[\|](.+?))/g, '($1)')
.replace(/[\|]\((.+?)\)/g, '($1)')
);
});
});

こんな感じですかね?もっと短くなりそうなんですけど。どうでしょう。

ちなみに、正規表現におけるひらがな・カタカナ・漢字の指定方法ですが。

ひらがなのゾーンは、「ぁあ」から始まり「ん」で終わるので、[ぁ-ん]と指定します。

一方、カタカナのゾーンは、「ァアィイ」から始まり「ヮワヰヱヲンヴヵヶ」という順番で並んでいます。[ァ-ン]だと「ヴ」とかが漏れちゃうので[ァ-ヶ]と指定します。

漢字は……なんだか定義が難しいんですけど、[一-龠]としておけば支障ない、らしいです。ご指摘があればお願いします。


サンプルあるいは宣伝

このスクリプトの適用例を宣伝しておきます。「Redmineで始める異世界人心掌握術」の冒頭部分なんですけど。

こっちがルビ変換前。

https://github.com/8novels/redmine-fantasy/blob/master/episodes/001.md

こっちがルビ変換後です。

https://8novels.github.io/redmine-fantasy/episodes/001.html


おまけ


漢字として扱いたい文字

漢字ではないけれど、漢字として扱ってほしい文字ってありますよね。例えば、「度々」の「々」、「戦場ヶ原」の「ヶ」とかですね。

こういう場合は、[一-龠々ヶ]という風に個別に追加指定すれば漢字扱いにすることができます。


傍点(圏点)

カクヨムでは、《《それ》》というように、二重山括弧ふたつで囲んだ文字に傍点(圏点)を振ることができます。これをJavaScriptで表現するには、

.replace(/《《(.+?)》》/g, '<strong>$1</strong>')

という風にstrong要素にでも変換するのがいいでしょう。

(ただし、一番最初にreplaceしないと意図した結果にならないので注意です)

それから、スタイルシートで

strong {

-webkit-text-emphasis: filled circle black;
text-emphasis: filled circle black;
}

とでも指定するのがいいでしょう。

……ま、Markdownなら**それ**で表記できるので別にいいんですけど。