Currently reading: The Runaway Jury by John Grisham

Including Gutenberg patterns with WPML involved

I previously posted about how to render patterns in your templates using their ID or slug. With WPML involved, things may get a little more complicated.

Rather than do its translation on the fly on the user's end, WPML instead creates a copy of everything you do on the back end per language you've told it to use. If you have three non-English languages set up, each new post you make results in four posts total. Your one post is sent to WPML's server for translation three times, and the results are stored back on your server's end.

This is a mostly seamless affair, and you generally won't see or even have to know this is happening. Where it becomes a bit of a problem is in doing silly things like including Gutenberg patterns in your theme code!

One of WPML's options is to translate your site's URL slugs as well as its content. With that enabled you may, for instance, end up with a "contact us" page situation like this:

  • /en/contact-us
  • /es/contacta-con-nosotros
  • /fr/contactez-nous
  • /de/kontaktieren-sie-uns

The first is the page you made and the others are ones WPML makes for you. Note: I don't know if these slugs are correct or what WPML would produce; I just threw "contact us" into Google Translate and copy-pasted the result. 🤷‍♀️

How does WPML know which version to grab for the user clicking around your site? It applies various filters to your site's queries that modify their SQL statements. The user's current language and the translated slug are used in joins with WPML's own database tables. It's pretty slick.

Which brings us back to our pattern-getting code from last time. Remember how I said you could do it by ID or slug?

<?= get_pattern(42) ?>
<?= get_pattern('contact-us') ?>

And remember how I said patterns are just a post type to WP? Well, WPML has treated them as it does all other post types: It has created separate translated copies of them with translated slugs. As such, for your non-English users, it will look for a contact-us pattern in its database under their language and won't find one. Your template will now lack a contact section in all languages but English.

I tried to "trick" WPML at first by changing my template code to send contact-us through the gettext hook.

<?= get_pattern(__('contact-us')) ?>

I'm not entirely sure why that didn't work, because I know WPML uses gettext to do its various things, but much to my surprise and annoyance, I was still getting blank spots for the non-English views where that pattern should be.

After some poking around in the relevant documentation, I was unable to find a filter to get a translated slug. Note that wpml_get_translated_slug is, despite the name, not what the correct one; it's only concerned about the slug for post types! The answer may still lie somewhere in that hook list, but in the interest of time I simplified my pattern-getting code to only use an ID instead of also allowing a slug.

/**
 * Fetch and render a translated pattern by its ID.
 */
function get_pattern(int $id) : string {
	$id      = apply_filters('wpml_object_id', $id, 'wp_block');
	$pattern = get_post($id);

	return ($pattern) ? render_post($pattern) : '';
}

You could easily reduce this down a one-liner if you'd like.

The wpml_object_id filter matches up an original ID to a translated ID with a given language. One of the unused parameters to the hook is the language code, so in leaving it off WPML defaults to the user's current language. By cross-referencing your pattern's English ID with its own database when doing the query triggered by get_post(), it will now grab the translated version.

Yes, using an ID in your template is less clear than a slug, but that's nothing a comment can't fix. 🙃

<?= get_pattern(42) // contact-us ?>

Leave a comment

Your email address will not be published. Required fields are marked *