Rozszerzanie
RozszerzanieTłumaczenie dodatkowych bloków Gutenberg

Tłumaczenie dodatkowych bloków Gutenberg

Gato AI Translations for Polylang może tłumaczyć posty oparte na blokach.

Plugin zawiera obsługę wielu bloków od razu po instalacji. W przypadku wszystkiego poza tym — własnych niestandardowych bloków lub bloków z wtyczek innych firm, które nie zawierają pliku wpml-config.xml — możesz rozszerzyć obsługę za pomocą hooków PHP.

Tłumaczenie ciągów tekstowych

Aby zarejestrować dodatkowe atrybuty nadające się do tłumaczenia dla bloku, użyj filtra gatompl:gutenberg_block_type_translatable_attribute_regexes.

Dlaczego wyrażenia regularne?

Blok Gutenberg jest zapisywany w post_content jako komentarz HTML zawierający atrybuty JSON bloku, a następnie wyrenderowany HTML bloku, na przykład:

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

Tłumaczenie bloku oznacza znalezienie konkretnego podciągu do przetłumaczenia wewnątrz tego znacznika, zastąpienie go tłumaczeniem i pozostawienie wszystkiego innego bez zmian (nazwa bloku, inne atrybuty, struktura HTML, otaczające bloki). Wyrażenia regularne pozwalają pluginowi dokładnie wskazać, który podciąg zastąpić: tekst przed i po wartości jest przechwytywany w grupach, a sama wartość jest częścią, która zostaje zamieniona.

Standardowe atrybuty tekstowe (przechowywane w JSON bloku)

Jeśli właściwość jest zwykłym ciągiem tekstowym przechowywanym w atrybutach JSON bloku, przekaż true, a plugin użyje swojego domyślnego wyrażenia regularnego.

Na przykład, aby przetłumaczyć atrybuty daysLabel, hoursLabel, minutesLabel i secondsLabel bloku kadence/countdown — którego znacznik wygląda tak:

<!-- 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 -->

…zarejestruj atrybuty w następujący sposób:

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;
    }
);

Wartość true jest wewnętrznie rozwijana do tego domyślnego wyrażenia regularnego:

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

…gdzie symbole zastępcze to:

  1. %1$s → wartość atrybutu
  2. %2$s → nazwa atrybutu
  3. %3$s → nazwa bloku

Dla atrybutu daysLabel w kadence/countdown symbole zastępcze są podstawiane następująco: %3$skadence/countdown, %2$sdaysLabel, %1$sDays, dając wynik:

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

Zastępowane jest tylko Days; nazwa bloku, inne atrybuty i komentarz zamykający są zachowane przez grupy przechwytywania.

Postać wyrażenia regularnego wygląda następująco:

#(wszystko przed)wartość atrybutu(wszystko po)#

Ciągi tekstowe przechowywane wewnątrz HTML bloku

Jeśli wartość nie jest przechowywana w atrybutach JSON, lecz wewnątrz wyrenderowanego HTML, dostarcz własne wyrażenie regularne. Możesz użyć %s (zamiast %1$s) w miejscu wartości atrybutu, a nazwę bloku i atrybutu zakodować bezpośrednio w wyrażeniu regularnym.

Przykład — tłumaczenie atrybutu content bloku generateblocks/text. Jego znacznik wygląda następująco — zauważ, że Hello world nie znajduje się w JSON, lecz między wyrenderowanymi tagami:

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

Domyślne wyrażenie regularne nigdy nie znalazłoby tego podciągu, dlatego dostarczasz własne:

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;
    }
);

Gdy ta sama wartość pojawia się w wielu miejscach

Jeśli ten sam atrybut pojawia się zarówno w atrybutach JSON jak i w HTML (lub w dwóch różnych miejscach), przekaż tablicę wyrażeń regularnych — każde z nich musi zostać uruchomione, aby wszystkie kopie ciągu zostały przetłumaczone.

Na przykład w bloku generateblocks/media atrybuty alt i title są przechowywane zarówno wewnątrz htmlAttributes w JSON, jak i jako atrybuty HTML na wyrenderowanym znaczniku <img>:

<!-- 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 -->

Dwa wyrażenia regularne na atrybut — jedno celujące w JSON, drugie w <img> — zapewniają, że obie kopie pozostają zsynchronizowane po tłumaczeniu:

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;
    }
);

Jeśli wartość atrybutu jest obiektem JSON, możesz celować w konkretną właściwość podrzędną, używając . (kropki) w nazwie atrybutu, jak pokazano powyżej z htmlAttributes.alt i htmlAttributes.title w generateblocks/media.

Wyłączanie tłumaczenia automatycznie tłumaczonego atrybutu

