Category Archives: Plugins and themes compatibility

Polylang 1.6.4: sitemaps for subdomains and multiple domains

For most users, WordPress SEO and Polylang should work correctly together, provided that you use pretty permalinks and don’t set the language from the content (yes, although I like this feature, I must agree that it does not play nice with all plugins).

For what is related to sitemaps, the basic idea of the WPSEO compatibilty code included in Polylang is to mix all languages in the same sitemap. This works great when setting the language from the directory name in url. This is also what is generally done in other compatible sitemap plugins such as XML sitemap and Google News feeds. I like this approach as it eases the task of the website admin.

This should have worked whatever option you choose to set the language (directory name, subdomain or multiple domains), but Joost de Valk and I don’t share the same view and WordPress SEO explicitely removes from the sitemap all the content from subdomains and domains which are not the one defined in your WordPress general settings, thus keeping only the content from the default language 😦

I had thus to work around this and Polylang 1.6.4 introduces a separate sitemap per subdomain or domain when using WordPress SEO (while keeping the unique sitemap in other cases). If you have subdomains and prefer to have only one sitemap, XML sitemap and Google News feeds seems to be currently a better choice.

Polylang 1.6.4 fixes a few other bugs reported by users during the past month.

Advertisements

Always use ‘get_search_form’ to create search forms

WordPress provides a lot of filters (and actions) to allow plugins or themes to modify the WordPress comportment. Among them, ‘get_search_form’ can be used to modify the search form.

If a theme author codes a search form which does not pass through this filter, no plugin is aware of this search form and thus no plugin can modify it for its own purposes. Polylang uses this filter to pass the language as parameter of the search query. This allows the search results to be only in one language. A search form which does not pass the ‘get_search_form’ filter may result in unfiltered content (potentially in all languages) or even an error 404.

So, how to do? As in most cases, everything is well explained in the Codex.

The first step is to use the template tag ‘get_search_form’ in the place(s) where you want to display your search form. It’s as easy as:

<?php get_search_form(); ?>

It is important to use this function as it is responsible for applying the ‘get_search_form’ filter thus informing WordPress and all plugins of the existence of your search form.

You can stop here as WordPress provides a default search form. But you may decide for a customized one. In that case, you have basically two ways. The easiest is probably to output your search form in a file called ‘searchform.php’. Here is an example taken from Twenty Eleven:

<form method="get" id="searchform" action="<?php echo esc_url( home_url( '/' ) ); ?>">
<label for="s" class="assistive-text"><?php _e( 'Search', 'twentyeleven' ); ?></label>
<input type="text" class="field" name="s" id="s" placeholder="<?php esc_attr_e( 'Search', 'twentyeleven' ); ?>" />
<input type="submit" class="submit" name="submit" id="searchsubmit" value="<?php esc_attr_e( 'Search', 'twentyeleven' ); ?>" />
</form>

But the Codex also proposes a method which uses the ‘get_search_form’ filter itself.

add_filter(‘get_search_form’, ‘my_search_form’);
function my_search_form($html) {
 $html = … // here you can either customize the WordPress builtin search form or totally overwrite it
 return $html;
}

Note that the search widget will automatically use the customized search form provided by the theme.

So don’t forget. Always call ‘get_search_form’.

Don’t take any action before ‘plugins_loaded’ is fired

Quite often, I see plugins calling WordPress functions before the action ‘plugins_loaded’ has been fired, i.e. before all plugins are loaded. Thus, plugins which are loaded after can’t make use of available filters.

Among the most frequent functions called as soon as the plugin is loaded are the function ‘load_plugin_textdomain’ and the function ‘get_option’. For example the code below prevents other plugins to use the filters ‘option_fantastic’, ‘override_load_texdomain’ and ‘locale’

<?php
/*
Plugin name: My fantastic plugin
*/
$my_options = get_option('fantastic');
load_plugin_textdomain('fantastic', false, basename(dirname(__FILE__)).'/languages');

