Block-Themes übersetzen WordPress anders als der typische Ansatz. Herkömmliche PHP-Vorlagendateien mit Übersetzungsfunktionen funktionieren nicht mit HTML-Vorlagen, JavaScript-gesteuerten Blöcken und dem Site Editor. Diese Veränderung erfordert ein anderes Verständnis der WordPress-Block-Internationalisierungssysteme.
In diesem Leitfaden findest du Strategien, um deine Block-Themes mehrsprachig werden zu lassen. Du lernst, wie du die Herausforderungen der Block-Theme-Übersetzung meisterst, Lösungen implementierst und mit Übersetzungs-Plugins integrierst.
Warum Block-Themes herkömmliche Übersetzungsmethoden zunichte machen (und wie man sie behebt)
Block-Themes ersetzen viele der PHP-Dateien von WordPress durch HTML-Vorlagen, die Block-Markup enthalten. Diese Umstellung bringt jedoch Herausforderungen mit sich, da HTML-Vorlagen keine PHP-Übersetzungsfunktionen wie _()
oder _e()
ausführen können. Das hat zur Folge, dass die bereits vorhandenen Übersetzungsstrings nutzlos in statischen Dateien liegen.
WordPress 6.8 bringt einige Verbesserungen, die die Internationalisierung von Block-Themes vereinfachen. In erster Linie benötigen Themes mit korrekten Text Domain und Domain Path Headern keine manual load_theme_textdomain()
Aufrufe mehr.
Stattdessen lädt WordPress die Übersetzungsdateien automatisch und gibt wp-content/languages/themes/
aus Leistungsgründen den Vorzug vor Theme-Verzeichnissen.
Richte dein Theme zunächst ganz klassisch ein, indem du der Datei style.css
Metadaten hinzufügst.
/*
Theme Name: My Block Theme
Text Domain: my-block-theme
Domain Path: /languages
*/
Beachte, dass der Text Domain Header mit dem Ordnernamen deines Themes übereinstimmen muss (normalerweise in Großbuchstaben), damit die Übersetzungsdateien in neueren WordPress-Versionen korrekt geladen werden.
Ähnlich wie bei style.css
ist für die Datei functions.php
nur eine minimale Einrichtung erforderlich:
<?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' );
Der Hauptunterschied zwischen klassischen Themes und Block-Themes besteht darin, dass bei letzteren die Verantwortung für die Übersetzung zwischen serverseitigem PHP und clientseitigem JavaScript aufgeteilt wird. Im Gegensatz dazu müssen klassische Themes die meisten Übersetzungen von PHP erledigen lassen.
Wie man block.json-Übersetzungen erstellt
Die Datei block.json ist dein „Konfigurationszentrum“ für den Block, den du übersetzen möchtest. Wenn du die richtige Internationalisierung einrichtest, wird sichergestellt, dass deine Blöcke sowohl im Editor als auch im Frontend korrekt übersetzt werden.
Der kanonische Weg, einen Block zu registrieren, ist block.json
. Wenn du mit der Konfiguration textdomain
beginnst, kann WordPress die Felder Titel, Beschreibung und Schlüsselwörter übersetzen, wenn textdomain
eingestellt ist:
{
"$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"
}
}
}
Für Szenarien, die einen „Kontext“ erfordern, ist jedoch eine serverseitige Registrierung erforderlich. Der Kontext ist in diesem Fall wichtig, weil ein und dasselbe Wort je nach Verwendung unterschiedlich übersetzt werden kann. Zum Beispiel erfordert „Post“ als Substantiv und als Verb in vielen Sprachen unterschiedliche Übersetzungen:
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' );
Alle Block-Varianten, die du einfügst, müssen ebenfalls strukturiert benannt werden, da WordPress beim Laden deiner Übersetzungen nach bestimmten Mustern sucht. Jeder Variationsname wird Teil des Übersetzungsschlüssels:
{
"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"
}
}
]
}
Für die JavaScript-Internationalisierung musst du die i18n-Funktionen von WordPress importieren und die Skriptübersetzungen konfigurieren. Das liegt daran, dass der Site Editor im Browser und nicht auf dem Server läuft. Da es in JavaScript keine PHP-Übersetzungsfunktionen gibt, stellt WordPress entsprechende Funktionen über das @wordpress/i18n
-Paket zur Verfügung:
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>
);
}
});
Außerdem ist es ratsam, JSON-Übersetzungsdateien für JavaScript zu erstellen, da WordPress ein anderes Format für clientseitige Übersetzungen verwendet. PHP verwendet .mo
Dateien, aber JavaScript braucht .json
Dateien mit speziellen Namenskonventionen. Du kannst dies mit WP-CLI-Befehlen automatisieren:
# 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
Die resultierenden JSON-Dateien folgen einem einheitlichen Muster: {textdomain}-{locale}-{handle}.json
. WordPress kann diese dann laden, wenn du wp_set_script_translations()
aufrufst.
Umwandlung deiner statischen HTML-Vorlagen in übersetzungsfähige PHP-Komponenten
Da HTML-Vorlagen statisch sind, ist die Arbeit mit ihnen für die Internationalisierung des Block-Themes eine Herausforderung, da deine bestehenden Übersetzungsfunktionen und -techniken nicht funktionieren.
PHP-gestützte Vorlagenteile können dieses Problem lösen, da WordPress sie als PHP-Dateien verarbeitet, obwohl sie in HTML-Vorlagen referenziert werden. Dieser hybride Ansatz behält die Struktur des Block-Themes bei und ermöglicht gleichzeitig dynamische Inhalte:
<!-- 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"} /-->
Beachte, dass der Template-Teil PHP enthalten kann:
<!-- 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 -->
Komplexe Blöcke benötigen die Datei render.php
, weil einige Inhalte eine serverseitige Verarbeitung erfordern, die die Blockattribute allein nicht leisten können. Datenbankabfragen, bedingte Logik und die Generierung dynamischer Inhalte erfordern alle die Ausführung von 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>
Das bedeutet, dass du deinen Block so konfigurieren musst, dass er die Renderdatei in block.json
verwendet:
{
"name": "my-theme/recent-posts",
"render": "file:./render.php",
"attributes": {
"count": {
"type": "number",
"default": 5
}
}
}
Wie man die Übersetzung dynamischer Inhalte für benutzerdefinierte Felder und Benutzereingaben implementiert
Obwohl dynamische Inhalte auf WordPress-Websites weit verbreitet sind, können sie zu Übersetzungsproblemen führen, da sie in der Datenbank und nicht in den Dateien deines Themes gespeichert sind. Daher müssen alle Übersetzungs-Plugins von Drittanbietern diese Inhalte getrennt von den statischen Theme-Strings erkennen und verwalten.
Die Registrierung von benutzerdefinierten Feldern mit der richtigen Metakonfiguration ist deshalb so wichtig, weil Übersetzungsplugins das Metasystem von WordPress nutzen, um übersetzbare Inhalte zu erkennen. Der Parameter show_in_rest
ermöglicht die Kompatibilität mit dem Site Editor:
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>';
}
Datenbankabfragen müssen ebenfalls nach Sprachen gefiltert werden, da WordPress die Inhalte nicht automatisch nach Sprachen filtert. Übersetzungsplugins fügen Abfrageänderungen hinzu, die du berücksichtigen musst:
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 );
}
Bei der Verarbeitung deiner Formulare werden dynamische und statische Inhalte gemischt, aber Formularbeschriftungen, Fehlermeldungen und Admin-Benachrichtigungen müssen alle in eine bestimmte Sprache übersetzt werden. Auch die E-Mail-Empfänger können sich je nach Sprache unterscheiden:
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' );
Es ist auch wichtig, die Sprachkenntnisse deiner Navigation zu prüfen, denn Menüpunkte, URLs und die Struktur können sich von Sprache zu Sprache unterscheiden. Dein Übersetzungs-Plugin verfügt wahrscheinlich über eine API zur Erstellung von Sprachumschaltern:
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;
}
Die Arbeit mit Übersetzungsplugins wird wahrscheinlich einen großen Teil deiner Arbeit ausmachen, also schauen wir uns diesen Aspekt als Nächstes an.
Die Arbeit mit Übersetzungsplugins: Kompatibilität und Optimierung
Jedes WordPress-Übersetzungsplugin behandelt Block-Themes auf seine eigene Art und Weise. Wenn du die Ansätze der verschiedenen Lösungen verstehst, kannst du von Anfang an für Kompatibilität und Flexibilität sorgen.
In der Dokumentation von WPML zur vollständigen Bearbeitung von Websites wird beschrieben, wie du eine bestimmte Konfiguration für Block-Themes benötigst:
// 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.'
);
}
});
Polylang Pro unterstützt den Site Editor seit Version 3.2. Das Plugin behandelt Block-Themes über seine Standard-String-Übersetzungsschnittstelle:
// 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
);
}
Die Dokumentation von TranslatePress zeigt, dass bestimmte dynamische Elemente für eine optimale Leistung ausgeschlossen werden müssen:
// 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 );
Schließlich gibt es noch ein paar allgemeine Tipps für die Arbeit mit Codebasen von Drittanbietern (z. B. Plugins). Erstens solltest du beim Debuggen von Übersetzungsproblemen systematisch vorgehen.
// 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' );
Das Zwischenspeichern auf der Website kann die Aktualisierung von Übersetzungen beeinträchtigen, daher solltest du die Zwischenspeicher löschen, wenn sich die Übersetzungsdateien ändern:
# 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
Die Leistungsoptimierung wird mit Übersetzungs-Plugins entscheidend. Jedes Plugin verursacht zusätzliche Datenbankabfragen und Verarbeitungsaufwand, was wiederum durch das Zwischenspeichern häufig verwendeter Übersetzungen begünstigt wird:
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;
}
Alternativ kann es sinnvoll sein, das Caching zu überspringen, bis du bereit für die Bereitstellung bist. Dafür ist eine Staging-Umgebung ideal, in der du den Leistungsschub, den das Caching bietet, normalerweise nicht brauchst.
Zusammenfassung
Die Internationalisierung des Block-Themes erfordert, dass du mit beiden WordPress-Übersetzungsmethoden arbeitest und neue Ansätze im Site-Editor verwendest.
Indem du deine Theme-Metadaten konfigurierst, Template-Strategien implementierst und die Anforderungen von Übersetzungs-Plugins verstehst, kannst du mehrsprachige Block-Themes erstellen, die gut funktionieren und ein hochwertiges Nutzererlebnis bieten.
Wenn du bereit bist, zu starten, bietet das Managed Hosting für WordPress von Kinsta die Leistung und globale Reichweite, die deine Website braucht, mit integriertem Caching, einem CDN mit 37 Standorten und Tools wie Git-Integration und Staging.