diff --git a/feeds.info b/feeds.info
index 11934e0d83268c1c57a51daa187ab736337fb805..c57e20329ef1c3215b2441a7e9935ce60f5551a7 100644
--- a/feeds.info
+++ b/feeds.info
@@ -46,6 +46,9 @@ files[] = tests/feeds_mapper_config.test
 files[] = tests/feeds_fetcher_file.test
 files[] = tests/feeds_mapper_format_config.test
 files[] = tests/feeds_fetcher_http.test
+files[] = tests/feeds_i18n.test
+files[] = tests/feeds_i18n_node.test
+files[] = tests/feeds_i18n_taxonomy.test
 files[] = tests/feeds_parser_sitemap.test
 files[] = tests/feeds_parser_syndication.test
 files[] = tests/feeds_processor_node.test
diff --git a/plugins/FeedsNodeProcessor.inc b/plugins/FeedsNodeProcessor.inc
index 2cfdf3061195f345bfff51fb89908ee019852fe6..4170c92d23e1de31f1c1a39e0fbcc79d6ef5787a 100644
--- a/plugins/FeedsNodeProcessor.inc
+++ b/plugins/FeedsNodeProcessor.inc
@@ -36,11 +36,10 @@ class FeedsNodeProcessor extends FeedsProcessor {
    * Creates a new node in memory and returns it.
    */
   protected function newEntity(FeedsSource $source) {
-    $node = new stdClass();
+    $node = parent::newEntity($source);
     $node->type = $this->bundle();
     $node->changed = REQUEST_TIME;
     $node->created = REQUEST_TIME;
-    $node->language = LANGUAGE_NONE;
     $node->is_new = TRUE;
     node_object_prepare($node);
     // Populate properties that are set by node_object_prepare().
@@ -127,6 +126,8 @@ class FeedsNodeProcessor extends FeedsProcessor {
    * Validates a node.
    */
   protected function entityValidate($entity) {
+    parent::entityValidate($entity);
+
     if (!isset($entity->uid) || !is_numeric($entity->uid)) {
        $entity->uid = $this->config['author'];
     }
diff --git a/plugins/FeedsProcessor.inc b/plugins/FeedsProcessor.inc
index 385ee0a720f2fe099dc8568c87dd19227f814e52..0644febbdf0c0020c5b7cf0493886cbce43cc155 100644
--- a/plugins/FeedsProcessor.inc
+++ b/plugins/FeedsProcessor.inc
@@ -86,13 +86,22 @@ abstract class FeedsProcessor extends FeedsPlugin {
   /**
    * Create a new entity.
    *
-   * @param $source
+   * @param FeedsSource $source
    *   The feeds source that spawns this entity.
    *
-   * @return
+   * @return object
    *   A new entity object.
    */
-  protected abstract function newEntity(FeedsSource $source);
+  protected function newEntity(FeedsSource $source) {
+    $entity = new stdClass();
+
+    $info = $this->entityInfo();
+    if (!empty($info['entity keys']['language'])) {
+      $entity->{$info['entity keys']['language']} = $this->entityLanguage();
+    }
+
+    return $entity;
+  }
 
   /**
    * Load an existing entity.
@@ -109,28 +118,46 @@ abstract class FeedsProcessor extends FeedsPlugin {
    *   existing ids first.
    */
   protected function entityLoad(FeedsSource $source, $entity_id) {
+    $info = $this->entityInfo();
+
     if ($this->config['update_existing'] == FEEDS_UPDATE_EXISTING) {
       $entities = entity_load($this->entityType(), array($entity_id));
-      return reset($entities);
+      $entity = reset($entities);
+    }
+    else {
+      $args = array(':entity_id' => $entity_id);
+      $table = db_escape_table($info['base table']);
+      $key = db_escape_field($info['entity keys']['id']);
+      $entity = db_query("SELECT * FROM {" . $table . "} WHERE $key = :entity_id", $args)->fetchObject();
     }
 
-    $info = $this->entityInfo();
-
-    $args = array(':entity_id' => $entity_id);
-
-    $table = db_escape_table($info['base table']);
-    $key = db_escape_field($info['entity keys']['id']);
+    if ($entity && !empty($info['entity keys']['language'])) {
+      $entity->{$info['entity keys']['language']} = $this->entityLanguage();
+    }
 
-    return db_query("SELECT * FROM {" . $table . "} WHERE $key = :entity_id", $args)->fetchObject();
+    return $entity;
   }
 
   /**
-   * Validate an entity.
+   * Validates an entity.
    *
    * @throws FeedsValidationException $e
-   *   If validation fails.
+   *   Thrown if validation fails.
    */
-  protected function entityValidate($entity) {}
+  protected function entityValidate($entity) {
+    $info = $this->entityInfo();
+    if (empty($info['entity keys']['language'])) {
+      return;
+    }
+
+    // Ensure that a valid language is always set.
+    $key = $info['entity keys']['language'];
+    $languages = language_list('enabled');
+
+    if (empty($entity->$key) || !isset($languages[1][$entity->$key])) {
+      $entity->$key = $this->entityLanguage();
+    }
+  }
 
   /**
    * Access check for saving an enity.
@@ -169,6 +196,28 @@ abstract class FeedsProcessor extends FeedsPlugin {
     return entity_get_info($this->entityType());
   }
 
+  /**
+   * Returns the current language for entities.
+   *
+   * This checks if the configuration value is valid.
+   *
+   * @return string
+   *   The current language code.
+   */
+  protected function entityLanguage() {
+    if (!module_exists('locale')) {
+      // language_list() may return languages even if the locale module is
+      // disabled. See https://www.drupal.org/node/173227 why.
+      // When the locale module is disabled, there are no selectable languages
+      // in the UI, so the content should be imported in LANGUAGE_NONE.
+      return LANGUAGE_NONE;
+    }
+
+    $languages = language_list('enabled');
+
+    return isset($languages[1][$this->config['language']]) ? $this->config['language'] : LANGUAGE_NONE;
+  }
+
   /**
    * @}
    */
@@ -764,6 +813,7 @@ abstract class FeedsProcessor extends FeedsPlugin {
       'input_format' => NULL,
       'skip_hash_check' => FALSE,
       'bundle' => $bundle,
+      'language' => LANGUAGE_NONE,
     );
   }
 
@@ -790,6 +840,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['insert_new'] = array(
diff --git a/plugins/FeedsTermProcessor.inc b/plugins/FeedsTermProcessor.inc
index c600b978771262eac0bec42f66bc0e32d523ad50..bae88a4a481330a6a4d53e550dafbb4829fd1234 100644
--- a/plugins/FeedsTermProcessor.inc
+++ b/plugins/FeedsTermProcessor.inc
@@ -31,9 +31,10 @@ class FeedsTermProcessor extends FeedsProcessor {
    */
   protected function newEntity(FeedsSource $source) {
     $vocabulary = $this->vocabulary();
-    $term = new stdClass();
+    $term = parent::newEntity($source);
     $term->vid = $vocabulary->vid;
     $term->vocabulary_machine_name = $vocabulary->machine_name;
+
     return $term;
   }
 
@@ -41,6 +42,8 @@ class FeedsTermProcessor extends FeedsProcessor {
    * Validates a term.
    */
   protected function entityValidate($term) {
+    parent::entityValidate($term);
+
     if (drupal_strlen($term->name) == 0) {
       throw new FeedsValidationException(t('Term name missing.'));
     }
diff --git a/plugins/FeedsUserProcessor.inc b/plugins/FeedsUserProcessor.inc
index 778c72bedb2864f1b5437e07e03881b1caf4a8df..47d26e5cda4b8931b7ad02a07fd5bfddcd2f8c6a 100644
--- a/plugins/FeedsUserProcessor.inc
+++ b/plugins/FeedsUserProcessor.inc
@@ -37,10 +37,11 @@ class FeedsUserProcessor extends FeedsProcessor {
    * Creates a new user account in memory and returns it.
    */
   protected function newEntity(FeedsSource $source) {
-    $account = new stdClass();
+    $account = parent::newEntity($source);
     $account->uid = 0;
     $account->roles = array_filter($this->config['roles']);
     $account->status = $this->config['status'];
+
     return $account;
   }
 
@@ -59,6 +60,8 @@ class FeedsUserProcessor extends FeedsProcessor {
    * Validates a user account.
    */
   protected function entityValidate($account) {
+    parent::entityValidate($account);
+
     if (empty($account->name) || empty($account->mail) || !valid_email_address($account->mail)) {
       throw new FeedsValidationException(t('User name missing or email not valid.'));
     }
diff --git a/tests/feeds/content_i18n.csv b/tests/feeds/content_i18n.csv
new file mode 100644
index 0000000000000000000000000000000000000000..13093581272cef60936b2d4b4e01ff4584e77efe
--- /dev/null
+++ b/tests/feeds/content_i18n.csv
@@ -0,0 +1,3 @@
+"guid","title","created","alpha","beta","gamma","delta","body","language"
+1,"Lorem ipsum",1251936720,"Lorem",42,"4.2",3.14159265,"Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.","nl"
+2,"Ut wisi enim ad minim veniam",1251932360,"Ut wisi",32,"1.2",5.62951413,"Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat."
diff --git a/tests/feeds_i18n.test b/tests/feeds_i18n.test
new file mode 100644
index 0000000000000000000000000000000000000000..9db997edd56732fe7f81346f22b5f32b67560842
--- /dev/null
+++ b/tests/feeds_i18n.test
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Contains Feedsi18nTestCase.
+ */
+
+/**
+ * Tests importing data in a language.
+ */
+class Feedsi18nTestCase extends FeedsMapperTestCase {
+
+  /**
+   * The entity type to be tested.
+   *
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * The processor being used.
+   *
+   * @var string
+   */
+  protected $processorName;
+
+  public function setUp($modules = array(), $permissions = array()) {
+    $modules = array_merge($modules, array(
+      'locale',
+    ));
+    $permissions = array_merge(array(
+      'administer languages',
+    ));
+    parent::setUp($modules, $permissions);
+
+    // Setup other languages.
+    $edit = array(
+      'langcode' => 'nl',
+    );
+    $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+    $this->assertText(t('The language Dutch has been created and can now be used.'));
+    $edit = array(
+      'langcode' => 'de',
+    );
+    $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+    $this->assertText(t('The language German has been created and can now be used.'));
+
+    // Include FeedsProcessor.inc to make its constants available.
+    module_load_include('inc', 'feeds', 'plugins/FeedsProcessor');
+
+    // Create and configure importer.
+    $this->createImporterConfiguration('Multilingual term importer', 'i18n');
+    $this->setPlugin('i18n', 'FeedsFileFetcher');
+    $this->setPlugin('i18n', 'FeedsCSVParser');
+    $this->setPlugin('i18n', $this->processorName);
+  }
+
+  /**
+   * Tests if entities get the language assigned that is set in the processor.
+   */
+  public function testImport() {
+    // Import content in German.
+    $this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
+
+    // Assert that the entity's language is in German.
+    $entities = entity_load($this->entityType, array(1, 2));
+    foreach ($entities as $entity) {
+      $this->assertEqual('de', entity_language($this->entityType, $entity));
+    }
+  }
+
+  /**
+   * Tests if entities get a different language assigned when the processor's language
+   * is changed.
+   */
+  public function testChangedLanguageImport() {
+    // Import content in German.
+    $this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
+
+    // Change processor's language to Dutch.
+    $this->setSettings('i18n', $this->processorName, array('language' => 'nl'));
+
+    // Re-import content.
+    $this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
+
+    // Assert that the entity's language is now in Dutch.
+    $entities = entity_load($this->entityType, array(1, 2));
+    foreach ($entities as $entity) {
+      $this->assertEqual('nl', entity_language($this->entityType, $entity));
+    }
+  }
+
+  /**
+   * Tests if items are imported in LANGUAGE_NONE if the processor's language is disabled.
+   */
+  public function testDisabledLanguage() {
+    // Disable the German language.
+    $path = 'admin/config/regional/language';
+    $edit = array(
+      'enabled[de]' => FALSE,
+    );
+    $this->drupalPost($path, $edit, t('Save configuration'));
+
+    // Import content.
+    $this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
+
+    // Assert that the entities have no language assigned.
+    $entities = entity_load($this->entityType, array(1, 2));
+    foreach ($entities as $entity) {
+      $language = entity_language($this->entityType, $entity);
+      $this->assertEqual(LANGUAGE_NONE, $language, format_string('The entity is language neutral (actual: !language).', array('!language' => $language)));
+    }
+  }
+
+  /**
+   * Tests if items are imported in LANGUAGE_NONE if the processor's language is removed.
+   */
+  public function testRemovedLanguage() {
+    // Remove the German language.
+    $path = 'admin/config/regional/language/delete/de';
+    $this->drupalPost($path, array(), t('Delete'));
+
+    // Import content.
+    $this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
+
+    // Assert that the entities have no language assigned.
+    $entities = entity_load($this->entityType, array(1, 2));
+    foreach ($entities as $entity) {
+      $language = entity_language($this->entityType, $entity);
+      $this->assertEqual(LANGUAGE_NONE, $language, format_string('The entity is language neutral (actual: !language).', array('!language' => $language)));
+    }
+  }
+}
diff --git a/tests/feeds_i18n_node.test b/tests/feeds_i18n_node.test
new file mode 100644
index 0000000000000000000000000000000000000000..00300eb05ebe49b0c18c67eebf1de269585aa29c
--- /dev/null
+++ b/tests/feeds_i18n_node.test
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * @file
+ * Contains Feedsi18nNodeTestCase.
+ */
+
+/**
+ * Tests importing nodes in a language.
+ */
+class Feedsi18nNodeTestCase extends Feedsi18nTestCase {
+
+  /**
+   * Name of created content type.
+   *
+   * @var string
+   */
+  private $contentType;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Multilingual content',
+      'description' => 'Tests Feeds multilingual support for nodes.',
+      'group' => 'Feeds',
+      'dependencies' => array('locale'),
+    );
+  }
+
+  public function setUp($modules = array(), $permissions = array()) {
+    $this->entityType = 'node';
+    $this->processorName = 'FeedsNodeProcessor';
+
+    parent::setUp($modules, $permissions);
+
+    // Create content type.
+    $this->contentType = $this->createContentType();
+
+    // Configure importer.
+    $this->setSettings('i18n', $this->processorName, array(
+      'bundle' => $this->contentType,
+      'language' => 'de',
+      'update_existing' => FEEDS_UPDATE_EXISTING,
+      'skip_hash_check' => TRUE,
+    ));
+    $this->addMappings('i18n', array(
+      0 => array(
+        'source' => 'guid',
+        'target' => 'guid',
+        'unique' => '1',
+      ),
+      1 => array(
+        'source' => 'title',
+        'target' => 'title',
+      ),
+    ));
+  }
+
+  /**
+   * Tests if the language setting is available on the processor.
+   */
+  public function testAvailableProcessorLanguageSetting() {
+    // Check if the language setting is available when the locale module is enabled.
+    $this->drupalGet('admin/structure/feeds/i18n/settings/FeedsNodeProcessor');
+    $this->assertField('language', 'Language field is available on the node processor settings when the locale module is enabled.');
+
+    // Disable the locale module and check if the language setting is no longer available.
+    module_disable(array('locale'));
+    $this->drupalGet('admin/structure/feeds/i18n/settings/FeedsNodeProcessor');
+    $this->assertNoField('language', 'Language field is not available on the node processor settings when the locale module is disabled.');
+  }
+
+  /**
+   * Tests processor language setting in combination with language mapping target.
+   */
+  public function testWithLanguageMappingTarget() {
+    $this->addMappings('i18n', array(
+      2 => array(
+        'source' => 'language',
+        'target' => 'language',
+      ),
+    ));
+
+    // Import csv file. The first item has a language specified (Dutch), the second
+    // one has no language specified and should be imported in the processor's language (German).
+    $this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content_i18n.csv');
+
+    // The first node should be Dutch.
+    $node = node_load(1);
+    $this->assertEqual('nl', entity_language('node', $node), 'Item 1 has the Dutch language assigned.');
+
+    // The second node should be German.
+    $node = node_load(2);
+    $this->assertEqual('de', entity_language('node', $node), 'Item 2 has the German language assigned.');
+  }
+
+  /**
+   * Tests if nodes get imported in LANGUAGE_NONE when the locale module gets disabled.
+   */
+  public function testDisabledLocaleModule() {
+    module_disable(array('locale'));
+    // Make sure that entity info is reset.
+    drupal_flush_all_caches();
+    drupal_static_reset();
+
+    // Import content.
+    $this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
+
+    // Assert that the content has no language assigned.
+    for ($i = 1; $i <= 2; $i++) {
+      $node = node_load($i);
+      $language = entity_language('node', $node);
+      $this->assertEqual(LANGUAGE_NONE, $language, format_string('The node is language neutral (actual: !language).', array('!language' => $language)));
+    }
+  }
+
+  /**
+   * Tests if nodes get imported in LANGUAGE_NONE when the locale module gets uninstalled.
+   */
+  public function testUninstalledLocaleModule() {
+    module_disable(array('locale'));
+    drupal_uninstall_modules(array('locale'));
+    // Make sure that entity info is reset.
+    drupal_flush_all_caches();
+    drupal_static_reset();
+
+    // Import content.
+    $this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
+
+    // Assert that the content has no language assigned.
+    for ($i = 1; $i <= 2; $i++) {
+      $node = node_load($i);
+      $language = entity_language('node', $node);
+      $this->assertEqual(LANGUAGE_NONE, $language, format_string('The node is language neutral (actual: !language).', array('!language' => $language)));
+    }
+  }
+}
diff --git a/tests/feeds_i18n_taxonomy.test b/tests/feeds_i18n_taxonomy.test
new file mode 100644
index 0000000000000000000000000000000000000000..1b69209368122a8627c3739bf2315bd4c5eddc69
--- /dev/null
+++ b/tests/feeds_i18n_taxonomy.test
@@ -0,0 +1,119 @@
+<?php
+
+/**
+ * @file
+ * Contains Feedsi18nTaxonomyTestCase.
+ */
+
+/**
+ * Tests importing terms in a language.
+ */
+class Feedsi18nTaxonomyTestCase extends Feedsi18nTestCase {
+
+  /**
+   * Name of created vocabulary.
+   *
+   * @var string
+   */
+  private $vocabulary;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Multilingual terms',
+      'description' => 'Tests Feeds multilingual support for taxonomy terms.',
+      'group' => 'Feeds',
+      'dependencies' => array('locale', 'i18n_taxonomy'),
+    );
+  }
+
+  public function setUp($modules = array(), $permissions = array()) {
+    $this->entityType = 'taxonomy_term';
+    $this->processorName = 'FeedsTermProcessor';
+
+    $modules = array_merge($modules, array(
+      'i18n_taxonomy',
+    ));
+    parent::setUp($modules, $permissions);
+
+    // Create vocabulary.
+    $this->vocabulary = strtolower($this->randomName(8));
+    $edit = array(
+      'name' => $this->vocabulary,
+      'machine_name' => $this->vocabulary,
+    );
+    $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
+
+    // Configure importer.
+    $this->setSettings('i18n', $this->processorName, array(
+      'bundle' => $this->vocabulary,
+      'language' => 'de',
+      'update_existing' => FEEDS_UPDATE_EXISTING,
+      'skip_hash_check' => TRUE,
+    ));
+    $this->addMappings('i18n', array(
+      0 => array(
+        'source' => 'guid',
+        'target' => 'guid',
+        'unique' => '1',
+      ),
+      1 => array(
+        'source' => 'title',
+        'target' => 'name',
+      ),
+    ));
+  }
+
+  /**
+   * Tests if the language setting is available on the processor.
+   */
+  public function testAvailableProcessorLanguageSetting() {
+    // Check if the language setting is available when the i18n_taxonomy module is enabled.
+    $this->drupalGet('admin/structure/feeds/i18n/settings/FeedsTermProcessor');
+    $this->assertField('language', 'Language field is available on the term processor settings when the i18n_taxonomy module is enabled.');
+
+    // Disable the i18n_taxonomy module and check if the language setting is no longer available.
+    module_disable(array('i18n_taxonomy'));
+    $this->drupalGet('admin/structure/feeds/i18n/settings/FeedsTermProcessor');
+    $this->assertNoField('language', 'Language field is not available on the term processor settings when the i18n_taxonomy module is disabled.');
+  }
+
+  /**
+   * Tests if terms get imported in LANGUAGE_NONE when the i18n_taxonomy module gets disabled.
+   */
+  public function testDisabledi18nTaxonomyModule() {
+    module_disable(array('i18n_taxonomy'));
+    // Make sure that entity info is reset.
+    drupal_flush_all_caches();
+    drupal_static_reset();
+
+    // Import content.
+    $this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
+
+    // Assert that the terms have no language assigned.
+    $entities = entity_load($this->entityType, array(1, 2));
+    foreach ($entities as $entity) {
+      // Terms shouldn't have a language property.
+      $this->assertFalse(isset($entity->language), 'The term does not have a language.');
+    }
+  }
+
+  /**
+   * Tests if terms get imported in LANGUAGE_NONE when the i18n_taxonomy module gets uninstalled.
+   */
+  public function testUninstalledi18nTaxonomyModule() {
+    module_disable(array('i18n_taxonomy'));
+    drupal_uninstall_modules(array('i18n_taxonomy'));
+    // Make sure that entity info is reset.
+    drupal_flush_all_caches();
+    drupal_static_reset();
+
+    // Import content.
+    $this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
+
+    // Assert that the terms have no language assigned.
+    $entities = entity_load($this->entityType, array(1, 2));
+    foreach ($entities as $entity) {
+      $this->assertFalse(isset($entity->language), 'The term does not have a language.');
+    }
+  }
+}