That’s an issue especially with multilingual plugins which need to filter the locale before any text domain is loaded and also need to filter the options in case they contain strings which need to be translated.

Every plugin authors should prefer the code below which does exactly the same but does not conflict with other plugins:

<?php
/*
Plugin name: My fantastic plugin
*/
add_action('plugins_loaded', 'fantastic_init');
function fantastic_init {
  $GLOBALS['my_options'] = get_option('fantastic');
  load_plugin_textdomain('fantastic', false, basename(dirname(__FILE__)).'/languages');
}

Don’t override query vars with query_posts arguments

Quite often, i find in themes custom queries to customize the content displayed on a page. These themes use either ‘query_posts’ or ‘WP_Query’ to achieve that. For example, the code below allows to exclude posts of some categories from homepage.

if (is_home()) {
  $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
  $build_homepage_query = array (
    'paged'=> $paged,
    'category__not_in' => $theme->get_option('homepage_exclude_categories'),
    'posts_per_page' => $theme->get_option('homepage_posts_per_page')
  );
  query_posts($build_homepage_query);
}

The author of this code took care to not override the query var ‘paged’, not to break the pagination. But if there are some other query vars in the initial query, there are not included in the custom query. All is great when the developper makes his tests because, on a fresh WordPress install, there is no other query vars in the inital query for the homepage.

However, in the case of a multilingual website, the query does contain a new query var ‘lang’ to allow the content to be filtered by language. And the custom query above does not contain the query var ‘lang’, so the code above will not play nicely on multilingual websites.

So a better code is proposed below to keep *all* query vars from the initial query (not only the query var ‘paged’ ) and to modify *only* the query vars needed for the customization purpose.

if(is_home()) {
  $build_homepage_query = array (
    'category__not_in' => $theme->get_option('homepage_exclude_categories'),
    'posts_per_page' => $theme->get_option('homepage_posts_per_page')
  );
  $build_homepage_query = array_merge($GLOBALS['wp_query']->query_vars, $build_homepage_query); // don't reset other query_vars!
  query_posts($build_homepage_query);
}

 

Fatal error: Call to a member function … on a non-object in …

With the time, more and more plugins and themes intend to be multilingual compatible. As WPML is the most widely used plugin, they implement this multilingual compatibility on top of WPML.

What’s fine is that Polylang mimics WPML by implementing the WPML documented API and the language configuration file wpml-config.xml. This is very interesting for themes and plugins developpers as they can hit two targets with one bullet. For example, WordPress SEO or the theme Responsive are compatible with Polylang through the WPML compatibility mode. Good ! So why this fatal error with some other themes and plugins?

Frequently, themes and plugins detect the presence of WPML by testing a documented constant or function. For example:

if (defined('ICL_LANGUAGE_CODE')) {
  // do something with WPML functions
}

If the plugin or theme uses only documented WPML API functions, there should be no issue as Polylang provides these functions. But if the plugin or the theme then makes usage of WPML internal function like the code below

if (defined('ICL_LANGUAGE_CODE')) {
  global $sitepress;
  $lang = $sitepress->get_default_language(); // wrong as no test is made to see if the object exists
}

Polylang users will experience an ugly Fatal error: Call to a member function get_default_language() on a non-object in…

What are the fixes?

For plugins and themes developers, there are two ways. The easiest is of course to test some statement which is unique to WPML (so not documented constants and API functions, but the for example the $sitepress object you intend to use). Doing this, the theme or plugin will support only WPML and won’t take profit of Polylang, but at least, the fatal error will disappear.

But the smartest way is of course to make the theme or plugin compatible with Polylang, by using the Polylang API to replace WPML internal functions.

And for users who just want things to work? The best is to ask for support to author of the theme or plugin which provoques the fatal error. Maybe he will modify his code either to just suppress the error, or to add full compatibility with Polylang.

