diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index aca6911..bed004a 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -11,84 +11,6 @@
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
- * User interface for the language overview screen.
- */
-function language_admin_overview_form($form, &$form_state) {
-  drupal_static_reset('language_list');
-  $languages = language_list();
-  $default = language_default();
-
-  $form['languages'] = array(
-    '#languages' => $languages,
-    '#language_default' => $default,
-    '#type' => 'table',
-    '#header' => array(
-      t('Name'),
-      t('Weight'),
-      t('Operations'),
-    ),
-    '#tabledrag' => array(
-      array('order', 'sibling', 'language-order-weight'),
-    ),
-  );
-
-  foreach ($languages as $langcode => $language) {
-    $form['languages'][$langcode]['#attributes']['class'][] = 'draggable';
-    $form['languages'][$langcode]['name'] = array(
-      '#markup' => check_plain($language->name),
-    );
-    $form['languages'][$langcode]['weight'] = array(
-      '#type' => 'weight',
-      '#title' => t('Weight for @title', array('@title' => $language->name)),
-      '#title_display' => 'invisible',
-      '#default_value' => $language->weight,
-      '#attributes' => array(
-        'class' => array('language-order-weight'),
-      ),
-      '#delta' => 30,
-    );
-    $links = array();
-    $links['edit'] = array(
-      'title' => t('edit'),
-      'href' => 'admin/config/regional/language/edit/' . $langcode,
-    );
-    if ($langcode != $default->id) {
-      $links['delete'] = array(
-        'title' => t('delete'),
-        'href' => 'admin/config/regional/language/delete/' . $langcode,
-      );
-    }
-    $form['languages'][$langcode]['operations'] = array(
-      '#type' => 'operations',
-      '#links' => $links,
-      '#weight' => 100,
-    );
-  }
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save configuration'),
-  );
-
-  return $form;
-}
-
-/**
- * Process language overview form submissions, updating existing languages.
- */
-function language_admin_overview_form_submit($form, &$form_state) {
-  $languages = language_list();
-
-  foreach ($languages as $langcode => $language) {
-    $language->weight = $form_state['values']['languages'][$langcode]['weight'];
-    language_save($language);
-  }
-
-  drupal_set_message(t('Configuration saved.'));
-}
-
-/**
  * User interface for the language addition screen.
  */
 function language_admin_add_form($form, &$form_state) {
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index b26105f..4d7aeb2 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -70,10 +70,7 @@ function language_menu() {
   $items['admin/config/regional/language'] = array(
     'title' => 'Languages',
     'description' => 'Configure languages for content and the user interface.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('language_admin_overview_form'),
-    'access arguments' => array('administer languages'),
-    'file' => 'language.admin.inc',
+    'route_name' => 'language_admin_overview',
     'weight' => -10,
   );
   $items['admin/config/regional/language/list'] = array(
@@ -160,6 +157,8 @@ function language_menu() {
 
 /**
  * Editing or deleting locked languages should not be possible.
+ *
+ * @deprecated Use \Drupal\language\LanguageAccessController instead.
  */
 function language_access_language_edit_or_delete($language) {
   return !$language->locked && user_access('administer languages');
diff --git a/core/modules/language/language.routing.yml b/core/modules/language/language.routing.yml
index 4a2fc8c..e75e1ce 100644
--- a/core/modules/language/language.routing.yml
+++ b/core/modules/language/language.routing.yml
@@ -18,3 +18,10 @@ language_negotiation_selected:
     _form: 'Drupal\language\Form\NegotiationSelectedForm'
   requirements:
     _permission: 'administer languages'
+
+language_admin_overview:
+  pattern: '/admin/config/regional/language'
+  defaults:
+    _entity_list: 'language_entity'
+  requirements:
+    _permission: 'administer languages'
diff --git a/core/modules/language/lib/Drupal/language/LanguageAccessController.php b/core/modules/language/lib/Drupal/language/LanguageAccessController.php
new file mode 100644
index 0000000..82f994e
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageAccessController.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageAccessController.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Entity\EntityAccessController;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Session\AccountInterface;
+
+class LanguageAccessController extends EntityAccessController {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(EntityInterface $entity, $operation, $langcode = Language::LANGCODE_DEFAULT, AccountInterface $account = NULL) {
+    switch ($operation) {
+      case 'edit':
+      case 'delete':
+        return !$entity->locked && user_access('administer languages');
+        break;
+    }
+    return FALSE;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/LanguageListController.php b/core/modules/language/lib/Drupal/language/LanguageListController.php
new file mode 100644
index 0000000..9dbe40a
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageListController.php
@@ -0,0 +1,163 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\language\Form\LanguageListController.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Config\Entity\ConfigEntityListController;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\FormInterface;
+
+/**
+ * User interface for the language overview screen.
+ */
+class LanguageListController extends ConfigEntityListController implements FormInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function load() {
+    $entities = $this->storage->loadByProperties(array('locked' => '0'));
+    uasort($entities, array($this->entityInfo['class'], 'sort'));
+    return $entities;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'language_admin_overview_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations(EntityInterface $entity) {
+    $operations = parent::getOperations($entity);
+    $default = language_default();
+
+    // Edit and delete path for Languages entities have a different scheme
+    // then other config entities.
+    $path = 'admin/config/regional/language';
+    if (isset($operations['edit'])) {
+      $operations['edit']['href'] = $path . '/edit/' . $entity->id();
+    }
+    if (isset($operations['delete'])) {
+      $operations['delete']['href'] = $path . '/delete/' . $entity->id();
+    }
+
+    if ($entity->id() == $default->id) {
+      unset($operations['delete']);
+    }
+
+    return $operations;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $row = parent::buildHeader();
+    unset($row['id']);
+    $row['label'] = t('Name');
+    $row['weight'] = t('Weight');
+    return $row;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row = parent::buildRow($entity);
+
+    $row['#attributes']['class'][] = 'draggable';
+    unset($row['id']);
+
+    $row['label'] = array(
+      '#markup' => check_plain($entity->get('label')),
+    );
+
+    $row['#weight'] = $entity->get('weight');
+    // Add weight column.
+    $row['weight'] = array(
+      '#type' => 'weight',
+      '#title' => t('Weight for @title', array('@title' => $entity->label())),
+      '#title_display' => 'invisible',
+      '#default_value' => $entity->get('weight'),
+      '#attributes' => array('class' => array('weight')),
+      '#delta' => 30,
+    );
+
+    return $row;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state) {
+    $languages = $this->load();
+
+    $form['languages'] = array(
+      '#languages' => $languages,
+      '#type' => 'table',
+      '#header' => $this->buildHeader(),
+      '#empty' => t('There are no languages', array('@label' => $this->entityInfo['label'])),
+      '#tabledrag' => array(
+        array('order', 'sibling', 'weight'),
+      ),
+    );
+
+    foreach ($languages as $entity) {
+      $form['languages'][$entity->id()] = $this->buildRow($entity);
+    }
+
+    $form['actions']['#type'] = 'actions';
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Save configuration'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, array &$form_state) {
+    // No validation.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    $languages = $form_state['values']['languages'];
+
+    $language_entities = $this->load();
+    foreach ($languages as $langcode => $language) {
+      if (isset($language_entities[$langcode]) && $language['weight'] != $language_entities[$langcode]->get('weight')) {
+        // Update changed weight.
+        $language_entities[$langcode]->set('weight', $language['weight']);
+        $language_entities[$langcode]->save();
+      }
+    }
+
+    // Kill the static cache in language_list().
+    drupal_static_reset('language_list');
+
+    // Update weight of locked system languages.
+    language_update_locked_weights();
+
+    drupal_set_message(t('Configuration saved.'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render() {
+    return drupal_get_form($this);
+  }
+}
diff --git a/core/modules/language/lib/Drupal/language/Plugin/Core/Entity/Language.php b/core/modules/language/lib/Drupal/language/Plugin/Core/Entity/Language.php
index 4172e96..f80c37f 100644
--- a/core/modules/language/lib/Drupal/language/Plugin/Core/Entity/Language.php
+++ b/core/modules/language/lib/Drupal/language/Plugin/Core/Entity/Language.php
@@ -20,7 +20,9 @@
  *   label = @Translation("Language"),
  *   module = "language",
  *   controllers = {
- *     "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
+ *     "storage" = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *     "list" = "Drupal\language\LanguageListController",
+ *     "access" = "Drupal\language\LanguageAccessController"
  *   },
  *   config_prefix = "language.entity",
  *   entity_keys = {
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
index fa718da..1483aa4 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
@@ -79,8 +79,8 @@ function testLanguageList() {
     $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
     $this->assertText(t('The default language cannot be deleted.'), 'Failed to delete the default language.');
 
-    // Ensure 'edit' link works.
-    $this->clickLink(t('edit'));
+    // Ensure 'Edit' link works.
+    $this->clickLink(t('Edit'));
     $this->assertTitle(t('Edit language | Drupal'), 'Page title is "Edit language".');
     // Edit a language.
     $name = $this->randomName(16);
@@ -98,8 +98,8 @@ function testLanguageList() {
     $this->drupalPost($path, $edit, t('Save configuration'));
     // Ensure 'delete' link works.
     $this->drupalGet('admin/config/regional/language');
-    $this->clickLink(t('delete'));
-    $this->assertText(t('Are you sure you want to delete the language'), '"delete" link is correct.');
+    $this->clickLink(t('Delete'));
+    $this->assertText(t('Are you sure you want to delete the language'), '"Delete" link is correct.');
     // Delete a language.
     $this->drupalGet('admin/config/regional/language/delete/' . $langcode);
     // First test the 'cancel' link.
