GNU/Linux, Open Source, Cloud Computing, DevOps and more...

Fixing Qtranslate slug problems


There are basically two options when creating a multi-language website based on WordPress. One involves duplicating posts and pages by creating one different post for each language, wich is the approach followed by some plugins like WPML, Polylang or xili-language. Another approach is to introduce all the translations into the same post, separated by meta-tags within the content itself.


The issues and fixes described in this post are applicable to these plugin versions:

  • Qtranslate 2.5.39
  • Qtranslate Slug 1.1.7

Each translation is displayed in different tabs within the HTML editor of a single post. This is the case of Qtranslate, which happens to be the simplest solution from my point of view, as it is not necessary to modify the WordPress database in order to create relationships between a post or page and their translations, as well as among other WordPress objects like categories, tags, widgets, menus, etc. Therefore Qtranslate is my favorite choice when it comes to creating a multilingual website based on WordPress.

However, Qtranslate is not without complications as it has some bugs and lack of features that we need to overcome in order to make our site truly multilingual:

1. Qtranslate doesn’t support URL’s slug translation

The main impediment we find is that Qtranslate doesn’t translate our site’s URLs properly, which is a major limitation that can completely invalidate its use and force us to choose another alternative. Fortunately there is an additional plugin that will allow full URL translation: Qtranslate Slug. It supports not only posts and pages slug translation, but also categories and tags.

Qtranslate Slug

2. Qtranslate Slug causes duplicate content

However, Qtranslate does not solve another awkward problem: although our URLs are properly translated by Qtranslate Slug, when we request any post, page or category using the slug in our site’s default language, no matter what variable or subdirectory language we indicate, the same content is always displayed. So, being English our default language, if you request an URL like /es/article-name-in-english you’ll get exactly the same result as if you request /en/article-name-in-english, which is wrong, because the post’s address in Spanish is /es/nombre-del-articulo-en-espanol. The /es/article-name-in-english URL makes no sense and should always return a 404 not found error. However the same post in English is displayed with exactly the same content as in the case of /en/article-name-in-english, which results in duplicate content that Google and other search engines can index and even flag as duplicate content and create us problems.

The same applies to pages, categories and tags. For example, the address /es/category/programming shows the same content as /en/category/programming.

To fix this issue and force WordPress to display 404 errors when bad URLs are requested it is necessary to make some changes to the wp-content/plugins/qtranslate-slug/qtranslate-slug.php file of Qtranslate Slug plugin:

882                         // <DLA>
883                         if (!$post)
884                         {
885                                 $query['name'] = 'dladladladla';
886                                 return $query;
887                         }
888                         // </DLA>


900                         // <DLA>
901                         if (!$term)
902                         {
903                                 $query['category_name'] = 'dladladladla';
904                                 return $query;
905                         }
906                         // </DLA>


1068                     /*} else {
1069                         $last_part = array_pop($parts);
1070                         $page_id = $wpdb->get_var( "SELECT ID FROM $wpdb->posts WHERE post_name = '$last_part' AND (post_type = '$post_type_sql' OR post_type = 'attachment')" );
1072                         if ( $page_id )
1073                                 return $page_id;
1074                     }*/


1560                 /*if ( !$term && 'slug' == $original_field ) {
1561                         $field = 't.slug';
1562                         $term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND $field = %     s LIMIT 1", $taxonomy, $value) );
1563                 }*/

3. Pagination doesn’t work properly

When we are in an internal page of the blog, category or archived posts and we select another language from the change language widget the paging information is lost and always takes us to the main page, so we can never see the translated version of that page in other languages ​​available. That is, if we point to /es/blog/page/2 and change language to English, the page /en/blog is displayed instead of /en/blog/page/2.

