diff --git a/xmlsitemap.admin.inc b/xmlsitemap.admin.inc
index 55d6c2e..c6993bf 100644
--- a/xmlsitemap.admin.inc
+++ b/xmlsitemap.admin.inc
@@ -330,6 +330,32 @@ function xmlsitemap_settings_form($form, &$form_state) {
'#description' => t('This is the default base URL used for sitemaps and sitemap links.'),
'#required' => TRUE,
);
+ $form['advanced']['xmlsitemap_multilingual'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Multilingual sitemap'),
+ '#description' => t('When enabled, rel="alternate" hreflang="x" links are added to each item. More information !link.', array('!link' => l(t('here'), 'https://support.google.com/webmasters/answer/2620865?hl=en'))),
+ '#default_value' => xmlsitemap_var('multilingual'),
+ );
+ $form['advanced']['xmlsitemap_multilingual_only_nodes'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Only add rel="alternate" hreflang="x" links to translated nodes.'),
+ '#description' => t('When enabled the rel="alternate" hreflang="x" links only apply to translated nodes. Multilingual sitemap has to be checked. More information !link.', array('!link' => l(t('here'), 'https://support.google.com/webmasters/answer/189077?hl=en'))),
+ '#default_value' => xmlsitemap_var('multilingual_only_nodes'),
+ );
+ $x_default_options = array(
+ 'none' => t("Don't use X-Default link"),
+ 'default' => t('Use default language'),
+ );
+ foreach(language_list() as $lang_code => $language){
+ $x_default_options[$lang_code] = $language->name;
+ }
+ $form['advanced']['xmlsitemap_multilingual_x_default'] = array(
+ '#type' => 'select',
+ '#title' => t('Add a hreflang="x-default" link'),
+ '#description' => t("The hreflang='x-default' link is used to set the default page when the visitor's language is not available as a site language. Multilingual sitemap has to be checked. More information !link.", array('!link' => l(t('here'), 'http://googlewebmastercentral.blogspot.nl/2013/04/x-default-hreflang-for-international-pages.html'))),
+ '#default_value' => xmlsitemap_var('multilingual_x_default'),
+ '#options' => $x_default_options,
+ );
$form['advanced']['xmlsitemap_lastmod_format'] = array(
'#type' => 'select',
'#title' => t('Last modification date format'),
diff --git a/xmlsitemap.generate.inc b/xmlsitemap.generate.inc
index 3072fac..9a5df60 100644
--- a/xmlsitemap.generate.inc
+++ b/xmlsitemap.generate.inc
@@ -184,6 +184,7 @@ function xmlsitemap_generate_chunk(stdClass $sitemap, XMLSitemapWriter $writer,
while ($link = $links->fetchAssoc()) {
$link['language'] = $link['language'] != LANGUAGE_NONE ? xmlsitemap_language_load($link['language']) : $url_options['language'];
if ($url_options['alias']) {
+ $link['original'] = $link['loc'];
$link['loc'] = xmlsitemap_get_path_alias($link['loc'], $link['language']->language);
}
$link_options = array(
@@ -208,6 +209,11 @@ function xmlsitemap_generate_chunk(stdClass $sitemap, XMLSitemapWriter $writer,
$element = array();
$element['loc'] = $link_url;
+
+ if (xmlsitemap_var('multilingual')) {
+ $element['xhtml:link']['attributes'] = xmlsitemap_generate_multilingual($link);
+ }
+
if ($link['lastmod']) {
$element['lastmod'] = gmdate($lastmod_format, $link['lastmod']);
// If the link has a lastmod value, update the changefreq so that links
@@ -521,3 +527,52 @@ function xmlsitemap_get_rebuildable_link_types() {
return $rebuild_types;
}
+
+/**
+ * Generate multilingual attributes.
+ * See https://support.google.com/webmasters/answer/2620865?hl=en.
+ *
+ * @param array $link
+ * @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'])->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)->execute()->fetchCol();
+ }
+ }
+
+ // Only add hreflang if we have different URL's for different languages.
+ // For that locale-url has to be activated for language negotiation.
+ if (language_negotiation_get('language', 'locale-url')) {
+ foreach (language_list() as $lang) {
+ if (xmlsitemap_var('multilingual_only_nodes')) {
+ if (!$translation) {
+ continue;
+ }
+ if (!$lang->enabled || !in_array($lang->language, $translation)) {
+ continue;
+ }
+ }
+ $xhtml_links[] = array(
+ 'rel' => 'alternate',
+ 'href' => url($link['original'], array('language' => $lang, 'absolute' => TRUE)),
+ 'hreflang' => $lang->language,
+ );
+ $x_default = xmlsitemap_var('multilingual_x_default');
+ }
+ if ($x_default != 'none') {
+ $x_default_lang = $x_default == "default" ? language_default()->language : $x_default;
+ $xhtml_links[] = array(
+ 'rel' => 'alternate',
+ 'href' => url($link['original'], array('language' => $x_default_lang, 'absolute' => TRUE)),
+ 'hreflang' => 'x-default',
+ );
+ }
+ }
+ return isset($xhtml_links) ? $xhtml_links : array();
+}
diff --git a/xmlsitemap.install b/xmlsitemap.install
index 169bd96..4340e44 100644
--- a/xmlsitemap.install
+++ b/xmlsitemap.install
@@ -544,6 +544,15 @@ function xmlsitemap_update_7203() {
_xmlsitemap_sitemap_rehash_all();
}
+/**
+ * Implement hook_update_N().
+ * Change primary keys to id, type, language of xmlsitemap table.
+ */
+function xmlsitemap_update_7204() {
+ db_drop_primary_key('xmlsitemap');
+ db_add_primary_key('xmlsitemap', array('id', 'type', 'language'));
+}
+
function _xmlsitemap_sitemap_rehash_all() {
// Reload the schema cache and reprocess all sitemap hashes into smids.
drupal_load('module', 'xmlsitemap');
diff --git a/xmlsitemap.module b/xmlsitemap.module
index 9f4868b..52703ed 100644
--- a/xmlsitemap.module
+++ b/xmlsitemap.module
@@ -291,6 +291,9 @@ function xmlsitemap_variables() {
'xmlsitemap_batch_limit' => 100,
'xmlsitemap_path' => 'xmlsitemap',
'xmlsitemap_base_url' => $GLOBALS['base_url'],
+ 'xmlsitemap_multilingual' => 0,
+ 'xmlsitemap_multilingual_only_nodes' => 0,
+ 'xmlsitemap_multilingual_x_default' => 'default',
'xmlsitemap_developer_mode' => 0,
'xmlsitemap_frontpage_priority' => 1.0,
'xmlsitemap_frontpage_changefreq' => XMLSITEMAP_FREQUENCY_DAILY,
diff --git a/xmlsitemap.xmlsitemap.inc b/xmlsitemap.xmlsitemap.inc
index 3e23622..2a8ee92 100644
--- a/xmlsitemap.xmlsitemap.inc
+++ b/xmlsitemap.xmlsitemap.inc
@@ -83,6 +83,7 @@ class XMLSitemapWriter extends XMLWriter {
*/
public function getRootAttributes() {
$attributes['xmlns'] = 'http://www.sitemaps.org/schemas/sitemap/0.9';
+ $attributes['xmlns:xhtml'] = 'http://www.w3.org/1999/xhtml';
if (variable_get('xmlsitemap_developer_mode', 0)) {
$attributes['xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance';
$attributes['xsi:schemaLocation'] = 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd';
@@ -133,23 +134,38 @@ class XMLSitemapWriter extends XMLWriter {
*
* @param string $name
* The element name.
- * @param string|array $content
+ * @param string|array $data
* The element contents or an array of the elements' sub-elements.
*
* @todo Missing a return value since XMLWriter::writeElement() has one.
*/
- public function writeElement($name, $content = NULL) {
- if (is_array($content)) {
+ public function writeElement($name, $data = NULL) {
+ if (is_string($data)) {
+ $data = array('content' => $data);
+ }
+
+ if (isset($data['attributes']) || isset($data['content'])) {
+ if (!empty($data['attributes'])) {
+ foreach ($data['attributes'] as $attributes) {
+ $this->startElement($name);
+ foreach ($attributes as $attr_title => $attr_content) {
+ parent::writeAttribute($attr_title, $attr_content);
+ }
+ $this->endElement();
+ }
+ }
+ if (!empty($data['content'])) {
+ parent::writeElement($name, check_plain((string) $data['content']));
+ }
+ }
+ else {
$this->startElement($name);
- $xml_content = format_xml_elements($content);
+ $xml_content = format_xml_elements($data);
// Remove additional spaces from the output.
$xml_content = str_replace(array(" <", ">\n"), array("<", ">"), $xml_content);
$this->writeRaw($xml_content);
$this->endElement();
}
- else {
- parent::writeElement($name, check_plain((string) $content));
- }
}
public function getURI() {