diff --git a/xmlsitemap.admin.inc b/xmlsitemap.admin.inc
index 199c51e..bdddb2f 100644
--- a/xmlsitemap.admin.inc
+++ b/xmlsitemap.admin.inc
@@ -414,6 +414,18 @@ function xmlsitemap_settings_form($form, &$form_state) {
     '#default_value' => variable_get('xmlsitemap_disable_cron_regeneration', 0),
     '#description' => t('This can be disabled if other methods are being used to generate the sitemap files, like <em>drush xmlsitemap-regenerate</em>.'),
   );
+  $form['advanced']['xmlsitemap_language_fallback'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Include links to untranslated entities to localized sitemaps'),
+    '#description' => t('If the option disabled, it will not include links to localized sitemaps even the language fallback enabled.'),
+    '#default_value' => variable_get('xmlsitemap_language_fallback', TRUE),
+  );
+  $form['advanced']['xmlsitemap_language_remove_neutral'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Exclude links to language neutral entities to localized sitemaps'),
+    '#description' => t('If the option is enabled, it will not include links to language neutral to the localized sitemaps.'),
+    '#default_value' => variable_get('xmlsitemap_language_remove_neutral', FALSE),
+  );
   $form['advanced']['xmlsitemap_output_elements'] = array(
     '#type' => 'checkboxes',
     '#title' => t('Enable or disable the individual @loc elements from output', array('@loc' => '<loc>')),
@@ -733,15 +745,32 @@ function xmlsitemap_link_bundle_settings_form(array $form, array &$form_state, a
  *
  * @todo Add changefreq overridability.
  */
-function xmlsitemap_add_form_link_options(array &$form, $entity, $bundle, $id) {
+function xmlsitemap_add_form_link_options(array &$form, $entity, $bundle, $id, $form_state = NULL) {
   $info = xmlsitemap_get_link_info($entity);
 
   if (!$info || empty($info['bundles'][$bundle])) {
     return;
   }
 
-  if (!$link = xmlsitemap_link_load($entity, $id)) {
-    $link = array();
+  $language = NULL;
+  if (module_exists('entity_translation') && entity_translation_enabled($entity)) {
+    if ($et_handler = entity_translation_entity_form_get_handler($form, $form_state)) {
+      $language = $et_handler->getActiveLanguage();
+    }
+  }
+  if (!$link = xmlsitemap_link_load($entity, $id, $language)) {
+    // Attempt a fallback to the entity source language. This is necessary when
+    // creating a new translation based on a source language.
+    if ($language) {
+      $source_language = $et_handler->getSourceLanguage();
+      if ($source_language != $language) {
+        $link = xmlsitemap_link_load($entity, $id, $language);
+      }
+    }
+
+    if (!$link) {
+      $link = array();
+    }
   }
 
   $bundle_info = xmlsitemap_link_bundle_load($entity, $bundle);
diff --git a/xmlsitemap.install b/xmlsitemap.install
index 0a15f23..a60d0db 100644
--- a/xmlsitemap.install
+++ b/xmlsitemap.install
@@ -246,7 +246,7 @@ function xmlsitemap_schema() {
         'default' => 0,
       ),
     ),
-    'primary key' => array('id', 'type'),
+    'primary key' => array('id', 'type', 'language'),
     'indexes' => array(
       'loc' => array('loc'),
       'access_status_loc' => array('access', 'status', 'loc'),
@@ -575,6 +575,16 @@ function xmlsitemap_update_7204() {
   _xmlsitemap_sitemap_rehash_all();
 }
 
+/**
+ * Add new primary key including the language.
+ */
+function xmlsitemap_update_7205() {
+  $table = 'xmlsitemap';
+  db_drop_primary_key($table);
+  db_drop_unique_key($table, 'language');
+  db_add_primary_key($table, array('id', 'type', 'language'));
+}
+
 /**
  * Rehash all.
  */
diff --git a/xmlsitemap.module b/xmlsitemap.module
index 9119f27..163b450 100644
--- a/xmlsitemap.module
+++ b/xmlsitemap.module
@@ -577,12 +577,18 @@ function xmlsitemap_sitemap_uri(stdClass $sitemap) {
  *   A string with the entity type.
  * @param int $entity_id
  *   An integer with the entity ID.
+ * @param string $language
+ *   Optionally, a language code.
  *
  * @return array
  *   A sitemap link (array) or FALSE if the conditions were not found.
  */
-function xmlsitemap_link_load($entity_type, $entity_id) {
-  $link = xmlsitemap_link_load_multiple(array('type' => $entity_type, 'id' => $entity_id));
+function xmlsitemap_link_load($entity_type, $entity_id, $language = NULL) {
+  $conditions = array('type' => $entity_type, 'id' => $entity_id);
+  if ($language) {
+    $conditions['language'] = $language;
+  }
+  $link = xmlsitemap_link_load_multiple($conditions);
   return $link ? reset($link) : FALSE;
 }
 
@@ -672,7 +678,16 @@ function xmlsitemap_link_save(array $link, array $context = array()) {
     $link['changecount'] = 0;
   }
 
-  $existing = db_query_range("SELECT loc, access, status, lastmod, priority, changefreq, changecount, language FROM {xmlsitemap} WHERE type = :type AND id = :id", 0, 1, array(':type' => $link['type'], ':id' => $link['id']))->fetchAssoc();
+  $link_keys = array('type', 'id');
+  // If entity translation exists and the link has a language we need to add the
+  // language to the query to fetch the unique / language specific link.
+  if (module_exists('entity_translation') && entity_get_info($link['type']) && entity_translation_enabled($link['type']) && !empty($link['language'])) {
+    $existing = db_query_range("SELECT loc, access, status, lastmod, priority, changefreq, changecount, language FROM {xmlsitemap} WHERE type = :type AND id = :id AND language = :language", 0, 1, array(':type' => $link['type'], ':id' => $link['id'], ':language' => $link['language']))->fetchAssoc();
+    $link_keys[] = 'language';
+  }
+  else {
+    $existing = db_query_range("SELECT loc, access, status, lastmod, priority, changefreq, changecount, language FROM {xmlsitemap} WHERE type = :type AND id = :id", 0, 1, array(':type' => $link['type'], ':id' => $link['id']))->fetchAssoc();
+  }
 
   // Check if this is a changed link and set the regenerate flag if necessary.
   if (!variable_get('xmlsitemap_regenerate_needed', FALSE)) {
@@ -681,7 +696,7 @@ function xmlsitemap_link_save(array $link, array $context = array()) {
 
   // Save the link and allow other modules to respond to the link being saved.
   if ($existing) {
-    drupal_write_record('xmlsitemap', $link, array('type', 'id'));
+    drupal_write_record('xmlsitemap', $link, $link_keys);
     module_invoke_all('xmlsitemap_link_update', $link, $context);
   }
   else {
@@ -736,12 +751,18 @@ function xmlsitemap_link_update_multiple($updates = array(), $conditions = array
  *   A string with the entity type.
  * @param int $entity_id
  *   An integer with the entity ID.
+ * @param $langcode
+ *   An optional language code for the link that should be deleted.
+ *   If omitted, links for that entity will be removed in all languages.
  *
  * @return int
  *   The number of links that were deleted.
  */
-function xmlsitemap_link_delete($entity_type, $entity_id) {
+function xmlsitemap_link_delete($entity_type, $entity_id, $langcode = NULL) {
   $conditions = array('type' => $entity_type, 'id' => $entity_id);
+  if ($langcode) {
+    $conditions['language'] = $langcode;
+  }
   return xmlsitemap_link_delete_multiple($conditions);
 }
 
@@ -1276,6 +1297,14 @@ function xmlsitemap_field_attach_delete_bundle($entity_type, $bundle, $instances
   xmlsitemap_link_bundle_delete($entity_type, $bundle, TRUE);
 }
 
+/**
+ * Implements hook_entity_translation_delete().
+ */
+function xmlsitemap_entity_translation_delete($entity_type, $entity, $langcode) {
+  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+  xmlsitemap_link_delete($entity_type, $id, $langcode);
+}
+
 /**
  * Determine the frequency of updates to a link.
  *
@@ -1747,3 +1776,48 @@ function xmlsitemap_link_enqueue($type, $ids) {
   $queue = DrupalQueue::get('xmlsitemap_link_process');
   $queue->createItem($data);
 }
+
+/**
+ * Determines the languages to process for an entity.
+ *
+ * If entity_translation is available the languages are determined by using the
+ * translation handler, otherwise it falls back to the default language.
+ *
+ * @param string $entity_type
+ *   The type of entity.
+ * @param object $entity
+ *   The entity.
+ * @param string $default_language
+ *   Set the default language to use if no translations / entity languages where
+ *   found.
+ *
+ * @return array
+ *   Array with the language codes of available languages.
+ *   Returns only languages of published translations. If entity isn't handled
+ *   by entity translation the default language is used.
+ */
+function xmlsitemap_get_entity_languages($entity_type, $entity, $default_language = LANGUAGE_NONE) {
+  if (module_exists('entity_translation')) {
+    if (entity_translation_enabled($entity_type, $entity)) {
+      $handler = entity_translation_get_handler($entity_type, $entity);
+      if (!empty($handler)) {
+        $languages = array();
+        $translations = $handler->getTranslations();
+        foreach ($translations->data as $translation_lang => $translation_data) {
+          $languages[$translation_lang] = (int) $translation_data['status'];
+        }
+        // Detect allowing fallback for languages for entities not yet translated.
+        $fallback = (int) (variable_get('locale_field_language_fallback', TRUE) && variable_get('xmlsitemap_language_fallback', TRUE));
+        // Retrieve all active languages in the site.
+        $available_languages = locale_language_list('language');
+        // Add languages for entities not yet translated.
+        $languages += array_fill_keys(array_diff_key($available_languages, $languages), $fallback);
+        if (variable_get('xmlsitemap_language_remove_neutral', FALSE) && !empty($languages[LANGUAGE_NONE])) {
+          $languages[LANGUAGE_NONE] = 0;
+        }
+        return $languages;
+      }
+    }
+  }
+  return array($default_language => 1);
+}
diff --git a/xmlsitemap_i18n/xmlsitemap_i18n.install b/xmlsitemap_i18n/xmlsitemap_i18n.install
new file mode 100644
index 0000000..5a56452
--- /dev/null
+++ b/xmlsitemap_i18n/xmlsitemap_i18n.install
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the xmlsitemap_i18n module.
+ *
+ * @ingroup xmlsitemap
+ */
+
+/**
+ * Implements hook_install().
+ *
+ * If there are any languages enabled, install a sitemap for each, and delete
+ * the default sitemap (created by xmlsitemap) with no language context.
+ */
+function xmlsitemap_i18n_install() {
+  if (module_exists('locale') && ($languages = array_keys(locale_language_list()))) {
+    // Delete the default sitemap with no language context. It should be
+    // removed so that it doesn't conflict with the to-be-created sitemap for
+    // the default language.
+    $context = array();
+    db_delete('xmlsitemap_sitemap')
+      ->condition('context', serialize($context))
+      ->execute();
+
+    // Add a sitemap for each language.
+    foreach ($languages as $language) {
+      $context = array('language' => $language);
+      db_insert('xmlsitemap_sitemap')
+        ->fields(array(
+          'smid' => xmlsitemap_sitemap_get_context_hash($context),
+          'context' => serialize($context),
+        ))
+        ->execute();
+    }
+  }
+}
+
+/**
+ * Implements hook_uninstall().
+ *
+ * Delete sitemaps related to languages and set 1 default sitemap.
+ */
+function xmlsitemap_i18n_uninstall() {
+  if (module_exists('locale') && ($languages = array_keys(locale_language_list()))) {
+    // Delete sitemap for each language.
+    foreach ($languages as $language) {
+      $context = array('language' => $language);
+      db_delete('xmlsitemap_sitemap')
+        ->condition('context', serialize($context))
+        ->execute();
+    }
+
+    // Add default sitemap.
+    db_insert('xmlsitemap_sitemap')
+      ->fields(array(
+        'smid' => xmlsitemap_sitemap_get_context_hash($context),
+        'context' => serialize($context),
+      ))
+      ->execute();
+  }
+}
diff --git a/xmlsitemap_node/xmlsitemap_node.module b/xmlsitemap_node/xmlsitemap_node.module
index c287f26..7944796 100644
--- a/xmlsitemap_node/xmlsitemap_node.module
+++ b/xmlsitemap_node/xmlsitemap_node.module
@@ -33,8 +33,15 @@ function xmlsitemap_node_cron() {
       // The node could have been deleted in the meantime, skip XML sitemap
       // updates in this case.
       if ($node) {
-        $link = xmlsitemap_node_create_link($node);
-        xmlsitemap_link_save($link, array($link['type'] => $node));
+        $default_language = isset($node->language) ? $node->language : LANGUAGE_NONE;
+        $link_languages = xmlsitemap_get_entity_languages('node', $node, $default_language);
+        foreach ($link_languages as $lang => $lang_status) {
+          $lang_link = xmlsitemap_node_create_link($node, $lang);
+          $link_status = (int) $lang_link['status'];
+          $lang_link['language'] = $lang;
+          $lang_link['status'] = (int) ($link_status && $lang_status);
+          xmlsitemap_link_save($lang_link, array($lang_link['type'] => $node));
+        }
       }
       $queue->deleteItem($item);
     }
@@ -73,8 +80,7 @@ function xmlsitemap_node_xmlsitemap_process_node_links(array $nids) {
     foreach ($nids_chunks as $chunk) {
       $nodes = node_load_multiple($chunk);
       foreach ($nodes as $node) {
-        $link = xmlsitemap_node_create_link($node);
-        xmlsitemap_link_save($link, array($link['type'] => $node));
+        xmlsitemap_node_node_update($node);
       }
       // Flush each entity from the load cache after processing, to avoid
       // exceeding PHP memory limits if $nids is large.
@@ -94,9 +100,16 @@ function xmlsitemap_node_node_insert(stdClass $node) {
  * Implements hook_node_update().
  */
 function xmlsitemap_node_node_update(stdClass $node) {
+  $language = NULL;
+  if (module_exists('entity_translation') && entity_translation_enabled('node', $node)) {
+    if ($et_handler = entity_translation_get_handler('node', $node)) {
+      $language = $et_handler->getActiveLanguage();
+    }
+  }
+
   // Save a sitemap link with revoked access until the node permissions are
   // checked in the cron.
-  $link = xmlsitemap_node_create_link($node);
+  $link = xmlsitemap_node_create_link($node, $language);
   xmlsitemap_link_presave($link, array($link['type'] => $node));
   // Node access can not be accurately determined in hook_node_update() because
   // node grants have not yet been written to the table, so we defer checking
@@ -181,7 +194,7 @@ function xmlsitemap_node_form_node_type_form_alter(array &$form, array $form_sta
 function xmlsitemap_node_form_node_form_alter(array &$form, array &$form_state) {
   // Add the link options.
   module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
-  xmlsitemap_add_form_link_options($form, 'node', $form['type']['#value'], $form['nid']['#value']);
+  xmlsitemap_add_form_link_options($form, 'node', $form['type']['#value'], $form['nid']['#value'], $form_state);
   $form['xmlsitemap']['#weight'] = 30;
 }
 
@@ -215,11 +228,13 @@ function xmlsitemap_node_get_timestamps(stdClass $node) {
  *
  * @param object $node
  *   A node object.
+ * @param string $language
+ *   Optionally, a language code.
  */
-function xmlsitemap_node_create_link(stdClass $node) {
+function xmlsitemap_node_create_link(stdClass $node, $language = NULL) {
   if (!isset($node->xmlsitemap) || !is_array($node->xmlsitemap)) {
     $node->xmlsitemap = array();
-    if ($node->nid && $link = xmlsitemap_link_load('node', $node->nid)) {
+    if ($node->nid && $link = xmlsitemap_link_load('node', $node->nid, $language)) {
       $node->xmlsitemap = $link;
     }
   }
@@ -248,7 +263,10 @@ function xmlsitemap_node_create_link(stdClass $node) {
   $node->xmlsitemap['loc'] = $uri['path'];
   $node->xmlsitemap['lastmod'] = count($timestamps) ? max($timestamps) : 0;
   $node->xmlsitemap['access'] = $node->nid ? xmlsitemap_node_view_access($node, drupal_anonymous_user()) : 1;
-  $node->xmlsitemap['language'] = isset($node->language) ? $node->language : LANGUAGE_NONE;
+  // Fetches the languages for the sitemap link / links.
+  $language = isset($node->language) ? $node->language : LANGUAGE_NONE;
+  $node->xmlsitemap['language'] = $language;
+  $node->xmlsitemap['languages'] = xmlsitemap_get_entity_languages('node', $node, $language);
 
   return $node->xmlsitemap;
 }
diff --git a/xmlsitemap_taxonomy/xmlsitemap_taxonomy.module b/xmlsitemap_taxonomy/xmlsitemap_taxonomy.module
index d97f9c3..f20728c 100644
--- a/xmlsitemap_taxonomy/xmlsitemap_taxonomy.module
+++ b/xmlsitemap_taxonomy/xmlsitemap_taxonomy.module
@@ -54,8 +54,7 @@ function xmlsitemap_taxonomy_xmlsitemap_index_links($limit) {
 function xmlsitemap_taxonomy_xmlsitemap_process_taxonomy_term_links(array $tids) {
   $terms = taxonomy_term_load_multiple($tids);
   foreach ($terms as $term) {
-    $link = xmlsitemap_taxonomy_create_link($term);
-    xmlsitemap_link_save($link, array($link['type'] => $term));
+    xmlsitemap_taxonomy_term_update($term);
   }
 }
 
@@ -86,7 +85,7 @@ function xmlsitemap_taxonomy_form_taxonomy_form_term_alter(&$form, $form_state)
 
   // Add the link options.
   module_load_include('inc', 'xmlsitemap', 'xmlsitemap.admin');
-  xmlsitemap_add_form_link_options($form, 'taxonomy_term', $form['#term']['vocabulary_machine_name'], $form['tid']['#value']);
+  xmlsitemap_add_form_link_options($form, 'taxonomy_term', $form['#term']['vocabulary_machine_name'], $form['tid']['#value'], $form_state);
 }
 
 /**
@@ -111,16 +110,29 @@ function xmlsitemap_taxonomy_vocabulary_update(stdClass $vocabulary) {
  * Implements hook_taxonomy_term_insert().
  */
 function xmlsitemap_taxonomy_term_insert(stdClass $term) {
-  $link = xmlsitemap_taxonomy_create_link($term);
-  xmlsitemap_link_save($link, array($link['type'] => $term));
+  xmlsitemap_taxonomy_term_update($term);
 }
 
 /**
  * Implements hook_taxonomy_term_update().
  */
 function xmlsitemap_taxonomy_term_update(stdClass $term) {
-  $link = xmlsitemap_taxonomy_create_link($term);
-  xmlsitemap_link_save($link, array($link['type'] => $term));
+  $language = NULL;
+  if (module_exists('entity_translation') && entity_translation_enabled('taxonomy_term', $term)) {
+    if ($et_handler = entity_translation_get_handler('taxonomy_term', $term)) {
+      $language = $et_handler->getActiveLanguage();
+    }
+  }
+
+  $default_language = isset($term->language) ? $term->language : LANGUAGE_NONE;
+  $link_languages = xmlsitemap_get_entity_languages('taxonomy_term', $term, $default_language);
+  foreach ($link_languages as $lang => $lang_status) {
+    $lang_link = xmlsitemap_taxonomy_create_link($term, $language);
+    $link_status = (int) $lang_link['status'];
+    $lang_link['language'] = $lang;
+    $lang_link['status'] = (int) ($link_status && $lang_status);
+    xmlsitemap_link_save($lang_link, array($lang_link['type'] => $term));
+  }
 }
 
 /**
@@ -150,14 +162,16 @@ function xmlsitemap_taxonomy_field_extra_fields() {
  *
  * @param object $term
  *   A taxonomy term object.
+ * @param string $language
+ *   Optionally, a language code.
  *
  * @return array
  *   An array representing a sitemap link.
  */
-function xmlsitemap_taxonomy_create_link(stdClass &$term) {
+function xmlsitemap_taxonomy_create_link(stdClass $term, $language = NULL) {
   if (!isset($term->xmlsitemap)) {
     $term->xmlsitemap = array();
-    if ($term->tid && $link = xmlsitemap_link_load('taxonomy_term', $term->tid)) {
+    if ($term->tid && $link = xmlsitemap_link_load('taxonomy_term', $term->tid, $language)) {
       $term->xmlsitemap = $link;
     }
   }
@@ -181,7 +195,10 @@ function xmlsitemap_taxonomy_create_link(stdClass &$term) {
   // @todo How can/should we check taxonomy term access?
   $term->xmlsitemap['loc'] = $uri['path'];
   $term->xmlsitemap['access'] = 1;
-  $term->xmlsitemap['language'] = isset($term->language) ? $term->language : LANGUAGE_NONE;
+  // Fetches the languages for the sitemap link / links.
+  $language = isset($term->language) ? $term->language : LANGUAGE_NONE;
+  $term->xmlsitemap['language'] = $language;
+  $term->xmlsitemap['languages'] = xmlsitemap_get_entity_languages('taxonomy_term', $term, $language);
 
   return $term->xmlsitemap;
 }
