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

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

8 comments

One of the most widespread options to create a multi-language site with WordPress is to use the qTranslate plugin, wich works pretty well and it’s free. Perhaps the most sensitive aspect of multilingual sites ​​is URL translation, since it affects website’s information architecture and search engine optimization (SEO).

Precisely qTranslate does not support URL’s translation, so we must choose to install an additional plugin: qTranslate Slug. At first all works well with these two plugins installed, but problems arise when we need an XML sitemap to upload to Google.


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"]

                  That’s when we realize, perhaps too late, that many of the URLs within the XML sitemap are incorrect. And I say too late because if we don’t detect the problem quickly, Google will get plenty of links that end in a 404 page not found error, and the number of indexing errors in Google Webmaster Tools will dramatically increase, which in turn may have side effects on SEO if we don’t fix it soon.

                  The problem’s root

                  Qtranslate logo

                  The cause of this problem is that Google XML Sitemaps v3 for qTranslate plugin doesn’t translate the slug part of URLs. Thus, a link that should be /es/google-xml-sitemaps-v3-for-qtranslate-no-funciona-con-qtranslate-slug/ appears as /es/google-xml-sitemaps-v3-for-qtranslate-doesnt-work-with-qtranslate-slug, ie, in English rather than Spanish, although the conversion of /en/ by /es/ is done properly.

                  This malfunction also affects categories and tags. In that case we won’t get 404 not found errors because for some reason the qTranslate Slug plugin does accept URLs in website’s default language and displays the content in the correct language, but URLs have a the wrong structure and they don’t appear on the sitemap in the desired language.

                  Where is the error?

                  Doing some reverse engineering I found out that there is a failure inside sitemap-qtranslate.php file located at plugins/google-xml-sitemaps-v3-for-qtranslate/ directory, which makes a call from qt_permalink() function to qtrans_convertURL() when it comes to a URL in site’s non default language. However, qtrans_convertURL() function doesn’t perform the translation of the URL’s slug part, causing the entire problem. This qtrans_convertURL() method doesn’t belong to Google XML Sitemaps plugin, or even the qTranslate Slug plugin, but the qTranslate main plugin (wp-content/plugins/qtranslate/qtranslate_core.php), which as said earlier doesn’t support URL translation. Thus, it seems that this is a bad implementation of qt_permalink() method that hopefully will be fixed in future versions by plugin developers.


                  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"]

                                  How to fix it?

                                  Until that time comes, here I offer a temporary workaround that will allow you to continue using the plugin and have a valid and error free XML sitemap. Two files are involved that we must modify: sitemap-qtranslate.php and sitemap-core.php, both located in the wp-content/plugins/google-xml-sitemaps-v3-for-qtranslate/ directory.

                                  sitemap-qtranslate.php

                                  Replace all this excerpt:

                                  50    // Add an extra permalink url for every non-default qTranslate language
                                  51    foreach ($languages as $language) {
                                  52          if ($qt["hide_default_language"] == 1 && $qt["default_language"] == $language) {
                                  53              $sitemap->AddUrl($permalink, $modified_time, $change_freq, $priority);
                                  54          } else {
                                  55              $sitemap->AddUrl(qtrans_convertURL($permalink, $language, true), $modified_time, $change_freq, $priority);
                                  56          }
                                  57    }
                                  

                                  With this one:

                                  global $q_config;
                                                  $qts_options = get_option('qts_options');
                                                  $oslug = new QtranslateSlug();
                                  
                                                  foreach($q_config['enabled_languages'] as $language) {
                                                          if ($qt["hide_default_language"] == 1 && $qt["default_language"] == $language) {
                                                                  $sitemap->AddUrl($permalink, $modified_time, $change_freq, $priority);
                                                          } else {
                                                                  // Posts
                                                                  if (isset($bean->ID))
                                                                  {
                                                                          $p = qtrans_convertURL($permalink, $language, true);
                                                                          if(!in_array($p, array('http://', 'http:/'))) {
                                                                                  $def_slug = get_post_meta( $bean->ID, $oslug->get_meta_key($qt["default_language"]), true);
                                                                                  $slug = get_post_meta( $bean->ID, $oslug->get_meta_key($language), true);
                                                                                  $turl = str_replace($def_slug, $slug, $p);
                                                                                  $sitemap->AddUrl($turl, $modified_time, $change_freq, $priority);
                                                                          }
                                                                  }
                                                                  // categories
                                                                  else if (isset($bean->taxonomy) && $bean->taxonomy == 'category')
                                                                  {
                                                                          $def_taxonomy_category = $qts_options["_qts_taxonomy_category"][$qt["default_language"]];
                                                                          $taxonomy_category = $qts_options["_qts_taxonomy_category"][$language];
                                                                          $p = qtrans_convertURL($permalink, $language, true);
                                                                          if(!in_array($p, array('http://', 'http:/'))) {
                                                                                  $def_slug = get_term_meta( $bean->term_id, $oslug->get_meta_key($qt["default_language"]), true);
                                                                                  $slug = get_term_meta( $bean->term_id, $oslug->get_meta_key($language), true);
                                                                                  $turl = str_replace($def_slug, $slug, $p);
                                                                                  $turl = str_replace($def_taxonomy_category, $taxonomy_category, $turl);
                                                                                  $sitemap->AddUrl($turl, $modified_time, $change_freq, $priority);
                                                                          }
                                                                  }
                                                                  // tags
                                                                  else if (isset($bean->taxonomy) && $bean->taxonomy == 'post_tag')
                                                                  {
                                                                          $def_taxonomy_tag = $qts_options["_qts_taxonomy_post_tag"][$qt["default_language"]];
                                                                          $taxonomy_tag = $qts_options["_qts_taxonomy_post_tag"][$language];
                                                                          $p = qtrans_convertURL($permalink, $language, true);
                                                                          if(!in_array($p, array('http://', 'http:/'))) {
                                                                                  $turl = str_replace('/'.$language.'/'.$def_taxonomy_tag.'/', '/'.$language.'/'.$taxonomy_tag.'/', $p);
                                                                                  $sitemap->AddUrl($turl, $modified_time, $change_freq, $priority);
                                                                          }
                                                                  }
                                                                  else
                                                                  {
                                                                          $sitemap->AddUrl(qtrans_convertURL($permalink, $language, true), $modified_time, $change_freq, $priority);
                                                                  }
                                                          }
                                                  }
                                  

                                  And don’t forget to add a new $bean parameter to the qt_permalink() function definition found in the same file:

                                   34 // Add additional qTranslate language permalniks
                                   35 //function qt_permalink($qt, $permalink, $post_content, $modified_time, $change_freq, $priority, &$sitemap) {
                                   36 function qt_permalink($qt, $permalink, $post_content, $modified_time, $change_freq, $priority, &$sitemap, $bean) {
                                  
                                  sitemap-core.php

                                  Add a last parameter to all qt_permalink() calls inside this file. This parameter will be the object instance referenced by the URL we are trying to add to our sitemap, as a post, category or tag. The lines between [dla] and [/dla] tags are the ones I have changed in this file.

                                  Posts
                                  1923  // <DLA>
                                  1924  //else qt_permalink($qt, $permalink, $post->post_content, ($post->post_modified_gmt && $post->post_modified_gmt!='0000-00-00 00:00:00'?$post->post_modified_gm     t:$post->post_date_gmt), ($isPage?$cf_pages:$cf_posts), $prio, $this);
                                  1925  else qt_permalink($qt, $permalink, $post->post_content, ($post->post_modified_gmt && $post->post_modified_gmt!='0000-00-00 00:00:00'?$post->post_modified_gmt:     $post->post_date_gmt), ($isPage?$cf_pages:$cf_posts), $prio, $this, $post);
                                  1926  // </DLA>
                                  
                                  
                                  Categories
                                  2013  // <DLA>
                                  2014  //else qt_permalink($qt, get_category_link($cat->ID), null, $cat->last_mod, $this->GetOption("cf_cats"), $this->GetOption("pr_cats"), $this);
                                  2015  else qt_permalink($qt, get_category_link($cat->ID), null, $cat->last_mod, $this->GetOption("cf_cats"), $this->GetOption("pr_cats"), $this, $cat);
                                  2016  //</DLA>
                                  
                                  ...
                                  
                                  2027  // <DLA>
                                  2028  //else qt_permalink($qt, get_category_link($cat->term_id), null, 0, $this->GetOption("cf_cats"), $this->GetOption("pr_cats"), $this);
                                  2029  else qt_permalink($qt, get_category_link($cat->term_id), null, 0, $this->GetOption("cf_cats"), $this->GetOption("pr_cats"), $this, $cat);
                                  2030  // </DLA>
                                  
                                  
                                  Tags
                                  2145  // <DLA>
                                  2146  //else qt_permalink($qt, get_tag_link($tag->term_id), null, 0, $this->GetOption("cf_tags"), $this->GetOption("pr_tags"), $this);
                                  2147  else qt_permalink($qt, get_tag_link($tag->term_id), null, 0, $this->GetOption("cf_tags"), $this->GetOption("pr_tags"), $this, $tag);
                                  2148  // </DLA>
                                  
                                  

                                  Finally, remember that it’s very important to backup changed files so that a plugin update wich still doesn’t provide a fix for this issue doesn’t crush our changes and we can recover them manually.

                                  Was this solution helpful to you?

                                   

                                  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.

                                  DanielGoogle XML Sitemaps v3 for qTranslate doesn’t work with Qtranslate Slug

                                  Related Posts

                                  8 comments

                                  Join the conversation
                                  • Edgars Ritmanis - 18/10/2014 reply

                                    This is a great post. The fix works for pages and posts.
                                    However, custom post type slug translations that can be set through Settings>Slug options still don’t work.

                                  • gurumance - 21/04/2015 reply

                                    Hello Daniel, can you update your post as there is no qtranslate plugin anymore now it is qtranslate-x(and it is much better) can you check and share the way how to fix with latest plugins.

                                  • Lungile - 18/02/2016 reply

                                    Thank you!!! I have spent the better part of an afrtenoon looking for a simple solution to this.A quick clarification: Line 2 hooks the my_save_post’ function into the save_post action. But the function in line 3 is named simply save_post’. Is that a typo, or am I missing something else?

                                  • Arthur - 25/06/2016 reply

                                    Hello, can you please make an archieve with you patch? because tehre is something wrong on my side. I made all cahnges you made, but in my sitemap there is something like url – http://example.com/de/p=123
                                    http://example.com/en/p=123
                                    instead of :
                                    http://example.com/ru/hardware/apple-ipad-air-review
                                    http://example.com/de/hardware/apple-ipad-air-bewertung

                                  • Arthur - 25/06/2016 reply

                                    Ok, I found the problem. Need to check the “Allow manual sitemap creatin by GET queries”

                                  • Cristian Gologan - 24/08/2017 reply

                                    interesting but does not seem to work for me please have a look:
                                    http://water-ionizers.info/sitemap.xml
                                    I can send you the modified files to have a look as well

                                    Cristian Gologan - 26/08/2017 reply

                                    set default language to english and it works! but I have the same problem as Arthur above and “GET” option does not help-strange!

                                    Daniel - 26/08/2017 reply

                                    Cristian, I also have English as my default language in qTranslate, but if I change it to Spanish I get the same XML sitemap. I can’t reproduce neither Arthur’s problem nor yours, sorry…

                                    I have Version 3.4.1 of Google XML Sitemaps v3 for qTranslate, Version 1.1.7 of qTranslate slug and Version 2.5.39 of qTranslate. Which versions do you have?

                                  Leave a Reply

                                  Your email address will not be published.