拡張
拡張追加のGutenbergブロックを翻訳する

追加のGutenbergブロックを翻訳する

Gato AI Translations for Polylang は、ブロックベースの投稿を翻訳できます。

このプラグインは、多くのブロックをすぐにサポートしています。それ以外のケース——独自のカスタムブロックや、wpml-config.xml を同梱していないサードパーティプラグインのブロック——については、PHPフックを使ってサポートを拡張できます。

文字列の翻訳

ブロックに追加の翻訳可能な属性を登録するには、gatompl:gutenberg_block_type_translatable_attribute_regexes フィルターを使用します。

なぜ正規表現を使うのか?

Gutenbergブロックは、ブロックのJSON属性を含むHTMLコメントとして post_content に保存され、その後にブロックのレンダリングされたHTMLが続きます。例えば:

<!-- wp:my-plugin/my-block {"title":"Hello"} -->
<div class="wp-block-my-plugin-my-block">Hello</div>
<!-- /wp:my-plugin/my-block -->

ブロックを翻訳するということは、そのマークアップ内で翻訳すべき特定の部分文字列を見つけ、その翻訳に置き換え、その他すべて(ブロック名、他の属性、HTML構造、周囲のブロック)はそのままにすることを意味します。正規表現は、プラグインが置き換えるべき部分文字列を正確に特定する手段です。値の前後のボイラープレートはキャプチャグループで取得され、値そのものが置き換えられる部分です。

標準文字列属性(ブロックのJSONに格納されている場合)

プロパティがブロックのJSON属性に格納された通常の文字列である場合、true を渡すとプラグインはデフォルトの正規表現を使用します。

例えば、kadence/countdown ブロックの daysLabelhoursLabelminutesLabelsecondsLabel 属性を翻訳するには——そのマークアップは以下のようになります——:

<!-- wp:kadence/countdown {"uniqueID":"_abc123","date":"2026-12-31 00:00:00","daysLabel":"Days","hoursLabel":"Hours","minutesLabel":"Minutes","secondsLabel":"Seconds"} -->
<div class="wp-block-kadence-countdown">…</div>
<!-- /wp:kadence/countdown -->

…次のように属性を登録します:

add_filter(
    'gatompl:gutenberg_block_type_translatable_attribute_regexes',
    static function (array $regexes): array {
        $regexes['kadence/countdown'] = [
            'daysLabel'    => true,
            'hoursLabel'   => true,
            'minutesLabel' => true,
            'secondsLabel' => true,
        ];
        return $regexes;
    }
);

true の値は内部的に以下のデフォルト正規表現に展開されます:

#(<!-- wp:%3$s \{.*?\"%2$s\":\")%1$s(\".*?\}/?-->)#

…プレースホルダーは次の通りです:

  1. %1$s → 属性の値
  2. %2$s → 属性名
  3. %3$s → ブロック名

kadence/countdowndaysLabel 属性の場合、プレースホルダーは %3$skadence/countdown%2$sdaysLabel%1$sDays と置き換えられ、以下が生成されます:

#(<!-- wp:kadence/countdown \{.*?\"daysLabel\":\")Days(\".*?\}/?-->)#

Days のみが置き換えられ、ブロック名、他の属性、閉じコメントはキャプチャグループによって保持されます。

正規表現の形式は次の通りです:

#(値の前のすべて)属性値(値の後のすべて)#

ブロックのHTML内に格納されている文字列

値がJSON属性ではなくレンダリングされたHTML内に格納されている場合は、独自の正規表現を提供してください。属性値が入る箇所に %s%1$s の代わりに)を使用でき、ブロック名と属性名は正規表現内にハードコードできます。

例——generateblocks/text ブロックの content 属性を翻訳する場合。そのマークアップは以下のようになります——Hello world はJSONの中ではなく、レンダリングされたタグの間にあることに注意してください——:

<!-- wp:generateblocks/text {"uniqueId":"abc123","tagName":"p"} -->
<p class="gb-text">Hello world</p>
<!-- /wp:generateblocks/text -->

デフォルトの正規表現ではその部分文字列を見つけられないため、独自の正規表現を指定します:

add_filter(
    'gatompl:gutenberg_block_type_translatable_attribute_regexes',
    static function (array $regexes): array {
        $regexes['generateblocks/text'] = [
            'content' => '#(<!-- wp:generateblocks/text [^>]*?-->\n?<[a-z0-9]+ ?[^>]*?>)%s(</[a-z0-9]+>\n?<!-- /wp:generateblocks/text -->)#',
        ];
        return $regexes;
    }
);

