マストドンに送信する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 );
    
タイトルとURLをコピーしました