diff --git a/xmlsitemap.api.php b/xmlsitemap.api.php index 80062bf..624d0fc 100644 --- a/xmlsitemap.api.php +++ b/xmlsitemap.api.php @@ -314,6 +314,20 @@ function hook_xmlsitemap_sitemap_delete(stdClass $sitemap) { ->execute(); } +/** + * Inform xmlsitemap process of any language negotiation methods based on URLs. + * + * This hook is invoked from _xmlsitemap_is_url_based_language_negotiation() + * during the generation of the multilingual attribute of the XML sitemap. + * + * @param array $method_list + * List of language negotiation methods that have been identified as based on + * URLSs. + */ +function hook_xmlsitemap_url_based_negotiation_alter(array &$method_list) { + $method_list[] = 'nexteuropa_multilingual_url_suffix'; +} + /** * @} End of "addtogroup hooks". */ diff --git a/xmlsitemap.generate.inc b/xmlsitemap.generate.inc index 7fd8e4f..91a0f66 100644 --- a/xmlsitemap.generate.inc +++ b/xmlsitemap.generate.inc @@ -239,7 +239,10 @@ function xmlsitemap_generate_chunk(stdClass $sitemap, XMLSitemapWriter $writer, $element['loc'] = $link_url; if (xmlsitemap_var('multilingual')) { - $element['xhtml:link']['attributes'] = xmlsitemap_generate_multilingual($link); + $xhtml_links = xmlsitemap_generate_multilingual($link); + if (!empty($xhtml_links)) { + $element['xhtml:link']['attributes'] = $xhtml_links; + } } if ($link['lastmod']) { @@ -597,95 +600,206 @@ function xmlsitemap_rebuild_clear(array $types, $save_custom) { * @return array */ function xmlsitemap_generate_multilingual($link) { - if (xmlsitemap_var('multilingual_only_nodes')) { - if (module_exists('entity_translation')) { - $translation = db_select('entity_translation', 'et') - ->fields('et', array('language')) - ->condition('et.entity_type', $link['type']) - ->condition('et.entity_id', $link['id']) - ->condition('et.status', 1) - ->execute() - ->fetchCol(); - } - else { - $tnid = db_select('node', 'n') - ->fields('n', array('tnid')) - ->condition('n.nid', $link['id']) - ->execute() - ->fetchField(); - $translation = db_select('node', 'n') - ->fields('n', array('language')) - ->condition('n.tnid', $tnid) - ->condition('n.status', 1) - ->execute() - ->fetchCol(); - } + // If the language negotiation method is not based on URLs, + // no need to continue as it is the same URL for all languages. + if (!_xmlsitemap_is_url_based_language_negotiation()) { + return array(); } - // Only add hreflang if we have different URL's for different languages. - // For that locale-url has to be activated for language negotiation. - module_load_include('inc', NULL, 'includes/language'); - if (language_negotiation_get('language', 'locale-url')) { - $languages = language_list(); - if (module_exists('translation') && !module_exists('entity_translation')) { - $link_urls = translation_path_get_translations($link['original']); - } + if (xmlsitemap_var('multilingual_only_nodes') && $link['type'] != 'node') { + return array(); + } + + $original_path = $link['original']; + $original_language = $link['language']; + $languages = language_list('enabled'); + $languages = reset($languages); + $xhtml_links = array(); + + // Default front page mechanism. + if ('frontpage'== $link['type'] && empty($original_path)) { foreach ($languages as $lang) { - if (xmlsitemap_var('multilingual_only_nodes')) { - if (!$translation) { + _xmlsitemap_add_xhtml_link($xhtml_links, '', $lang); + } + _xmlsitemap_add_x_default_link($xhtml_links, $languages); + + return $xhtml_links; + } + + // Let's work with Entity translation first. + if (module_exists('entity_translation')) { + $available_link_languages = db_select('entity_translation', 'et') + ->fields('et', array('language')) + ->condition('et.entity_type', $link['type']) + ->condition('et.entity_id', $link['id']) + ->condition('et.status', 1) + ->execute() + ->fetchCol(); + + if (!empty($available_link_languages)) { + foreach ($languages as $lang_code => $lang) { + if (!in_array($lang_code, $available_link_languages)) { continue; } - if (!$lang->enabled || !in_array($lang->language, $translation)) { + _xmlsitemap_add_xhtml_link($xhtml_links, $original_path, $lang); + } + _xmlsitemap_add_x_default_link($xhtml_links, $languages); + + return $xhtml_links; + } + } + + switch ($link['type']) { + case 'node': + // Let's treat node types that use content translation. + if (!module_exists('translation') || !translation_supported_type($link['subtype'])) { + break; + } + + $path_translations = translation_path_get_translations($original_path); + // Ensure that the original path is set. When the content is not + // translated yet, $path_translations is empty. + $path_translations[$original_language->language] = $original_path; + + foreach ($languages as $lang_code => $lang) { + if (!isset($path_translations[$lang_code])) { continue; } + $href = $path_translations[$lang_code]; + _xmlsitemap_add_xhtml_link($xhtml_links, $href, $lang); + } + _xmlsitemap_add_x_default_link($xhtml_links, $languages); + + return $xhtml_links; + + case 'taxonomy_term': + // If the module does not exist, then no need to continue because there + // is no other language set for the item and then no other URL. + if (!module_exists('i18n_taxonomy')) { + break; + } + + // Let's cover situation where some taxonomy terms still use + // the mode "Translate. Different terms will be allowed for each + // language and they can be translated". + $path_translations = i18n_taxonomy_translate_path($original_path); + + if (empty($path_translations)) { + break; } - $link_url = isset($link_urls) ? $link_urls[$lang->language] : $link['original']; - $xhtml_links[] = array( - 'rel' => 'alternate', - 'href' => url($link_url, array( - 'language' => $lang, - 'absolute' => TRUE, - 'base_url' => variable_get('xmlsitemap_base_url', $GLOBALS['base_url']), - )), - 'hreflang' => $lang->language, - ); - } - // Adds translation for the frontpage. - if ($link['type'] == 'frontpage' && !isset($xhtml_links)) { - $languages = language_list(); - foreach ($languages as $lang) { - if (!$lang->enabled) { + foreach ($languages as $lang_code => $lang) { + if (!isset($path_translations[$lang_code])) { continue; } - $link_url = isset($link_urls[$lang->language]) ? $link_urls[$lang->language] : $link['original']; - $xhtml_links[] = array( - 'rel' => 'alternate', - 'href' => url($link_url, array( - 'language' => $lang, - 'absolute' => TRUE, - 'base_url' => variable_get('xmlsitemap_base_url', $GLOBALS['base_url']), - )), - 'hreflang' => $lang->language, - ); + $href = $path_translations[$lang_code]['href']; + _xmlsitemap_add_xhtml_link($xhtml_links, $href, $lang); } + _xmlsitemap_add_x_default_link($xhtml_links, $languages); + + return $xhtml_links; + } + + // Let's give a last chance to i18n module to retrieve the translation paths, + // if enabled. + if (!module_exists('i18n')) { + return array(); + } + + $path_translations = i18n_get_path_translations($original_path); + + if (!empty($path_translations)) { + foreach ($languages as $lang_code => $lang) { + if (!isset($path_translations[$lang_code])) { + continue; + } + $menu_item = $path_translations[$lang_code]; + $href = isset($menu_item['href']) ? $menu_item['href'] : $original_path; + _xmlsitemap_add_xhtml_link($xhtml_links, $href, $lang); } + } - // Add x-default if available. - $x_default = xmlsitemap_var('multilingual_x_default'); - $x_default_lang = $x_default == 'default' ? language_default() : $languages[$x_default]; - if ($x_default != 'none' && in_array($x_default_lang->language, $translation)) { - $link_url = isset($link_urls[$x_default_lang->language]) ? $link_urls[$x_default_lang->language] : $link['original']; - $xhtml_links[] = array( - 'rel' => 'alternate', - 'href' => url($link_url, array( - 'language' => $x_default_lang, - 'absolute' => TRUE, - 'base_url' => variable_get('xmlsitemap_base_url', $GLOBALS['base_url']), - )), - 'hreflang' => 'x-default', - ); + _xmlsitemap_add_x_default_link($xhtml_links, $languages); + + return $xhtml_links; +} + +/** + * Add a "xhtml:link" value to a existing list. + * + * @param array $xhtml_links + * The "xhtml:link" list where to add the value. + * @param string $path + * The raw path used to generate the "xhtml:link" value. + * @param object $language + * The language object linked to the "xhtml:link" value. + * + * @see xmlsitemap_generate_multilingual() + */ +function _xmlsitemap_add_xhtml_link(array &$xhtml_links, $path, $language) { + $href = url($path, array( + 'language' => $language, + 'absolute' => TRUE, + 'base_url' => variable_get('xmlsitemap_base_url', $GLOBALS['base_url']), + )); + + $xhtml_links[$language->language] = array( + 'rel' => 'alternate', + 'href' => $href, + 'hreflang' => $language->language, + ); +} + +/** + * Add the "hreflang='x-default'" link to "xhtml:link" list. + * + * @param array $xhtml_links + * The "xhtml:link" list where to add the link. + * @param array $enabled_languages + * The list of the enabled language objects. + */ +function _xmlsitemap_add_x_default_link(array &$xhtml_links, array $enabled_languages) { + $x_default = xmlsitemap_var('multilingual_x_default'); + if ($x_default == 'none') { + return; + } + + $x_default_lang = ($x_default == 'default') ? language_default() : $enabled_languages[$x_default]; + if (empty($xhtml_links) || !isset($xhtml_links[$x_default_lang->language])) { + return; + } + + $xhtml_links['x-default'] = array( + 'rel' => 'alternate', + 'href' => $xhtml_links[$x_default_lang->language]['href'], + 'hreflang' => 'x-default', + ); +} + +/** + * Checks if the language negotiation method is based on urls or not. + * + * @return bool + * TRUE if the language negotiation method is based on urls. + */ +function _xmlsitemap_is_url_based_language_negotiation() { + $is_url_based = &drupal_static(__FUNCTION__); + + if (isset($is_url_based)) { + return (bool) $is_url_based; + } + + $language_negotiation_method = array('locale-url'); + + drupal_alter('xmlsitemap_url_based_negotiation', $language_negotiation_method); + + $is_url_based = FALSE; + foreach ($language_negotiation_method as $method) { + if (language_negotiation_get('language', $method)) { + $is_url_based = TRUE; + break; } } - return isset($xhtml_links) ? $xhtml_links : array(); + + return $is_url_based; }