Os temas de bloco traduzem o WordPress de forma diferente de uma abordagem típica. Os arquivos de modelo PHP tradicionais com funções de tradução não funcionam com modelos HTML, blocos acionados por JavaScript e o Editor de Sites. Essa mudança exige que você entenda os sistemas de internacionalização de blocos do WordPress de forma diferente.
Neste guia, você encontrará estratégias para tornar seus temas de bloco multilíngues. Você aprenderá a navegar pelos desafios da tradução de temas de bloco, implementar soluções e integrar com plugins de tradução.
Por que os temas de bloco quebram os métodos de tradução tradicionais (e como corrigi-los)
Os temas de bloco substituem muitos dos arquivos PHP do WordPress por modelos HTML que contêm marcação de bloco. No entanto, essa troca cria desafios porque os modelos HTML não podem executar funções de tradução PHP, como _()
ou _e()
. Como resultado, as cadeias de tradução que você já tem ficam inutilmente em arquivos estáticos.
O WordPress 6.8 traz algumas melhorias que simplificam a internacionalização de temas de bloco. Principalmente, os temas com cabeçalhos Text Domain e Domain Path adequados não precisam mais das chamadas manual load_theme_textdomain()
.
Em vez disso, o WordPress carrega automaticamente os arquivos de tradução e prioriza wp-content/languages/themes/
em relação aos diretórios de temas para fins de desempenho.
Para começar, configure seu tema usando uma abordagem clássica, adicionando metadados ao arquivo style.css
.
/*
Theme Name: My Block Theme
Text Domain: my-block-theme
Domain Path: /languages
*/
Observe que o cabeçalho Text Domain deve corresponder ao nome da pasta do seu tema (geralmente em kebab-case) para garantir que os arquivos de tradução sejam carregados corretamente nas versões recentes do WordPress.
Da mesma forma que em style.css
, o arquivo functions.php
requer uma configuração mínima:
<?php
// Optional in WordPress 6.8+ but included for backward compatibility
function my_block_theme_setup() {
load_theme_textdomain( 'my-block-theme', get_template_directory() . '/languages' );
}
add_action( 'after_setup_theme', 'my_block_theme_setup' );
// Register block scripts with translation support
function my_block_theme_scripts() {
wp_enqueue_script(
'my-block-theme-scripts',
get_template_directory_uri() . '/assets/js/theme.js',
array( 'wp-i18n' ),
'1.0.0',
true
);
wp_set_script_translations(
'my-block-theme-scripts',
'my-block-theme',
get_template_directory() . '/languages'
);
}
add_action( 'wp_enqueue_scripts', 'my_block_theme_scripts' );
A principal diferença entre os temas clássicos e os temas de Bloco aqui é que os últimos dividem a responsabilidade da tradução entre o PHP do lado do servidor e o JavaScript do lado do cliente. Em contraste, os temas clássicos dependem do PHP para lidar com a maioria das traduções.
Como criar traduções para o block.json
O arquivo block.json é o seu “centro de configuração” para o bloco que você deseja traduzir. Ao configurar a internacionalização adequada, você garante que seus blocos sejam traduzidos corretamente no editor e no frontend.
A maneira canônica de registrar um bloco é por meio do block.json
. Se você começar com a configuração textdomain
, o WordPress poderá traduzir os campos de título, descrição e palavras-chave quando a configuração textdomain
estiver definida:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "my-theme/testimonial",
"title": "Testimonial",
"category": "text",
"description": "Display customer testimonials",
"keywords": ["quote", "review", "testimonial"],
"textdomain": "my-block-theme",
"attributes": {
"content": {
"type": "string",
"source": "html",
"selector": "blockquote"
}
}
}
No entanto, os cenários que exigem “contexto” precisam de registro no lado do servidor. O contexto, nesse caso, é importante porque a mesma palavra pode ser traduzida de forma diferente com base em seu uso. Por exemplo, “post” como substantivo ou como verbo requer traduções diferentes em muitos idiomas:
function my_theme_register_testimonial_block() {
register_block_type_from_metadata(
get_template_directory() . '/blocks/testimonial',
array(
'title' => _x( 'Testimonial', 'block title', 'my-block-theme' ),
'description' => _x(
'Display customer testimonials',
'block description',
'my-block-theme'
),
'keywords' => array(
_x( 'quote', 'block keyword', 'my-block-theme' ),
_x( 'review', 'block keyword', 'my-block-theme' )
)
)
);
}
add_action( 'init', 'my_theme_register_testimonial_block' );
Todas as variações de bloco que você incluir também precisam de nomes estruturados, pois o WordPress procura padrões específicos ao carregar suas traduções. Cada nome de variação se torna parte do código de tradução:
{
"name": "my-theme/button",
"title": "Button",
"textdomain": "my-block-theme",
"variations": [{
"name": "primary",
"title": "Primary Button",
"attributes": {
"className": "is-style-primary"
}
},
{
"name": "secondary",
"title": "Secondary Button",
"attributes": {
"className": "is-style-secondary"
}
}
]
}
A internacionalização do JavaScript exige que você importe as funções i18n do WordPress e configure as traduções de script. Isso ocorre porque o Site Editor é executado no navegador e não no servidor. Como as funções de tradução do PHP não existem no JavaScript, o WordPress fornece funções equivalentes por meio do pacote @wordpress/i18n
:
import {
registerBlockType
} from '@wordpress/blocks';
import {
__
} from '@wordpress/i18n';
import {
useBlockProps,
RichText
} from '@wordpress/block-editor';
registerBlockType('my-theme/testimonial', {
edit: ({
attributes,
setAttributes
}) => {
const blockProps = useBlockProps();
return ( < div { ...blockProps } >
< RichText tagName = "blockquote" value = { attributes.content } onChange = { (content) => setAttributes({
content
})
}
placeholder = {
__('Add testimonial text...', 'my-block-theme')
}
/> < cite >
< RichText tagName = "span" value = { attributes.author } onChange = { (author) => setAttributes({
author
})
}
placeholder = {
__('Author name', 'my-block-theme')
}
/> < /cite> < /div>
);
}
});
Além disso, é uma boa ideia gerar arquivos de tradução JSON para JavaScript porque o WordPress usa um formato diferente para traduções no lado do cliente. O PHP usa arquivos .mo
, mas o JavaScript precisa de arquivos .json
com convenções de nomenclatura específicas. Você pode automatizar isso usando comandos WP-CLI:
# Extract strings from JavaScript files into POT
wp i18n make-pot . languages/my-block-theme.pot
# Convert PO files to JSON for JavaScript
wp i18n make-json languages/ --no-purge --pretty-print
Os arquivos JSON resultantes seguem um padrão consistente: {textdomain}-{locale}-{handle}.json
. O WordPress pode carregá-los quando você chamar wp_set_script_translations()
.
Convertendo seus modelos HTML estáticos em componentes PHP prontos para tradução
Dado que os modelos HTML são estáticos, trabalhar com eles para a internacionalização de temas de Bloco é um desafio, pois suas funções e técnicas de tradução existentes não funcionarão.
As partes de modelo baseadas em PHP podem resolver esse problema porque o WordPress as processa como arquivos PHP, apesar de serem referenciadas em modelos HTML. Essa abordagem híbrida mantém a estrutura do tema de Bloco enquanto permite conteúdo dinâmico:
<!-- templates/page.html -->
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
<!-- wp:post-title {"level":1} /-->
<!-- wp:post-content /-->
<!-- wp:template-part {"slug":"post-meta"} /-->
</main>
<!-- /wp:group →
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->
Observe que a parte do modelo pode conter PHP:
<!-- parts/post-meta.html -->
<!-- wp:group {"className":"post-meta"} -->
<div class="wp-block-group post-meta">
<?php
echo sprintf(
/* translators: 1: Post date, 2: Post author */
__( 'Published on %1$s by %2$s', 'my-block-theme' ),
get_the_date(),
get_the_author()
);
?>
</div>
<!-- /wp:group -->
Os blocos complexos precisam do arquivo render.php
porque alguns conteúdos requerem processamento no lado do servidor que os atributos do bloco não conseguem lidar sozinhos. Consultas a bancos de dados, lógica condicional e geração de conteúdo dinâmico exigem a execução de PHP:
// blocks/recent-posts/render.php
<?php
$recent_posts = get_posts( array(
'numberposts' => $attributes['count'] ?? 5
) );
?>
<div <?php echo get_block_wrapper_attributes(); ?>>
<h3><?php echo esc_html__( 'Recent Posts', 'my-block-theme' ); ?></h3>
<?php if ( $recent_posts ) : ?>
<ul>
<?php foreach ( $recent_posts as $post ) : ?>
<li>
<a href="<?php echo get_permalink( $post ); ?>">
<?php echo get_the_title( $post ); ?>
</a>
<span class="post-date">
<?php echo get_the_date( '', $post ); ?>
</span>
</li>
<?php endforeach; ?>
</ul>
<?php else : ?>
<p><?php esc_html_e( 'No posts found.', 'my-block-theme' ); ?></p>
<?php endif; ?>
</div>
Isso significa configurar seu bloco para usar o arquivo de renderização em block.json
:
{
"name": "my-theme/recent-posts",
"render": "file:./render.php",
"attributes": {
"count": {
"type": "number",
"default": 5
}
}
}
Como implementar a tradução de conteúdo dinâmico para campos personalizados e entradas de usuário
Apesar de sua prevalência em sites do WordPress, o conteúdo dinâmico pode causar problemas de tradução porque existe no banco de dados e não nos arquivos do seu tema. Dessa forma, qualquer plugin de tradução de terceiros que você usar precisa identificar e gerenciar esse conteúdo separadamente das strings estáticas do tema.
É nesse ponto que o registro de campos personalizados com a configuração de metadados adequada se torna valioso, pois os plugins de tradução se integram ao sistema de metadados do WordPress para detectar qualquer conteúdo traduzível. O parâmetro show_in_rest
habilita a compatibilidade com o Editor de Sites:
function my_theme_register_meta_fields() {
register_post_meta( 'page', 'custom_subtitle', array(
'type' => 'string',
'description' => __( 'Page subtitle', 'my-block-theme' ),
'single' => true,
'show_in_rest' => true,
'auth_callback' => function() {
return current_user_can( 'edit_posts' );
}
));
}
add_action( 'init', 'my_theme_register_meta_fields' );
// Display with plugin compatibility
function my_theme_display_subtitle( $post_id ) {
$subtitle = get_post_meta( $post_id, 'custom_subtitle', true );
if ( ! $subtitle ) {
return;
}
// WPML compatibility
// (documented at wpml.org/documentation/support/wpml-coding-api/wpml-hooks-reference/)
if ( function_exists( 'icl_t' ) ) {
$subtitle = icl_t(
'my-block-theme',
'subtitle_' . $post_id,
$subtitle
);
}
// Polylang compatibility
// (documented at polylang.pro/doc/function-reference/)
if ( function_exists( 'pll_translate_string' ) ) {
$subtitle = pll_translate_string( $subtitle, 'my-block-theme' );
}
echo '<h2 class="page-subtitle">' . esc_html( $subtitle ) . '</h2>';
}
As consultas ao banco de dados também precisam de filtragem de idioma, porque o WordPress não filtra o conteúdo por idioma automaticamente. Os plugins de tradução adicionam modificações de consulta que você precisa acomodar:
function my_theme_get_localized_posts( $args = array() ) {
$defaults = array(
'post_type' => 'post',
'posts_per_page' => 10
);
$args = wp_parse_args( $args, $defaults );
// Polylang adds language taxonomy
// (documented at polylang.pro/doc/developpers-how-to/)
if ( function_exists( 'pll_current_language' ) ) {
$args['lang'] = pll_current_language();
}
// WPML filters queries automatically when suppress_filters is false
// (wpml.org/documentation/getting-started-guide/translating-custom-posts/)
if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
$args['suppress_filters'] = false;
}
return get_posts( $args );
}
Seu processamento de formulários mistura conteúdo dinâmico e estático, mas os rótulos dos formulários, as mensagens de erro e as notificações do administrador precisam de tradução com reconhecimento de idioma. Os destinatários dos e-mails também podem variar de acordo com o idioma:
function my_theme_process_contact_form() {
if ( ! isset( $_POST['contact_nonce'] ) ||
! wp_verify_nonce( $_POST['contact_nonce'], 'contact_form' ) ) {
return;
}
$name = sanitize_text_field( $_POST['name'] );
$email = sanitize_email( $_POST['email'] );
$message = sanitize_textarea_field( $_POST['message'] );
// Get admin email in current language
$admin_email = get_option( 'admin_email' );
// For language-specific admin emails, use WPML's string translation
// (documented at wpml.org/documentation/support/wpml-coding-api/wpml-hooks-reference/)
if ( function_exists( 'icl_t' ) ) {
// First register the string if not already registered
if ( function_exists( 'icl_register_string' ) ) {
icl_register_string( 'my-block-theme', 'contact_email', $admin_email );
}
$admin_email = icl_t(
'my-block-theme',
'contact_email',
$admin_email
);
}
$subject = sprintf(
/* translators: %s: Sender name */
__( 'Contact form submission from %s', 'my-block-theme' ),
$name
);
wp_mail( $admin_email, $subject, $message );
}
add_action( 'init', 'my_theme_process_contact_form' );
Também é importante avaliar a sensibilidade ao idioma da sua navegação, pois os itens de menu, os URLs e a estrutura podem ser diferentes entre os idiomas. Seu plugin de tradução provavelmente tem uma API para construir seletores de idioma:
function my_theme_language_switcher_block() {
if ( ! function_exists( 'pll_the_languages' ) &&
! function_exists( 'icl_get_languages' ) ) {
return;
}
$output = '<div class="language-switcher">';
// Polylang language switcher
// (documented at polylang.pro/doc/function-reference/)
if ( function_exists( 'pll_the_languages' ) ) {
$languages = pll_the_languages( array( 'raw' => 1 ) );
foreach ( $languages as $lang ) {
$output .= sprintf(
'<a href="%s" class="%s">%s</a>',
esc_url( $lang['url'] ),
$lang['current_lang'] ? 'current-lang' : '',
esc_html( $lang['name'] )
);
}
}
// WPML language switcher
// (documented at wpml.org/documentation/support/wpml-coding-api/multi-language-api/)
elseif ( function_exists( 'icl_get_languages' ) ) {
$languages = icl_get_languages();
foreach ( $languages as $lang ) {
$output .= sprintf(
'<a href="%s" class="%s">%s</a>',
esc_url( $lang['url'] ),
$lang['active'] ? 'current-lang' : '',
esc_html( $lang['native_name'] )
);
}
}
$output .= '</div>';
return $output;
}
Trabalhar com plugins de tradução provavelmente será uma grande parte do seu trabalho, portanto, vamos analisar esse aspecto a seguir.
Trabalhando com plugins de tradução: compatibilidade e otimização
Cada plugin de tradução do WordPress lida com temas de bloco de uma maneira única. Entender as abordagens que as diferentes soluções adotam ajuda a construir compatibilidade e flexibilidade desde o início.
A documentação de Full Site Editing do WPML descreve como você precisa de uma configuração específica para temas de blocos:
// WPML FSE compatibility based on official documentation
add_action( 'init', function() {
if ( ! defined( 'WPML_VERSION' ) ) {
return;
}
// FSE themes are automatically detected in WPML 4.5.3+ // Enable FSE support
add_filter( 'wpml_is_fse_theme', '__return_true' );
// Register custom strings per WPML String Translation documentation
// (documented at wpml.org/documentation/support/wpml-coding-api/wpml-hooks-reference/)
if ( function_exists( 'icl_register_string' ) ) {
icl_register_string(
'my-block-theme',
'footer-copyright',
'© My Company.'
);
}
});
O Polylang Pro é compatível com o Site Editor desde a versão 3.2. O plugin lida com temas de blocos por meio de sua interface padrão de tradução de strings:
// Polylang string registration per official documentation
if ( function_exists( 'pll_register_string' ) ) {
pll_register_string(
'Footer Copyright',
'© My Company.',
'my-block-theme',
true // Multiline support
);
}
A documentação do TranslatePress mostra que determinados elementos dinâmicos precisam ser excluídos para que você tenha um desempenho ideal:
// TranslatePress optimization based on official recommendations
// (documented at translatepress.com/docs/developers/)
add_filter( 'trp_stop_translating_page', function( $stop, $url ) {
// Skip admin and API requests per TranslatePress documentation
if ( is_admin() || wp_is_json_request() ) {
return true;
}
// Skip pattern preview URLs that can cause rendering issues
if ( strpos( $url, 'pattern-preview' ) !== false ) {
return true;
}
return $stop;
}, 10, 2 );
Finalmente, há algumas dicas de propósito geral para transmitir ao trabalhar com bases de código de terceiros (como plugins). Primeiro, certifique-se de que você usa uma abordagem sistemática para depurar problemas de tradução.
// Debug helper for translation issues
function my_theme_debug_translations() {
if ( ! WP_DEBUG || ! current_user_can( 'manage_options' ) ) {
return;
}
error_log( 'Text domain loaded: ' . is_textdomain_loaded(
'my-block-theme' ) );
error_log( 'Current locale: ' . get_locale() );
error_log( 'Translation test: ' . __(
'Hello World',
'my-block-theme'
)
);
// Check JSON translations for blocks
$json_file = WP_LANG_DIR . '/themes/my-block-theme-' . get_locale() . '-script-handle.json';
error_log( 'JSON translation exists: ' . file_exists( $json_file ) );
}
add_action( 'init', 'my_theme_debug_translations' );
O armazenamento em cache do site pode interferir nas atualizações de tradução, portanto, talvez você queira limpar os caches quando os arquivos de tradução forem alterados:
# Clear WordPress transients
wp transient delete --all
# Generate fresh translation files
wp i18n make-pot . languages/my-block-theme.pot
wp i18n make-json languages/ --no-purge
A otimização de desempenho se torna crítica com plugins de tradução. Cada plugin adiciona consultas ao banco de dados e sobrecarga de processamento, o que novamente se beneficia do cache de traduções usadas com frequência:
function my_theme_cached_translation( $text, $domain = 'my-block-theme' ) {
$cache_key = 'translation_' . md5( $text . get_locale() );
$cached = wp_cache_get( $cache_key, 'my_theme_translations' );
if ( false === $cached ) {
$cached = __( $text, $domain );
wp_cache_set( $cache_key, $cached, 'my_theme_translations', HOUR_IN_SECONDS );
}
return $cached;
}
Como alternativa, talvez seja aconselhável ignorar o armazenamento em cache até que você esteja pronto para implementar. O uso de um ambiente de teste é ideal para isso e, normalmente, você não precisará do aumento de desempenho proporcionado pelo armazenamento em cache.
Resumo
A internacionalização de temas de bloco exige que você trabalhe com os métodos de tradução do WordPress e utilize novas abordagens dentro do Editor de Sites.
Ao configurar os metadados do seu tema, implementar estratégias de modelo e entender os requisitos dos plugins de tradução, você pode criar temas de bloco multilíngues que tenham um bom desempenho e forneçam uma experiência de usuário de alta qualidade.
Quando você estiver pronto para o lançamento, a hospedagem gerenciada da Kinsta para WordPress oferece o desempenho e o alcance global de que seu site precisa, com cache integrado, um CDN de 37 locais e ferramentas como integração com Git e ambiente de testes.