diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index cfc0497bbc..30bbeac9f5 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1741,7 +1741,7 @@ function install_finish_translations(&$install_state) {
   $languages = \Drupal::languageManager()->getLanguages();
   $batches = [];
   if (count($projects) > 1) {
-    $options = _locale_translation_default_update_options();
+    $options = \Drupal::service('locale.translation_updater')->getDefaultOptions();
     if ($batch = locale_translation_batch_update_build([], array_keys($languages), $options)) {
       $batches[] = $batch;
     }
diff --git a/core/modules/locale/locale.compare.inc b/core/modules/locale/locale.compare.inc
index c66af14108..9feb33331b 100644
--- a/core/modules/locale/locale.compare.inc
+++ b/core/modules/locale/locale.compare.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Utility\ProjectInfo;
+use Drupal\locale\TranslationUpdaterInterface;
 
 /**
  * Load common APIs.
@@ -161,7 +162,7 @@ function locale_translation_default_translation_server() {
   // An additional check is required here. During the upgrade process
   // \Drupal::config()->get() returns NULL. We use the defined value as
   // fallback.
-  $pattern  = $pattern ? $pattern : LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN;
+  $pattern  = $pattern ? $pattern : TranslationUpdaterInterface::DEFAULT_SERVER_PATTERN;
 
   return [
     'pattern' => $pattern,
@@ -234,7 +235,7 @@ function locale_translation_check_projects_batch($projects = [], $langcodes = []
 function locale_translation_batch_status_build($projects = [], $langcodes = []) {
   $projects = $projects ? $projects : array_keys(locale_translation_get_projects());
   $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
-  $options = _locale_translation_default_update_options();
+  $options = \Drupal::service('locale.translation_updater')->getDefaultOptions();
 
   $operations = _locale_translation_batch_status_operations($projects, $langcodes, $options);
 
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index b1298e4b67..c0346d90a1 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -24,6 +24,7 @@
 use Drupal\locale\Locale;
 use Drupal\locale\LocaleEvent;
 use Drupal\locale\LocaleEvents;
+use Drupal\locale\TranslationUpdaterInterface;
 
 /**
  * Regular expression pattern used to localize JavaScript strings.
@@ -83,6 +84,9 @@
  * used. Any remote translation file will be ignored. Also custom modules and
  * themes which have set a "server pattern" to use a remote translation server
  * will be ignored.
+ *
+ * @deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\locale\TranslationUpdaterInterface::USE_LOCAL_SOURCE instead.
  */
 const LOCALE_TRANSLATION_USE_SOURCE_LOCAL = 'local';
 
@@ -91,6 +95,10 @@
  *
  * When checking for available translation updates, both local and remote files
  * will be checked.
+ *
+ * @deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\locale\TranslationUpdaterInterface::USE_REMOTE_AND_LOCAL_SOURCE
+ *   instead.
  */
 const LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL = 'remote_and_local';
 
@@ -98,28 +106,45 @@
  * Default location of gettext file on the translation server.
  *
  * @see locale_translation_default_translation_server().
+ *
+ * @deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\locale\TranslationUpdaterInterface::DEFAULT_SERVER_PATTERN instead.
  */
 const LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN = 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po';
 
 /**
  * The number of seconds that the translations status entry should be considered.
+ *
+ * @deprecated in Drupal 8.2.0, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\locale\TranslationUpdaterInterface::STATUS_TTL instead.
  */
 const LOCALE_TRANSLATION_STATUS_TTL = 600;
 
 /**
  * UI option for override of existing translations. Override any translation.
+ *
+ * @deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\locale\TranslationUpdaterInterface::OVERWRITE_ALL instead.
  */
 const LOCALE_TRANSLATION_OVERWRITE_ALL = 'all';
 
 /**
  * UI option for override of existing translations. Only override non-customized
  * translations.
+ *
+ * @deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\locale\TranslationUpdaterInterface::OVERWRITE_NON_CUSTOMIZED
+ *   instead.
  */
 const LOCALE_TRANSLATION_OVERWRITE_NON_CUSTOMIZED = 'non_customized';
 
 /**
  * UI option for override of existing translations. Don't override existing
  * translations.
+ *
+ * @deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\locale\TranslationUpdaterInterface::OVERWRITE_NONE instead.
+ *   instead.
  */
 const LOCALE_TRANSLATION_OVERWRITE_NONE = 'none';
 
@@ -417,7 +442,7 @@ function locale_system_update(array $components) {
         module_load_include('fetch.inc', 'locale');
         // Get translation status of the projects, download and update
         // translations.
-        $options = _locale_translation_default_update_options();
+        $options = \Drupal::service('locale.translation_updater')->getDefaultOptions();
         $batch = locale_translation_batch_update_build($list, [], $options);
         batch_set($batch);
       }
@@ -684,7 +709,7 @@ function locale_form_language_admin_add_form_alter(&$form, FormStateInterface $f
  */
 function locale_form_language_admin_add_form_alter_submit($form, FormStateInterface $form_state) {
   \Drupal::moduleHandler()->loadInclude('locale', 'fetch.inc');
-  $options = _locale_translation_default_update_options();
+  $options = \Drupal::service('locale.translation_updater')->getDefaultOptions();
 
   if ($form_state->isValueEmpty('predefined_langcode') || $form_state->getValue('predefined_langcode') == 'custom') {
     $langcode = $form_state->getValue('langcode');
@@ -1005,7 +1030,7 @@ function locale_translation_clear_status() {
  *   when checking or importing translation files, FALSE otherwise.
  */
 function locale_translation_use_remote_source() {
-  return \Drupal::config('locale.settings')->get('translation.use_source') == LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL;
+  return \Drupal::config('locale.settings')->get('translation.use_source') == TranslationUpdaterInterface::USE_REMOTE_AND_LOCAL_SOURCE;
 }
 
 /**
diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml
index b9f8fb807e..9257a5dbfb 100644
--- a/core/modules/locale/locale.services.yml
+++ b/core/modules/locale/locale.services.yml
@@ -14,6 +14,9 @@ services:
   locale.project:
     class: Drupal\locale\LocaleProjectStorage
     arguments: ['@keyvalue']
+  locale.translation_updater:
+    class: Drupal\locale\TranslationUpdater
+    arguments: ['@module_handler', '@state', '@locale.project', '@config.factory', '@datetime.time']
   locale.plural.formula:
     class: Drupal\locale\PluralFormula
     arguments: ['@language_manager', '@state']
diff --git a/core/modules/locale/locale.translation.inc b/core/modules/locale/locale.translation.inc
index 571f7d2247..792cbcaa29 100644
--- a/core/modules/locale/locale.translation.inc
+++ b/core/modules/locale/locale.translation.inc
@@ -353,7 +353,7 @@ function locale_cron_fill_queue() {
   // the queue.
   if ($updates) {
     module_load_include('fetch.inc', 'locale');
-    $options = _locale_translation_default_update_options();
+    $options = \Drupal::service('locale.translation_updater')->getDefaultOptions();
     $queue = \Drupal::queue('locale_translation', TRUE);
 
     foreach ($updates as $project => $languages) {
@@ -426,16 +426,11 @@ function _locale_translation_source_compare($source1, $source2) {
  *
  * @return array
  *   Array of translation import options.
+ *
+ * @deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use
+ *   \Drupal::service('locale.translation_updater')->getDefaultOptions()
+ *   instead.
  */
 function _locale_translation_default_update_options() {
-  $config = \Drupal::config('locale.settings');
-  return [
-    'customized' => LOCALE_NOT_CUSTOMIZED,
-    'overwrite_options' => [
-      'not_customized' => $config->get('translation.overwrite_not_customized'),
-      'customized' => $config->get('translation.overwrite_customized'),
-    ],
-    'finish_feedback' => TRUE,
-    'use_remote' => locale_translation_use_remote_source(),
-  ];
+  return \Drupal::service('locale.translation_updater')->getDefaultOptions();
 }
diff --git a/core/modules/locale/src/Controller/LocaleController.php b/core/modules/locale/src/Controller/LocaleController.php
index 2617c61cff..0ba3b8ffdf 100644
--- a/core/modules/locale/src/Controller/LocaleController.php
+++ b/core/modules/locale/src/Controller/LocaleController.php
@@ -3,6 +3,8 @@
 namespace Drupal\locale\Controller;
 
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\locale\TranslationUpdaterInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Return response for manual check translations.
@@ -10,6 +12,30 @@
 class LocaleController extends ControllerBase {
 
   /**
+   * The translation updater.
+   *
+   * @var \Drupal\locale\TranslationUpdaterInterface
+   */
+  protected $translationUpdater;
+
+  /**
+   * Constructs a LocaleController object.
+   *
+   * @param \Drupal\locale\TranslationUpdaterInterface $translation_updater
+   *   The translation updater.
+   */
+  public function __construct(TranslationUpdaterInterface $translation_updater) {
+    $this->translationUpdater = $translation_updater;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('locale.translation_updater'));
+  }
+
+  /**
    * Checks for translation updates and displays the translations status.
    *
    * Manually checks the translation status without the use of cron.
@@ -18,13 +44,7 @@ class LocaleController extends ControllerBase {
    *   A redirection to translations reports page.
    */
   public function checkTranslation() {
-    $this->moduleHandler()->loadInclude('locale', 'inc', 'locale.compare');
-
-    // Check translation status of all translatable project in all languages.
-    // First we clear the cached list of projects. Although not strictly
-    // necessary, this is helpful in case the project list is out of sync.
-    locale_translation_flush_projects();
-    locale_translation_check_projects();
+    $this->translationUpdater->check();
 
     // Execute a batch if required. A batch is only used when remote files
     // are checked.
diff --git a/core/modules/locale/src/Form/ImportForm.php b/core/modules/locale/src/Form/ImportForm.php
index f2bd45b844..d6e1705b73 100644
--- a/core/modules/locale/src/Form/ImportForm.php
+++ b/core/modules/locale/src/Form/ImportForm.php
@@ -7,6 +7,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\language\ConfigurableLanguageManagerInterface;
 use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\locale\TranslationUpdaterInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -36,12 +37,20 @@ class ImportForm extends FormBase {
   protected $languageManager;
 
   /**
+   * The translation updater.
+   *
+   * @var \Drupal\locale\TranslationUpdaterInterface
+   */
+  protected $translationUpdater;
+
+  /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('module_handler'),
-      $container->get('language_manager')
+      $container->get('language_manager'),
+      $container->get('locale.translation_updater')
     );
   }
   /**
@@ -51,10 +60,13 @@ public static function create(ContainerInterface $container) {
    *   The module handler service.
    * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
    *   The configurable language manager.
+   * @param \Drupal\locale\TranslationUpdaterInterface $translation_updater
+   *   The translation updater.
    */
-  public function __construct(ModuleHandlerInterface $module_handler, ConfigurableLanguageManagerInterface $language_manager) {
+  public function __construct(ModuleHandlerInterface $module_handler, ConfigurableLanguageManagerInterface $language_manager, TranslationUpdaterInterface $translation_updater) {
     $this->moduleHandler = $module_handler;
     $this->languageManager = $language_manager;
+    $this->translationUpdater = $translation_updater;
   }
 
   /**
@@ -174,7 +186,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       $language->save();
       drupal_set_message($this->t('The language %language has been created.', ['%language' => $this->t($language->label())]));
     }
-    $options = array_merge(_locale_translation_default_update_options(), [
+    $options = array_merge($this->translationUpdater->getDefaultOptions(), [
       'langcode' => $form_state->getValue('langcode'),
       'overwrite_options' => $form_state->getValue('overwrite_options'),
       'customized' => $form_state->getValue('customized') ? LOCALE_CUSTOMIZED : LOCALE_NOT_CUSTOMIZED,
diff --git a/core/modules/locale/src/Form/LocaleSettingsForm.php b/core/modules/locale/src/Form/LocaleSettingsForm.php
index e722cfb2b4..47f811a752 100644
--- a/core/modules/locale/src/Form/LocaleSettingsForm.php
+++ b/core/modules/locale/src/Form/LocaleSettingsForm.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\locale\TranslationUpdaterInterface;
 
 /**
  * Configure locale settings for this site.
@@ -54,29 +55,29 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#title' => $this->t('Translation source'),
       '#default_value' => $config->get('translation.use_source'),
       '#options' => [
-        LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL => $this->t('Drupal translation server and local files'),
-        LOCALE_TRANSLATION_USE_SOURCE_LOCAL => $this->t('Local files only'),
+        TranslationUpdaterInterface::USE_REMOTE_AND_LOCAL_SOURCE => $this->t('Drupal translation server and local files'),
+        TranslationUpdaterInterface::USE_LOCAL_SOURCE => $this->t('Local files only'),
       ],
       '#description' => $this->t('The source of translation files for automatic interface translation.') . ' ' . $description,
     ];
 
     if ($config->get('translation.overwrite_not_customized') == FALSE) {
-      $default = LOCALE_TRANSLATION_OVERWRITE_NONE;
+      $default = TranslationUpdaterInterface::OVERWRITE_NONE;
     }
     elseif ($config->get('translation.overwrite_customized') == TRUE) {
-      $default = LOCALE_TRANSLATION_OVERWRITE_ALL;
+      $default = TranslationUpdaterInterface::OVERWRITE_ALL;
     }
     else {
-      $default = LOCALE_TRANSLATION_OVERWRITE_NON_CUSTOMIZED;
+      $default = TranslationUpdaterInterface::OVERWRITE_NON_CUSTOMIZED;
     }
     $form['overwrite'] = [
       '#type' => 'radios',
       '#title' => $this->t('Import behavior'),
       '#default_value' => $default,
       '#options' => [
-        LOCALE_TRANSLATION_OVERWRITE_NONE => $this->t("Don't overwrite existing translations."),
-        LOCALE_TRANSLATION_OVERWRITE_NON_CUSTOMIZED => $this->t('Only overwrite imported translations, customized translations are kept.'),
-        LOCALE_TRANSLATION_OVERWRITE_ALL => $this->t('Overwrite existing translations.'),
+        TranslationUpdaterInterface::OVERWRITE_NONE => $this->t("Don't overwrite existing translations."),
+        TranslationUpdaterInterface::OVERWRITE_NON_CUSTOMIZED => $this->t('Only overwrite imported translations, customized translations are kept.'),
+        TranslationUpdaterInterface::OVERWRITE_ALL => $this->t('Overwrite existing translations.'),
       ],
       '#description' => $this->t('How to treat existing translations when automatically updating the interface translations.'),
     ];
@@ -90,7 +91,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
   public function validateForm(array &$form, FormStateInterface $form_state) {
     parent::validateForm($form, $form_state);
 
-    if (empty($form['#translation_directory']) && $form_state->getValue('use_source') == LOCALE_TRANSLATION_USE_SOURCE_LOCAL) {
+    if (empty($form['#translation_directory']) && $form_state->getValue('use_source') == TranslationUpdaterInterface::USE_LOCAL_SOURCE) {
       $form_state->setErrorByName('use_source', $this->t('You have selected local translation source, but no <a href=":url">Interface translation directory</a> was configured.', [':url' => $this->url('system.file_system_settings')]));
     }
   }
@@ -106,21 +107,21 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $config->set('translation.use_source', $values['use_source'])->save();
 
     switch ($values['overwrite']) {
-      case LOCALE_TRANSLATION_OVERWRITE_ALL:
+      case TranslationUpdaterInterface::OVERWRITE_ALL:
         $config
           ->set('translation.overwrite_customized', TRUE)
           ->set('translation.overwrite_not_customized', TRUE)
           ->save();
         break;
 
-      case LOCALE_TRANSLATION_OVERWRITE_NON_CUSTOMIZED:
+      case TranslationUpdaterInterface::OVERWRITE_NON_CUSTOMIZED:
         $config
           ->set('translation.overwrite_customized', FALSE)
           ->set('translation.overwrite_not_customized', TRUE)
           ->save();
         break;
 
-      case LOCALE_TRANSLATION_OVERWRITE_NONE:
+      case TranslationUpdaterInterface::OVERWRITE_NONE:
         $config
           ->set('translation.overwrite_customized', FALSE)
           ->set('translation.overwrite_not_customized', FALSE)
diff --git a/core/modules/locale/src/Form/TranslationStatusForm.php b/core/modules/locale/src/Form/TranslationStatusForm.php
index cbf4d4a55e..24fba053b0 100644
--- a/core/modules/locale/src/Form/TranslationStatusForm.php
+++ b/core/modules/locale/src/Form/TranslationStatusForm.php
@@ -6,6 +6,7 @@
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\State\StateInterface;
+use Drupal\locale\TranslationUpdaterInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -28,12 +29,20 @@ class TranslationStatusForm extends FormBase {
   protected $state;
 
   /**
+   * The translation updater.
+   *
+   * @var \Drupal\locale\TranslationUpdaterInterface
+   */
+  protected $translationUpdater;
+
+  /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('module_handler'),
-      $container->get('state')
+      $container->get('state'),
+      $container->get('locale.translation_updater')
     );
   }
 
@@ -44,10 +53,13 @@ public static function create(ContainerInterface $container) {
    *   A module handler.
    * @param \Drupal\Core\State\StateInterface $state
    *   The state service.
+   * @param \Drupal\locale\TranslationUpdaterInterface $translation_updater
+   *   The translation updater.
    */
-  public function __construct(ModuleHandlerInterface $module_handler, StateInterface $state) {
+  public function __construct(ModuleHandlerInterface $module_handler, StateInterface $state, TranslationUpdaterInterface $translation_updater) {
     $this->moduleHandler = $module_handler;
     $this->state = $state;
+    $this->translationUpdater = $translation_updater;
   }
 
   /**
@@ -263,34 +275,10 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $this->moduleHandler->loadInclude('locale', 'fetch.inc');
-    $this->moduleHandler->loadInclude('locale', 'bulk.inc');
-
-    $langcodes = array_filter($form_state->getValue('langcodes'));
     $projects = array_filter($form_state->getValue('projects_update'));
+    $langcodes = array_filter($form_state->getValue('langcodes'));
 
-    // Set the translation import options. This determines if existing
-    // translations will be overwritten by imported strings.
-    $options = _locale_translation_default_update_options();
-
-    // If the status was updated recently we can immediately start fetching the
-    // translation updates. If the status is expired we clear it an run a batch to
-    // update the status and then fetch the translation updates.
-    $last_checked = $this->state->get('locale.translation_last_checked');
-    if ($last_checked < REQUEST_TIME - LOCALE_TRANSLATION_STATUS_TTL) {
-      locale_translation_clear_status();
-      $batch = locale_translation_batch_update_build([], $langcodes, $options);
-      batch_set($batch);
-    }
-    else {
-      // Set a batch to download and import translations.
-      $batch = locale_translation_batch_fetch_build($projects, $langcodes, $options);
-      batch_set($batch);
-      // Set a batch to update configuration as well.
-      if ($batch = locale_config_batch_update_components($options, $langcodes)) {
-        batch_set($batch);
-      }
-    }
+    $this->translationUpdater->update($projects, $langcodes);
   }
 
 }
diff --git a/core/modules/locale/src/TranslationUpdater.php b/core/modules/locale/src/TranslationUpdater.php
new file mode 100644
index 0000000000..3e7b41df5e
--- /dev/null
+++ b/core/modules/locale/src/TranslationUpdater.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace Drupal\locale;
+
+use Drupal\Component\Datetime\TimeInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\State\StateInterface;
+
+/**
+ * Checks for updated translations and imports them.
+ */
+class TranslationUpdater implements TranslationUpdaterInterface {
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * The interface translation project storage.
+   *
+   * @var \Drupal\locale\LocaleProjectStorageInterface
+   */
+  protected $localeProject;
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The time service.
+   *
+   * @var \Drupal\Component\Datetime\TimeInterface
+   */
+  protected $time;
+
+  /**
+   * Constructs a TranslationUpdater object.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\State\StateInterface $state
+   *   The state service.
+   * @param \Drupal\locale\LocaleProjectStorageInterface $locale_project
+   *   The interface translation project storage.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The configuration factory.
+   * @param \Drupal\Component\Datetime\TimeInterface
+   *   The time service.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler, StateInterface $state, LocaleProjectStorageInterface $locale_project, ConfigFactoryInterface $config_factory, TimeInterface $time) {
+    $this->moduleHandler = $module_handler;
+    $this->state = $state;
+    $this->localeProject = $locale_project;
+    $this->configFactory = $config_factory;
+    $this->time = $time;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function check() {
+    $this->moduleHandler->loadInclude('locale', 'inc', 'locale.compare');
+
+    // Check translation status of all translatable projects in all languages.
+    // First we clear the cached list of projects. Although not strictly
+    // necessary, this is helpful in case the project list is out of sync.
+    $this->localeProject->deleteAll();
+    locale_translation_check_projects();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function update(array $projects = NULL, array $langcodes = NULL) {
+    $this->moduleHandler->loadInclude('locale', 'fetch.inc');
+    $this->moduleHandler->loadInclude('locale', 'bulk.inc');
+
+    // If the status was updated recently we can immediately start fetching the
+    // translation updates. If the status is expired we clear it and run a batch
+    // to update the status and then fetch the translation updates.
+    $last_checked = $this->state->get('locale.translation_last_checked');
+    if ($last_checked < $this->time->getRequestTime() - TranslationUpdaterInterface::STATUS_TTL) {
+      $this->state->delete('locale.translation_status');
+      $this->state->delete('locale.translation_last_checked');
+
+      $batch = locale_translation_batch_update_build([], $langcodes, $this->getDefaultOptions());
+      batch_set($batch);
+    }
+    else {
+      // Set a batch to download and import translations.
+      $batch = locale_translation_batch_fetch_build($projects, $langcodes, $this->getDefaultOptions());
+      batch_set($batch);
+      // Set a batch to update configuration as well.
+      if ($batch = locale_config_batch_update_components($this->getDefaultOptions(), $langcodes)) {
+        batch_set($batch);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultOptions() {
+    $config = $this->configFactory->get('locale.settings')->get('translation');
+    return [
+      'customized' => LOCALE_NOT_CUSTOMIZED,
+      'overwrite_options' => [
+        'not_customized' => $config['overwrite_not_customized'],
+        'customized' => $config['overwrite_customized'],
+      ],
+      'finish_feedback' => TRUE,
+      'use_remote' => $config['use_source'] === static::USE_REMOTE_AND_LOCAL_SOURCE,
+    ];
+  }
+
+}
diff --git a/core/modules/locale/src/TranslationUpdaterInterface.php b/core/modules/locale/src/TranslationUpdaterInterface.php
new file mode 100644
index 0000000000..30b61065ad
--- /dev/null
+++ b/core/modules/locale/src/TranslationUpdaterInterface.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace Drupal\locale;
+
+/**
+ * Provides an interface for translation updaters.
+ *
+ * A translation updater checks for updated translations and imports them.
+ */
+interface TranslationUpdaterInterface {
+
+  /**
+   * Translation update mode: Use local files only.
+   *
+   * When checking for available translation updates, only local files will be
+   * used. Any remote translation file will be ignored. Also custom modules and
+   * themes which have set a "server pattern" to use a remote translation server
+   * will be ignored.
+   */
+  const USE_LOCAL_SOURCE = 'local';
+
+  /**
+   * Translation update mode: Use both remote and local files.
+   *
+   * When checking for available translation updates, both local and remote files
+   * will be checked.
+   */
+  const USE_REMOTE_AND_LOCAL_SOURCE = 'remote_and_local';
+
+  /**
+   * Default location of gettext file on the translation server.
+   *
+   * @see locale_translation_default_translation_server().
+   */
+  const DEFAULT_SERVER_PATTERN = 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po';
+
+  /**
+   * The number of seconds that the translations status entry should be considered.
+   */
+  const STATUS_TTL = 600;
+
+  /**
+   * UI option for override of existing translations. Override any translation.
+   */
+  const OVERWRITE_ALL = 'all';
+
+  /**
+   * UI option for override of existing translations. Only override non-customized
+   * translations.
+   */
+  const OVERWRITE_NON_CUSTOMIZED = 'non_customized';
+
+  /**
+   * UI option for override of existing translations. Don't override existing
+   * translations.
+   */
+  const OVERWRITE_NONE = 'none';
+
+  /**
+   * Checks whether updated translations are available.
+   *
+   * This checks for translations available in the local filesystem as well as
+   * on a remote translation server. If remote checking is enabled, this method
+   * will set a batch, which calling code needs to take into account:
+   * @code
+   *   $translation_update->check();
+   *   if (batch_get()) {
+   *     batch_process();
+   *   }
+   * @endcode
+   *
+   * @see batch_get()
+   * @see batch_process()
+   */
+  public function check();
+
+  /**
+   * Imports the available translation updates.
+   *
+   * Note that this does not actually check whether translation updates are
+   * available, TranslationUpdateInterface::check() needs to be used for that.
+   *
+   * This method will set a batch, which calling code needs to take into
+   * account:
+   * @code
+   *   $translation_update->update();
+   *   batch_process();
+   * @endcode
+   *
+   * @param array $projects
+   *   (optional) A list of projects to update. If omitted, all translatable
+   *   projects will be updated.
+   * @param array $langcodes
+   *   (optional) A list of languages to update translations for. If omitted,
+   *   translations will be updated for all languages.
+   *
+   * @see batch_process()
+   */
+  public function update(array $projects = NULL, array $langcodes = NULL);
+
+  /**
+   * Returns the default options that are used for importing translations.
+   *
+   * @return array
+   *   An array of options for updating the translations containing the
+   *   following keys:
+   *   - customized: LOCALE_NOT_CUSTOMIZED if community translations are being
+   *     imported or LOCALE_CUSTOMIZED if custom translations are being
+   *     imported.
+   *   - overwrite_options: An array specifying which types of translations to
+   *     overwrite containing the following keys:
+   *     - not_customized: A boolean indicating whether or not to overwrite
+   *       community translations.
+   *     - customized: A boolean indicating whether or not to overwrite custom
+   *       community translations.
+   *   - finish_feedback: Whether or not to provide feedback for the update
+   *     process in the batch context.
+   *   - use_remote: Whether or not to check a remote source for translations.
+   */
+  public function getDefaultOptions();
+
+}
diff --git a/core/modules/locale/tests/src/Functional/LocaleUpdateTest.php b/core/modules/locale/tests/src/Functional/LocaleUpdateTest.php
index 907e572c42..243d77531b 100644
--- a/core/modules/locale/tests/src/Functional/LocaleUpdateTest.php
+++ b/core/modules/locale/tests/src/Functional/LocaleUpdateTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\locale\Functional;
 
 use Drupal\Core\Language\LanguageInterface;
+use Drupal\locale\TranslationUpdaterInterface;
 
 /**
  * Tests for updating the interface translations of projects.
@@ -75,7 +76,7 @@ public function testUpdateCheckStatus() {
 
     // Set the test conditions.
     $edit = [
-      'use_source' => LOCALE_TRANSLATION_USE_SOURCE_LOCAL,
+      'use_source' => TranslationUpdaterInterface::USE_LOCAL_SOURCE,
     ];
     $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration'));
 
@@ -91,7 +92,7 @@ public function testUpdateCheckStatus() {
 
     // Set the test conditions.
     $edit = [
-      'use_source' => LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL,
+      'use_source' => TranslationUpdaterInterface::USE_REMOTE_AND_LOCAL_SOURCE,
     ];
     $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration'));
 
@@ -125,8 +126,8 @@ public function testUpdateImportSourceRemote() {
 
     // Set the update conditions for this test.
     $edit = [
-      'use_source' => LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL,
-      'overwrite' => LOCALE_TRANSLATION_OVERWRITE_ALL,
+      'use_source' => TranslationUpdaterInterface::USE_REMOTE_AND_LOCAL_SOURCE,
+      'overwrite' => TranslationUpdaterInterface::OVERWRITE_ALL,
     ];
     $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration'));
 
@@ -187,8 +188,8 @@ public function testUpdateImportSourceLocal() {
 
     // Set the update conditions for this test.
     $edit = [
-      'use_source' => LOCALE_TRANSLATION_USE_SOURCE_LOCAL,
-      'overwrite' => LOCALE_TRANSLATION_OVERWRITE_ALL,
+      'use_source' => TranslationUpdaterInterface::USE_LOCAL_SOURCE,
+      'overwrite' => TranslationUpdaterInterface::OVERWRITE_ALL,
     ];
     $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration'));
 
@@ -241,8 +242,8 @@ public function testUpdateImportModeNonCustomized() {
 
     // Set the test conditions.
     $edit = [
-      'use_source' => LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL,
-      'overwrite' => LOCALE_TRANSLATION_OVERWRITE_NON_CUSTOMIZED,
+      'use_source' => TranslationUpdaterInterface::USE_REMOTE_AND_LOCAL_SOURCE,
+      'overwrite' => TranslationUpdaterInterface::OVERWRITE_NON_CUSTOMIZED,
     ];
     $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration'));
 
@@ -277,8 +278,8 @@ public function testUpdateImportModeNone() {
 
     // Set the test conditions.
     $edit = [
-      'use_source' => LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL,
-      'overwrite' => LOCALE_TRANSLATION_OVERWRITE_NONE,
+      'use_source' => TranslationUpdaterInterface::USE_REMOTE_AND_LOCAL_SOURCE,
+      'overwrite' => TranslationUpdaterInterface::OVERWRITE_NONE,
     ];
     $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration'));
 