同じ値が複数の場所に現れる場合

同じ属性がJSON属性HTML(または2つの異なる箇所)の両方に現れる場合は、正規表現の配列を渡してください——文字列の各コピーを翻訳するために、それぞれの正規表現が実行される必要があります。

例えば、generateblocks/media ブロックでは、alttitle はJSONの htmlAttributes 内と、レンダリングされた <img> のHTML属性の両方に格納されています:

<!-- wp:generateblocks/media {"mediaId":42,"htmlAttributes":{"alt":"Cat sitting","title":"My cat"}} -->
<figure class="gb-media"><img src="…" alt="Cat sitting" title="My cat"></figure>
<!-- /wp:generateblocks/media -->

属性ごとに2つの正規表現——JSONを対象とするものと <img> を対象とするもの——を用意することで、翻訳後も両方のコピーが同期された状態を保てます:

add_filter(
    'gatompl:gutenberg_block_type_translatable_attribute_regexes',
    static function (array $regexes): array {
        $regexes['generateblocks/media'] = [
            'htmlAttributes.alt' => [
                '#(<!-- wp:generateblocks/media \{.*?\"htmlAttributes\":\{.*?\"alt\":\")%s(\".*?\}.*?\} -->)#',
                '#(<!-- wp:generateblocks/media [^>]*?-->\n?.*<img [^>]*alt=\")%s(\"[^>]*?>.*\n?<!-- /wp:generateblocks/media -->)#',
            ],
            'htmlAttributes.title' => [
                '#(<!-- wp:generateblocks/media \{.*?\"htmlAttributes\":\{.*?\"title\":\")%s(\".*?\}.*?\} -->)#',
                '#(<!-- wp:generateblocks/media [^>]*?-->\n?.*<img [^>]*title=\")%s(\"[^>]*?>.*\n?<!-- /wp:generateblocks/media -->)#',
            ],
        ];
        return $regexes;
    }
);

属性値がJSONオブジェクトである場合、generateblocks/mediahtmlAttributes.althtmlAttributes.title のように、属性名に .(ドット)を使用することで特定のサブプロパティを対象にできます。

自動翻訳される属性の翻訳を無効にする

false または null を渡すと、プラグインが自動的に翻訳する属性の翻訳を削除します。これは例えば、PHPのみのブロックで特定の文字列属性を自動翻訳から除外したい場合や、翻訳したくない宣言済み属性を持つ wpml-config.xml から取り込んだブロックに対して便利です:

add_filter(
    'gatompl:gutenberg_block_type_translatable_attribute_regexes',
    static function (array $regexes): array {
        // Disable translation of the `header` attribute on the
        // `my-plugin/duplicate-alert` block (either form works)
        unset($regexes['my-plugin/duplicate-alert']['header']);
        $regexes['my-plugin/duplicate-alert']['implications'] = false;
        return $regexes;
    }
);

エンティティ参照の翻訳

エンティティ参照(ブロック属性に格納された投稿・メディア・タクソノミー・メニューのID)は、翻訳時に対象言語の対応するエンティティに再マッピングできます。参照の種類に応じて、以下のフィルターのいずれかを使用してください:

参照の種類フィルター
カスタム投稿とメディアgatompl:gutenberg_block_type_custompost_and_media_reference_attribute_regexes
タクソノミータームtermgatompl:gutenberg_block_type_taxonomy_term_reference_attribute_regexes
IDによるメニューgatompl:gutenberg_block_type_menu_reference_by_id_attribute_regexes
スラッグによるメニューgatompl:gutenberg_block_type_menu_reference_by_slug_attribute_regexes

各フィルターは、翻訳可能属性フィルターと同じ構造(デフォルト正規表現には true、カスタム正規表現には文字列または配列)を受け取ります。

例えば、woocommerce/single-product ブロックは、リンクされた商品を数値の productId として格納します:

<!-- wp:woocommerce/single-product {"productId":42} /-->

投稿が翻訳されると、その 42(ソース言語の商品)を対象言語の対応する商品(例えば 87)に再マッピングする必要があります。プラグインが翻訳時にIDをキャプチャして置き換えられるよう、productId をカスタム投稿参照としてマークします:

