Plugin: Polylang

Plugin: Polylang

There is also a free, easy to use, and very light-weight plugin called Polylang. Migration from WPML to Polylang should be painless with dedicated plugin WPML to Polylang by PolyLang creators.

Polylang Pro is required for ACF Pro compatibility, as stated in official document. They also recommend to use third-party plugin to translate ACF options: ACF Options For Polylang

Not sure if Pro is really required as some users are stating that “ACF works great with Polylang and everything works out of the box as expected. No dirty hacks are needed, except for “options” page.”

For CF7, if you don´t want to create a form for each language, you can use one single CF7 form for all languages using plugin Multilingual Contact Form 7 with Polylang

For “String Translation” Polylang officially recommends utilizing any PO/MO tools: How to translate the themes and plugins hardcoded strings?. More precisely, they now provide a resource that explains Loco Translate as the ideal complement to Polylang. BEA – Fix Loco Translate

In addition, there is a solution that I personally employ, which is a WP plugin: Theme and plugin translation for Polylang (TTfP) that accomplishes precisely what I require and works marvelously.


Using Polylang without paying for Pro

If you need proper WooCommerce, I guess you must buy that module, but for everything else, free is sufficient.

Main differences Pro vs Free:

  1. Important: Translations can share the same slug
  2. Important: Translate slugs in URLS (custom post types, taxonomies and more…)
  3. Good to have: Duplicate posts across languages
  4. Good to have: Additional integration with ACF Pro, meaning ACF Options fields

Every difference is solved with free plugins:

Translations can share the same slug in the URL

When we say “same slug” we mean that /wizard, /nl/wizard and /de/wizard are the same. This is the main reason to buy Pro, as explained here

It is properly solved with plugin grappler/polylang-slug, which is not actively developed. An alternative plugin that should also work is lufton/polylang-share-slug, which has not been updated for quite some time. The official Polylang Pro also references the polylang-slug project in its source code.

Bad news it that on this plugin that works, in repository states: “This plugin is not being actively developed”

Duplicate posts across languages

Not sure this is essential functionality, but it is useful. It means that when we create a new translation, the whole post content, title and attachments are copied.

Based on reviews, this one is quite active and tested: Polylang Duplicate Content, but as usual, we have a viable alternative: aucor/polylang-copy-content

Also not sure about the following problem, that probably arises when cloning post using some specialized plugins: mindtwo/post-duplicator-polylang

Rewriting slugs aka translate slugs in post types

Based on this article on Translating URLS slugs from 2015, I believe this option was free before but now it is not.

There are too many plugins to enable rewriting slugs, but I’ve discovered that this one is only maintained and works: webnucleo/wp-polylang-translate-rewrite-slugs

The one on the plugin repository is real exploding bomb with bad code: WP Slug Post Type Custom Language (Polylang) | carlosramosweb/wp-slug-post-type-custom-language.

There is also this code snippet that might work as well: stouch/wp-plugin-polylang-localized-taxonomy-slug and I think this for similar purpose extracted from Pro: rizkiaprita/polylang-pro

REST API Support

Is probably solved with maru3l/wp-rest-polylang

Additional integration with ACF Pro

Is perfectly resolved with “Options” plugin: ACF Options For Polylang | BeAPI/acf-options-for-polylang

Amazing additions to the plugin

Essential

Very Good

Interesting


How to remove the Polylang plugin

Quite nice and possible.


Polylang and WP-CLI

Polylang stores language information as a taxonomy ‘language’ for posts and ‘term_language’ for terms.

In WP-CLI we can use https://github.com/diggy/polylang-cli, but without it, we can use:

To list languages:

wp term list language

List English pages

wp post list --post_type=page --lang=en

or:

wp post list --post_type=page --lang=nl --fields=ID,post_title

Using SQL to modify Polylang data

Source: Search and replace query with multilanguage site (polylang)

List all English pages

1
2
SELECT id, post_title FROM `wp_posts` WHERE post_type = 'page' AND id IN (
    SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(`description`, '"en";i:', -1), ';',1) FROM `wp_term_taxonomy` WHERE `taxonomy` = 'post_translations' )

Do a basic search-replace on one language, for example nl for Dutch

1
2
3
4
5
6
UPDATE `wp_posts` AS a
INNER JOIN (
  SELECT id, post_title FROM `wp_posts` WHERE post_type = 'page' AND id IN (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(`description`, '"en";i:', -1), ';',1) FROM `wp_term_taxonomy` WHERE `taxonomy` = 'post_translations' )
) AS b
ON a.ID = b.ID
SET a.post_content = replace(a.post_content, '<!-- wp:block {"ref":2323} /-->', '<!-- wp:block {"ref":4247} /-->');

In above query, I’m actually replacing one Gutenberg Reusable Block with another for specific language.


Translation of Reusable Blocks

The problem exists both on WPML and on Polylang and most of the other translation plugins for WordPress.

It sometimes worked, but just by luck

