WordPress の一覧ページやアーカイブで the_excerpt() を使うと、本文の先頭が自動で短く切り取られ、末尾に […] が付きます。この自動抜粋は便利な反面、「もう少し長く表示したい」「[…] を『続きを読む』リンクに変えたい」と思っても、テーマのどこをいじればよいか分かりにくいものです。この記事では、excerpt_length と excerpt_more という2つのフィルターを使って、自動抜粋の文字数(語数)と末尾テキストを functions.php からカスタマイズする方法を解説します。日本語特有のつまずきにも触れます。WordPress テーマを触り始めた初心者〜中級者の方を対象にしています。
目次
自動抜粋とは何か
WordPress の投稿には「抜粋」という欄があり、ここに手で文章を入れておくと、それが記事の要約として使われます。しかし多くの場合この欄は空のまま運用されます。そのとき WordPress は本文の先頭から一定量を自動で切り出し、要約の代わりに表示します。これが「自動抜粋」です。the_excerpt() を呼んだときに、手動抜粋がなければこの自動抜粋が出力されます。
自動抜粋の既定の長さは 55語です。英語であれば単語をスペースで区切って55語ぶんを取り出せますが、日本語は単語ごとにスペースを入れないため、「55語」という単位が直感に合いません。実際には文章中のスペースや記号の位置で区切られ、想定より長く出てしまったり、思った位置で切れなかったりします。日本語サイトで自動抜粋の挙動が分かりにくいのはこのためです。だからこそ、長さや切り方を自分でカスタマイズする意味があります。
excerpt_lengthとexcerpt_moreの違い
自動抜粋をカスタマイズするフィルターは2つあり、それぞれ役割が分かれています。混同しやすいので、先に整理しておきます。
| フィルター | 変更できるもの | 渡される値 |
|---|---|---|
excerpt_length | 自動抜粋の語数(いくつ取り出すか) | 既定の語数(整数 55) |
excerpt_more | 抜粋の末尾に付く文字列(既定は […]) | 既定の文字列 |
どちらも、テーマの functions.php に add_filter() で関数を登録して使います。excerpt_length は「数」を返すフィルター、excerpt_more は「文字列」を返すフィルターだと覚えておくと取り違えません。なお、これらが効くのは自動抜粋のときだけです。投稿の抜粋欄に手で文章を入れている場合、その内容はそのまま使われ、これらのフィルターの影響を受けません。
excerpt_lengthで語数を変える
まずは抜粋の語数を変えてみます。excerpt_length フィルターに登録した関数で、新しい語数を return するだけです。次の例では既定の55語を20語に短くしています。
/**
* 自動抜粋の語数を変更する
*
* @param int $length 既定の語数(55)
* @return int 変更後の語数
*/
function mytheme_excerpt_length( $length ) {
return 20; // 20語ぶんだけ取り出す
}
add_filter( 'excerpt_length', 'mytheme_excerpt_length' );
引数の $length には既定の語数(55)が渡されてきます。今回は使っていませんが、「既定より少し短く」といった相対的な指定をしたいときに利用できます。前述のとおり、この語数はスペースで区切れる言語向けの単位です。日本語では語数を変えても切れる位置が読みにくくなることがあるため、文字数で正確に切りたい場合は後述の方法を使います。
excerpt_moreで「続きを読む」に変える
次に、抜粋の末尾に付く […] を変更します。これは excerpt_more フィルターで、新しい文字列を返します。記事へのリンク付きで「続きを読む」を表示する例が実用的です。
/**
* 自動抜粋の末尾テキストを変更する
*
* @param string $more 既定の末尾文字列( [...] )
* @return string 変更後の末尾文字列
*/
function mytheme_excerpt_more( $more ) {
// 現在の投稿へのリンク付き「続きを読む」を返す
return sprintf(
'… <a class="read-more" href="%1$s">%2$s</a>',
esc_url( get_permalink() ),
'続きを読む'
);
}
add_filter( 'excerpt_more', 'mytheme_excerpt_more' );
ループの中で実行されるため get_permalink() がその投稿の URL を返します。URL は esc_url() で必ずエスケープしてください。リンクを付けず単に「(続きを読む)」のような文字列にしたいだけなら、return '… 続きを読む'; のようにシンプルな文字列を返すだけでもかまいません。
日本語は文字数で切る
excerpt_length は内部的に wp_trim_words() という関数で語数を数えて切り取ります。wp_trim_words() は空白文字を区切りとして語を数えるため、日本語のように単語の区切りがない文章では1文全体を「1語」と数えてしまい、語数指定がうまく働かないことがあります。そこで、日本語では「語数」ではなく「文字数」で切る方が確実です。
文字数で切るには、抜粋そのものを組み立て直す get_the_excerpt フィルターを使い、本文から不要なタグを除いたうえで mb_substr() で先頭の指定文字数だけを取り出します。mb_substr() はマルチバイト(日本語)に対応した部分文字列の関数で、全角文字を正しく1文字として数えてくれます。
/**
* 日本語向けに「文字数」で抜粋を作る
*
* @param string $excerpt 既定の抜粋
* @param WP_Post $post 対象の投稿
* @return string 文字数で切った抜粋
*/
function mytheme_excerpt_by_chars( $excerpt, $post ) {
// 手動で抜粋が入力されている場合はそのまま使う
if ( ! empty( $post->post_excerpt ) ) {
return $excerpt;
}
$length = 80; // 取り出す文字数
// 本文からタグとショートコードを除いてプレーンテキストにする
$text = wp_strip_all_tags( strip_shortcodes( $post->post_content ) );
// 改行や連続する空白を1つにまとめる
$text = trim( preg_replace( '/\s+/u', ' ', $text ) );
// 指定文字数より長ければ切り詰めて末尾を付ける
if ( mb_strlen( $text ) > $length ) {
$text = mb_substr( $text, 0, $length ) . '…';
}
return $text;
}
add_filter( 'get_the_excerpt', 'mytheme_excerpt_by_chars', 10, 2 );
mb_strlen() で文字数を測り、$length を超えるときだけ mb_substr() で先頭80文字を取り出しています。wp_strip_all_tags() でHTMLタグを、strip_shortcodes() でショートコードを取り除き、純粋な本文だけを対象にしているのがポイントです。手動抜粋がある投稿はそのまま尊重するよう、最初に post_excerpt の有無を確認しています。なお mb_substr() などのマルチバイト関数を使うには、PHP の mbstring 拡張が有効になっている必要があります。多くのレンタルサーバーでは既定で有効です。
the_excerptとget_the_excerptの使い分け
テンプレート側で抜粋を表示する関数も2つあり、フィルターを書くときに関係してきます。違いを押さえておきましょう。
| 関数 | 動作 |
|---|---|
the_excerpt() | 抜粋をそのまま出力(echo)する。自動で <p> 段落に包まれる |
get_the_excerpt() | 抜粋を文字列として返す。自分で echo したり加工したりできる |
どちらを呼んでも、内部では同じ get_the_excerpt フィルターが通ります。つまり、前の章で書いた文字数制御は the_excerpt() で表示している箇所にも反映されます。the_excerpt() は出力時に <p> で囲まれるため、末尾にリンクを入れたいなら段落の中にリンクが入る形になる点だけ意識しておくとよいでしょう。
抜粋のカスタマイズが反映されないとき
手動抜粋が入力されている
excerpt_length や excerpt_more は自動抜粋専用です。投稿編集画面の「抜粋」欄に文章を入れていると、自動抜粋の処理自体が走らないため、これらのフィルターは効きません。語数や末尾を変えたのに変化がないときは、まず対象の投稿で手動抜粋を入力していないかを確認してください。
テーマが抜粋を使っていない
そもそもテーマのテンプレートが the_excerpt() ではなく the_content()(本文全体)を出力している場合、抜粋のフィルターをいくら調整しても表示は変わりません。一覧ページの見た目が「本文がそのまま全部出ている」状態なら、テンプレートが抜粋を使っていない可能性が高いです。archive.php や index.php がどちらを呼んでいるかを確認しましょう。
excerpt_moreのフィルター優先度
add_filter() の第3引数は「優先度」で、既定は 10 です。数が小さいほど先に、大きいほど後に実行されます。プラグインや親テーマが同じフィルターに登録していて自分の変更が打ち消されるときは、優先度を add_filter( 'excerpt_more', 'mytheme_excerpt_more', 20 ); のように大きくして後から上書きすると効くことがあります。第4引数は関数が受け取る引数の数で、get_the_excerpt のように複数の値を受け取るフィルターでは 10, 2 のように指定が必要です。
まとめ
自動抜粋のカスタマイズは、語数を変える excerpt_length と、末尾テキストを変える excerpt_more の2つのフィルターが基本です。どちらも functions.php に関数を登録し、それぞれ「数」「文字列」を返します。日本語では wp_trim_words() の語数指定がうまく働かないことがあるため、get_the_excerpt フィルターと mb_substr() を使って文字数で切ると安定します。変更が反映されないときは、手動抜粋の有無、テーマが抜粋を使っているか、フィルターの優先度を順に確認してみてください。一覧ページの見せ方を自分の手で整えたいときに役立ちます。