WordPressのプラグインActivityPubが7.6.0に更新されてから、マストドンで表示される日本語のハッシュタグがエンコード文字列から%が抜けたハッシュタグになるバグが発生した。
"published":"2025-11-11T22:36:45Z","tag":[{"type":"Hashtag","href":"https://ishii00141.stars.ne.jp/tag/activitypub/","name":"#activitypub"},{"type":"Hashtag","href":"https://ishii00141.stars.ne.jp/tag/fediverse/","name":"#fediverse"},{"type":"Hashtag","href":"https://ishii00141.stars.ne.jp/tag/wordpress/","name":"#wordpress"},{"type":"Hashtag","href":"https://ishii00141.stars.ne.jp/tag/%e3%83%97%e3%83%a9%e3%82%b0%e3%82%a4%e3%83%b3/","name":"#E38397E383A9E382B0E382A4E383B3"},{"type":"Hashtag","href":"https://ishii00141.stars.ne.jp/tag/%e4%bb%95%e6%a7%98/","name":"#E4Bb95E6A798"}],
Geminiに相談したら、その原因は、どうやら、/activitypub/includes/transformer/class-post.php のコードの次の部分らしい。
/**
* Returns a list of Tags, used in the Post.
*
* This includes Hash-Tags and Mentions.
*
* @return array The list of Tags.
*/
protected function get_tag() {
$tags = parent::get_tag();
$post_tags = \get_the_tags( $this->item->ID );
if ( $post_tags ) {
foreach ( $post_tags as $post_tag ) {
// Tag can be empty.
if ( ! $post_tag ) {
continue;
}
$tags[] = array(
'type' => 'Hashtag',
'href' => \esc_url( \get_tag_link( $post_tag->term_id ) ),
'name' => esc_hashtag( $post_tag->slug ),
);
}
}
return \array_unique( $tags, SORT_REGULAR );
}
このコードの「'name' => esc_hashtag( $post_tag->slug )」の部分が怪しいらしい。これを「'name' => esc_hashtag( $post_tag->name )」に修正するべきらしい。
Geminiによると「URLとスラッグの関係」は次の通りらしい。
💾 URLとスラッグの関係
WordPress のタグの URL に日本語が直接使われているように見えても、
slugは内部データとして存在しています。1. 内部データとしての
slug
$post_tag->slugは、そのタグを一意に識別するためのデータベース上の値であり、URL 形式の決定に重要な役割を果たします。WordPress のターム(タグやカテゴリー)は、構造上、必ずterm_id、name、slugのプロパティを持っています。2. URLエンコードとスラッグ
ユーザーが見るブラウザのアドレスバーには、日本語(
プラグイン)がそのまま表示されることがあります。しかし、これはブラウザが表示のためにデコードしているだけで、実際にはサーバーへは URL エンコードされた形式で送信されています。URL の送信形式
表示 サーバーへの送信形式 内部の slug ~/tag/プラグイン~/tag/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3e38397e383a9...(あなたのケース)3. スラッグが使われる場所
たとえ URL に日本語名が使われている場合でも、WordPress がそのタグをデータベースから読み出す際、URL パス(
プラグインの部分)を解析してスラッグやIDを特定します。スラッグは、クエリや内部の処理で使われる、必須の識別子です。したがって、ActivityPub プラグインが
$post_tagsを読み込んだ時点で、そのオブジェクトにはあなたのデータベースに格納されている$post_tag->slugの値(今回の場合は%が抜けたバイト列)が含まれています。
さて、この記事のタイトルには"%"が含まれているのだが、記事タイトルを基に自動で生成されたスラッグを見ると次のようになっていて、"%"が抜けている。
wordpressが自動生成するスラッグにはが含まれない

一つ勉強になった。

コメント
#ActivityPub
この記事に書いたことが誤りの可能性がある。
この記事の「'name' => esc_hashtag( $post_tag->slug )」が怪しく、解決策として「'name' => esc_hashtag( $post_tag->name )」を使った方が良さそうなことは同じだが、これを書く前提として「$post_tag->slug」の段階で"%"が削除されていると推測していた。実際、WordPressのスラッグ自動生成では"%"が削除される。
しかし、ハッシュタグの場合、ハッシュタグが格納されている wp_terms テーブルの該当するハッシュタグの slug が使われているらしく、その slug では%が残っていることがテーブルを見て確認できた。
そのためのテストを行った。
https://ishii00141.stars.ne.jp/20251114-2029-3973/
https://ishii00141.stars.ne.jp/20251114-2034-3977/
このテストで #テスト というハッシュタグを新たに作り、 wp_terms テーブルでは slug に「%e3%83%86%e3%82%b9%e3%83%88」と格納されたことを確認した。そして #Mastodon で確認したところ、"%"が削除されて #E38386E382B9E38388 となっていた。
その後、wp_terms テーブルの「テスト」の slug を「%e3%82%b9%e3%83%a9%e3%83%83%e3%82%b0」に変えた。これは「スラッグ」のエンコードである。
そして、新たな記事で #テスト というハッシュタグを付けたところ、マストドンでは「#E382B9E383A9E38383E382B0」となった。すなわち wp_terms テーブルの改竄が反映された。
これは、JSON生成時に wp_terms テーブルの slug を読み込んでいることを示している。
では、wp_terms テーブルの slug に"%"が残っているのにJSON生成時に"%"が消えるのはなぜか?
#Gemini の推測では esc_hashtag 関数が怪しいとのことである。