Currently, reusable blocks can’t be translated, as they are stored on pages by their ID.

Manifestation and temporary solution

In any language some page is displayed, the reusable block is presented in the original language used when that block was inserted into that page.

I’ve used a temporary fix that is not bulletproof, but it worked for some time. The idea is to check during page rendering if a reusable block present on the page has translation available and if it has it, to use that translation instead of the original reusable block.

Read more about this idea on Polylang support thread: https://wordpress.org/support/topic/reusable-block-translations/

Proper permanent solution

The only proper solution is to fully ignore “language” info on reusable blocks as that is obviously not working. Then on every page translation insert the block that you need in that exact language, without expecting automatic language switching on reusable blocks.

To ease this process, it is best to include language abbreviations in block titles (EN, DE, etc) so that when using them on pages, to distinguish them easily.


I’m assuming that URL modifications > Hide URL language information for default language option is set and active

Polylang uses a cookie to remember the language selected by the user when he comes back to visit again the website. This cookie is also used to get the language information when not available in another way. Examples are AJAX requests (quite important) or the login page (less important).

About Polylang Cookie and EU cookie law

Polylang plugin is setting a cookie for language preference using the ˙set-cookie` header from PHP.

Polylang works just fine without the cookie, if you have turned on “URL modifications > The language is set from the directory name in pretty permalinks”. In this case, it is safe to disable the cookie by using define('PLL_COOKIE', false);

There are more Polylang options that could be set in wp-config.php

It is possible to modify cookie expiration using pll_cookie_expiration hook. For example, if you prefer a cookie expiring at the end of the session, add this: add_filter( 'pll_cookie_expiration', function() { return 0; } );; more info here

Polylang Language Auto-detection

Nginx Cache and Polylang have a problem, a huge one. Let’s try to explain a problem.

When the Polylang setting Detect browser language: When the front page is visited, set the language according to the browser preference is checked, then the first visitor that comes along will get redirected to his/her language correctly, but … that redirect response will be cached by Nginx! Now subsequent visitors with different browser languages will get served the same cached response, and they will be redirected to the wrong language.

That’s why we have a problem with static caching. On the server side, it can be solved by updating the proxy_cache_key directive to include pll_language cookie as mentioned in this support thread

Raidboxes.io uses a Nginx web server to cache pages. There is a known issue that Polylang language detection has with caching on the server. It is described more in the following support comment - Nginx fastcgi_cache and Polylang homepage redirects and another article

… and another article from WP Rocket Knowledge Base

Possibilities in solving the homepage cache problem

  1. One solution could be NOT to cache the homepage, but as our server is quite slow, we must avoid this. There is also no way to bust cache only the homepage on Raidboxes.io. For regular PHP-based caching plugins, Polylang is exactly using this cache-busting technique, as seen in the code here polylang/cache-compat.php](https://github.com/polylang/polylang/blob/master/integrations/cache/cache-compat.php)

Polylang can’t turn off the cache on the homepage, as the cache resides later in the response chain, on Nginx server and not on the PHP plugin level.

  1. Turn off language detection and turn off cookies, so user will never redirect. This is a viable solution, but in our case with 90% of users from non-default language, we shouldn’t use this one

  2. Write custom auto-detection and redirection code, based not on server PHP but on client-side JavaScript. This is the best way to go.

Final approach

  1. Don’t use Detect browser language option as it won’t work. It detects language using PHP $_SERVER['HTTP_ACCEPT_LANGUAGE'] and as such has a problem with server caching
  2. Leave PLL_COOKIE as it doesn’t influence anything else, and we won’t use it anyway
  3. Use custom JavaScript code for language detection and store selection inside a custom cookie

Ideas on JavaScript implementation

Interesting JavaScript implementations found: inside this support thread and this one

Redirection is done only on the home page and only on the default language, URL without language suffix.

Verify functionality
  1. Test language detection — in new incognito mode (so that there are no cookies set), navigate to cnc24.com, and you’ll be redirected to your browser language, probably German
  2. Test last used language — in normal mode, switch using language switcher to a different language. After that, open a new tab and navigate to cnc24.com. You should be redirected to that selected language.

ACF Options in Polylang

BeAPI/acf-options-for-polylang: A WordPress plugin for adding ACF options page support for Polylang.

Options page + polylang - ACF Support Usage of get_fields(‘options’) · Issue #62 · BeAPI/acf-options-for-polylang

The plugin is designed to get the Polylang “All languages” value if the current lang one is empty. You can easily disable that to avoid confusion.


I purchased the Polylang Block Language plugin, but I have a problem with it that is becoming increasingly annoying.

Hello Jakub,

I am contacting you because I am a paid user of your plugin “Polylang block language”. Everything is working as expected, but I am not satisfied with one detail that I assume is not difficult to implement.

Namely, the plugin inserts a lot of “extra code” into the HTML code produced by the editor.

For example:

date 15. Jul 2021 | modified 17. Aug 2024
filename: Plugins » Localization » Polylang