diff --git a/mappers/date.inc b/mappers/date.inc
index d5ea81f..c75f125 100644
--- a/mappers/date.inc
+++ b/mappers/date.inc
@@ -37,7 +37,7 @@ function date_feeds_processor_targets($entity_type, $bundle_name) {
 /**
  * Callback for setting date values.
  */
-function date_feeds_set_target(FeedsSource $source, $entity, $target, array $values) {
+function date_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
   list($field_name, $sub_field) = explode(':', $target, 2);
 
   $delta = 0;
@@ -56,7 +56,7 @@ function date_feeds_set_target(FeedsSource $source, $entity, $target, array $val
       }
     }
 
-    $value->buildDateField($entity, $field_name, $delta);
+    $value->buildDateField($entity, $field_name, $delta, $mapping['language']);
     $delta++;
   }
 }
diff --git a/mappers/entity_translation.inc b/mappers/entity_translation.inc
new file mode 100644
index 0000000..3d09bff
--- /dev/null
+++ b/mappers/entity_translation.inc
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * @file
+ * On behalf implementation of Feeds mapping API for entity_translation.module.
+ */
+
+/**
+ * Implements hook_feeds_presave().
+ */
+function entity_translation_feeds_presave(FeedsSource $source, $entity, $item, $entity_id) {
+  $entity_type = $entity->feeds_item->entity_type;
+
+  if (!$handler = entity_translation_get_handler($entity_type, $entity)) {
+    return;
+  }
+
+  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+
+  $languages_seen = array();
+
+  $entity_info = entity_get_info($entity_type);
+  $translation_key = isset($entity_info['entity keys']['translations']) ? $entity_info['entity keys']['translations'] : FALSE;
+
+  if (!$translation_key) {
+    return;
+  }
+
+  foreach (field_info_instances($entity_type, $bundle) as $instance) {
+    $field_name = $instance['field_name'];
+
+    // No values in this field, skip it.
+    if (empty($entity->$field_name)) {
+      continue;
+    }
+
+    // Not translatable.
+    if (($info = field_info_field($field_name)) && !$info['translatable']) {
+      continue;
+    }
+
+    // Init the translation handler.
+    if (empty($entity->$translation_key->data)) {
+      $handler->initTranslations();
+    }
+
+    // Avoid invalid user configuration. Entity translation does this when
+    // loading the translation overview page.
+    if (count($entity->$field_name) === 1 && key($entity->$field_name) === LANGUAGE_NONE) {
+      $entity->{$field_name}[$handler->getLanguage()] = $entity->{$field_name}[LANGUAGE_NONE];
+      unset($entity->{$field_name}[LANGUAGE_NONE]);
+    }
+
+    // Look at all languages in this field.
+    foreach ($entity->$field_name as $language => $v) {
+      if (isset($languages_seen[$language]) || $language === LANGUAGE_NONE) {
+        continue;
+      }
+
+      $languages_seen[$language] = TRUE;
+
+      if ($language === $handler->getLanguage()) {
+        continue;
+      }
+
+      $translation = array(
+        'translate' => 0,
+        'status' => 1,
+        'language' => $language,
+        'source' => $handler->getLanguage(),
+      );
+
+      $handler->setTranslation($translation, $entity);
+    }
+  }
+
+  // Loop through every language for the site, and remove translations for the
+  // ones that don't have any values.
+  foreach (language_list() as $language) {
+    if (!isset($languages_seen[$language->language])) {
+      $handler->removeTranslation($language->language);
+    }
+  }
+}
diff --git a/mappers/file.inc b/mappers/file.inc
index d46c319..07349f2 100644
--- a/mappers/file.inc
+++ b/mappers/file.inc
@@ -54,7 +54,9 @@ function file_feeds_processor_targets($entity_type, $bundle_name) {
 /**
  * Callback for mapping file fields.
  */
-function file_feeds_set_target(FeedsSource $source, $entity, $target, array $values) {
+function file_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
+  $language = $mapping['language'];
+
   // Add default of uri for backwards compatibility.
   list($field_name, $sub_field) = explode(':', $target . ':uri');
   $info = field_info_field($field_name);
@@ -90,22 +92,22 @@ function file_feeds_set_target(FeedsSource $source, $entity, $target, array $val
   }
 
   // Populate entity.
-  $field = isset($entity->$field_name) ? $entity->$field_name : array(LANGUAGE_NONE => array());
+  $field = isset($entity->$field_name) ? $entity->$field_name : array($language => array());
   $delta = 0;
   foreach ($values as $v) {
     if ($info['cardinality'] == $delta) {
       break;
     }
 
-    if (!isset($field[LANGUAGE_NONE][$delta])) {
-      $field[LANGUAGE_NONE][$delta] = array();
+    if (!isset($field[$language][$delta])) {
+      $field[$language][$delta] = array();
     }
 
     switch ($sub_field) {
       case 'alt':
       case 'title':
       case 'description':
-        $field[LANGUAGE_NONE][$delta][$sub_field] = $v;
+        $field[$language][$delta][$sub_field] = $v;
         break;
 
       case 'uri':
@@ -113,9 +115,9 @@ function file_feeds_set_target(FeedsSource $source, $entity, $target, array $val
           try {
             $v->setAllowedExtensions($instance_info['settings']['file_extensions']);
             $file = $v->getFile($destination);
-            $field[LANGUAGE_NONE][$delta] += (array) $file;
+            $field[$language][$delta] += (array) $file;
             // @todo: Figure out how to properly populate this field.
-            $field[LANGUAGE_NONE][$delta]['display'] = 1;
+            $field[$language][$delta]['display'] = 1;
           }
           catch (Exception $e) {
             watchdog('feeds', check_plain($e->getMessage()));
diff --git a/mappers/link.inc b/mappers/link.inc
index e9c3632..1dc19dc 100644
--- a/mappers/link.inc
+++ b/mappers/link.inc
@@ -39,10 +39,12 @@ function link_feeds_processor_targets($entity_type, $bundle_name) {
 /**
  * Callback for mapping link fields.
  */
-function link_feeds_set_target(FeedsSource $source, $entity, $target, array $values) {
+function link_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
+  $language = $mapping['language'];
+
   list($field_name, $column) = explode(':', $target);
 
-  $field = isset($entity->$field_name) ? $entity->$field_name : array('und' => array());
+  $field = isset($entity->$field_name) ? $entity->$field_name : array($language => array());
   $delta = 0;
 
   foreach ($values as $value) {
@@ -51,7 +53,7 @@ function link_feeds_set_target(FeedsSource $source, $entity, $target, array $val
     }
 
     if (is_scalar($value)) {
-      $field['und'][$delta][$column] = (string) $value;
+      $field[$language][$delta][$column] = (string) $value;
     }
     $delta++;
   }
diff --git a/mappers/list.inc b/mappers/list.inc
index 8c5dc36..6a30829 100644
--- a/mappers/list.inc
+++ b/mappers/list.inc
@@ -49,8 +49,10 @@ function list_feeds_processor_targets($entity_type, $bundle_name) {
 /**
  * Callback for setting list_boolean fields.
  */
-function list_feeds_set_boolean_target(FeedsSource $source, $entity, $target, array $values) {
-  $field = isset($entity->$target) ? $entity->$target : array(LANGUAGE_NONE => array());
+function list_feeds_set_boolean_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
+  $language = $mapping['language'];
+
+  $field = isset($entity->$target) ? $entity->$target : array($language => array());
 
   foreach ($values as $value) {
 
@@ -58,7 +60,7 @@ function list_feeds_set_boolean_target(FeedsSource $source, $entity, $target, ar
       $value = $value->getValue();
     }
 
-    $field[LANGUAGE_NONE][] = array('value' => (int) (bool) $value);
+    $field[$language][] = array('value' => (int) (bool) $value);
   }
 
   $entity->$target = $field;
diff --git a/mappers/locale.inc b/mappers/locale.inc
new file mode 100644
index 0000000..4d18dcc
--- /dev/null
+++ b/mappers/locale.inc
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @file
+ * On behalf implementation of Feeds mapping API for locale.module.
+ */
+
+/**
+ * Implements hook_feeds_processor_targets_alter().
+ */
+function locale_feeds_processor_targets_alter(array &$targets, $entity_type, $bundle_name) {
+  foreach (array_keys($targets) as $target) {
+    $targets[$target]['preprocess_callbacks'][] = 'locale_feeds_preprocess_callback';
+    $targets[$target]['summary_callbacks'][] = 'locale_feeds_summary_callback';
+    $targets[$target]['form_callbacks'][] = 'locale_feeds_form_callback';
+  }
+}
+
+function locale_feeds_preprocess_callback(FeedsSource $source, $target_item, array $target, array &$mapping) {
+  if (empty($mapping['field_language'])) {
+    return;
+  }
+
+  $mapping['language'] = $mapping['field_language'];
+}
+
+function locale_feeds_summary_callback(array $mapping, array $target, array $form, array $form_state) {
+  $entity_type = $form_state['build_info']['args'][0]->processor->entityType();
+  $translatable = _locale_feeds_target_is_translatable($entity_type, $mapping['target']);
+
+  $mapping += array('field_language' => LANGUAGE_NONE);
+
+  // This is an invalid configuration that can come from disabling
+  // entity_translation.
+  $error = $mapping['field_language'] !== LANGUAGE_NONE && !$translatable;
+
+  // Nothing to see here.
+  if (!$error && !$translatable) {
+    return;
+  }
+
+  if ($error) {
+    return t('Language: <strong>@error</strong>', array('@error' => t('Error')));
+  }
+
+  $language_options = array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name');
+
+  return t('Language: %lang', array('%lang' => $language_options[$mapping['field_language']]));
+}
+
+function locale_feeds_form_callback(array $mapping, array $target, array $form, array $form_state) {
+  $form = array();
+
+  $entity_type = $form_state['build_info']['args'][0]->processor->entityType();
+
+  $translatable = _locale_feeds_target_is_translatable($entity_type, $mapping['target']);
+  $mapping += array('field_language' => LANGUAGE_NONE);
+
+  // This is an invalid configuration that can come from disabling
+  // entity_translation.
+  $error = $mapping['field_language'] !== LANGUAGE_NONE && !$translatable;
+
+  // Nothing to see here.
+  if (!$error && !$translatable) {
+    return $form;
+  }
+
+  $language_options = array(LANGUAGE_NONE => t('Language neutral'));
+
+  if (!$error) {
+    $language_options += locale_language_list('name');
+  }
+
+  $form['field_language'] = array(
+    '#type' => 'select',
+    '#title' => t('Language'),
+    '#options' => $language_options,
+    '#default_value' => $mapping['field_language'],
+  );
+
+  return $form;
+}
+
+function _locale_feeds_target_is_translatable($entity_type, $target) {
+  list($field_name) = explode(':', $target, 2);
+
+  $info = field_info_field($field_name);
+
+  return !empty($info) && field_is_translatable($entity_type, $info);
+}
diff --git a/mappers/number.inc b/mappers/number.inc
index 7f9a0d8..406b4f8 100644
--- a/mappers/number.inc
+++ b/mappers/number.inc
@@ -34,9 +34,11 @@ function number_feeds_processor_targets($entity_type, $bundle_name) {
 /**
  * Callback for mapping number fields.
  */
-function number_feeds_set_target(FeedsSource $source, $entity, $target, array $values) {
+function number_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
+  $language = $mapping['language'];
+
   // Iterate over all values.
-  $field = isset($entity->$target) ? $entity->$target : array('und' => array());
+  $field = isset($entity->$target) ? $entity->$target : array($language => array());
 
   foreach ($values as $value) {
 
@@ -45,7 +47,7 @@ function number_feeds_set_target(FeedsSource $source, $entity, $target, array $v
     }
 
     if (is_numeric($value)) {
-      $field['und'][] = array('value' => $value);
+      $field[$language][] = array('value' => $value);
     }
   }
 
diff --git a/mappers/taxonomy.inc b/mappers/taxonomy.inc
index 6fbdec6..bd7fed4 100644
--- a/mappers/taxonomy.inc
+++ b/mappers/taxonomy.inc
@@ -85,6 +85,8 @@ function taxonomy_feeds_processor_targets($entity_type, $bundle_name) {
  * Callback for mapping taxonomy terms.
  */
 function taxonomy_feeds_set_target(FeedsSource $source, $entity, $target, array $terms, array $mapping) {
+  $language = $mapping['language'];
+
   // Add in default values.
   $mapping += array(
     'term_search' => FEEDS_TAXONOMY_SEARCH_TERM_NAME,
@@ -118,10 +120,10 @@ function taxonomy_feeds_set_target(FeedsSource $source, $entity, $target, array
     ->range(0, 1);
 
 
-  $field = isset($entity->$target) ? $entity->$target : array('und' => array());
+  $field = isset($entity->$target) ? $entity->$target : array($language => array());
 
   // Allow for multiple mappings to the same target.
-  $delta = count($field['und']);
+  $delta = count($field[$language]);
 
   // Iterate over all values.
   foreach ($terms as $term) {
@@ -181,7 +183,7 @@ function taxonomy_feeds_set_target(FeedsSource $source, $entity, $target, array
     }
 
     if ($tid && isset($cache['allowed_values'][$target][$tid])) {
-      $field['und'][] = array('tid' => $tid);
+      $field[$language][] = array('tid' => $tid);
       $delta++;
     }
   }
diff --git a/mappers/text.inc b/mappers/text.inc
index aa9c2e1..1cf76bf 100644
--- a/mappers/text.inc
+++ b/mappers/text.inc
@@ -49,6 +49,8 @@ function text_feeds_processor_targets($entity_type, $bundle_name) {
  * Callback for mapping text fields.
  */
 function text_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
+  $language = $mapping['language'];
+
   list($field_name, $column) = explode(':', $target . ':value');
 
   if ($column === 'value' && isset($source->importer->processor->config['input_format'])) {
@@ -59,7 +61,7 @@ function text_feeds_set_target(FeedsSource $source, $entity, $target, array $val
     );
   }
 
-  $field = isset($entity->$field_name) ? $entity->$field_name : array('und' => array());
+  $field = isset($entity->$field_name) ? $entity->$field_name : array($language => array());
 
   // Iterate over all values.
   $delta = 0;
@@ -71,10 +73,10 @@ function text_feeds_set_target(FeedsSource $source, $entity, $target, array $val
 
     if (is_scalar($value) && strlen($value)) {
 
-      $field['und'][$delta][$column] = (string) $value;
+      $field[$language][$delta][$column] = (string) $value;
 
       if (isset($mapping['format'])) {
-        $field['und'][$delta]['format'] = $mapping['format'];
+        $field[$language][$delta]['format'] = $mapping['format'];
       }
     }
 
diff --git a/plugins/FeedsNodeProcessor.inc b/plugins/FeedsNodeProcessor.inc
index 2cfdf30..d04d546 100644
--- a/plugins/FeedsNodeProcessor.inc
+++ b/plugins/FeedsNodeProcessor.inc
@@ -40,7 +40,7 @@ class FeedsNodeProcessor extends FeedsProcessor {
     $node->type = $this->bundle();
     $node->changed = REQUEST_TIME;
     $node->created = REQUEST_TIME;
-    $node->language = LANGUAGE_NONE;
+    $node->language = $this->config['language'];
     $node->is_new = TRUE;
     node_object_prepare($node);
     // Populate properties that are set by node_object_prepare().
diff --git a/plugins/FeedsParser.inc b/plugins/FeedsParser.inc
index 8b430c0..7e86e9e 100644
--- a/plugins/FeedsParser.inc
+++ b/plugins/FeedsParser.inc
@@ -548,13 +548,13 @@ class FeedsDateTimeElement extends FeedsElement {
    * Helper method for buildDateField(). Build a FeedsDateTimeElement object
    * from a standard formatted node.
    */
-  protected static function readDateField($entity, $field_name, $delta = 0) {
+  protected static function readDateField($entity, $field_name, $delta = 0, $language = LANGUAGE_NONE) {
     $ret = new FeedsDateTimeElement();
-    if (isset($entity->{$field_name}['und'][$delta]['date']) && $entity->{$field_name}['und'][$delta]['date'] instanceof FeedsDateTime) {
-      $ret->start = $entity->{$field_name}['und'][$delta]['date'];
+    if (isset($entity->{$field_name}[$language][$delta]['date']) && $entity->{$field_name}[$language][$delta]['date'] instanceof FeedsDateTime) {
+      $ret->start = $entity->{$field_name}[$language][$delta]['date'];
     }
-    if (isset($entity->{$field_name}['und'][$delta]['date2']) && $entity->{$field_name}['und'][$delta]['date2'] instanceof FeedsDateTime) {
-      $ret->end = $entity->{$field_name}['und'][$delta]['date2'];
+    if (isset($entity->{$field_name}[$language][$delta]['date2']) && $entity->{$field_name}[$language][$delta]['date2'] instanceof FeedsDateTime) {
+      $ret->end = $entity->{$field_name}[$language][$delta]['date2'];
     }
     return $ret;
   }
@@ -569,10 +569,10 @@ class FeedsDateTimeElement extends FeedsElement {
    * @param int $delta
    *   The delta in the field.
    */
-  public function buildDateField($entity, $field_name, $delta = 0) {
+  public function buildDateField($entity, $field_name, $delta = 0, $language = LANGUAGE_NONE) {
     $info = field_info_field($field_name);
 
-    $oldfield = FeedsDateTimeElement::readDateField($entity, $field_name, $delta);
+    $oldfield = FeedsDateTimeElement::readDateField($entity, $field_name, $delta, $language);
     // Merge with any preexisting objects on the field; we take precedence.
     $oldfield = $this->merge($oldfield);
     $use_start = $oldfield->start;
@@ -605,27 +605,27 @@ class FeedsDateTimeElement extends FeedsElement {
 
     $db_tz = new DateTimeZone($db_tz);
     if (!isset($entity->{$field_name})) {
-      $entity->{$field_name} = array('und' => array());
+      $entity->{$field_name} = array($language => array());
     }
     if ($use_start) {
-      $entity->{$field_name}['und'][$delta]['timezone'] = $use_start->getTimezone()->getName();
-      $entity->{$field_name}['und'][$delta]['offset'] = $use_start->getOffset();
+      $entity->{$field_name}[$language][$delta]['timezone'] = $use_start->getTimezone()->getName();
+      $entity->{$field_name}[$language][$delta]['offset'] = $use_start->getOffset();
       $use_start->setTimezone($db_tz);
-      $entity->{$field_name}['und'][$delta]['date'] = $use_start;
+      $entity->{$field_name}[$language][$delta]['date'] = $use_start;
       /**
        * @todo the date_type_format line could be simplified based upon a patch
        *   DO issue #259308 could affect this, follow up on at some point.
        *   Without this, all granularity info is lost.
        *   $use_start->format(date_type_format($field['type'], $use_start->granularity));
        */
-      $entity->{$field_name}['und'][$delta]['value'] = $use_start->format(date_type_format($info['type']));
+      $entity->{$field_name}[$language][$delta]['value'] = $use_start->format(date_type_format($info['type']));
     }
     if ($use_end) {
       // Don't ever use end to set timezone (for now)
-      $entity->{$field_name}['und'][$delta]['offset2'] = $use_end->getOffset();
+      $entity->{$field_name}[$language][$delta]['offset2'] = $use_end->getOffset();
       $use_end->setTimezone($db_tz);
-      $entity->{$field_name}['und'][$delta]['date2'] = $use_end;
-      $entity->{$field_name}['und'][$delta]['value2'] = $use_end->format(date_type_format($info['type']));
+      $entity->{$field_name}[$language][$delta]['date2'] = $use_end;
+      $entity->{$field_name}[$language][$delta]['value2'] = $use_end->format(date_type_format($info['type']));
     }
   }
 }
diff --git a/plugins/FeedsProcessor.inc b/plugins/FeedsProcessor.inc
index ab0df88..662f595 100644
--- a/plugins/FeedsProcessor.inc
+++ b/plugins/FeedsProcessor.inc
@@ -719,10 +719,15 @@ abstract class FeedsProcessor extends FeedsPlugin {
 
     if (isset($targets[$target]['preprocess_callbacks'])) {
       foreach ($targets[$target]['preprocess_callbacks'] as $callback) {
-        call_user_func_array($callback, array($source, $target_item, $target, &$mapping));
+        call_user_func_array($callback, array($source, $target_item, $targets[$target], &$mapping));
       }
     }
 
+    // Ensure there's always a language set.
+    if (empty($mapping['language'])) {
+      $mapping['language'] = LANGUAGE_NONE;
+    }
+
     // Map the source element's value to the target.
     // If the mapping specifies a callback method, use the callback instead of
     // setTargetElement().
@@ -765,6 +770,7 @@ abstract class FeedsProcessor extends FeedsPlugin {
       'input_format' => NULL,
       'skip_hash_check' => FALSE,
       'bundle' => $bundle,
+      'language' => LANGUAGE_NONE,
     );
   }
 
@@ -791,6 +797,16 @@ abstract class FeedsProcessor extends FeedsPlugin {
       );
     }
 
+    if (module_exists('locale') && !empty($info['entity keys']['language'])) {
+      $form['language'] = array(
+        '#type' => 'select',
+        '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'),
+        '#title' => t('Language'),
+        '#required' => TRUE,
+        '#default_value' => $this->config['language'],
+      );
+    }
+
     $tokens = array('@entities' => strtolower($info['label plural']));
 
     $form['update_existing'] = array(
diff --git a/tests/feeds_mapper_taxonomy.test b/tests/feeds_mapper_taxonomy.test
index 231235e..801b2d0 100644
--- a/tests/feeds_mapper_taxonomy.test
+++ b/tests/feeds_mapper_taxonomy.test
@@ -234,6 +234,7 @@ class FeedsMapperTaxonomyTestCase extends FeedsMapperTestCase {
     $target = 'field_tags';
     $mapping = array(
       'term_search' => FEEDS_TAXONOMY_SEARCH_TERM_ID,
+      'language' => LANGUAGE_NONE,
     );
 
     $source = FeedsSource::instance('tmp', 0);
@@ -284,6 +285,7 @@ class FeedsMapperTaxonomyTestCase extends FeedsMapperTestCase {
     $target = 'field_tags';
     $mapping = array(
       'term_search' => FEEDS_TAXONOMY_SEARCH_TERM_GUID,
+      'language' => LANGUAGE_NONE,
     );
 
     $source = FeedsSource::instance('tmp', 0);
diff --git a/tests/feeds_tests.module b/tests/feeds_tests.module
index 1ddd2a6..a083df0 100644
--- a/tests/feeds_tests.module
+++ b/tests/feeds_tests.module
@@ -307,7 +307,7 @@ class FeedsTestsPreprocess {
  *
  * @see feeds_tests_feeds_processor_targets()
  */
-  public static function callback(FeedsSource $source, $target_item, $target, array &$mapping) {
+  public static function callback(FeedsSource $source, $target_item, array $target, array &$mapping) {
     $mapping['required_value'] = TRUE;
   }
 