Error: Your Requested widget " ai_widget-6" is not in the widget list.
  • [do_widget_area above-nav-left]
    • [do_widget_area above-nav-right]
      • [do_widget_area footer-1]
        • [do_widget id="wpp-4"]
      • [do_widget_area footer-2]
        • [do_widget id="recent-posts-4"]
      • [do_widget_area footer-3]
        • [do_widget id="recent-comments-3"]
      • [do_widget_area footer-4]
        • [do_widget id="archives-4"]
      • [do_widget_area logo-bar]
        • [do_widget id="oxywidgetwpml-3"]
      • [do_widget_area menu-bar]
        • [do_widget id="search-3"]
      • [do_widget_area sidebar]
        • [do_widget id="search-4"]
        • [do_widget id="ai_widget-2"]
        • [do_widget id="categories-5"]
        • [do_widget id="ai_widget-3"]
        • [do_widget id="ai_widget-4"]
        • [do_widget id="ai_widget-5"]
      • [do_widget_area sub-footer-1]
        • [do_widget id="text-4"]
      • [do_widget_area sub-footer-2]
        • [do_widget_area sub-footer-3]
          • [do_widget_area sub-footer-4]
            • [do_widget_area upper-footer-1]
              • [do_widget id="search-2"]
              • [do_widget id="recent-posts-2"]
              • [do_widget id="recent-comments-2"]
              • [do_widget id="archives-2"]
              • [do_widget id="categories-2"]
              • [do_widget id="meta-2"]
            • [do_widget_area upper-footer-2]
              • [do_widget_area upper-footer-3]
                • [do_widget_area upper-footer-4]
                  • [do_widget_area widgets_for_shortcodes]
                    • [do_widget id="search-5"]
                    • [do_widget id="ai_widget-6"]
                  • [do_widget_area wp_inactive_widgets]
                    • [do_widget id="wpp-2"]
                    • [do_widget id="text-1"]
                    • [do_widget id="recent-posts-3"]
                    • [do_widget id="categories-3"]
                    • [do_widget id="archives-3"]
                    • [do_widget id="icl_lang_sel_widget-3"]

                  I solved it adding the following lines at the end of the filter_request() function in qtranslate-slug.php file, although there is probably a better way to achieve this if we dive deeper into qTranslate Slug’s code:

                   972                 // <DLA>
                   973                 // Add support for pagination to qtranslate slug widget
                   974                 if (isset($query['paged']))
                   975                 {
                   976                         if ($this->current_url == null)
                   977                         {
                   978                                 global $q_config;
                   979                                 foreach ($q_config['enabled_languages'] as $lang)
                   980                                 {
                   981                                         $this->current_url[$lang] = '/'.$lang . str_replace('/'.qtrans_getLanguage().'/', '/'.$lang.'/', $_SERVER['REQUEST_URI']);
                   982                                 }
                   983                         }
                   984                         else
                   985                         {
                   986                                 foreach ($this->current_url as $lang => $language_url)
                   987                                 {
                   988                                         $this->current_url[$lang] .= 'page/'.$query['paged'];
                   989                                 }
                   990                         }
                   991                 }
                   992                 // </DLA>

                  4. Qtranslate generates wrong hreflang links

                  Another undesirable side effect caused by Qtranslate not supporting slug translation is that wrong hreflang links are created inside HEAD section within all site’s pages. Thus, the URL of this post you are reading would be rendered as /en/solucionando-problemas-qtranslate-slug/ instead of /es/solucionando-problemas-qtranslate-slug/ in the Spanish version, while in the English version would be rendered as /es/fixing-qtranslate-slug-problems/ instead of /en/fixing-qtranslate-slug-problems/, resulting in a lot of 404 page not found errors in Google Webmaster Tools.

                  This can be solved by making a small change to the wp-content/plugins/qtranslate/qtranslate_hooks.php file:

                   41         foreach($q_config['enabled_languages'] as $language) {
                   42                 if($language != qtrans_getLanguage())
                   43                         echo '<link hreflang="'.$language.'" href="'.qtrans_convertURL('',$language).'" rel="alternate" />'."\n";
                   44         }        
                   41         foreach($q_config['enabled_languages'] as $language) {
                   42                 if($language != qtrans_getLanguage())
                   43                 // <DLA>
                   44                 {       
                   45                         global $qtranslate_slug;
                   46                         $language_url = $qtranslate_slug->get_current_url($language);
                   47                         //echo '<link hreflang="'.$language.'" href="'.qtrans_convertURL('',$language).'" rel="alternate" />'."\n";
                   48                         echo '<link hreflang="'.$language.'" href="'.$language_url.'" rel="alternate" />'."\n";
                   49                 }
                   50                 // </DLA>
                   51         }

                  5. Comments reply links doesn’t include language

                  Each post comment normally includes a “Reply” link wich is not filtered by Qtranslate in order to add the language variable or subdirectory to href’s URL. This causes that links that should be like /en/fixing-qtranslate-slug-problems/?replytocom=1868#respond appear like /fixing-qtranslate-slug-problems/?replytocom=1868#respond, wich also causes 404 errors on Google Webmaster Tools.

                  Fix qtranslate-slug.php file like this:

                   533                 // <DLA>
                   534                 add_filter( 'comment_reply_link', array(&$this, 'fix_comment_reply_link'));
                   535                 add_filter( 'cancel_comment_reply_link', array(&$this, 'fix_cancel_comment_reply_link'));
                   536                 // </DLA>
                   538         }
                   540         // <DLA>
                   541         /*
                   542          * fixes comments reply link adding language subdirectory to href URL
                   543          */
                   544         function fix_comment_reply_link($link)
                   545         {
                   546                 preg_match('/href=\'([^\']*)\'/', $link, $href);
                   548                 return str_replace($href[1], qtrans_convertURL($href[1]), $link);
                   549         }
                   550         /*
                   551          * Fixes cancel comment reply link by adding language subdirectory to href URL
                   552          */
                   553         function fix_cancel_comment_reply_link($link)
                   554         {
                   555                 preg_match('/href=\"([^\']*)\"/', $link, $href);
                   557                 return str_replace($href[1], qtrans_convertURL($href[1]), $link);
                   558         }
                   559         // </DLA>

                  6. Google XML Sitemaps v3 for Qtranslate doesn’t work with Qtranslate Slug

                  This problem causes wrong URLs are generated in our sitemap, which results in a sitemap riddled of wrong URLs returning 404 errors. I describe better this problem and its resolution in another article: Google XML Sitemaps v3 for Qtranslate doesn’t work with Qtranslate Slug.


                  About the author

                  Daniel López Azaña
                  Freelance AWS Cloud Solution Architect & Linux Sysadmin

                  Entrepreneur, a generator of ideas and restless mind. Passionate about new technologies, especially Linux systems and Open Source Software. I also like to write about Technology News, Cloud Computing, AWS, DevOps, DevSecOps, System Security, Web Development and Programming, SEO, Science, Innovation, Entrepreneurship, etc.

                  DanielFixing Qtranslate slug problems

                  Related Posts


                  Join the conversation
                  • Alex - 25/10/2016 reply

                    Hi Daniel, very interesting your article. I would try to customize qTraslate slug plugin as you have shown in your post.

                    In particoular, I’m interested in the first snipped of code: I don’t understand how to insert that code inside qtranslate-slug.php. At what point do I have to insert that code? And then, I see you have put some dummy text, what I have to put inside $query[‘name’] = ‘…’; for example?

                    The same thing, for other snippets.

                    Than you very much

                  • Jörg - 30/04/2017 reply

                    Hi Alex, thank’s for the detailed explanation but I’m having the same issue as Alex. Could you please provide us with an update? Thank you very much in advance!

                  • Prihod - 13/06/2018 reply

                    Hi Daniel!
                    Now this code

                    I need (without /ru)

                    Russian version by delault.
                    Can u help me?

                    Prihod - 13/06/2018 reply

                    cut code _ screenshot

                  • Daniel - 02/08/2018 reply

                    Sorry, but the version of Qtranslate in the article is now too old and I am not aware of the problems of the most recent versions, as I switched to WPML – The WordPress Multilingual Plugin.

                  Leave a Reply

                  Your email address will not be published.