Categories
Development WordPress

🌎A bilingual blog with Polylang: display Posts in their original language if not available in the current language

❓The problem

The problem was initially raised by WP Core committer and Google developer Pascal Birchler.

How to query posts in all languages using Polylang but only show one version if a post has multiple translations?

I had the same need on my personal website.

The idea is to display ALL blogposts on the main page of the blog.

  • If the post is available in the current language (= current language selected/used by the visitor), then display it in the current language and don’t display other translations of the post.
  • If the post is not available in the current language, then display an alternate translation.
  • Bonus: make it as accessible as possible 😌

💥The solution

In our functions.php file, let’s modify the main query to remove Polylang’s parameters. Thus, all posts will be displayed on your blog page:

/*
 * Query all posts versions on home page.
 */
function jba_modify_home_query( $query ) {
	if ( function_exists( 'pll__' ) && ! is_admin() && is_home() ) {
		$query->set('tax_query', '');
		$query->set('lang', '');
	}
}
add_action( 'pre_get_posts', 'jba_modify_home_query' );

I used some conditions before altering the query:

  • function pll__() should exists (if not, it means Polylang is not installed, so our blog is not multilingual!)
  • we are on front-end (not on WP Admin)
  • we are on the home page (= blog page)

Then, let’s tweak a bit the main loop on home.php.

Let’s say we have the following basic loop:

<?php if ( have_posts() ) : ?>
	<?php while ( have_posts() ) : the_post(); ?>
		<article id="post-<?php the_ID(); ?>">
			<h2 class="entry-title">
				<a href="<?php the_permalink(); ?>">
					<?php the_title(); ?>
				</a>
			</h2>
			<div class="entry-content excerpt-home">
				<?php the_excerpt(); ?>
			</div>
		</article>
	<?php endwhile; ?>
<?php endif; ?>

Right after the while (line 2), let’s add the following instructions:

<?php
if ( pll_get_post( get_the_ID() ) && get_the_ID() !== pll_get_post( get_the_ID() ) ) {
	continue;
}
?>

With that code, we’ll check if the current post in the loop is a translation of a post which exists in the current language. If so, we’ll use continue to ignore the following instructions in the while loop. Thus, the post won’t be displayed at all (because it exists in the current language the website is visited).

And that’s all for the functional part! We now have a bilingual blog where posts are displayed in the visitor’s language at first, and if they don’t exist in that language, then we propose them in another language! 🙌

But this is not very accessible since we are moving from one language to another, without giving any information to the user (and to search engines as well).

☝️Wait! Bring me some accessibility, please!

Alright, let’s prepare some useful stuff for displaying language informations in posts that are available in other languages:

<?php
$lang_attribute = '';
$lang_title = '';
$lang_slug = pll_get_post_language( get_the_ID(), 'slug' );
$lang_name = pll_get_post_language( get_the_ID(), 'name' );
if ( pll_current_language() !== $lang_slug ) {
	$lang_attribute = 'lang="' . $lang_slug . '"';
	$lang_title = '<span aria-label="(' . $lang_name . ')">[' . strtoupper( $lang_slug ) . ']</span> ';
}
?>

With this snippet, I’m filling two variables (only when the post is not on the current language):

  • $lang_attribute: it will contains something like lang="fr" so we will be able to add the information about the language on the <article> element: <article lang="fr">. It’s necessary for search engines and mandatory for assistive technologies like screen reader (so the screen reader can switch to the corresponding language).
  • $lang_title: it will contains a text to visually indicate the lang of the Post in the post’s title. Example: something like “[FR] My post title”. I included it in a <span>element with an aria-label attribute to use a full language label so screen readers will read “Français” instead of “FR”.

Then, we just have to use it in our previous HTML part:

<article id="post-<?php the_ID(); ?>" <?php echo $lang_attribute; ?>>
	<h2 class="entry-title">
		<a href="<?php the_permalink(); ?>">
			<?php echo $lang_title; ?><?php the_title(); ?>
		</a>
	</h2>
	<div class="entry-content excerpt-home">
		<?php the_excerpt(); ?>
	</div>
</article>

That’s all folks!

To see a working example of this, you just have to visit my home page 😃

🌮Full snippet, to take away!

In functions.php:

/*
 * Query all posts versions on home page.
 */
function jba_modify_home_query( $query ) {
	if ( function_exists( 'pll__' ) && ! is_admin() && is_home() ) {
		$query->set('tax_query', '');
		$query->set('lang', '');
	}
}
add_action( 'pre_get_posts', 'jba_modify_home_query' );

In home.php:

<?php if ( have_posts() ) : ?>
		
	<?php while ( have_posts() ) : the_post(); ?>
		
		<?php
		if ( pll_get_post( get_the_ID() ) && get_the_ID() !== pll_get_post( get_the_ID() ) ) {
			// Do not display other languages posts if a translation in the current language exists.
			continue;
		}
		$lang_attribute = '';
		$lang_title = '';
		$lang_slug = pll_get_post_language( get_the_ID(), 'slug' );
		$lang_name = pll_get_post_language( get_the_ID(), 'name' );
		if ( pll_current_language() !== $lang_slug ) {
			$lang_attribute = 'lang="' . $lang_slug . '"';
			$lang_title = '<span aria-label="(' . $lang_name . ')">[' . strtoupper( $lang_slug ) . ']</span> ';
		}
		?>
		<article id="post-<?php the_ID(); ?>" <?php echo $lang_attribute; ?>>
			<h2 class="entry-title">
				<a href="<?php the_permalink(); ?>">
					<?php echo $lang_title; ?><?php the_title(); ?>
				</a>
			</h2>
			<div class="entry-content excerpt-home">
				<?php the_excerpt(); ?>
			</div>
		</article>
	<?php endwhile; ?>

<?php endif; ?>

Now you just have to adapt this snippets for your needs. Feel free to comment below if you have questions or if you are using this solution on your website! Or even if you are using a different (and maybe better) solution! 💜💬

Leave a Reply

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