マストドンに送信するJSONの日本語ハッシュタグの異常の解決策

 WordPressのプラグインActivityPubが7.6.0に更新されてから、マストドンで表示される日本語のハッシュタグがエンコード文字列から%が抜けたハッシュタグになるバグが発生しているが、解決策が分かったかもしれない。

 まず、/activitypub/includes/transformer/class-post.php のコードの「'name' => esc_hashtag( $post_tag->slug )」が怪しく、「'name' => esc_hashtag( $post_tag->name )」にすべきではないかと検討したが、「 $post_tag->slug 」でなければいけない理由があった。それは、WordPressのタグには「Test for tags with spaces」のように半角スペースが含まれていることがあり、wp_terms テーブルの「name」はタグそのままなので処理しにくいのかもしれない。「slug」であれば、「test-for-tags-with-spaces」と"-"で繋がる。

wp_terms テーブル内のハッシュタグの格納状態

 また、「'name' => esc_hashtag( $post_tag->name )」で試してみたが、esc_hashtag()関数に渡された時点でURLエンコードされていて「slug」で渡したのと同じ状態だったらしく、結果が同じだった。したがって、「'name' => esc_hashtag( $post_tag->slug )」のままで対策を考える必要がある。「slug」は単語の先頭の文字が小文字になるので、それが気に入らないが…。

 さて、esc_hashtag()関数を修正するわけだが、前回の記事にあるようなGeminiの考察を読み直して、次のコードが問題ではないかと考えた。

$hashtag = \preg_replace( '/emoji-regex(*SKIP)(?!)|[^\p{L}\p{Nd}-]+/u', '-', $hashtag );

 [^\p{L}\p{Nd}-]+ という部分が「(文字、数字、ハイフン)ではないすべての文字」を意味していて、それを"-"に置換するコードらしい。このコードで"%"も"-"に置換されるらしい。"%"が消えたのは、このコードが原因だと考えるのが妥当だから、"%"が"-"に置換されないように、次のようにコードを修正した。

$hashtag = \preg_replace( '/emoji-regex(*SKIP)(?!)|[^\p{L}\p{Nd}\-%]+/u', '-', $hashtag );

 ちなみに、この後、"-"は単語の区切りとして認識され、"-"の後ろのアルファベットが大文字にされて、"-"は削除されるらしい。例えば、WordPressのタグ「Test for tags with spaces」はslugの「test-for-tags-with-spaces」が使われ、マストドンのハッシュタグは「#testForTagsWithSpaces」になる。

 さて、これだけだと、マストドンではURLエンコードされたままでハッシュタグになってしまう。ap_outboxのためのJSONに入れる際に、Unicodeエスケープされていないといけないのだが、URLエンコードされたままの文字はUnicodeエスケープされないらしい。また、esc_hashtag()関数内でUnicodeエスケープする必要はなく、デコードされた日本語で渡せば、外部でUnicodeエスケープしてくれるらしい。
 そこで、esc_hashtag()関数の最後の「return esc_html( $hashtag );」の前に「$hashtag = urldecode( $hashtag );」を追加して次のようにする。

$hashtag = urldecode( $hashtag );
return esc_html( $hashtag );

 これで試したところ、WordPressの「テスト」というタグはマストドンで「#テスト」と表示された。

 この変更はプラグイン「ActivityPub」のコードを変更しちゃっているので、本当は元に戻した方が良いのだが、いつまで経っても修正されない可能性があるので、このままにする。アップデートされたら、ファイルが丸ごと変更されて、問題生じないだろうと思うことにする。

未分類
管理人のマストドンアカウントへのリンクなど

コメント

  1. ishii ishii より:

    apply_filters( 'activitypub_esc_hashtag', $hashtag, $input )があるので、
    テーマの functions.phpに次のコードを追加すれば、元の関数のソースコードは変更しなくて良いらしい。 #ChatGPT の回答。

    add_filter( 'activitypub_esc_hashtag', function( $hashtag, $input ) {
    
      // ここで独自にハッシュタグを作り直す
    
      // 1) 元の入力をデコード
      $tag = wp_specialchars_decode( $input, ENT_QUOTES );
    
      // 2) あなたの望む正規表現に変更
      // 「%」を許可
      $tag = preg_replace( '/emoji-regex(*SKIP)(?!)|[^\p{L}\p{Nd}\-%]+/u/', '-', $tag );
    
      // 3) ハイフンのあとの文字を大文字化
      $tag = preg_replace_callback(
        '/-+(.)/',
        function ( $matches ) {
          return strtoupper( $matches[1] );
        },
        $tag
      );
    
      // 4) # を付ける(元関数と同じ仕様)
      $tag = ltrim( $tag, '#' );
      $tag = trim( $tag, '-' );
      $tag = '#' . $tag;
    
      // 5) 追加したい処理:「urldecode」
      $tag = urldecode( $tag );
    
      return $tag; // esc_html() は元関数側で実行される
    
    }, 10, 2 );
    
  2. ishii ishii より:

    > 「'name' => esc_hashtag( $post_tag->name )」にすべきではないかと検討したが、「 $post_tag->slug 」でなければいけない理由があった。それは、WordPressのタグには「Test for tags with spaces」のように半角スペースが含まれていることがあり、wp_terms テーブルの「name」はタグそのままなので処理しにくいのかもしれない。

    「'name' => esc_hashtag( $post_tag->name )」の場合、「Test for tags with spaces」は「Test-for-tags-with-spaces」となって「TestForTagsWithSpaces」になる。「'name' => esc_hashtag( $post_tag->name )」で問題ない。

    https://ishii00141.stars.ne.jp/?c=1156

    • ishii ishii より:

      #ActivityPub #Mastodon
      気になったので、
      /plugins/activitypub/includes/transformer/class-post.php
      の「'name' => esc_hashtag( $post_tag->slug )」を「'name' => esc_hashtag( $post_tag->name )」にして、様子を見る。次のテスト結果のようなap_outboxのJSONなのに、マストドンでは
      #testForTagsWithSpaces #walder #テスト
      となって、slugの時と変わらなかった。

      テスト結果:

      "published":"2025-11-14T17:45:02Z","tag":[{"type":"Hashtag","href":"https://ishii00141.stars.ne.jp/tag/test-for-tags-with-spaces/","name":"#TestForTagsWithSpaces"},{"type":"Hashtag","href":"https://ishii00141.stars.ne.jp/tag/walder/","name":"#W\u00e4lder"},{"type":"Hashtag","href":"https://ishii00141.stars.ne.jp/tag/%e3%83%86%e3%82%b9%e3%83%88/","name":"#\u30c6\u30b9\u30c8"}],"updated":"2025-11-24T08:06:37Z",
タイトルとURLをコピーしました