Polylang allows you to manage the translations of your posts and pages. However, you may experience issues when using Polylang and Rank Math together, that includes problems with redirections, sitemaps, and canonical URLs. This is because Polylang doesn’t have native compatibility for Rank Math and doesn’t intend to, despite all the efforts from our end.
In this knowledgebase article, we will help you resolve these compatibility issues by manually adding the code to your website.
1 Add Compatibility Code to Your rank-math-ppl.php File
Start by creating a new file named rank-math-ppl.php
in your theme’s folder. You will need either FTP or cPanel File Manager to create this PHP file.
1.1 Create rank-math-ppl.php File
If you’re using cPanel, then you can use their File Manager to create your new file named rank-math-ppl.php
inside your theme folder (/wp-content/themes/theme-name/), as shown below.
Alternatively, if you are using FTP, navigate to your theme’s folder (/wp-content/themes/theme-name/) and then create a new file named rank-math-ppl.php
, as shown below.
1.2 Navigate to Theme Editor
Once you have created the PHP file in your theme folder, you can edit the file by navigating to Appearance → Theme File Editor (for Classic Theme) or Tools → Theme File Editor (for Block Theme) in your WordPress admin area.
The right side of your screen lists all the theme files and templates. The files being shown here would vary depending upon the theme you’re using on your website. You can access the rank-math-ppl.php file you created in the previous step from the list of files.
1.3 Add Code to rank-math-ppl.php file
Here, copy and paste the below code into the file.
<?php
use RankMath\Sitemap\Sitemap;
/**
* Manages the compatibility with the Rank Math plugin
*
*/
class PLL_RankMath {
/**
* Add specific filters and actions
*/
public function init() {
add_action( 'wp_loaded', [ $this, 'rm_translate_options_keys' ] );
if ( PLL() instanceof PLL_Frontend ) {
// Filters sitemap queries to remove inactive language or to get
// one sitemap per language when using multiple domains or subdomains
// because Rank Math does not accept several domains or subdomains in one sitemap
add_filter( 'rank_math/sitemap/post_count/join', [ $this, 'rank_math_sitemap_join_clause' ], 10, 2);
add_filter( 'rank_math/sitemap/post_count/where', [ $this, 'rank_math_sitemap_where_clause' ], 10, 2);
add_filter( 'rank_math/sitemap/get_posts/join', [ $this, 'rank_math_sitemap_join_clause' ], 10, 2 );
add_filter( 'rank_math/sitemap/get_posts/where', [ $this, 'rank_math_sitemap_where_clause' ], 10, 2 );
if ( PLL()->options['force_lang'] > 1 ) {
add_filter( 'rank_math/sitemap/enable_caching', '__return_false' ); // Disable cache! otherwise Rank Math keeps only one domain
add_filter( 'home_url', [ $this, 'rank_math_home_url' ], 10, 2 );
} else {
add_filter( 'rank_math/sitemap/enable_caching', '__return_false' );
// Get all terms in all languages when the language is set from the content or directory name
add_filter( 'get_terms_args', [ $this, 'rm_update_term_query_args' ] );
// Include links to homepage of all languages.
add_filter( 'rank_math/sitemap/exclude_post_type', [ $this, 'update_sitemap_contents' ], 0 , 2 );
}
add_filter( 'pll_home_url_white_list', [ $this, 'rm_white_list_home_url' ] );
add_filter( 'rank_math/frontend/canonical', [ $this, 'rm_home_canonical' ], 10, 1 );
add_filter( 'rank_math/opengraph/facebook', [ $this, 'rank_math_alt_locales' ], 10 );
} else {
// Copy post metas
add_filter('pll_copy_post_meta', [ $this, 'rm_sync_post_metas' ], 10, 4);
// translate post metas
add_filter( 'pll_translate_post_meta', [ $this, 'rm_translate_post_meta' ], 10, 3 );
// Export post metas
add_filter( 'pll_post_metas_to_export', [ $this, 'rm_export_post_metas' ], 10, 1 );
}
}
/**
* Register options keys for translation.
*
* @return void
*/
public function rm_translate_options_keys() {
// @TODO clear cache!
// keys for rank-math-options-general option
$keys = [
'breadcrumbs_separator',
'breadcrumbs_home_label',
'breadcrumbs_archive_format',
'breadcrumbs_search_format',
'breadcrumbs_404_label',
'content_ai_country',
'content_ai_tone',
'content_ai_audience',
'content_ai_language',
'toc_block_title',
];
new PLL_Translate_Option( 'rank-math-options-general', array_fill_keys( $keys, 1 ), [ 'context' => 'rank-math' ] );
// Keys for rank-math-options-titles
$keys = [
'knowledgegraph_name',
'homepage_title',
'author_archive_title',
'date_archive_title',
'search_title',
'404_title',
'pt_post_title',
'pt_post_description',
'pt_post_default_snippet_*',
'pt_page_title',
'pt_page_description',
'pt_page_default_snippet_*',
'pt_attachment_title',
'pt_attachment_description',
'pt_attachment_default_snippet_*',
'pt_product_title',
'pt_product_description',
'pt_product_default_snippet_*',
];
new PLL_Translate_Option( 'rank-math-options-titles', array_fill_keys( $keys, 1 ), [ 'context' => 'rank-math' ] );
}
/**
* Updates the home and stylesheet URLs when using multiple domains or subdomains.
*
* @param string $url
* @param string $path
* @return $url
*/
public function rank_math_home_url( $url, $path ) {
$uri = empty( $path ) ? ltrim( (string) wp_parse_url( pll_get_requested_url(), PHP_URL_PATH ), '/' ) : $path;
if ( 'sitemap_index.xml' === $uri || preg_match( '#([^/]+?)-sitemap([0-9]+)?\.xml|([a-z]+)?-?sitemap\.xsl#', $uri ) ) {
$url = PLL()->links_model->switch_language_in_link( $url, PLL()->curlang );
}
return $url;
}
/**
* Get the active languages.
*
* @return array list of active language slugs, empty if all languages are active
*/
protected function rank_math_get_active_languages() {
$languages = PLL()->model->get_languages_list();
if ( wp_list_filter( $languages, [ 'active' => false ] ) ) {
return wp_list_pluck( wp_list_filter( $languages, [ 'active' => false ], 'NOT' ), 'slug' );
}
return [];
}
/**
* Modifies the sql request for posts sitemaps
* Only when using multiple domains or subdomains or if some languages are not active
*
* @param string $sql JOIN clause
* @param string $post_type
* @return string
*/
public function rank_math_sitemap_join_clause( $sql, $post_type ) {
return pll_is_translated_post_type( $post_type ) ? $sql . PLL()->model->post->join_clause( 'p' ) : $sql;
}
/**
* Modifies the sql request for posts sitemaps
* Only when using multiple domains or subdomains or if some languages are not active
*
* @param string $sql WHERE clause
* @param string $post_type
* @return string
*/
public function rank_math_sitemap_where_clause( $sql, $post_type ) {
if ( ! pll_is_translated_post_type( $post_type ) ) {
return $sql;
}
if ( PLL()->options['force_lang'] > 1 && PLL()->curlang instanceof PLL_Language ) {
return $sql . PLL()->model->post->where_clause( PLL()->curlang );
}
$languages = $this->rank_math_get_active_languages();
if ( empty( $languages ) ) { // Empty when all languages are active.
$languages = pll_languages_list();
}
return $sql . PLL()->model->post->where_clause( $languages );
}
/**
* When the language is set from the content or directory name, the language filter (and inactive languages) need to be removed for the taxonomy sitemaps.
*
* @param array $args get_terms arguments
* @return array modified list of arguments
*/
public function rm_update_term_query_args( $args ) {
if ( isset( $GLOBALS['wp_query']->query['sitemap'] ) ) {
$args['lang'] = implode( ',', $this->rank_math_get_active_languages() );
}
return $args;
}
/**
* A way to apply rank_math/sitemap/{$type}_content for all indexable post types.
*
* Updates homepage and archive pages to include links to the active languages.
*
* Always returns $excluded without altering it's value.
* @param $exclude
* @param $type
* @return mixed
*/
public function update_sitemap_contents( $exclude, $type ) {
if ( pll_is_translated_post_type( $type ) && ( 'post' !== $type || ! get_option( 'page_on_front' ) ) ) {
// Include post, post type archives, and the homepages in all languages to the sitemap when the front page displays posts get_ogp_alternate_languages() !
\add_action( "rank_math/sitemap/{$type}_content", function() use( $type ) {
$generator = new \RankMath\Sitemap\Generator();
$post_type_obj = get_post_type_object( $type );
$languages = wp_list_filter( PLL()->model->get_languages_list(), [ 'active' => false ], 'NOT' );
$mod = Sitemap::get_last_modified_gmt( $type );
$output = '';
if ( 'post' === $type ) {
if ( ! empty( PLL()->options['hide_default'] ) ) {
// The home url is of course already added by WPSEO.
$languages = wp_list_filter( $languages, [ 'slug' => pll_default_language() ], 'NOT' );
}
foreach ( $languages as $lang ) {
$output .= $generator->sitemap_url([
'loc' => pll_home_url( $lang->slug ),
'mod' => $mod
]);
}
}
elseif ( $post_type_obj->has_archive ) {
// Exclude cases where a post type archive is attached to a page (ex: WooCommerce).
$slug = true === $post_type_obj->has_archive ? $post_type_obj->rewrite['slug'] : $post_type_obj->has_archive;
if ( ! wpcom_vip_get_page_by_path( $slug ) ) {
// The post type archive in the current language is already added by WPSEO.
$languages = wp_list_filter( $languages, [ 'slug' => pll_current_language() ], 'NOT' );
foreach ( $languages as $lang ) {
PLL()->curlang = $lang; // Switch the language to get the correct archive link.
$output .= $generator->sitemap_url([
'loc' => pll_home_url( $lang->slug ),
'mod' => $mod
]);
}
}
}
return $output;
});
}
return $exclude;
}
/**
* Include language code in the canonical URL.
*
* @param string $canonical The canonical URL.
*
* @return mixed|string
*/
public function rm_home_canonical( $canonical ) {
if ( ! is_home() ) {
return $canonical;
}
$path = ltrim( (string) wp_parse_url( pll_get_requested_url(), PHP_URL_PATH ), '/' );
return $canonical . $path;
}
/**
* Filters home url.
*
* @param array $arr
* @return array
*/
public function rm_white_list_home_url( $arr ) {
return array_merge( $arr, [ [ 'file' => 'seo-by-rank-math' ] ] );
}
/**
* Updates OpenGraph meta output by adding support for translations.
*
* @return void
*/
public function rank_math_alt_locales() {
$og = new \RankMath\OpenGraph\OpenGraph();
$og->network = 'facebook';
foreach ( $this->update_ogp_alternate_languages() as $lang ) {
$og->tag('og:locale:alternate', $lang );
}
}
/**
* Get alternate language codes for Opengraph.
*
* @return string[]
*/
protected function update_ogp_alternate_languages() {
$alternates = [];
foreach ( PLL()->model->get_languages_list() as $language ) {
if ( isset( PLL()->curlang ) && PLL()->curlang->slug !== $language->slug && PLL()->links->get_translation_url( $language ) && isset( $language->facebook ) ) {
$alternates[] = $language->facebook;
}
}
// There is a risk that 2 languages have the same Facebook locale. So let's make sure to output each locale only once.
return array_unique( $alternates );
}
/**
* Synchronizes or copies the metas.
*
* @param $metas
* @param $sync
* @param $from
* @param $to
* @return array
*/
public function rm_sync_post_metas( $metas, $sync, $from, $to ) {
if ( ! $sync ) {
$metas = array_merge( $metas, $this->rm_translatable_meta_keys() );
// Copy image URLS
$metas[] = 'rank_math_facebook_image';
$metas[] = 'rank_math_facebook_image_id';
$metas[] = 'rank_math_twitter_use_facebook';
$metas[] = 'rank_math_twitter_image';
$metas[] = 'rank_math_twitter_image_id';
$metas[] = 'rank_math_robots';
}
$taxonomies = get_taxonomies([
'hierarchical' => true,
'public' => true,
]);
$sync_taxonomies = PLL()->sync->taxonomies->get_taxonomies_to_copy( $sync, $from, $to );
$taxonomies = array_intersect( $taxonomies, $sync_taxonomies );
foreach ( $taxonomies as $taxonomy ) {
$metas[] = 'rank_math_primary_' . $taxonomy;
}
return $metas;
}
/**
* Translate the primary term during the synchronization process
*
* @param int $value Meta value.
* @param string $key Meta key.
* @param string $lang Language of target.
*
* @return int
*/
public function rm_translate_post_meta( $value, $key, $lang ) {
if ( ! \MyThemeShop\Helpers\Str::starts_with( 'rank_math_primary_', $key) ) {
return $value;
}
$taxonomy = str_replace( 'rank_math_primary_', '', $key );
if ( ! PLL()->model->is_translated_taxonomy( $taxonomy ) ) {
return $value;
}
return pll_get_term( $value, $lang );
}
/**
* Meta key with translatable values.
*
* @return string[]
*/
private function rm_translatable_meta_keys() {
return [
'rank_math_title',
'rank_math_description',
'rank_math_facebook_title',
'rank_math_facebook_description',
'rank_math_twitter_title',
'rank_math_twitter_description',
'rank_math_focus_keyword',
];
}
/**
* Rank math translatable metas to export.
*
* @param array $metas
*
* @return string[]
*/
public function rm_export_post_metas( $metas ) {
$rm_metas = array_fill_keys( $this->rm_translatable_meta_keys(), 1 );
return array_merge( $metas, $rm_metas );
}
}
Then, click the Update File button to save the changes.
2 Adding Compatibility Code to Your rank-math.php File
Next, copy the code below and add it to your theme’s rank-math.php
file. If you haven’t created this file before, then follow this tutorial.
<?php
add_action(
'plugins_loaded',
function() {
if ( defined( 'RANK_MATH_VERSION' ) && class_exists( 'PLL_Integrations' ) ) {
require_once __DIR__ . '/rank-math-ppl.php';
add_action( 'pll_init', array( PLL_Integrations::instance()->rankmath = new PLL_RankMath(), 'init' ) );
}
},
0
);
Finally, click the Update File button to save your changes.
That’s it! Once done, you can continue using Rank Math and Polylang normally. We hope you are able to resolve the compatibility issues with Polylang. If you have any questions, you’re more than welcome to contact our dedicated support team. We’re available 24/7, 365 days a year…