Waiting for this, there is a way to disable the WPML compatibility mode of Polylang. You can insert:

define('PLL_WPML_COMPAT', false);

in your wp-config.php or in wp-content/polylang/pll-config.php. With the latest, you have to create both the directory and the file. Be aware however that this will disable the WPML compatibility mode for your theme and all plugins, not only the one causing the fatal error.

Incorrect usage of wp_nav_menu

Using the WordPress navigation menus is quite straightforward. First a theme location is registered. Here is an example taken from Twenty Twelve:

register_nav_menu( 'primary', __( 'Primary Menu', 'twentytwelve' ) );

Second, the navigation menu is displayed with ‘wp_nav_menu’. Here again is an example taken from Twenty Twelve:

wp_nav_menu( array( 'theme_location' => 'primary', 'menu_class' => 'nav-menu' ) );

The function accepts quite a lot of (optional) arguments, but the most important one is ‘theme_location’. When registering only one theme location with ‘register_nav_menu’, WordPress appears to work if it is not specified in the ‘wp_nav_menu’ call, but it is a wrong assumption. Just make the following test (with Polylang inactive to be certain that it does not interefere in your test):

  • don’t specify the theme_location parameter in your ‘wp_nav_menu’ call
  • create a first menu in appearance->menus and save it
  • create a second menu and save it
  • assign the second menu to your unique theme location

You will see that WordPress displays the first created menu instead of the second one. That’s not a WordPress bug ! But a frequent mistake found in themes (including Premium themes). And of course since Polylang needs at least one menu per language, such theme conflicts.

So to theme developpers, please, always specify the ‘theme_location’ parameter in your ‘wp_nav_menu’ calls…

Polylang and the Customizr theme

EDIT May 14th, 2014: I am glad to announce that ElectricFeet now proposes a complete guide on how to make Polylang and Customizr working together. He proposes a better code snippet, including compatibitility with Featured Pages Unlimited.

During the past days, I received a lot of support requests asking how to make mutlilingual the front page of the popular theme Customizr. So here is a simple custom plugin to make this happen.

1. Open a text editor (such as notepad) and paste the code below:

<?php
/*
Plugin Name: Polylang Customizr
Description: Makes Customizr frontpage multilingual with Polylang
License: GPLv3
*/

if (defined('POLYLANG_VERSION'))
add_filter('option_tc_theme_options', 'pll_tc_options');

function pll_tc_options($options) {
$areas = array('one', 'two', 'three');
if (is_admin()) {
pll_register_string('tc_featured_page_button_text', $options['tc_featured_page_button_text'], 'Customizr');

foreach ($areas as $area)
pll_register_string('tc_featured_text_'.$area, $options['tc_featured_text_'.$area], 'Customizr', true);
}

else {
$options['tc_featured_page_button_text'] = pll__($options['tc_featured_page_button_text']);

foreach ($areas as $area) {
$options['tc_featured_text_'.$area] = pll__($options['tc_featured_text_'.$area]);
$options['tc_featured_page_'.$area] = pll_get_post($options['tc_featured_page_'.$area]);
}

// filters sliders by language
$pll_options = get_option('polylang');

if (!empty($options['tc_sliders']) && $pll_options['media_support']) {
foreach ($options['tc_sliders'] as $slider => $images)
foreach ($images as $key => $img)
if (!(($id = pll_get_post($img)) && $id == $img))
unset($options['tc_sliders'][$slider][$key]);
}
}

return $options;
}

2. Save as pll-customizr.php
3. Upload the file pll-customizr.php in your wp-content/plugins directory
4. Activate the plugin “Polylang Customizr” in WordPress backend

You now just have to translate your featured pages as usual and translate your featured text in Polylang strings translations table. The slider will also be filtered by language if you activated the languages and translations for media in Polylang settings.

Important: for the moment it works only if the language is set from url (directory, subdomain or domain name).