add_filter(
    'gatompl:gutenberg_block_type_custompost_and_media_reference_attribute_regexes',
    static function (array $regexes): array {
        $regexes['woocommerce/single-product'] = [
            'productId' => true,
            // …or a custom regex if `productId` is not stored in the standard JSON shape:
            // 'productId' => '#(<!-- wp:woocommerce/single-product \{.*?\"productId\":)%s([,\}].*? /?-->)#',
        ];
        return $regexes;
    }
);

他の参照の種類にも同じパターンを使用します。いずれもブロックマークアップでの見た目は同じです——JSONに埋め込まれた数値IDまたはスラッグ——違いはプラグインが対象言語への解決方法です:

<!-- wp:my-plugin/related-category {"categoryId":17} /-->
<!-- wp:my-plugin/menu-picker {"menuId":5} /-->
<!-- wp:my-plugin/menu-picker {"menuSlug":"main-nav"} /-->
// Taxonomy term reference
add_filter(
    'gatompl:gutenberg_block_type_taxonomy_term_reference_attribute_regexes',
    static function (array $regexes): array {
        $regexes['my-plugin/related-category'] = [
            'categoryId' => true,
        ];
        return $regexes;
    }
);
 
// Menu reference by ID
add_filter(
    'gatompl:gutenberg_block_type_menu_reference_by_id_attribute_regexes',
    static function (array $regexes): array {
        $regexes['my-plugin/menu-picker'] = [
            'menuId' => true,
        ];
        return $regexes;
    }
);
 
// Menu reference by slug
add_filter(
    'gatompl:gutenberg_block_type_menu_reference_by_slug_attribute_regexes',
    static function (array $regexes): array {
        $regexes['my-plugin/menu-picker'] = [
            'menuSlug' => true,
        ];
        return $regexes;
    }
);

属性名を調べる方法

属性名とその格納方法を最も素早く見つけるには、Translate custom posts GraphQLクエリを実行し、対象ブロックの blockFlattenedDataItems レスポンスフィールドを確認します。

そのクエリの実行方法と出力の読み方については、翻訳するページビルダーデータの取得 ガイドをご覧ください。

属性に処理が必要なブロックへの対応

上記のフックは、blockFlattenedDataItems を通じて公開される属性値がすでに翻訳対象の値(スカラーまたは配列)であることを前提としています。

値がラップされている場合——例えば、属性が <li>Some text</li> を格納していて Some text だけを翻訳したい場合——は、gatompl:gutenberg_block_flattened_data_item_attributes フィルターを使って事前に抽出する必要があります。

generateblocks/image ブロックは実際の例です。その alttitle はスタンドアロンな属性として公開されておらず、innerContent のHTML内に存在しており、正規表現で抽出する必要があります。

add_filter(
    'gatompl:gutenberg_block_flattened_data_item_attributes',
    static function (?\stdClass $attributes, string $blockTypeName, \stdClass $blockDataItem): ?\stdClass {
        if ($attributes === null || $blockTypeName !== 'generateblocks/image') {
            return $attributes;
        }
 
        $innerContent = $blockDataItem->innerContent ?? null;
        if (!is_array($innerContent) || !isset($innerContent[0]) || !is_string($innerContent[0])) {
            return $attributes;
        }
        $html = $innerContent[0];
 
        if (preg_match('#<img [^>]*alt="([^"]*)"[^>]*?>#', $html, $matches) === 1 && $matches[1] !== '') {
            $attributes->alt = $matches[1];
        }
        if (preg_match('#<img [^>]*title="([^"]*)"[^>]*?>#', $html, $matches) === 1 && $matches[1] !== '') {
            $attributes->title = $matches[1];
        }
        return $attributes;
    },
    10,
    3
);

属性オブジェクトに alttitle が存在するようになれば、上記の正規表現ベースのフックで他の属性と同様に対象にできます。

サンプルの参照先

プラグイン自体の統合は、実際の良い参考例です。インストールしたプラグイン内の以下のファイルを参照してください:

  • ブロック属性の正規表現: wp-content/plugins/gato-ai-translations-for-polylang/src/Constants/BlockTypeAttributeValues.php
  • ブロック属性の前処理: wp-content/plugins/gato-ai-translations-for-polylang/src/ConditionalOnContext/LicenseIsActive/Hooks/CoreBlockFlattenedDataItemAttributesHookSet.php