【Hugo】投稿本文の文字数をカウントする

投稿日:

カテゴリ:Web 制作

ブログを書いていると、書いた文章の文字数を確認したくなることがある。

以前は JavaScript を使って文字数をカウントしていたが、Hugo に移行してからというものそれを使わずともできるようになった。そこで本記事では、Hugo における投稿本文の文字数のカウント方法を紹介する。

※Hugo v0.151.2 を用いた方法です。Hugo のバージョンによってはこの投稿の方法が使えない場合がありますのでご了承ください。

この記事の目次

投稿本文の文字数をカウントする

.WordCount で、そのページのコンテンツに含まれる単語数を取得できる。

{{ .WordCount }}

ただしこれで返ってくるのはあくまで単語数なので、 中国語(Chinese)、日本語(Japanese)、韓国語(Korean)からなる CJK 言語だとそのままと正確にカウントできない。

その場合は hugo.toml に以下を設定すると、CJK 言語のブログでも .WordCount が動作するようになる。

hasCJKLanguage = true

また、各投稿ごとに設定したい場合は投稿の Front Matter に以下を設定する。

isCJKLanguage = true

より正確に文字数をカウントする

.WordCount 1つで投稿本文の文字数がわかるのは便利だが、前述の CJK の設定をしても実際の文字数とは微妙に数値が違うことがあった。もう少し正確にカウントできないものかと思い見つけたのが、strings.RuneCountstrings.CountRunes だった。

strings.RuneCount は、文字列内の Unicode コードポイント数をカウントする関数である。

コードポイントとは、Unicode で各文字に割り当てられた番号で、概ね人が認識する1文字に相当する。

以下のコードで、投稿本文のスペースや改行を含むすべてのコードポイント数、つまりは文字数を出力できる。

{{ .Plain | strings.RuneCount }}

.Plain は、投稿本文から Markdown や HTML の装飾を除いたテキストを返す。

一方 strings.CountRunes は、空白文字を除いた文字列内のコードポイント数をカウントする。

以下のコードで、投稿本文からスペースや改行を除いた文字数を出力できる。

{{ .Plain | strings.CountRunes }}

特定の要素内の文字数だけを数える

ここまで投稿本文の文字数カウントについて述べてきたが、特定の要素内の文字数だけを数えたい場合もあるかもしれない。

以下のコードは、投稿本文 .Content から段落 p 要素を抽出し、その中身だけを対象に文字数をカウントする例だ。

{{ $pars := findRE `(?s)<p(\s.*?)?>.*?</p>` .Content }}
{{ $texts := delimit (apply (apply (apply $pars "plainify" ".") "htmlUnescape" ".") "strings.TrimSpace" ".") "" }}
{{ $texts | strings.RuneCount }}  // スペース・改行含む
{{ $texts | strings.CountRunes }} // スペース・改行除く

findREp タグ全体を抽出し、plainify で装飾を除去、htmlUnescape でエンティティを変換(例えば &lt;<)、strings.TrimSpace で前後の空白を削除している。この処理のあと、複数の段落を delimit で1つの文字列にする。

こうしてできあがった文字列に対し strings.RuneCountstrings.CountRunes をそれぞれ使うことで、投稿本文の段落におけるスペースや改行を含んだ文字数およびスペースや改行を含まない文字数を取得できる。

ローカルサーバー上で文字数をリアルタイム表示する

hugo server でローカルサーバーを起動している間のみ、投稿のタイトルと本文の文字数を画面上に表示する方法を考えた。

以下のコードを、single.html などの投稿用テンプレートに記述する。

{{ if hugo.IsServer }}
  {{ $pars := findRE `(?s)<p(\s.*?)?>.*?</p>` .Content }}
  {{ $texts := delimit (apply (apply (apply $pars "plainify" ".") "htmlUnescape" ".") "strings.TrimSpace" ".") "" }}
<ul style="position: fixed; top: 5vh;right: 10vw;padding: 1em;background: #eee;line-height: 1.4;opacity: .875;z-index: 100;">
  <li>タイトル:{{ .Title | strings.RuneCount }} 文字</li>
  <li>本文(スペース・改行含む): {{ $texts | strings.RuneCount }} 文字</li>
  <li>本文(スペース・改行除く): {{ $texts | strings.CountRunes }} 文字</li>
</ul>
{{ end }}

タイトル .Title と、本文 .Content の段落 p の文字数をカウントし、画面右上に文字数を固定表示する。hugo.IsServer を使うことで、ローカルサーバーを起動している間のみ表示される。管理者にしか見えない部分なので、CSSは style 属性にそのまま埋め込んだ。

実際にこのブログにもこの文字数カウントを導入しているが、手前味噌な発言をするととても便利だ。

あとがき

Hugo で投稿本文の文字数をカウントする方法について書いた。.WordCount による単語数の取得から、strings.RuneCountstrings.CountRunes を使ったコードポイント単位でのカウント、さらには特定の要素内だけを対象にした文字数の取得方法も紹介した。

ブログを書いているとどうしても文字数が気になってしまう人間なので、紆余曲折ありながらも Hugo で文字数カウントを導入できてよかった。しかし、文字数を気にしすぎて記事を書くのが億劫になったら本末転倒だし、あくまで参考程度にゆるーく使っていこうと思う。