WPML

Some things work differently on a multilingual site by design: a WordPress function keyed on a default-language term, a date suffix WordPress cannot localize, a slug the Advanced Translation Editor does not invent. Support questions raise them as bugs; they are not. This page says what happens, why, and what to do instead.

has_category() and other conditional tags on translated posts

WordPress core's has_category() is not filtered by WPML, on purpose. On a translation the category is the translated term, which has its own slug, so has_category('news') is false on the French post even though the French post is in the French "news" category. The same holds for has_term(), in_category() and any code that compares a default-language slug or term id. Call the conditional with the translated slug, or resolve the term id through the wpml_object_id filter first.

What you see

The native WordPress function has_category does not work for translated posts.

An example of this is as follows:

  1. We have a category with the English slug “test” and its French translation “test-fr”.
  2. This category is assigned to posts.
  3. When you invoke the has_category("test") inside a loop in your theme’s single.php file, it returns true for the default language but false for the translation.

What to do

Define a new filter in your theme’s functions.php file:

function wp_has_category ($category) {
$category = get_term_by('name', $category, 'category');
return has_category($category);
}
add_filter('wp_has_category', 'wp_has_category', 10, 1);

Replace the has_category function with this new filter:
apply_filters( 'wp_has_category', 'test' )

This filter takes the translated category based on the name of original one and checks if the translated category is assigned to a post.

Reference: wpmlcore-3024.

Date suffixes (1st, 2nd, 3rd) are not translatable

This is a limitation of WordPress core, not of WPML: the suffix comes from PHP's date formatting and never passes through a translation function.

What you see

Some  themes or plugins use the wp_date() or date_i18n() WordPress functions to display dates. These dates may use a suffix as 1st or 26th, depending on the parameters selected.

Currently, it’s not possible to translate date suffixes because of a longstanding known limitation in WordPress core.

What to do

As a workaround, you can use the wp_date filter to get the date, then modify the suffix before it’s rendered on the page. Please make a full backup of your site before proceeding and follow the steps below:


  • Add a small wp_date filter to your theme's functions.php that registers each suffix as a string (icl_register_string) and returns its translation (icl_t) in place of the English suffix:

  • Open the page where the dates are displayed. This will execute the filter added.

  • Go to WPMLString Translation and translate the dates.

  •  You will need to translate each suffix separately (e.g. 1st, 2nd…) because it may change depending on the language.

Reference: compsupp-5773.

The Advanced Translation Editor does not invent a slug

The editor translates the slug field when you fill it and keeps the original slug when you leave it empty. It never derives a slug from the translated title on its own.

What you see

If you translate a post using our Advanced Translation Editor, the slug field is not filled automatically. If you then decided to leave the slug field empty, it loads the original language slug in the translated post.

Reference: wpmlsupp-8574.

Page-builder shortcodes in a WooCommerce product short description

The short description is the post excerpt, a plain-text field by WordPress's definition, so WPML does not parse page-builder shortcodes in it the way it does in the content. WooCommerce happens to render them, which is why the mismatch surprises people.

What you see

The WooCommerce product short description is based on the post excerpt field, and that is ordinarily a simple text field that does not handle shortcodes. As such it is not expected that if you paste page-builder shortcodes into the post excerpt they would work, and when translating post excerpts WPML does not attempt to handle such shortcodes.

But WC handles these “excerpts” a little differently, and shortcodes do work, but WPML still acts as if they do not, and if the shortcode includes attributes that need translating or internal links that should be handled automatically that is not readily possible.

What to do

We have prepared code that can be added as a plugin to provide this functionality. Copy and paste the below code into a file that you might name wpmlpb-excerpts.php and save in your wp-content/plugins/ directory, then activate the plugin.

<?php
/**
 * Plugin Name: WPML Page Builders - Proces Excerpts
 * Description: Parses shortcode strings in exceprts.
 * Version: 1.0
 */
namespace WPMLPB686;

( new Excerpts() )->add_hooks();

class Excerpts {

	const SEPARATOR_START = '<!--WPML_EXCERPT_START-->';
	const SEPARATOR_END   = '<!--WPML_EXCERPT_END-->';

	public function add_hooks() {
		add_filter( 'wpml_pb_shortcode_content_for_translation', [ $this, 'joinWithContent' ], 10, 2 );
		add_filter( 'wpml_tm_translation_job_data', [ $this, 'excludeFromJob' ], 10, 2 );
		add_action( 'wpml_pro_translation_completed', [ $this, 'splitFromContent' ], 999 );
	}

	public function joinWithContent( $content, $id ) {
		$post = get_post( $id );
		if ( $post && $post->post_excerpt ) {
			$content .= self::SEPARATOR_START . $post->post_excerpt . self::SEPARATOR_END;
		}

		return $content;
	}

	public function excludeFromJob( $package, $post ) {
		if ( preg_match( '/[[^]]+]/', $post->post_excerpt ) ) {
			$package['contents']['excerpt']['translate'] = 0;
			$package['contents']['excerpt']['data']      = '';
		}

		return $package;
	}

	public function splitFromContent( $post_id ) {
		$post = get_post( $post_id );
		if ( $post && false !== strpos( $post->post_content, self::SEPARATOR_START ) ) {
			$start_pos = strpos( $post->post_content, self::SEPARATOR_START );
			$end_pos   = strpos( $post->post_content, self::SEPARATOR_END, $start_pos );

			if ( false !== $end_pos ) {
				$content = substr( $post->post_content, 0, $start_pos );
				$excerpt = substr( $post->post_content, $start_pos + strlen( self::SEPARATOR_START ), $end_pos - $start_pos - strlen( self::SEPARATOR_START ) );

				if ( $excerpt ) {
					wp_update_post(
						[
							'ID'           => $post_id,
							'post_content' => $content,
							'post_excerpt' => $excerpt,
						]
					);
				}
			}
		}
	}
}

Reference: wpmlpb-686.

Different domains per language on WordPress.com hosting

WordPress.com maps one domain to a site; the "different domains per language" URL format needs every language domain to reach the same WordPress, which that hosting does not offer.

What you see

Because of limitations on how domain mapping works with WordPress.com it is not possible to choose the language URLs setting to use different domains per language when sites are hosted with them.

What to do

Either of the other language URL options, with languages in directories or added as a URL parameter, can be used instead of different domains per language.

A different host would be needed if different domains per language were a requirement.

Reference: wpmltriage-2045.

Imported translations and the translation editors

An import writes finished translations; it does not create translation jobs, and the editors work from jobs. The translations are complete on the front end and simply not editable in the Advanced or Classic Translation Editor.

What you see

Whenever you import content using WP All Import and the WPML All Import add-on, you will see the imported post translations perfectly on the front-end. However, you will encounter issues if you try to update them using WPML’s translation editors:

This is because when you import multilingual content, WPML does not create translation jobs, which are needed for string segmentation.

What to do

To edit or update your imported translations, please switch to the native WordPress editor and use manual translation.

Reference: WPMLAI-168.