diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index f59fb38e7d485445a18e8d4c9260d9e948f1738c..a3500fd2918f9a9f1cfd876e6e2335de594b690f 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -561,6 +561,205 @@ function language_negotiation_configure_form_submit($form, &$form_state) {
   drupal_set_message(t('Language negotiation configuration saved.'));
 }
 
+/**
+ * Builds the URL language negotiation method configuration form.
+ *
+ * @see language_negotiation_configure_url_form_validate()
+ * @see language_negotiation_configure_url_form_submit()
+ */
+function language_negotiation_configure_url_form($form, &$form_state) {
+  global $base_url;
+  language_negotiation_include();
+
+  $form['language_negotiation_url_part'] = array(
+    '#title' => t('Part of the URL that determines language'),
+    '#type' => 'radios',
+    '#options' => array(
+      LANGUAGE_NEGOTIATION_URL_PREFIX => t('Path prefix'),
+      LANGUAGE_NEGOTIATION_URL_DOMAIN => t('Domain'),
+    ),
+    '#default_value' => config('language.negotiation')->get('url.source'),
+  );
+
+  $form['prefix'] = array(
+    '#type' => 'details',
+    '#tree' => TRUE,
+    '#title' => t('Path prefix configuration'),
+    '#description' => t('Language codes or other custom text to use as a path prefix for URL language detection. For the default language, this value may be left blank. <strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "deutsch" as the path prefix code for German results in URLs like "example.com/deutsch/contact".'),
+    '#states' => array(
+      'visible' => array(
+        ':input[name="language_negotiation_url_part"]' => array(
+          'value' => (string) LANGUAGE_NEGOTIATION_URL_PREFIX,
+        ),
+      ),
+    ),
+  );
+  $form['domain'] = array(
+    '#type' => 'details',
+    '#tree' => TRUE,
+    '#title' => t('Domain configuration'),
+    '#description' => t('The domain names to use for these languages. Leave blank for the default language. Use with caution in a production environment.<strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "de.example.com" as language domain for German will result in an URL like "http://de.example.com/contact".'),
+    '#states' => array(
+      'visible' => array(
+        ':input[name="language_negotiation_url_part"]' => array(
+          'value' => (string) LANGUAGE_NEGOTIATION_URL_DOMAIN,
+        ),
+      ),
+    ),
+  );
+
+  $languages = language_list();
+  $prefixes = language_negotiation_url_prefixes();
+  $domains = language_negotiation_url_domains();
+  foreach ($languages as $langcode => $language) {
+    $t_args = array('%language' => $language->name, '%langcode' => $language->langcode);
+    $form['prefix'][$langcode] = array(
+      '#type' => 'textfield',
+      '#title' => $language->default ? t('%language (%langcode) path prefix (Default language)', $t_args) : t('%language (%langcode) path prefix', $t_args),
+      '#maxlength' => 64,
+      '#default_value' => isset($prefixes[$langcode]) ? $prefixes[$langcode] : '',
+      '#field_prefix' => $base_url . '/',
+    );
+    $form['domain'][$langcode] = array(
+      '#type' => 'textfield',
+      '#title' => t('%language (%langcode) domain', array('%language' => $language->name, '%langcode' => $language->langcode)),
+      '#maxlength' => 128,
+      '#default_value' => isset($domains[$langcode]) ? $domains[$langcode] : '',
+    );
+  }
+
+  $form_state['redirect'] = 'admin/config/regional/language/detection';
+
+  return system_config_form($form, $form_state);
+}
+
+/**
+ * Validates the URL language negotiation method configuration.
+ *
+ * Validate that the prefixes and domains are unique, and make sure that
+ * the prefix and domain are only blank for the default.
+ */
+function language_negotiation_configure_url_form_validate($form, &$form_state) {
+  $languages = language_list();
+
+  // Count repeated values for uniqueness check.
+  $count = array_count_values($form_state['values']['prefix']);
+  foreach ($languages as $langcode => $language) {
+    $value = $form_state['values']['prefix'][$langcode];
+
+    if ($value === '') {
+      if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_PREFIX) {
+        // Throw a form error if the prefix is blank for a non-default language,
+        // although it is required for selected negotiation type.
+        form_error($form['prefix'][$langcode], t('The prefix may only be left blank for the default language.'));
+      }
+    }
+    elseif (strpos($value, '/') !== FALSE) {
+      // Throw a form error if the string contains a slash,
+      // which would not work.
+      form_error($form['prefix'][$langcode], t('The prefix may not contain a slash.'));
+    }
+    elseif (isset($count[$value]) && $count[$value] > 1) {
+      // Throw a form error if there are two languages with the same
+      // domain/prefix.
+      form_error($form['prefix'][$langcode], t('The prefix for %language, %value, is not unique.', array('%language' => $language->name, '%value' => $value)));
+    }
+  }
+
+  // Count repeated values for uniqueness check.
+  $count = array_count_values($form_state['values']['domain']);
+  foreach ($languages as $langcode => $language) {
+    $value = $form_state['values']['domain'][$langcode];
+
+    if ($value === '') {
+      if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_DOMAIN) {
+        // Throw a form error if the domain is blank for a non-default language,
+        // although it is required for selected negotiation type.
+        form_error($form['domain'][$langcode], t('The domain may only be left blank for the default language.'));
+      }
+    }
+    elseif (isset($count[$value]) && $count[$value] > 1) {
+      // Throw a form error if there are two languages with the same
+      // domain/domain.
+      form_error($form['domain'][$langcode], t('The domain for %language, %value, is not unique.', array('%language' => $language->name, '%value' => $value)));
+    }
+  }
+
+  // Domain names should not contain protocol and/or ports.
+  foreach ($languages as $langcode => $name) {
+    $value = $form_state['values']['domain'][$langcode];
+    if (!empty($value)) {
+      // Ensure we have exactly one protocol when checking the hostname.
+      $host = 'http://' . str_replace(array('http://', 'https://'), '', $value);
+      if (parse_url($host, PHP_URL_HOST) != $value) {
+        form_error($form['domain'][$langcode], t('The domain for %language may only contain the domain name, not a protocol and/or port.', array('%language' => $name)));
+      }
+    }
+  }
+}
+
+/**
+ * Form submission handler for language_negotiation_configure_url_form().
+ */
+function language_negotiation_configure_url_form_submit($form, &$form_state) {
+  // Save selected format (prefix or domain).
+  config('language.negotiation')
+    ->set('url.source', $form_state['values']['language_negotiation_url_part'])
+    ->save();
+
+  // Save new domain and prefix values.
+  language_negotiation_url_prefixes_save($form_state['values']['prefix']);
+  language_negotiation_url_domains_save($form_state['values']['domain']);
+}
+
+/**
+ * Builds the session language negotiation method configuration form.
+ *
+ * @see language_negotiation_configure_session_form_submit()
+ */
+function language_negotiation_configure_session_form($form, &$form_state) {
+  $form['language_negotiation_session_param'] = array(
+    '#title' => t('Request/session parameter'),
+    '#type' => 'textfield',
+    '#default_value' => config('language.negotiation')->get('session.parameter'),
+    '#description' => t('Name of the request/session parameter used to determine the desired language.'),
+  );
+
+  $form_state['redirect'] = 'admin/config/regional/language/detection';
+
+  return system_config_form($form, $form_state);
+}
+
+/**
+ * Form submission handler for language_negotiation_configure_session_form().
+ */
+function language_negotiation_configure_session_form_submit($form, &$form_state) {
+  config('language.negotiation')
+    ->set('session.parameter', $form_state['values']['language_negotiation_session_param'])
+    ->save();
+}
+
+/**
+ * Builds the selected language negotiation method configuration form.
+ */
+function language_negotiation_configure_selected_form($form, &$form_state) {
+  $form['selected_langcode'] = array(
+    '#type' => 'language_select',
+    '#title' => t('Language'),
+    '#languages' => LANGUAGE_CONFIGURABLE | LANGUAGE_SITE_DEFAULT,
+    '#default_value' => config('language.negotiation')->get('selected_langcode'),
+  );
+
+  return system_config_form($form, $form_state);
+}
+
+/**
+ * Form submission handler for language_negotiation_configure_selected_form().
+ */
+function language_negotiation_configure_selected_form_submit($form, &$form_state) {
+  config('language.negotiation')->set('selected_langcode', $form_state['values']['selected_langcode'])->save();
+}
+
 /**
  * Builds the browser language negotiation method configuration form.
  */
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index e42316e6adbcd89fd73447f14242b156a202d451..1c20695263d783fb4dbe45143b2dca1be356611b 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -114,14 +114,18 @@ function language_menu() {
   );
   $items['admin/config/regional/language/detection/url'] = array(
     'title' => 'URL language detection configuration',
-    'page callback' => 'NOT_USED',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('language_negotiation_configure_url_form'),
     'access arguments' => array('administer languages'),
+    'file' => 'language.admin.inc',
     'type' => MENU_VISIBLE_IN_BREADCRUMB,
   );
   $items['admin/config/regional/language/detection/session'] = array(
     'title' => 'Session language detection configuration',
-    'page callback' => 'NOT_USED',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('language_negotiation_configure_session_form'),
     'access arguments' => array('administer languages'),
+    'file' => 'language.admin.inc',
     'type' => MENU_VISIBLE_IN_BREADCRUMB,
   );
   $items['admin/config/regional/language/detection/browser'] = array(
@@ -141,8 +145,10 @@ function language_menu() {
   );
   $items['admin/config/regional/language/detection/selected'] = array(
     'title' => 'Selected language detection configuration',
-    'page callback' => 'NOT_USED',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('language_negotiation_configure_selected_form'),
     'access arguments' => array('administer languages'),
+    'file' => 'language.admin.inc',
     'type' => MENU_VISIBLE_IN_BREADCRUMB,
   );
 
diff --git a/core/modules/language/language.routing.yml b/core/modules/language/language.routing.yml
deleted file mode 100644
index 4a2fc8c61898ef75e195cb4043237ff6b3e42ce6..0000000000000000000000000000000000000000
--- a/core/modules/language/language.routing.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-language_negotiation_url:
-  pattern: '/admin/config/regional/language/detection/url'
-  defaults:
-    _form: 'Drupal\language\Form\NegotiationUrlForm'
-  requirements:
-    _permission: 'administer languages'
-
-language_negotiation_session:
-  pattern: '/admin/config/regional/language/detection/session'
-  defaults:
-    _form: 'Drupal\language\Form\NegotiationSessionForm'
-  requirements:
-    _permission: 'administer languages'
-
-language_negotiation_selected:
-  pattern: '/admin/config/regional/language/detection/selected'
-  defaults:
-    _form: 'Drupal\language\Form\NegotiationSelectedForm'
-  requirements:
-    _permission: 'administer languages'
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationSelectedForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationSelectedForm.php
deleted file mode 100644
index 9ef236d3b359be6504a1603491d9b4f1237185dd..0000000000000000000000000000000000000000
--- a/core/modules/language/lib/Drupal/language/Form/NegotiationSelectedForm.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\language\Form\NegotiationSelectedForm.
- */
-
-namespace Drupal\language\Form;
-
-use Drupal\system\SystemConfigFormBase;
-
-/**
- * Configure the selected language negotiation method for this site.
- */
-class NegotiationSelectedForm extends SystemConfigFormBase {
-
-  /**
-   * Implements \Drupal\Core\Form\FormInterface::getFormID().
-   */
-  public function getFormID() {
-    return 'language_negotiation_configure_selected_form';
-  }
-
-  /**
-   * Implements \Drupal\Core\Form\FormInterface::buildForm().
-   */
-  public function buildForm(array $form, array &$form_state) {
-    $config = $this->configFactory->get('language.negotiation');
-    $form['selected_langcode'] = array(
-      '#type' => 'language_select',
-      '#title' => t('Language'),
-      '#languages' => LANGUAGE_CONFIGURABLE | LANGUAGE_SITE_DEFAULT,
-      '#default_value' => $config->get('selected_langcode'),
-    );
-
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * Implements \Drupal\Core\Form\FormInterface::submitForm().
-   */
-  public function submitForm(array &$form, array &$form_state) {
-    $this->configFactory->get('language.negotiation')
-      ->set('selected_langcode', $form_state['values']['selected_langcode'])
-      ->save();
-
-    parent::submitForm($form, $form_state);
-  }
-
-}
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationSessionForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationSessionForm.php
deleted file mode 100644
index 7372d810b8cce8648d5fdf4657d88c526f2671e1..0000000000000000000000000000000000000000
--- a/core/modules/language/lib/Drupal/language/Form/NegotiationSessionForm.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\language\Form\NegotiationSessionForm.
- */
-
-namespace Drupal\language\Form;
-
-use Drupal\system\SystemConfigFormBase;
-
-/**
- * Configure the session language negotiation method for this site.
- */
-class NegotiationSessionForm extends SystemConfigFormBase {
-
-  /**
-   * Implements \Drupal\Core\Form\FormInterface::getFormID().
-   */
-  public function getFormID() {
-    return 'language_negotiation_configure_session_form';
-  }
-
-  /**
-   * Implements \Drupal\Core\Form\FormInterface::buildForm().
-   */
-  public function buildForm(array $form, array &$form_state) {
-    $config = $this->configFactory->get('language.negotiation');
-    $form['language_negotiation_session_param'] = array(
-      '#title' => t('Request/session parameter'),
-      '#type' => 'textfield',
-      '#default_value' => $config->get('session.parameter'),
-      '#description' => t('Name of the request/session parameter used to determine the desired language.'),
-    );
-
-    $form_state['redirect'] = 'admin/config/regional/language/detection';
-
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * Implements \Drupal\Core\Form\FormInterface::submitForm().
-   */
-  public function submitForm(array &$form, array &$form_state) {
-    $this->configFactory->get('language.settings')
-      ->set('session.parameter', $form_state['values']['language_negotiation_session_param'])
-      ->save();
-
-    parent::submitForm($form, $form_state);
-  }
-
-}
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php
deleted file mode 100644
index 9a9532987b4723ab85b4c2e6217bc276dcb2957c..0000000000000000000000000000000000000000
--- a/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php
+++ /dev/null
@@ -1,174 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\language\Form\NegotiationUrlForm.
- */
-
-namespace Drupal\language\Form;
-
-use Drupal\system\SystemConfigFormBase;
-
-/**
- * Configure the URL language negotiation method for this site.
- */
-class NegotiationUrlForm extends SystemConfigFormBase {
-
-  /**
-   * Implements \Drupal\Core\Form\FormInterface::getFormID().
-   */
-  public function getFormID() {
-    return 'language_negotiation_configure_url_form';
-  }
-
-  /**
-   * Implements \Drupal\Core\Form\FormInterface::buildForm().
-   */
-  public function buildForm(array $form, array &$form_state) {
-    global $base_url;
-    $config = $this->configFactory->get('language.negotiation');
-    language_negotiation_include();
-
-    $form['language_negotiation_url_part'] = array(
-      '#title' => t('Part of the URL that determines language'),
-      '#type' => 'radios',
-      '#options' => array(
-        LANGUAGE_NEGOTIATION_URL_PREFIX => t('Path prefix'),
-        LANGUAGE_NEGOTIATION_URL_DOMAIN => t('Domain'),
-      ),
-      '#default_value' => $config->get('url.source'),
-    );
-
-    $form['prefix'] = array(
-      '#type' => 'details',
-      '#tree' => TRUE,
-      '#title' => t('Path prefix configuration'),
-      '#description' => t('Language codes or other custom text to use as a path prefix for URL language detection. For the default language, this value may be left blank. <strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "deutsch" as the path prefix code for German results in URLs like "example.com/deutsch/contact".'),
-      '#states' => array(
-        'visible' => array(
-          ':input[name="language_negotiation_url_part"]' => array(
-            'value' => (string) LANGUAGE_NEGOTIATION_URL_PREFIX,
-          ),
-        ),
-      ),
-    );
-    $form['domain'] = array(
-      '#type' => 'details',
-      '#tree' => TRUE,
-      '#title' => t('Domain configuration'),
-      '#description' => t('The domain names to use for these languages. Leave blank for the default language. Use with caution in a production environment.<strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "de.example.com" as language domain for German will result in an URL like "http://de.example.com/contact".'),
-      '#states' => array(
-        'visible' => array(
-          ':input[name="language_negotiation_url_part"]' => array(
-            'value' => (string) LANGUAGE_NEGOTIATION_URL_DOMAIN,
-          ),
-        ),
-      ),
-    );
-
-    $languages = language_list();
-    $prefixes = language_negotiation_url_prefixes();
-    $domains = language_negotiation_url_domains();
-    foreach ($languages as $langcode => $language) {
-      $t_args = array('%language' => $language->name, '%langcode' => $language->langcode);
-      $form['prefix'][$langcode] = array(
-        '#type' => 'textfield',
-        '#title' => $language->default ? t('%language (%langcode) path prefix (Default language)', $t_args) : t('%language (%langcode) path prefix', $t_args),
-        '#maxlength' => 64,
-        '#default_value' => isset($prefixes[$langcode]) ? $prefixes[$langcode] : '',
-        '#field_prefix' => $base_url . '/',
-      );
-      $form['domain'][$langcode] = array(
-        '#type' => 'textfield',
-        '#title' => t('%language (%langcode) domain', array('%language' => $language->name, '%langcode' => $language->langcode)),
-        '#maxlength' => 128,
-        '#default_value' => isset($domains[$langcode]) ? $domains[$langcode] : '',
-      );
-    }
-
-    $form_state['redirect'] = 'admin/config/regional/language/detection';
-
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * Implements \Drupal\Core\Form\FormInterface::validateForm().
-   */
-  public function validateForm(array &$form, array &$form_state) {
-    $languages = language_list();
-
-    // Count repeated values for uniqueness check.
-    $count = array_count_values($form_state['values']['prefix']);
-    foreach ($languages as $langcode => $language) {
-      $value = $form_state['values']['prefix'][$langcode];
-
-      if ($value === '') {
-        if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_PREFIX) {
-          // Throw a form error if the prefix is blank for a non-default language,
-          // although it is required for selected negotiation type.
-          form_error($form['prefix'][$langcode], t('The prefix may only be left blank for the default language.'));
-        }
-      }
-      elseif (strpos($value, '/') !== FALSE) {
-        // Throw a form error if the string contains a slash,
-        // which would not work.
-        form_error($form['prefix'][$langcode], t('The prefix may not contain a slash.'));
-      }
-      elseif (isset($count[$value]) && $count[$value] > 1) {
-        // Throw a form error if there are two languages with the same
-        // domain/prefix.
-        form_error($form['prefix'][$langcode], t('The prefix for %language, %value, is not unique.', array('%language' => $language->name, '%value' => $value)));
-      }
-    }
-
-    // Count repeated values for uniqueness check.
-    $count = array_count_values($form_state['values']['domain']);
-    foreach ($languages as $langcode => $language) {
-      $value = $form_state['values']['domain'][$langcode];
-
-      if ($value === '') {
-        if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_DOMAIN) {
-          // Throw a form error if the domain is blank for a non-default language,
-          // although it is required for selected negotiation type.
-          form_error($form['domain'][$langcode], t('The domain may only be left blank for the default language.'));
-        }
-      }
-      elseif (isset($count[$value]) && $count[$value] > 1) {
-        // Throw a form error if there are two languages with the same
-        // domain/domain.
-        form_error($form['domain'][$langcode], t('The domain for %language, %value, is not unique.', array('%language' => $language->name, '%value' => $value)));
-      }
-    }
-
-    // Domain names should not contain protocol and/or ports.
-    foreach ($languages as $langcode => $name) {
-      $value = $form_state['values']['domain'][$langcode];
-      if (!empty($value)) {
-        // Ensure we have exactly one protocol when checking the hostname.
-        $host = 'http://' . str_replace(array('http://', 'https://'), '', $value);
-        if (parse_url($host, PHP_URL_HOST) != $value) {
-          form_error($form['domain'][$langcode], t('The domain for %language may only contain the domain name, not a protocol and/or port.', array('%language' => $name)));
-        }
-      }
-    }
-
-    parent::validateForm($form, $form_state);
-  }
-
-  /**
-   * Implements \Drupal\Core\Form\FormInterface::submitForm().
-   */
-  public function submitForm(array &$form, array &$form_state) {
-    // Save selected format (prefix or domain).
-    $this->configFactory->get('language.negotiation')
-      ->set('url.source', $form_state['values']['language_negotiation_url_part'])
-      ->save();
-
-    // Save new domain and prefix values.
-    language_negotiation_url_prefixes_save($form_state['values']['prefix']);
-    language_negotiation_url_domains_save($form_state['values']['domain']);
-
-    parent::submitForm($form, $form_state);
-  }
-
-}