Przekazanie false lub null usuwa tłumaczenie atrybutu, który plugin w innym przypadku przetłumaczyłby automatycznie. Jest to przydatne na przykład, aby wykluczyć konkretny atrybut tekstowy z automatycznego tłumaczenia w blokach tylko PHP lub dla bloków importowanych z wpml-config.xml, których zadeklarowanych atrybutów nie chcesz tłumaczyć:

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;
    }
);

Tłumaczenie odwołań do encji

Odwołania do encji (identyfikator posta/mediów/terminu/menu przechowywany w atrybucie bloku) mogą być przemapowane na odpowiadającą im encję w języku docelowym w czasie tłumaczenia. Użyj jednego z poniższych filtrów w zależności od rodzaju odwołania:

Rodzaj odwołaniaFiltr
Custom posts i mediagatompl:gutenberg_block_type_custompost_and_media_reference_attribute_regexes
Terminy taksonomiigatompl:gutenberg_block_type_taxonomy_term_reference_attribute_regexes
Menu po IDgatompl:gutenberg_block_type_menu_reference_by_id_attribute_regexes
Menu po sluggatompl:gutenberg_block_type_menu_reference_by_slug_attribute_regexes

Każdy filtr otrzymuje tę samą strukturę co filtr atrybutów nadających się do tłumaczenia (true dla domyślnego wyrażenia regularnego, ciąg lub tablica dla niestandardowych wyrażeń regularnych).

Na przykład blok woocommerce/single-product przechowuje powiązany produkt jako numeryczny productId:

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

Gdy post jest tłumaczony, to 42 (produkt w języku źródłowym) musi zostać przemapowane na jego odpowiednik w języku docelowym (powiedzmy 87). Oznacz productId jako odwołanie do custom posta, aby plugin przechwycił i zamienił identyfikator w czasie tłumaczenia:

add_filter(
    'gatompl:gutenberg_block_type_custompost_and_media_reference_attribute_regexes',
    static function (array $regexes): array {
        $regexes['woocommerce/single-product'] = [
            'productId' => true,
            // …lub niestandardowe wyrażenie regularne, jeśli `productId` nie jest przechowywany w standardowym formacie JSON:
            // 'productId' => '#(<!-- wp:woocommerce/single-product \{.*?\"productId\":)%s([,\}].*? /?-->)#',
        ];
        return $regexes;
    }
);

Używaj tego samego wzorca dla innych rodzajów odwołań. Każdy rodzaj wygląda tak samo w znaczniku bloku — numeryczny identyfikator lub slug osadzony w JSON — różnica polega na tym, jak plugin rozwiązuje go w języku docelowym:

<!-- 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;
    }
);

Odkrywanie nazw atrybutów

Najszybszym sposobem na znalezienie nazw atrybutów i sposobu ich przechowywania jest uruchomienie zapytania GraphQL Translate custom posts i sprawdzenie pola odpowiedzi blockFlattenedDataItems dla danego bloku.

Zapoznaj się z przewodnikiem Pobieranie danych page buildera do tłumaczenia, aby dowiedzieć się, jak uruchomić to zapytanie i odczytać jego wyniki.

Obejście bloków, których atrybuty wymagają przetworzenia

Powyższe hooki zakładają, że wartość atrybutu udostępniana przez blockFlattenedDataItems jest już wartością do przetłumaczenia (skalar lub tablica).

Jeśli wartość jest opakowana — na przykład atrybut przechowuje <li>Some text</li>, a Ty chcesz przetłumaczyć tylko Some text — musisz najpierw ją wyodrębnić za pomocą filtra gatompl:gutenberg_block_flattened_data_item_attributes.

Blok generateblocks/image jest przykładem z rzeczywistego życia: jego atrybuty alt i title nie są udostępniane jako samodzielne atrybuty, lecz znajdują się wewnątrz HTML innerContent i muszą być wyodrębnione za pomocą wyrażenia regularnego.

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
);

Gdy alt i title istnieją już w obiekcie atrybutów, hooki oparte na wyrażeniach regularnych opisane powyżej mogą je wskazywać jak każdy inny atrybut.

Gdzie znaleźć przykłady

Własne integracje pluginu są dobrymi odwołaniami z rzeczywistego życia. Zapoznaj się z tymi plikami wewnątrz zainstalowanego pluginu:

  • Wyrażenia regularne atrybutów bloku: wp-content/plugins/gato-ai-translations-for-polylang/src/Constants/BlockTypeAttributeValues.php
  • Wstępne przetwarzanie atrybutów bloku: wp-content/plugins/gato-ai-translations-for-polylang/src/ConditionalOnContext/LicenseIsActive/Hooks/CoreBlockFlattenedDataItemAttributesHookSet.php