7 Code Tweaks to Tame WordPress Multisite and Multilingual Mayhem
Ah, WordPress Multisite and Multilingual (in this case WPML) — because managing one site wasn’t quite complex enough! Whether you’re trying to make Bing respect your multilingual site or want to simplify activation steps in Multisite, these seven code tweaks will keep you sane.
(I’ve gathered these trusty code snippets from some of my older blog posts. Some might be a bit old, but they might still come in handy.)
1. Ensure Bing Knows Your Languages
Bing doesn’t always automatically recognise the different languages used on your multilingual website. This can affect how well your pages perform in search results, particularly in non-English languages.
This code snippet solves this by adding the appropriate language meta tags to your site’s header, allowing Bing to understand the various languages your site uses and serve the right content to users.
function matt_watson_bing_compatible_wpml_header() {
if ( ! function_exists( 'icl_get_languages' ) ) {
return;
}
$langs = icl_get_languages();
if ( empty( $langs ) ) {
return;
}
foreach ( $langs as $lang ) {
echo '<meta http-equiv="content-language" content="' . esc_attr( $lang['code'] ) . '"/>' . "
";
}
}
add_action( 'wp_head', 'matt_watson_bing_compatible_wpml_header', 0 );
2. WPML + Bing = A Match Made in Headers
When your WordPress site supports multiple languages, it’s important to tell search engines the language of each individual page to prevent them from misinterpreting your content. This code snippet helps by adding Content-Language
meta tags to your posts.
The snippet checks the current post and outputs the language code only if it matches the current language context. This ensures that Bing correctly identifies the language of each page it crawls.
function matt_watson_wpml_bing_compatible_header() {
if ( ! function_exists( 'icl_get_languages' ) || ! is_singular() ) {
return;
}
$langs = icl_get_languages();
if ( empty( $langs ) ) {
return;
}
$current_post_id = get_the_ID();
foreach ( $langs as $lang ) {
$id = icl_object_id( $current_post_id, get_post_type(), false, $lang['code'] );
if ( ! $id || $id !== $current_post_id ) {
continue;
}
echo '<meta http-equiv="content-language" content="' . esc_attr( $lang['code'] ) . '">' . PHP_EOL;
}
}
add_action( 'wp_head', 'matt_watson_wpml_bing_compatible_header', 0 );
3. Set the Right Content Language for Browsers
Browsers and crawlers rely on server responses to determine the correct language of your content, but WPML doesn’t automatically send this information in the HTTP headers. This can lead to browsers and crawlers incorrectly interpreting the language of your pages, especially when serving non-default languages.
The provided code snippet adds the correct Content-Language
header to your server responses, ensuring that both browsers and search engines accurately detect the language of the page.
function matt_watson_wpml_set_content_langauge( $headers ) {
if ( is_admin() || ! defined( 'ICL_LANGUAGE_CODE' ) ) {
return $headers;
}
$headers['Content-Language'] = ICL_LANGUAGE_CODE;
return $headers;
}
add_filter( 'wp_headers', 'matt_watson_wpml_set_content_langauge', 0 );
4. Add Alternate Language Links to Yoast Sitemaps
When using Yoast SEO to manage sitemaps on a multilingual site, it’s crucial to inform search engines about the alternative language versions of each page. However, Yoast doesn’t automatically include these alternate links in the sitemap when WPML is involved.
The following code snippet enhances Yoast’s sitemaps by adding alternate language links. This tells search engines that each page has a corresponding version in other languages, helping them to index the right content for each region.
function matt_watson_yoast_wpml_alternate_links_to_sitemap( $url, $type, $post ) {
if ( ! function_exists( 'icl_get_languages' ) || empty( $post ) ) {
return $url;
}
$langs = icl_get_languages();
if ( empty( $langs ) ) {
return $url;
}
$id = ( 'post' === $type ) ? $post->ID : $post->term_id;
$object_type = ( 'post' === $type ) ? $post->post_type : $post->taxonomy;
foreach ( $langs as $lang ) {
$translated_id = icl_object_id( $id, $object_type, false, $lang['code'] );
if ( ! $translated_id || $translated_id === $id ) {
continue;
}
$link = ( 'post' === $type ) ? get_the_permalink( $translated_id ) : get_term_link( $translated_id );
$url['wpml']['xhtml:link'][] = '<xhtml:link rel="alternate" hreflang="' . esc_attr( $lang['code'] ) . '" href="' . esc_url( $link ) . '"/>';
}
return $url;
}
add_filter( 'wpseo_sitemap_entry', 'matt_watson_yoast_wpml_alternate_links_to_sitemap', 100, 3 );
5. Sitemaps per Translation with WPML and Yoast SEO
When managing a multilingual site, generating a single sitemap might not be enough, as each language version of your site should have its own sitemap. This ensures search engines fully crawl all translations and index the correct language versions.
The following code snippet modifies the Yoast SEO sitemap query to generate separate sitemaps for each language, based on the current language context of WPML. This allows search engines to accurately map out each language version of your content.
function matt_watson_yoast_wpml_sitemap_per_translation( $join, $type ) {
global $wpdb, $sitepress;
if ( ! isset( $sitepress ) ) {
return $join;
}
$lang = $sitepress->get_current_language();
return $wpdb->prepare(
"JOIN {$wpdb->prefix}icl_translations ON element_id = ID AND element_type = %s AND language_code = %s",
'post_' . $type,
$lang
);
}
add_filter( 'wpseo_posts_join', 'matt_watson_yoast_wpml_sitemap_per_translation', 10, 2 );
6. Create a Virtual File in WordPress Multisite
In a WordPress Multisite setup, you may need to generate virtual files that change based on the specific site being accessed. This could be useful for things like dynamic sitemaps or XML feeds.
The code snippet below adds a query variable and creates a custom rewrite rule to serve a virtual file dynamically. It ensures that each individual site in the Multisite network can have its own version of the virtual file.
The rewrites:
function matt_watson_sitemap_rewrites() {
global $wp;
$wp->add_query_var('map');
if ( get_current_blog_id() == 1 ) {
add_rewrite_rule( 'sitemap.xml$', 'index.php?map=sitemap', 'top' );
}
if ( get_current_blog_id() === get_id_from_blogname('fr') ) {
add_rewrite_rule( '^sitemap-fr.xml$', 'index.php?map=sitemap-fr', 'top' );
}
}
add_action( 'init', 'matt_watson_sitemap_rewrites', 99 );
An example of a virtual file creation:
function matt_watson_generate_sitemap() {
$map = get_query_var( 'map' );
if ( ! $map ) {
return;
}
header( 'Content-Type: application/xml; charset=utf-8' );
echo '<?xml version="1.0" encoding="UTF-8"?>';
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
$posts = get_posts( [
'post_type' => 'post',
'posts_per_page' => -1,
'post_status' => 'publish',
] );
foreach ( $posts as $post ) {
echo '<url>';
echo '<loc>' . get_permalink( $post ) . '</loc>';
echo '<lastmod>' . get_the_modified_date( 'Y-m-d', $post ) . '</lastmod>';
echo '</url>';
}
echo '</urlset>';
exit;
}
add_action( 'template_redirect', 'matt_watson_generate_sitemap' );
7. Disable the WordPress Multisite Activation Step
In a WordPress Multisite environment, the default process requires users to activate their accounts via an email confirmation link. This can be unnecessary if you're managing internal users or trusted accounts.
The following code disables the activation step, allowing users to be added and activated instantly without requiring an additional email verification.
function matt_watson_remove_wpmu_activation( $user_login, $user_email, $key ) {
wpmu_activate_signup( $key );
return false;
}
add_filter( 'wpmu_signup_user_notification', 'matt_watson_remove_wpmu_activation', 10, 3 );
With these seven code tweaks, your WordPress Multisite and multilingual setup will be much easier to manage. From improving Bing’s understanding of your language setup to simplifying user registration, these snippets will help you streamline your operations and ensure your site runs smoothly across languages and networks.
Subscribe to my newsletter
Read articles from Matt Watson directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Matt Watson
Matt Watson
Hello there! I’m Matt Watson — a developer, father, and husband, remotely coding from the charming landscapes of Yorkshire, UK. I’ve been crafting wonderful things with WordPress since 2006 and spinning webs (the good kind) since 1996 — yes, I survived the dial-up era! Solving (and occasionally committing) WordPress-based crimes, I juggle code by day and dad jokes by night. Remote worker since 2014, I’ve mastered the art of debugging in my slippers while keeping the coffee industry in business.