Ghostのトップページには記事抜粋が並ぶが、本文を要約表示するexcerptヘルパーに備わるwordsとcharactersのどちらのアルゴリズムも日本語相手には適切に機能しないので、段落抽出ができる機能を追加してみた。
excerpt.patch
excerptヘルパーが提供するwordsは英単語数を数え、charactersは文字数を数えているようなのだが、何れも半角文字を含まないベタな日本語に適用すると処理結果が安定しない1。頑張って修正するにも元がそれでは手間が見合わないので、新たに段落を切り出す機能を追加することにしてみた。これならn回目の空行までが要約文になるという見た目で効果範囲が理解しやすい記述ルールも作れることになる。
このpatchがやってることはtruncateOptionsにparagraphオプションを追加し、その指定数だけ<p>
または<h1〜6>
タグを持つ地の文を抽出して返すようにしているだけだ。
--- Ghost-0.6.2/core/server/helpers/excerpt.js Tue Feb 17 18:07:11 2015
+++ GhostBlog/core/server/helpers/excerpt.js Fri Apr 24 11:26:53 2015
@@ -14,13 +14,24 @@
var truncateOptions = (options || {}).hash || {},
excerpt;
- truncateOptions = _.pick(truncateOptions, ['words', 'characters']);
+ truncateOptions = _.pick(truncateOptions, ['words', 'characters', 'paragraph']);
_.keys(truncateOptions).map(function (key) {
truncateOptions[key] = parseInt(truncateOptions[key], 10);
});
/*jslint regexp:true */
excerpt = String(this.html);
+
+ if (truncateOptions.paragraph) {
+ var paragraph = excerpt.match(/(.*?<\/(p|h[1-6])>)/ig);
+ if (paragraph) {
+ excerpt = '';
+ for (var i = 0; i < truncateOptions.paragraph; i++) {
+ if (paragraph.length) excerpt = paragraph.shift();
+ }
+ }
+ }
+
// Strip inline and bottom footnotes
excerpt = excerpt.replace(/<a href="#fn.*?rel="footnote">.*?<\/a>/gi, '');
excerpt = excerpt.replace(/<div class="footnotes"><ol>.*?<\/ol><\/div>/, '');
こうするとthemes/<THEME>/loop.hbs中の{{excerpt}}
で抽出パラグラフ数を指定できるようになる。処理的にはまずparagraphで本文を切り出したあとにwords/charactersの処理に入るので、出力結果が不用意に長くなりすぎることもない。
{{excerpt words="50" characters="80" paragraph="1"}}
この実装のミソはGhostが使っているMarkdown実装(Showdown)が処理したセンテンスが<p><pre><hr><h1-6>
の何れかで必ず括られていることを利用している。
UTF文字列もバイナリとみなしその中に現れる7bit文字だけを数えているかのような処理結果になることがある。 ↩