How to Customize Standard WordPress Comment Templates in Multi-Language Site Networks
Understanding WordPress Comment Template Hierarchy
When WordPress renders comments on a post or page, it follows a specific template hierarchy. For single-site installations, the primary template file is comments.php. However, in a multisite network, especially when dealing with different languages, this becomes more nuanced. WordPress looks for templates in a specific order: first in the active theme, then in parent themes, and finally in the core WordPress files. For multisite and multilingual setups, we often need to override these defaults to serve language-specific comment forms and lists.
Leveraging Theme Overrides for Multilingual Comments
The most robust way to customize comment templates for different languages is by creating language-specific template files within your theme. This approach ensures that each language version of your site can have its own distinct comment display and form.
Creating Language-Specific Comment Template Files
Let’s assume you are using a theme that supports internationalization and you have a way to detect the current language (e.g., using a plugin like WPML or Polylang). The standard WordPress function get_comments_template() is responsible for determining which comment template file to load. We can hook into this process to inject our custom logic.
First, identify the language code for the current site or post. If you’re using Polylang, you can get the current language object like this:
// In your theme's functions.php or a custom plugin
add_filter( 'comments_template', 'my_multilingual_comments_template' );
function my_multilingual_comments_template( $file ) {
// Check if Polylang is active and get the current language
if ( function_exists( 'pll_current_language' ) ) {
$current_lang = pll_current_language( 'slug' ); // e.g., 'en', 'fr', 'es'
// Define a path for language-specific comment templates
$lang_template_path = get_template_directory() . '/comments/' . $current_lang . '-comments.php';
// If a language-specific template exists, use it
if ( file_exists( $lang_template_path ) ) {
return $lang_template_path;
}
}
// Fallback to the default or a generic template if no language-specific one is found
// You might want to return a generic 'comments.php' or a specific fallback
return $file;
}
In this example, we’re looking for files named en-comments.php, fr-comments.php, etc., within a comments/ subdirectory of your active theme. If such a file exists for the current language, it will be loaded instead of the default comments.php.
Structuring Language-Specific Comment Templates
Once you have the filter in place, you need to create the actual template files. For instance, if your current language slug is ‘fr’, you would create wp-content/themes/your-theme/comments/fr-comments.php.
A typical comments.php file contains two main parts: the comment list and the comment form. You’ll want to translate these strings and potentially adjust the structure or fields for each language.
Example: French Comment Template (fr-comments.php)
<?php
/**
* The template for displaying comments.
*
* This file is used to display the comments.
*
* @package WordPress
* @subpackage YourThemeName
*/
// Prevent direct access to the file.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/*
* If the current post is protected by a password and
* the visitor has not yet entered the password we will
* return early without displaying the comments.
*/
if ( post_password_required() ) {
return;
}
?>
<!-- wp:paragraph -->
<h2 class="comments-title">
<?php
$comment_count = get_comments_number();
if ( 1 === $comment_count ) {
printf( _x( 'One thought on “%s”', 'comments title', 'your-text-domain' ), get_the_title() );
} else {
printf( _n( '%1$s thought on “%2$s”', '%1$s thoughts on “%2$s”', $comment_count, 'your-text-domain' ), number_format_i18n( $comment_count ), get_the_title() );
}
?>
</h2>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ol class="comment-list">
<?php
wp_list_comments( array(
'style' => 'ol',
'short_ping' => true,
'avatar_size' => 42,
) );
?>
</ol>
<!-- /wp:list -->
<!-- wp:paragraph -->
<div class="comment-navigation">
<?php previous_comments_link( esc_html__( '← Older Comments', 'your-text-domain' ) ); ?>
<?php next_comments_link( esc_html__( 'Newer Comments →', 'your-text-domain' ) ); ?>
</div>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<?php
// If comments are closed and there are no comments, let's leave the comment form hidden.
if ( ! comments_open() && get_comments_number() ) :
?>
<p class="no-comments"><?php esc_html_e( 'Comments are closed.', 'your-text-domain' ); ?></p>
<?php endif; ?>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<?php
comment_form( array(
'title_reply_before' => '<h3 id="reply-title" class="comment-reply-title">',
'title_reply_after' => '</h3>',
'title_reply' => esc_html__( 'Leave a Reply', 'your-text-domain' ), // French: 'Laisser une réponse'
'comment_notes_before' => '<p class="comment-notes"><span id="email-notes">' . esc_html__( 'Your email address will not be published.', 'your-text-domain' ) . '</span> <span class="required">' . esc_html__( 'Required fields are marked', 'your-text-domain' ) . '</span>',
'comment_notes_after' => '<span class="form-allowed-tags">' . sprintf( esc_html__( '%s', 'your-text-domain' ), 'You may use these <abbr title="HyperText Markup Language">HTML</abbr> tags and attributes: %s.' ) . '</span></p>',
'fields' => array(
'author' => '<p class="comment-form-author"><label for="author">' . esc_html__( 'Name', 'your-text-domain' ) . '</label><span class="required">*</span><input id="author" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30" aria-required="true" /></p>',
'email' => '<p class="comment-form-email"><label for="email">' . esc_html__( 'Email', 'your-text-domain' ) . '</label><span class="required">*</span><input id="email" name="email" type="email" value="' . esc_attr( $commenter['comment_author_email'] ) . '" size="30" aria-required="true" /></p>',
'url' => '<p class="comment-form-url"><label for="url">' . esc_html__( 'Website', 'your-text-domain' ) . '</label><input id="url" name="url" type="url" value="' . esc_attr( $commenter['comment_author_url'] ) . '" size="30" /></p>',
),
'comment_field' => '<p class="comment-form-comment"><label for="comment">' . esc_html__( 'Comment', 'your-text-domain' ) . '</label><textarea id="comment" name="comment" cols="45" rows="8" aria-required="true"></textarea></p>',
) );
?>
<!-- /wp:paragraph -->
Key points for translation:
- Text Domain: Ensure your theme has a unique text domain (e.g.,
your-text-domain) and that all translatable strings are wrapped in WordPress translation functions likeesc_html__(),esc_html_e(),_x(), and_n(). - Comment Count Title: The title displaying the number of comments is translated using
_x()and_n(). - Comment Navigation: Links for older and newer comments are translated.
- Comment Form Fields: Labels for Name, Email, Website, and the comment textarea itself are translated.
- Comment Form Titles: The “Leave a Reply” title and introductory notes are translated.
Handling Multisite Network and Subdirectories/Subdomains
In a multisite network, each site can have its own language settings. The filter function we defined earlier using pll_current_language() works well if Polylang is configured to assign a unique language slug to each site in the network. If you are using different themes per site or have a complex multisite setup, you might need to adjust the path to the template file.
For instance, if you need to get the template path relative to the network’s shared theme directory (if applicable) or based on the current site ID, you would modify the path construction:
add_filter( 'comments_template', 'my_multilingual_comments_template_multisite' );
function my_multilingual_comments_template_multisite( $file ) {
$current_site_id = get_current_blog_id();
$current_lang = '';
// Example: Using Polylang's site-specific language detection
if ( function_exists( 'PLL' ) && PLL()->options->get_option( 'force_lang_in_slug' ) ) {
// If language is forced in slug, it's usually part of the URL or site settings
// This might require more complex logic depending on Polylang's network setup
// For simplicity, let's assume we can get a language slug per site
$current_lang = get_blog_option( $current_site_id, 'pll_language' ); // This might not be directly available, check Polylang docs
} elseif ( function_exists( 'pll_current_language' ) ) {
// Fallback to current request language if site-specific isn't easily found
$current_lang = pll_current_language( 'slug' );
}
if ( ! empty( $current_lang ) ) {
// Option 1: Theme-specific directory (as before)
$lang_template_path_theme = get_template_directory() . '/comments/' . $current_lang . '-comments.php';
if ( file_exists( $lang_template_path_theme ) ) {
return $lang_template_path_theme;
}
// Option 2: Network-wide theme directory (if using a shared theme)
// Note: This path might vary based on your multisite configuration
$network_theme_dir = trailingslashit( WP_CONTENT_DIR ) . 'mu-plugins/themes/your-shared-theme/comments/' . $current_lang . '-comments.php';
if ( file_exists( $network_theme_dir ) ) {
return $network_theme_dir;
}
}
// Fallback to the default
return $file;
}
The logic for detecting the language in a multisite environment can be complex and depends heavily on how your multilingual plugin is configured. Always test thoroughly on each site within your network.
Alternative: Using a Single Template with Conditional Logic
For simpler multilingual sites or if you prefer to manage a single comments.php file, you can use conditional logic within that file to display language-specific strings or even modify the form fields. This is less maintainable for significant structural changes but can work for minor text adjustments.
// Inside your theme's comments.php
<?php
// ... (previous code)
// Get current language
$current_lang = '';
if ( function_exists( 'pll_current_language' ) ) {
$current_lang = pll_current_language( 'slug' );
}
// Dynamically set form titles and text based on language
$comment_form_args = array(
'title_reply' => ( 'fr' === $current_lang ) ? esc_html__( 'Laisser une réponse', 'your-text-domain' ) : esc_html__( 'Leave a Reply', 'your-text-domain' ),
'comment_notes_before' => ( 'fr' === $current_lang ) ? '<p class="comment-notes"><span id="email-notes">' . esc_html__( 'Votre adresse e-mail ne sera pas publiée.', 'your-text-domain' ) . '</span> <span class="required">' . esc_html__( 'Les champs obligatoires sont marqués', 'your-text-domain' ) . '</span>' : '<p class="comment-notes"><span id="email-notes">' . esc_html__( 'Your email address will not be published.', 'your-text-domain' ) . '</span> <span class="required">' . esc_html__( 'Required fields are marked', 'your-text-domain' ) . '</span>',
// ... other arguments
);
comment_form( $comment_form_args );
// ... (rest of the code)
?>
This method relies heavily on the translation functions and the language detection mechanism. While it keeps your theme cleaner by having fewer files, it can make the comments.php file quite lengthy and harder to manage if the differences between languages are substantial.
Best Practices and Considerations
- Theme Updates: If you’re modifying a parent theme’s
comments.phpdirectly, your changes will be lost on theme updates. Always use a child theme or a custom plugin for your modifications. - Performance: Ensure your language detection logic is efficient. Avoid unnecessary database queries within the filter.
- Caching: Be mindful of WordPress caching plugins. Ensure that language-specific comment templates are served correctly and not cached incorrectly. You might need to clear cache after making changes or configure cache exclusion rules.
- User Experience: Test the comment form and display on different devices and browsers for each language to ensure a consistent and positive user experience.
- Accessibility: Ensure all translated strings and form elements adhere to accessibility standards (WCAG).
By implementing language-specific comment templates, you can provide a more tailored and user-friendly experience for your international audience, ensuring that comments and discussions on your WordPress multisite network are as effective as possible across all languages.