Step-by-Step Guide to Localized Theme Text Domains and Translations for Premium Gutenberg-First Themes
Understanding Text Domains in WordPress Themes
For any WordPress theme, especially those built with a Gutenberg-first approach, proper internationalization (i18n) is paramount. This begins with the concept of a ‘text domain’. A text domain is a unique identifier for your theme’s translatable strings. It acts as a key to look up the correct translation for a given string in a specific language file. Without a correctly defined and consistently used text domain, your theme’s text will remain untranslated, hindering global adoption.
In WordPress, text domains are typically defined in the theme’s `style.css` file and registered within the theme’s PHP files. For Gutenberg-first themes, this means ensuring that not only the classic WordPress elements but also the dynamic content generated by Gutenberg blocks are translatable.
Defining Your Theme’s Text Domain
The primary place to declare your theme’s text domain is in its main stylesheet, `style.css`. This is a WordPress requirement for themes to be recognized and processed correctly. The `Text Domain:` header is crucial.
For a theme named “MyGutenbergTheme”, a typical `style.css` header would look like this:
/* Theme Name: MyGutenbergTheme Theme URI: https://example.com/my-gutenberg-theme/ Author: Your Name Author URI: https://example.com/ Description: A modern Gutenberg-first theme with advanced features. Version: 1.0.0 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Tags: block-theme, gutenberg, responsive, custom-background, custom-logo Text Domain: my-gutenberg-theme Domain Path: /languages */
Key points here:
- Text Domain: This should be a unique, lowercase string, typically derived from your theme’s slug. It’s best practice to use hyphens instead of underscores.
- Domain Path: This specifies the directory where your translation files will reside. The standard is `/languages`.
The `Domain Path` directive tells WordPress where to look for the `.mo` translation files. When WordPress needs to translate a string, it will look for a file named `[text-domain]-[locale].mo` (e.g., `my-gutenberg-theme-fr_FR.mo`) within the specified `languages` directory.
Internationalizing Strings in Your Theme’s PHP Files
Now, let’s move to the PHP code. Every translatable string within your theme’s PHP files must be wrapped in WordPress’s internationalization functions. The most common ones are `__()` and `_e()`. For Gutenberg-first themes, this applies to template files, `functions.php`, and any included PHP files.
The general syntax for these functions is:
__( 'String to translate', 'text-domain' ); _e( 'String to translate', 'text-domain' );
Here’s an example of how you’d internationalize strings in a hypothetical `template-parts/content.php` file:
<?php
/**
* Displays content for a single post.
*
* @package MyGutenbergTheme
*/
?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php
if ( is_singular() ) :
the_title( '<h1 class="entry-title">', '</h1>' );
else :
the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' );
endif;
if ( 'post' === get_post_type() ) :
?>
<div class="entry-meta">
<?php
// Translators: %s: Post author name.
printf( esc_html__( 'Posted by %s', 'my-gutenberg-theme' ), '<span class="author vcard">' . esc_html( get_the_author() ) . '</span>' );
?>
</div><?php
endif;
?>
</header><!-- .entry-header -->
<div class="entry-content">
<?php
the_content( sprintf(
/* translators: %s: Name of current post. */
wp_kses( __( 'Continue reading %s →', 'my-gutenberg-theme' ), array( 'span' => array( 'class' => array() ), 'a' => array( 'href' => array(), 'rel' => array() ) ) ),
the_title( '<span class="screen-reader-text">', '</span>', false )
) );
wp_link_pages( array(
'before' => '<div class="page-links">' . esc_html__( 'Pages:', 'my-gutenberg-theme' ),
'after' => '</div>',
) );
?>
</div><!-- .entry-content -->
<footer class="entry-footer">
<?php // translators: used between list items, there is a space after the comma. Edit sparingly.
$tags_list = get_the_tag_list( '', esc_html__( ', ', 'my-gutenberg-theme' ) );
if ( $tags_list ) {
printf( '<span class="tags-links">' . esc_html__( 'Tagged %s', 'my-gutenberg-theme' ) . '</span>', $tags_list ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
<?php edit_post_link( esc_html__( 'Edit', 'my-gutenberg-theme' ), '<span class="edit-link">', '</span>' ); ?>
</footer><!-- .entry-footer -->
</article><!-- #post-<?php the_ID(); ?> -->
In this example:
- The string “Posted by %s” is translated using `printf` and `esc_html__()`. The `%s` is a placeholder for the author’s name.
- “Continue reading %s →” uses `wp_kses` for safe HTML output and `the_title` to dynamically insert the post title.
- “Pages:” and “Edit” are also wrapped in translation functions.
- The `/* translators: … */` comments are crucial for translators to understand the context of the string.
Internationalizing Strings in Gutenberg Block PHP Files
For custom Gutenberg blocks, the process is similar but often involves JavaScript for the editor interface and PHP for the server-side rendering. You’ll need to internationalize strings in both contexts.
PHP Server-Side Rendering:
// Example of a block's render_callback function
function my_gutenberg_theme_render_custom_block( $attributes ) {
$title = isset( $attributes['title'] ) ? $attributes['title'] : '';
$content = isset( $attributes['content'] ) ? $attributes['content'] : '';
// Translate strings used in the rendered output
$output = '<div class="my-custom-block">';
if ( ! empty( $title ) ) {
$output .= '<h3>' . esc_html( $title ) . '</ όπως;'; // Assuming title is set in attributes and needs translation
}
if ( ! empty( $content ) ) {
// If content itself is translatable, you'd handle it differently.
// Here, we assume it's user-provided text.
$output .= '<p>' . wp_kses_post( $content ) . '</p>';
}
$output .= '<p>' . esc_html__( 'This is a custom block.', 'my-gutenberg-theme' ) . '</p>';
$output .= '</div>';
return $output;
}
In this PHP rendering example, “This is a custom block.” is made translatable using the theme’s text domain.
Internationalizing Strings in Gutenberg Block JavaScript Files
For strings that appear within the Gutenberg editor interface (e.g., block titles, labels, help text), you’ll use JavaScript internationalization functions. WordPress provides a `wp.i18n` module for this.
First, you need to register your theme’s text domain for JavaScript translation. This is typically done in your theme’s `functions.php` file:
function my_gutenberg_theme_enqueue_scripts() {
// ... other script enqueues ...
wp_set_script_translations( 'my-gutenberg-theme-editor-script', 'my-gutenberg-theme', get_template_directory() . '/languages' );
}
add_action( 'enqueue_block_editor_assets', 'my_gutenberg_theme_enqueue_scripts' );
The `wp_set_script_translations` function takes the handle of your script, your theme’s text domain, and the path to your languages directory. This makes the translations available to your JavaScript.
Now, in your JavaScript file (e.g., `assets/js/editor.js`):
/**
* WordPress dependencies
*/
const { __ } = wp.i18n;
const { registerBlockType } = wp.blocks;
const { RichText } = wp.editor; // Assuming you are using RichText for editable fields
registerBlockType( 'my-gutenberg-theme/custom-block', {
title: __( 'My Custom Block', 'my-gutenberg-theme' ),
icon: 'smiley',
category: 'common',
edit: function( props ) {
const { attributes, setAttributes } = props;
const blockProps = {
className: props.className,
};
return (
<div { ...blockProps }>
<RichText
tagName="h3"
placeholder={ __( 'Enter block title...', 'my-gutenberg-theme' ) }
value={ attributes.title }
onChange={ ( newTitle ) => setAttributes( { title: newTitle } ) }
/>
<RichText
tagName="p"
placeholder={ __( 'Enter block content...', 'my-gutenberg-theme' ) }
value={ attributes.content }
onChange={ ( newContent ) => setAttributes( { content: newContent } ) }
/>
<p>{ __( 'This is a static string in the editor.', 'my-gutenberg-theme' ) }</p>
</div>
);
},
save: function( props ) {
// The save function typically returns null if render_callback is used in PHP.
// If not, you'd return the JSX for the saved block here.
return null;
},
} );
In this JavaScript example:
- `wp.i18n.__` is used to translate strings like “My Custom Block”, “Enter block title…”, “Enter block content…”, and “This is a static string in the editor.”.
- The text domain `’my-gutenberg-theme’` is passed as the second argument, matching the one defined in PHP.
Generating Translation Files (.pot, .po, .mo)
To create the actual translation files, you’ll use a tool called WP-CLI or a dedicated plugin like Loco Translate. WP-CLI is the command-line interface for WordPress and is highly recommended for development workflows.
Using WP-CLI to Generate a .pot File
A `.pot` (Portable Object Template) file is a template containing all the translatable strings from your theme. It’s the source file from which individual language translation files are created.
Navigate to your theme’s directory in your terminal and run the following WP-CLI command:
wp i18n make-pot . --slug=my-gutenberg-theme --domain=my-gutenberg-theme --headers='{"Language-Team":"My Team <[email protected]>"}' --include=template-parts,inc,assets/js
Let’s break down this command:
wp i18n make-pot .: This tells WP-CLI to scan the current directory for translatable strings and create a POT file.--slug=my-gutenberg-theme: Specifies the theme’s slug.--domain=my-gutenberg-theme: Specifies the text domain to look for.--headers='{"Language-Team":"My Team <[email protected]>"}': Adds custom headers to the POT file, useful for identifying the translation team.--include=template-parts,inc,assets/js: This is crucial. It tells WP-CLI which directories to scan for translatable strings. Adjust this to include all relevant directories in your theme.
This command will generate a `my-gutenberg-theme.pot` file in your theme’s root directory. This file should be placed inside the `languages` folder as specified in your `style.css`.
Creating Language-Specific .po and .mo Files
Once you have your `.pot` file, you can create translations for specific languages. A `.po` (Portable Object) file is a human-readable file containing the original strings and their translations. A `.mo` (Machine Object) file is a compiled binary version of the `.po` file, which WordPress uses for actual translations.
You can use tools like Poedit or Loco Translate to open your `.pot` file and create translations. For example, to translate into French (fr_FR):
1. **Using Poedit:** Open `my-gutenberg-theme.pot` in Poedit. Go to File > Save as… and save it as `fr_FR.po` in your theme’s `languages` directory. Translate the strings. Poedit will automatically generate `fr_FR.mo` when you save.
2. **Using Loco Translate Plugin:** Install and activate the Loco Translate plugin. Navigate to Loco Translate > Themes > MyGutenbergTheme. Click “New language”, select “French (fr_FR)”, and choose the location (e.g., “System” or “Theme” within the `languages` folder). Translate strings and save.
The final structure in your theme’s `languages` directory should look like this:
my-gutenberg-theme/
└── languages/
├── my-gutenberg-theme.pot
├── fr_FR.po
└── fr_FR.mo
Testing Your Translations
To test your translations, you need to change your WordPress site’s language. Go to Settings > General > Site Language and select the language you’ve translated into (e.g., French). Save changes.
Now, browse your site and the WordPress admin area. Any strings you’ve correctly internationalized and translated should appear in the chosen language. This includes:
- Theme-specific text in templates (e.g., “Posted by”, “Continue reading”).
- Text within Gutenberg blocks displayed on the front-end.
- Text within the Gutenberg editor interface (block titles, labels, placeholders).
If translations are not appearing, double-check:
- The `Text Domain` and `Domain Path` in `style.css`.
- The text domain used in all `__()` and `_e()` calls in PHP and `wp.i18n.__` calls in JavaScript.
- The script translation registration in `functions.php` for JavaScript strings.
- The correctness of the `.po` and `.mo` file names and their location within the `languages` directory.
- The language selected in WordPress settings.
By following these steps, you can ensure your premium Gutenberg-first theme is fully prepared for localization, making it accessible and user-friendly for a global audience.