ちまちま小説書いてるサイトでPHP製のMarkdownパーサーを使用しているがMarkdownにはルビ記法ないのなんでなんだ……という苦しみからクライアントサイドで実装(多分サーバーサイドでやるほうが良いと思う、多分)。
実装方法も考え方もほぼほぼ次の参考記事の通りです。感謝しかない。自分はほんのちょっと変更して、jQuery使わず他にpixiv記法を追加した。
参考:各種小説投稿サイトのルビ記法をJavaScriptで実現する - Qiita
実装例
script.js
const rubyTemplate = "<ruby>$1<rt>$2</rt></ruby>";
const rubyRegexList = [
{ pattern: /[\||](.+?)《(.+?)》/g, replacement: rubyTemplate },
{ pattern: /[\||](.+?)((.+?))/g, replacement: rubyTemplate },
{ pattern: /[\||](.+?)\((.+?)\)/g, replacement: rubyTemplate },
{ pattern: /\[\[rb:(.+?) > (.+?)\]\]/g, replacement: rubyTemplate },
{ pattern: /([\p{sc=Han}]+)《(.+?)》/gu, replacement: rubyTemplate },
{ pattern: /([\p{sc=Han}]+)(([\p{sc=Hiragana}\p{sc=Katakana}ー~]+?))/gu, replacement: rubyTemplate },
{ pattern: /([\p{sc=Han}]+)\(([\p{sc=Hiragana}\p{sc=Katakana}ー~]+?)\)/gu, replacement: rubyTemplate },
{ pattern: /[\||]《(.+?)》/g, replacement: "《$1》" },
{ pattern: /[\||]((.+?))/g, replacement: "($1)" },
{ pattern: /[\||]\((.+?)\)/g, replacement: "($1)" }
];
const rubyRegexMapList = rubyRegexList.map(({ pattern, replacement }) => ({
pattern: new RegExp(pattern),
replacement
}));
const replaceRubyTags = (str) => {
return rubyRegexMapList.reduce((acc, { pattern, replacement }) => {
return acc.replace(pattern, replacement);
}, str);
}
const updateArticle = (el) => {
const replacedHtml = replaceRubyTags(el.innerHTML);
el.innerHTML = replacedHtml;
}
export default updateArticle;
index.html
<script type="module">
import updateArticle from './script.js';
const articleBody = document.getElementById("article-body");
updateArticle(articleBody);
</script>
ESModuleとかでimportして使えるようにしたかったのでこんな感じでどうだろう。
正規表現について
正規表現のマッチングには Unicode プロパティエスケープ - JavaScript | MDN を使うようにした。
カタカナがややこしいことになってるんですが、まあ自分用なので多少妥協しました。
- 参考:JavaScriptで平仮名(ひらがな)を判定する - Qiita
- 参考:
\p{sc=Katakana}
- Unicode Utilities: UnicodeSet - 参考:
:Script_Extensions=
- Unicode Utilities: UnicodeSet
[:Script_Extensions=Hira,Kana:]
[:Script_Extensions=Hani,Hira,Kana:]
[:Script_Extensions=Bopo,Hang,Hani,Hira,Kana:]
[:Script_Extensions=Bopo,Hang,Hani,Hira,Kana,Yiii:]
置換処理について
Array.prototype.reduce() - JavaScript | MDN
なかなか配列でのreduceメソッドを理解できてないんだけどこんなこともできるんだね……っていうのをジピって知りました。有難うOpenAI。
解説 by chatGPT 3.5
💬 Answer
このコードは、RubyタグをHTMLに追加するための正規表現と置換ルールを定義し、記事本文の要素に適用するJavaScript関数を提供しています。
まず、rubyTemplate
は、Rubyタグを含むHTMLテンプレート文字列を定義しています。$1
と$2
は、後でマッチしたテキストの置換に使用されます。
const rubyTemplate = "<ruby>$1<rt>$2</rt></ruby>";
次に、rubyRegexList
は、テキスト中でRubyタグを置換するために使用される正規表現と置換ルールのリストを定義します。各エントリは、pattern
とreplacement
フィールドを持ちます。pattern
は正規表現、replacement
は置換するHTMLテンプレートです。
const rubyRegexList = [
{ pattern: /[\||](.+?)《(.+?)》/g, replacement: rubyTemplate },
{ pattern: /[\||](.+?)((.+?))/g, replacement: rubyTemplate },
{ pattern: /[\||](.+?)\((.+?)\)/g, replacement: rubyTemplate },
{ pattern: /\[\[rb:(.+?) > (.+?)\]\]/g, replacement: rubyTemplate },
{ pattern: /([\p{sc=Han}]+)《(.+?)》/gu, replacement: rubyTemplate },
{ pattern: /([\p{sc=Han}]+)(([\p{sc=Hiragana}\p{sc=Katakana}ー~]+?))/gu, replacement: rubyTemplate },
{ pattern: /([\p{sc=Han}]+)\(([\p{sc=Hiragana}\p{sc=Katakana}ー~]+?)\)/gu, replacement: rubyTemplate },
{ pattern: /[\||]《(.+?)》/g, replacement: "《$1》" },
{ pattern: /[\||]((.+?))/g, replacement: "($1)" },
{ pattern: /[\||]\((.+?)\)/g, replacement: "($1)" }
];
このリストは、以下のようなテキストのパターンに一致するものを含みます。
|text《ruby》
、|text《ruby》
、|text(ruby)
、|text(ruby)
、|text(ruby)
、|text(ruby)
に一致するもの[[rb:ruby > text]]
に一致するもの漢字《ruby》
、漢字(kana)
、漢字(kana)
に一致するもの|《ruby》
、|《ruby》
、|(ruby)
、|(ruby)
、|(ruby)
、|(ruby)
に一致するもの
次に、rubyRegexMapList
は、rubyRegexList
の各エントリを正規表現オブジェクトに変換するための配列です。map
関数を使用して、オブジェクトを変換し、new RegExp()
を使用して、各パターンを正規表現オブジェクトに変換しています。
const rubyRegexMapList = rubyRegexList.map(({ pattern, replacement }) => ({
pattern: new RegExp(pattern),
replacement
}));
replaceRubyTags
関数は、記事の本文の要素を受け取り、各Rubyタグを置換するために、rubyRegexMapList
の各正規表現をループで処理し、replace()
メソッドを使用して、テキストの置換を実行します。
const replaceRubyTags = (str) => {
return rubyRegexMapList.reduce((acc, { pattern, replacement }) => {
return acc.replace(pattern, replacement);
}, str);
};
最後に、updateArticle
関数は、記事の本文の要素を受け取り、replaceRubyTags
関数を使用して、その要素内のテキストを置換し、結果を要素のinnerHTML
プロパティに設定します。
const updateArticle = (el) => {
const replacedHtml = replaceRubyTags(el.innerHTML);
el.innerHTML = replacedHtml;
};
const articleBody = document.getElementById("article-body");
updateArticle(articleBody);
このコードは、Rubyタグを含むテキストをHTMLに変換するために必要な正規表現と置換ルールを提供し、それを実際に記事本文の要素に適用するJavaScript関数を提供しています。