diff --git a/core/modules/language/migration_templates/language.yml b/core/modules/language/migration_templates/language.yml
new file mode 100644
index 0000000..6ca9c6d
--- /dev/null
+++ b/core/modules/language/migration_templates/language.yml
@@ -0,0 +1,19 @@
+id: language
+label: Languages
+migration_tags:
+  - Drupal 6
+  - Drupal 7
+source:
+  plugin: language
+process:
+  id: language
+  label: name
+  direction:
+    plugin: static_map
+    source: direction
+    map:
+      0: ltr
+      1: rtl
+  weight: weight
+destination:
+  plugin: entity:configurable_language
diff --git a/core/modules/language/src/Plugin/migrate/source/Language.php b/core/modules/language/src/Plugin/migrate/source/Language.php
new file mode 100644
index 0000000..09a8e1b
--- /dev/null
+++ b/core/modules/language/src/Plugin/migrate/source/Language.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Plugin\migrate\source\Language.
+ */
+
+namespace Drupal\language\Plugin\migrate\source;
+
+use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+
+/**
+ * @MigrateSource(
+ *   id = "language",
+ *   source_provider = "locale"
+ * )
+ */
+class Language extends DrupalSqlBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fields() {
+    return array(
+      'language' => $this->t('The language code.'),
+      'name' => $this->t('The English name of the language.'),
+      'native' => $this->t('The native name of the language.'),
+      'direction' => $this->t('The language direction. (0 = LTR, 1 = RTL)'),
+      'enabled' => $this->t('Whether the language is enabled.'),
+      'plurals' => $this->t('Number of plural indexes in this language.'),
+      'formula' => $this->t('PHP formula to get plural indexes.'),
+      'domain' => $this->t('Domain to use for this language.'),
+      'prefix' => $this->t('Path prefix used for this language.'),
+      'weight' => $this->t('The language weight when listed.'),
+      'javascript' => $this->t('Location of the JavaScript translation file.'),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIds() {
+    return array(
+      'language' => array(
+        'type' => 'string',
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    return $this->select('languages')->fields('languages');
+  }
+
+}
diff --git a/core/modules/language/src/Tests/Migrate/MigrateLanguageTest.php b/core/modules/language/src/Tests/Migrate/MigrateLanguageTest.php
new file mode 100644
index 0000000..7b225ae
--- /dev/null
+++ b/core/modules/language/src/Tests/Migrate/MigrateLanguageTest.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Tests\Migrate\MigrateLanguageTest.
+ */
+
+namespace Drupal\language\Tests\Migrate;
+
+use Drupal\language\ConfigurableLanguageInterface;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
+
+/**
+ * @group migrate_drupal_6
+ */
+class MigrateLanguageTest extends MigrateDrupal6TestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['language'];
+
+  /**
+   * Asserts various properties of a configurable language entity.
+   *
+   * @param string $id
+   *   The language ID.
+   * @param string $label
+   *   The language name.
+   * @param string $direction
+   *   (optional) The language's direction (one of the DIRECTION_* constants in
+   *   ConfigurableLanguageInterface). Defaults to LTR.
+   * @param int $weight
+   *   (optional) The weight of the language. Defaults to 0.
+   */
+  protected function assertLanguage($id, $label, $direction = ConfigurableLanguageInterface::DIRECTION_LTR, $weight = 0) {
+    /** @var \Drupal\language\ConfigurableLanguageInterface $language */
+    $language = ConfigurableLanguage::load($id);
+    $this->assertTrue($language instanceof ConfigurableLanguageInterface);
+    $this->assertIdentical($label, $language->label());
+    $this->assertIdentical($direction, $language->getDirection());
+    $this->assertIdentical(0, $language->getWeight());
+    $this->assertFalse($language->isLocked());
+  }
+
+  /**
+   * Tests migration of Drupal 6 languages to configurable language entities.
+   */
+  public function testLanguageMigration() {
+    $this->executeMigration('language');
+    $this->assertLanguage('en', 'English');
+    $this->assertLanguage('fr', 'French');
+  }
+
+}
diff --git a/core/modules/language/tests/src/Unit/Migrate/LanguageTest.php b/core/modules/language/tests/src/Unit/Migrate/LanguageTest.php
new file mode 100644
index 0000000..1c41c32
--- /dev/null
+++ b/core/modules/language/tests/src/Unit/Migrate/LanguageTest.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\language\Unit\Migrate\LanguageTest.
+ */
+
+namespace Drupal\Tests\language\Unit\Migrate;
+
+use Drupal\language\Plugin\migrate\source\Language;
+use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\language\Plugin\migrate\source\Language
+ * @group language
+ */
+class LanguageTest extends MigrateSqlSourceTestCase {
+
+  const PLUGIN_CLASS = Language::class;
+
+  protected $migrationConfiguration = array(
+    'id' => 'test',
+    'source' => array(
+      'plugin' => 'language',
+    ),
+  );
+
+  protected $databaseContents = array(
+    'languages' => array(
+      array(
+        'language' => 'en',
+        'name' => 'English',
+        'native' => 'English',
+        'direction' => '0',
+        'enabled' => '1',
+        'plurals' => '0',
+        'formula' => '',
+        'domain' => '',
+        'prefix' => '',
+        'weight' => '0',
+        'javascript' => '',
+      ),
+      array(
+        'language' => 'fr',
+        'name' => 'French',
+        'native' => 'Français',
+        'direction' => '0',
+        'enabled' => '0',
+        'plurals' => '2',
+        'formula' => '($n>1)',
+        'domain' => '',
+        'prefix' => 'fr',
+        'weight' => '0',
+        'javascript' => '',
+      ),
+    ),
+  );
+
+  protected $expectedResults = array(
+    array(
+      'language' => 'en',
+      'name' => 'English',
+      'native' => 'English',
+      'direction' => '0',
+      'enabled' => '1',
+      'plurals' => '0',
+      'formula' => '',
+      'domain' => '',
+      'prefix' => '',
+      'weight' => '0',
+      'javascript' => '',
+    ),
+    array(
+      'language' => 'fr',
+      'name' => 'French',
+      'native' => 'Français',
+      'direction' => '0',
+      'enabled' => '0',
+      'plurals' => '2',
+      'formula' => '($n>1)',
+      'domain' => '',
+      'prefix' => 'fr',
+      'weight' => '0',
+      'javascript' => '',
+    ),
+  );
+
+}
