diff --git a/core/core.services.yml b/core/core.services.yml
index 2e186ae..81f4f50 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -160,6 +160,13 @@ services:
     arguments: ['@event_dispatcher', '@service_container', '@controller_resolver']
   language_manager:
     class: Drupal\Core\Language\LanguageManager
+# Register a minimum translation service just in case a translation is
+# attempted before language initialization.
+  locale.translation.custom:
+    class: Drupal\Core\Translation\CustomStrings
+  locale.translation:
+    class: Drupal\Core\Translation\TranslationManager
+    arguments: ['@language_manager', ['@locale.translation.custom']]
   database.slave:
     class: Drupal\Core\Database\Connection
     factory_class: Drupal\Core\Database\Database
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index aa0fbcb..92875b2 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -1524,37 +1524,7 @@ function bootstrap_hooks() {
  * @ingroup sanitization
  */
 function t($string, array $args = array(), array $options = array()) {
-  static $custom_strings;
-
-  // Merge in default.
-  if (empty($options['langcode'])) {
-    $options['langcode'] = language(LANGUAGE_TYPE_INTERFACE)->langcode;
-  }
-  if (empty($options['context'])) {
-    $options['context'] = '';
-  }
-
-  // First, check for an array of customized strings. If present, use the array
-  // *instead of* database lookups. This is a high performance way to provide a
-  // handful of string replacements. See settings.php for examples.
-  // Cache the $custom_strings variable to improve performance.
-  if (!isset($custom_strings[$options['langcode']])) {
-    $custom_strings[$options['langcode']] = variable_get('locale_custom_strings_' . $options['langcode'], array());
-  }
-  // Custom strings work for English too, even if locale module is disabled.
-  if (isset($custom_strings[$options['langcode']][$options['context']][$string])) {
-    $string = $custom_strings[$options['langcode']][$options['context']][$string];
-  }
-  // Translate with locale module if enabled.
-  elseif ($options['langcode'] != LANGUAGE_SYSTEM && ($options['langcode'] != 'en' || variable_get('locale_translate_english', FALSE)) && function_exists('locale')) {
-    $string = locale($string, $options['context'], $options['langcode']);
-  }
-  if (empty($args)) {
-    return $string;
-  }
-  else {
-    return format_string($string, $args);
-  }
+  return Drupal::service('locale.translation')->translate($string, $args, $options);
 }
 
 /**
@@ -2686,18 +2656,14 @@ function drupal_installation_attempted() {
  * Use st() if your code will only run during installation and never any other
  * time. Use get_t() if your code could run in either circumstance.
  *
+ * @todo Remove this in favor of t().
+ *
  * @see t()
  * @see st()
  * @ingroup sanitization
  */
 function get_t() {
-  static $t;
-  // This is not converted to drupal_static because there is no point in
-  // resetting this as it can not change in the course of a request.
-  if (!isset($t)) {
-    $t = drupal_installation_attempted() ? 'st' : 't';
-  }
-  return $t;
+  return 't';
 }
 
 /**
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 47982dd..4e3f9ae 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -6,6 +6,7 @@
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\Install\TaskException;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Translation\FileTranslation;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
@@ -356,6 +357,13 @@ function install_begin_request(&$install_state) {
 
     // Register the 'language_manager' service.
     $container->register('language_manager', 'Drupal\Core\Language\LanguageManager');
+
+    // Register the 'locale translation' services.
+    $container->register('locale.translation.custom', 'Drupal\Core\Translation\CustomStrings');
+    $container->register('locale.translation', 'Drupal\Core\Translation\TranslationManager')
+      ->addArgument(new Reference('language_manager'))
+      ->addArgument(array(new Reference('locale.translation.custom')));
+
     foreach (array('bootstrap', 'config', 'cache', 'menu', 'page', 'path') as $bin) {
       $container
         ->register("cache.$bin", 'Drupal\Core\Cache\MemoryBackend')
@@ -401,6 +409,8 @@ function install_begin_request(&$install_state) {
 
   // Set up $language, so t() caller functions will still work.
   drupal_language_initialize();
+  // Append file translation to the translation chain.
+  Drupal::service('locale.translation')->appendTranslator(new FileTranslation(install_translation_directory()));
 
   require_once DRUPAL_ROOT . '/core/includes/ajax.inc';
 
diff --git a/core/includes/install.inc b/core/includes/install.inc
index 391750f..31d3bf8 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -943,48 +943,22 @@ function drupal_requirements_url($severity) {
  * @ingroup sanitization
  */
 function st($string, array $args = array(), array $options = array()) {
-  static $strings = NULL;
   global $install_state;
 
-  if (empty($options['context'])) {
-    $options['context'] = '';
-  }
-
-  if (!isset($strings)) {
-    $strings = array();
+  // If language and database initialized, use regular string translation.
+  // Otherwise skip translation and just output the string.
+  if (drupal_installation_attempted()) {
     if (isset($install_state['parameters']['langcode'])) {
-      // If the given langcode was selected, there should be at least one .po
-      // file with its name in the pattern drupal-$version.$langcode.po.
-      // This might or might not be the entire filename. It is also possible
-      // that multiple files end with the same suffix, even if unlikely.
-      $files = install_find_translation_files($install_state['parameters']['langcode']);
-      if (!empty($files)) {
-        // Register locale classes with the classloader. Locale module is not
-        // yet enabled at this stage, so this is not happening automatically.
-        drupal_classloader_register('locale', drupal_get_path('module', 'locale'));
-        $strings = Gettext::filesToArray($install_state['parameters']['langcode'], $files);
-      }
+      $options += array('langcode' => $install_state['parameters']['langcode']);
     }
-  }
-
-  require_once DRUPAL_ROOT . '/core/includes/theme.inc';
-  // Transform arguments before inserting them
-  foreach ($args as $key => $value) {
-    switch ($key[0]) {
-      // Escaped only
-      case '@':
-        $args[$key] = check_plain($value);
-        break;
-      // Escaped and placeholder
-      case '%':
-      default:
-        $args[$key] = '<em>' . check_plain($value) . '</em>';
-        break;
-      // Pass-through
-      case '!':
+    // Before database verified we don't have translation system in place yet.
+    if (empty($install_state['database_verified'])) {
+      return empty($args) ? $string : format_string($string, $args);
     }
   }
-  return strtr((!empty($strings[$options['context']][$string]) ? $strings[$options['context']][$string] : $string), $args);
+
+  // Fallback to standard locale.
+  return t($string, $args, $options);
 }
 
 /**
diff --git a/core/lib/Drupal/Core/Translation/CustomStrings.php b/core/lib/Drupal/Core/Translation/CustomStrings.php
new file mode 100644
index 0000000..307b1d2
--- /dev/null
+++ b/core/lib/Drupal/Core/Translation/CustomStrings.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\CustomStrings.
+ */
+
+namespace Drupal\Core\Translation;
+
+/**
+ * String translator using overrides from variables.
+ *
+ * This is a high performance way to provide a handful of string replacements.
+ * See settings.php for examples.
+ */
+class CustomStrings extends StaticTranslation {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function loadLanguage($langcode) {
+    return variable_get('locale_custom_strings_' . $langcode, array());
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Translation/FileTranslation.php b/core/lib/Drupal/Core/Translation/FileTranslation.php
new file mode 100644
index 0000000..a98f5b8
--- /dev/null
+++ b/core/lib/Drupal/Core/Translation/FileTranslation.php
@@ -0,0 +1,103 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Translation\FileTranslation.
+ */
+
+namespace Drupal\Core\Translation;
+
+use Drupal\Component\Gettext\PoStreamReader;
+use Drupal\Component\Gettext\PoMemoryWriter;
+
+/**
+ * File based string translation.
+ *
+ * Translates a string when some systems are not available.
+ *
+ * Used during the install process, when database, theme, and localization
+ * system is possibly not yet available.
+ *
+ * Use t() if your code will never run during the Drupal installation phase.
+ * Use st() if your code will only run during installation.
+ */
+class FileTranslation extends StaticTranslation {
+
+  /**
+   * Directory to find translation files in the file system.
+   *
+   * @var string
+   */
+  protected $directory;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct($directory) {
+    parent::__construct();
+    $this->directory = $directory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function loadLanguage($langcode) {
+    // If the given langcode was selected, there should be at least one .po
+    // file with its name in the pattern drupal-$version.$langcode.po.
+    // This might or might not be the entire filename. It is also possible
+    // that multiple files end with the same suffix, even if unlikely.
+    $files = $this->findTranslationFiles($langcode);
+
+    if (!empty($files)) {
+      return $this->filesToArray($langcode, $files);
+    }
+    else {
+      return array();
+    }
+  }
+
+  /**
+   * Finds installer translations either for a specific langcode or all languages.
+   *
+   * @param $langcode
+   *   (optional) The language code corresponding to the language for which we
+   *   want to find translation files. If omitted, information on all available
+   *   files will be returned.
+   *
+   * @return
+   *   An associative array of file information objects keyed by file URIs as
+   *   returned by file_scan_directory().
+   *
+   * @see file_scan_directory()
+   */
+  public function findTranslationFiles($langcode = NULL) {
+    $files = file_scan_directory($this->directory, '!drupal-\d+\.\d+\.' . (!empty($langcode) ? preg_quote($langcode, '!') : '[^\.]+') . '\.po$!', array('recurse' => FALSE));
+    return $files;
+  }
+
+  /**
+   * Reads the given Gettext PO files into a data structure.
+   *
+   * @param string $langcode
+   *   Language code string.
+   * @param array $files
+   *   List of file objects with URI properties pointing to read.
+   *
+   * @return array
+   *   Structured array as produced by a PoMemoryWriter.
+   *
+   * @see \Drupal\Component\Gettext\PoMemoryWriter
+   */
+  public static function filesToArray($langcode, array $files) {
+    $writer = new PoMemoryWriter();
+    $writer->setLangcode($langcode);
+    foreach ($files as $file) {
+      $reader = new PoStreamReader();
+      $reader->setURI($file->uri);
+      $reader->setLangcode($langcode);
+      $reader->open();
+      $writer->writeItems($reader, -1);
+    }
+    return $writer->getData();
+  }
+}
diff --git a/core/lib/Drupal/Core/Translation/StaticTranslation.php b/core/lib/Drupal/Core/Translation/StaticTranslation.php
new file mode 100644
index 0000000..e876577
--- /dev/null
+++ b/core/lib/Drupal/Core/Translation/StaticTranslation.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\StaticTranslation.
+ */
+
+namespace Drupal\Core\Translation;
+
+/**
+ * String translator with a static cache for translations.
+ *
+ * This is a high performance way to provide a handful of string replacements.
+ */
+class StaticTranslation implements TranslationInterface {
+
+  /**
+   * String translations
+   *
+   * @var array
+   *   Array of cached translations indexed by language and context.
+   */
+  protected $translations;
+
+  /**
+   * Constructs a translator from an array of translations.
+   *
+   * @param array $translations
+   *   Array of override strings indexed by language and context
+   */
+  public function __construct($translations = array()) {
+    $this->translations = $translations;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTranslation($langcode, $string, $context) {
+    if (!isset($this->translations[$langcode])) {
+      $this->translations[$langcode] = $this->loadLanguage($langcode);
+    }
+    if (isset($this->translations[$langcode][$context][$string])) {
+      return $this->translations[$langcode][$context][$string];
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function reset() {
+    $this->translations = array();
+  }
+
+  /**
+   * Add translations for new language.
+   */
+  protected function loadLanguage($langcode) {
+    return array();
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Translation/TranslationInterface.php b/core/lib/Drupal/Core/Translation/TranslationInterface.php
new file mode 100644
index 0000000..81d3cb3
--- /dev/null
+++ b/core/lib/Drupal/Core/Translation/TranslationInterface.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\TranslationInterface.
+ */
+
+namespace Drupal\Core\Translation;
+
+/**
+ * Interface for objects capable of string translation.
+ */
+interface TranslationInterface {
+
+  /**
+   * Retrieves English string to given language.
+   *
+   * @param string $langcode
+   *   Language code to translate to.
+   * @param string $string
+   *   The source string
+   * @param string $context
+   *   The string context
+   *
+   * @return string|FALSE
+   *   Translated string if there is a translation, FALSE if not.
+   */
+  public function getTranslation($langcode, $string, $context);
+
+  /**
+   * Resets translation cache.
+   *
+   * Since most translation systems implement some form of caching, this
+   * provides a way to delete that cache.
+   */
+  public function reset();
+
+}
diff --git a/core/lib/Drupal/Core/Translation/TranslationManager.php b/core/lib/Drupal/Core/Translation/TranslationManager.php
new file mode 100644
index 0000000..770e477
--- /dev/null
+++ b/core/lib/Drupal/Core/Translation/TranslationManager.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Translation\TranslationManager.
+ */
+
+namespace Drupal\Core\Translation;
+
+use Drupal\Core\Language\LanguageManager;
+use Drupal\Component\Utility\String;
+
+/**
+ * Defines a chained translation implementation for combining multiple systems.
+ *
+ * This defines a fallback system for translations, on which translation
+ * systems will be tried in order until one of them returns a valid string
+ * translation.
+ */
+class TranslationManager implements TranslationInterface {
+
+  /**
+   * Translation systems.
+   *
+   * @var array
+   *   Array of Drupal\Core\Language\TranslationInterface objects
+   */
+  protected $translators;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManager
+   */
+  protected $languageManager;
+
+  /**
+   * Constructs a \Drupal\Core\Language\TranslationManager object.
+   *
+   * The order of the translators in the array will be the order in which they
+   * will be tried for a translation.
+   *
+   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   *   The language manager to retrieve language objects from.
+   * @param $translator1, $translator2
+   *   Multiple containing \Drupal\Core\Language\TranslationInterface objects.
+   */
+  public function __construct(LanguageManager $language_manager, $translators) {
+    $this->languageManager = $language_manager;
+    $this->translators = $translators;
+  }
+
+  /**
+   * Appends a translation system to the translation chain.
+   *
+   * @param \Drupal\Core\Translation\TranslationInterface $translation
+   *   The translation interface to be appended to the translation chain.
+   *
+   * @return \Drupal\Core\Translation\TranslationManager
+   *   The called object.
+   */
+  public function appendTranslator(TranslationInterface $translation) {
+    $this->translators[] = $translation;
+
+    return $this;
+  }
+
+  /**
+   * Prepends a translation system to the translation chain.
+   *
+   * @param \Drupal\Core\Translation\TranslationInterface $translation
+   *   The translation interface to be prepended to the translation chain.
+   *
+   * @return \Drupal\Core\Translation\TranslationManager
+   *   The called object.
+   */
+  public function prependTranslator(TranslationInterface $translation) {
+    array_unshift($this->translators, $translation);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTranslation($langcode, $string, $context) {
+    foreach ($this->translators as $translator) {
+      $translation = $translator->getTranslation($langcode, $string, $context);
+      if ($translation !== FALSE) {
+        return $translation;
+      }
+    }
+    // No translator got a translation.
+    return FALSE;
+  }
+
+  /**
+   * Translates a string to the current language or to a given language.
+   *
+   * @param string $string
+   *   A string containing the English string to translate.
+   * @param array $args
+   *   An associative array of replacements to make after translation. Based
+   *   on the first character of the key, the value is escaped and/or themed.
+   *   See \Drupal\Core\Utility\String::format() for details.
+   * @param array $options
+   *   An associative array of additional options, with the following elements:
+   *   - 'langcode': The language code to translate to a language other than
+   *      what is used to display the page.
+   *   - 'context': The context the source string belongs to.
+   *
+   * @return string
+   *   The translated string.
+   *
+   * @see t()
+   * @see \Drupal\Core\Utility\String::format()
+   */
+    public function translate($string, array $args = array(), array $options = array()) {
+      // Merge in defaults.
+      if (empty($options['langcode'])) {
+        $options['langcode'] = $this->languageManager->getLanguage(LANGUAGE_TYPE_INTERFACE)->langcode;
+      }
+      if (empty($options['context'])) {
+        $options['context'] = '';
+      }
+      $translation = $this->getTranslation($options['langcode'], $string, $options['context']);
+      $string = $translation === FALSE ? $string : $translation;
+
+      if (empty($args)) {
+        return $string;
+      }
+      else {
+        return String::format($string, $args);
+      }
+    }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function reset() {
+    foreach ($this->translators as $translator) {
+      $translator->reset();
+    }
+  }
+
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Gettext.php b/core/modules/locale/lib/Drupal/locale/Gettext.php
index 40472f0..a24b9fc 100644
--- a/core/modules/locale/lib/Drupal/locale/Gettext.php
+++ b/core/modules/locale/lib/Drupal/locale/Gettext.php
@@ -23,32 +23,6 @@
 class Gettext {
 
   /**
-   * Reads the given Gettext PO files into a data structure.
-   *
-   * @param string $langcode
-   *   Language code string.
-   * @param array $files
-   *   List of file objects with URI properties pointing to read.
-   *
-   * @return array
-   *   Structured array as produced by a PoMemoryWriter.
-   *
-   * @see Drupal\Component\Gettext\PoMemoryWriter
-   */
-  static function filesToArray($langcode, array $files) {
-    $writer = new PoMemoryWriter();
-    $writer->setLangcode($langcode);
-    foreach ($files as $file) {
-      $reader = new PoStreamReader();
-      $reader->setURI($file->uri);
-      $reader->setLangcode($langcode);
-      $reader->open();
-      $writer->writeItems($reader, -1);
-    }
-    return $writer->getData();
-  }
-
-  /**
    * Reads the given PO files into the database.
    *
    * @param stdClass $file
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleLookup.php b/core/modules/locale/lib/Drupal/locale/LocaleLookup.php
index cc515b4..d4530cf 100644
--- a/core/modules/locale/lib/Drupal/locale/LocaleLookup.php
+++ b/core/modules/locale/lib/Drupal/locale/LocaleLookup.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Definition of LocaleLookup
+ * Contains \Drupal\locale\Locale\Lookup.
  */
 
 namespace Drupal\locale;
@@ -18,46 +18,48 @@ class LocaleLookup extends CacheArray {
 
   /**
    * A language code.
+   *
    * @var string
    */
   protected $langcode;
 
   /**
    * The msgctxt context.
+   *
    * @var string
    */
   protected $context;
 
   /**
-   * The locale storage
+   * The locale storage.
    *
-   * @var Drupal\locale\StringStorageInterface
+   * @var \Drupal\locale\StringStorageInterface
    */
   protected $stringStorage;
 
   /**
-   * Constructs a LocaleCache object.
+   * Constructs a \Drupal\locale\Locale\Lookup object.
    */
-  public function __construct($langcode, $context, $stringStorage) {
+  public function __construct($langcode, $context, $string_storage) {
     $this->langcode = $langcode;
     $this->context = (string) $context;
-    $this->stringStorage = $stringStorage;
+    $this->stringStorage = $string_storage;
 
     // Add the current user's role IDs to the cache key, this ensures that, for
     // example, strings for admin menu items and settings forms are not cached
     // for anonymous users.
-    $rids = implode(':', array_keys($GLOBALS['user']->roles));
+    $rids = isset($GLOBALS['user']) ? implode(':', array_keys($GLOBALS['user']->roles)) : '0';
     parent::__construct("locale:$langcode:$context:$rids", 'cache', array('locale' => TRUE));
   }
 
   /**
-   * Implements CacheArray::resolveCacheMiss().
+   * {@inheritdoc}
    */
   protected function resolveCacheMiss($offset) {
     $translation = $this->stringStorage->findTranslation(array(
       'language' => $this->langcode,
       'source' => $offset,
-      'context' => $this->context
+      'context' => $this->context,
     ));
 
     if ($translation) {
@@ -83,4 +85,13 @@ protected function resolveCacheMiss($offset) {
     }
     return $value;
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __destruct() {
+    // Do nothing to avoid segmentation faults. This can be restored after the
+    // cache collector from http://drupal.org/node/1786490 is used.
+  }
+
 }
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleTranslation.php b/core/modules/locale/lib/Drupal/locale/LocaleTranslation.php
new file mode 100644
index 0000000..9098829
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/LocaleTranslation.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\LocaleTranslation.
+ */
+
+namespace Drupal\locale;
+
+use Drupal\Core\Translation\TranslationInterface;
+use Drupal\locale\StringStorageInterface;
+use Drupal\locale\LocaleLookup;
+
+/**
+ * String translator using the locale module.
+ *
+ * Full featured translation system using locale's string storage and
+ * database caching.
+ */
+class LocaleTranslation implements TranslationInterface {
+
+  /**
+   * Storage for strings.
+   *
+   * @var \Drupal\locale\StringStorageInterface
+   */
+  protected $storage;
+
+  /**
+   * Cached translations
+   *
+   * @var array
+   *   Array of \Drupal\locale\LocaleLookup objects indexed by language code
+   *   and context.
+   */
+  protected $translations;
+
+  /**
+   * Constructs a translator using a string storage.
+   *
+   * @param \Drupal\locale\StringStorageInterface $storage
+   *   Storage to use when looking for new translations.
+   */
+  public function __construct($storage) {
+    $this->storage = $storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTranslation($langcode, $string, $context) {
+    // If the language is not suitable for locale module, just return.
+    if ($langcode == LANGUAGE_SYSTEM || ($langcode == 'en' && !variable_get('locale_translate_english', FALSE))) {
+      return FALSE;
+    }
+    // Strings are cached by langcode, context and roles, using instances of the
+    // LocaleLookup class to handle string lookup and caching.
+    if (!isset($this->translations[$langcode][$context])) {
+      $this->translations[$langcode][$context] = new LocaleLookup($langcode, $context, $this->storage);
+    }
+    $translation = $this->translations[$langcode][$context][$string];
+    return $translation === TRUE ? FALSE : $translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function reset() {
+    $this->translations = array();
+  }
+
+}
diff --git a/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php b/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php
index f2660fd..019ce95 100644
--- a/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php
+++ b/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php
@@ -145,11 +145,11 @@ private function loadStrings() {
         // Filter for string with translation.
         $conditions['translated'] = TRUE;
       }
-      return locale_storage()->getTranslations($conditions);
+      return \Drupal::service('locale.storage')->getTranslations($conditions);
     }
     else {
       // If no language, we don't need any of the target fields.
-      return locale_storage()->getStrings($conditions);
+      return \Drupal::service('locale.storage')->getStrings($conditions);
     }
   }
 
diff --git a/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php b/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
index 618b419..d1341c8 100644
--- a/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
+++ b/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
@@ -228,7 +228,7 @@ private function importString(PoItem $item) {
     $translation = $item->getTranslation();
 
     // Look up the source string and any existing translation.
-    $strings = locale_storage()->getTranslations(array(
+    $strings = \Drupal::service('locale.storage')->getTranslations(array(
       'language' => $this->_langcode,
       'source' => $source,
       'context' => $context
@@ -263,9 +263,9 @@ private function importString(PoItem $item) {
       }
       else {
         // No such source string in the database yet.
-        $string = locale_storage()->createString(array('source' => $source, 'context' => $context))
+        $string = \Drupal::service('locale.storage')->createString(array('source' => $source, 'context' => $context))
           ->save();
-        $target = locale_storage()->createTranslation(array(
+        $target = \Drupal::service('locale.storage')->createTranslation(array(
           'lid' => $string->getId(),
           'language' => $this->_langcode,
           'translation' => $translation,
diff --git a/core/modules/locale/lib/Drupal/locale/StringDatabaseStorage.php b/core/modules/locale/lib/Drupal/locale/StringDatabaseStorage.php
index bb1b28b..d18e35f 100644
--- a/core/modules/locale/lib/Drupal/locale/StringDatabaseStorage.php
+++ b/core/modules/locale/lib/Drupal/locale/StringDatabaseStorage.php
@@ -287,40 +287,19 @@ protected function dbStringTable($string) {
    *
    * @param Drupal\locale\StringInterface $string
    *   The string object.
-   * @param string $table
-   *   (optional) The table name.
    *
    * @return array
    *   Array with key fields if the string has all keys, or empty array if not.
    */
-  protected function dbStringKeys($string, $table = NULL) {
-    $table = $table ? $table : $this->dbStringTable($string);
-    if ($table && $schema = drupal_get_schema($table)) {
-      $keys = $schema['primary key'];
-      $values = $string->getValues($keys);
-      if (count($values) == count($keys)) {
-        return $values;
-      }
+  protected function dbStringKeys($string) {
+    if ($string->isSource()) {
+      $keys = array('lid');
     }
-    return NULL;
-  }
-
-  /**
-   * Gets field values from a string object that are in the database table.
-   *
-   * @param Drupal\locale\StringInterface $string
-   *   The string object.
-   * @param string $table
-   *   (optional) The table name.
-   *
-   * @return array
-   *   Array with field values indexed by field name.
-   */
-  protected function dbStringValues($string, $table = NULL) {
-    $table = $table ? $table : $this->dbStringTable($string);
-    if ($table && $schema = drupal_get_schema($table)) {
-      $fields = array_keys($schema['fields']);
-      return $string->getValues($fields);
+    elseif ($string->isTranslation()) {
+      $keys = array('lid', 'language');
+    }
+    if (!empty($keys) && ($values = $string->getValues($keys)) && count($keys) == count($values)) {
+      return $values;
     }
     else {
       return array();
@@ -328,27 +307,6 @@ protected function dbStringValues($string, $table = NULL) {
   }
 
   /**
-   * Sets default values from storage.
-   *
-   * @param Drupal\locale\StringInterface $string
-   *   The string object.
-   * @param string $table
-   *   (optional) The table name.
-   */
-  protected function dbStringDefaults($string, $table = NULL) {
-    $table = $table ? $table : $this->dbStringTable($string);
-    if ($table && $schema = drupal_get_schema($table)) {
-      $values = array();
-      foreach ($schema['fields'] as $name => $info) {
-        if (isset($info['default'])) {
-          $values[$name] = $info['default'];
-        }
-      }
-      $string->setValues($values, FALSE);
-    }
-  }
-
-  /**
    * Loads multiple string objects.
    *
    * @param array $conditions
@@ -504,9 +462,16 @@ protected function dbStringSelect(array $conditions, array $options = array()) {
    *   If the string is not suitable for this storage, an exception ithrown.
    */
   protected function dbStringInsert($string) {
-    if (($table = $this->dbStringTable($string)) && ($fields = $this->dbStringValues($string, $table))) {
-      $this->dbStringDefaults($string, $table);
-      return $this->connection->insert($table, $this->options)
+    if ($string->isSource()) {
+      $string->setValues(array('context' => '', 'version' => 'none'), FALSE);
+      $fields = $string->getValues(array('source', 'context', 'version'));
+    }
+    elseif ($string->isTranslation()) {
+      $string->setValues(array('customized' => 0), FALSE);
+      $fields = $string->getValues(array('lid', 'language', 'translation', 'customized'));
+    }
+    if (!empty($fields)) {
+      return $this->connection->insert($this->dbStringTable($string), $this->options)
         ->fields($fields)
         ->execute();
     }
@@ -531,13 +496,17 @@ protected function dbStringInsert($string) {
    *   If the string is not suitable for this storage, an exception is thrown.
    */
   protected function dbStringUpdate($string) {
-    if (($table = $this->dbStringTable($string)) && ($keys = $this->dbStringKeys($string, $table)) &&
-        ($fields = $this->dbStringValues($string, $table)) && ($values = array_diff_key($fields, $keys)))
-    {
-      return $this->connection->merge($table, $this->options)
-      ->key($keys)
-      ->fields($values)
-      ->execute();
+    if ($string->isSource()) {
+      $values = $string->getValues(array('source', 'context', 'version'));
+    }
+    elseif ($string->isTranslation()) {
+      $values = $string->getValues(array('translation', 'customized'));
+    }
+    if (!empty($values) && $keys = $this->dbStringKeys($string)) {
+      return $this->connection->merge($this->dbStringTable($string), $this->options)
+        ->key($keys)
+        ->fields($values)
+        ->execute();
     }
     else {
       throw new StringStorageException(format_string('The string cannot be updated: @string', array(
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php
index 7ae1bf7..954e74d 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php
@@ -79,9 +79,12 @@ function testExportTranslation() {
     ), t('Import'));
     drupal_unlink($name);
 
-    // We can't import a string with an empty translation, but calling
-    // locale() for an new string creates an entry in the locales_source table.
-    locale('February', NULL, 'fr');
+    // Create string without translation in the locales_source table.
+    $this->container
+      ->get('locale.storage')
+      ->createString()
+      ->setString('February')
+      ->save();
 
     // Export only customized French translations.
     $this->drupalPost('admin/config/regional/translate/export', array(
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php
index d8e028f..de5dd08 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php
@@ -37,7 +37,13 @@ function testFileParsing() {
     _locale_parse_js_file($filename);
 
     // Get all of the source strings that were found.
-    foreach (locale_storage()->getStrings(array('type' => 'javascript', 'name' => $filename)) as $string) {
+    $strings = $this->container
+      ->get('locale.storage')
+      ->getStrings(array(
+        'type' => 'javascript',
+        'name' => $filename,
+      ));
+    foreach ($strings as $string) {
       $source_strings[$string->source] = $string->context;
     }
     // List of all strings that should be in the file.
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleStringTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleStringTest.php
index 5dd4e23..37b796c 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleStringTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleStringTest.php
@@ -45,7 +45,7 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
     // Add a default locale storage for all these tests.
-    $this->storage = locale_storage();
+    $this->storage = $this->container->get('locale.storage');
     // Create two languages: Spanish and German.
     foreach (array('es', 'de') as $langcode) {
       $language = new Language(array('langcode' => $langcode));
@@ -134,11 +134,11 @@ function testStringSearchAPI() {
     $translate2 = $this->createAllTranslations($source2, array('customized' => LOCALE_CUSTOMIZED));
     // Try quick search function with different field combinations.
     $langcode = 'es';
-    $found = locale_storage()->findTranslation(array('language' => $langcode, 'source' => $source1->source, 'context' => $source1->context));
+    $found = $this->storage->findTranslation(array('language' => $langcode, 'source' => $source1->source, 'context' => $source1->context));
     $this->assertTrue($found && isset($found->language) && isset($found->translation) && !$found->isNew(), 'Translation found searching by source and context.');
     $this->assertEqual($found->translation, $translate1[$langcode]->translation, 'Found the right translation.');
     // Now try a translation not found.
-    $found = locale_storage()->findTranslation(array('language' => $langcode,  'source' => $source3->source, 'context' => $source3->context));
+    $found = $this->storage->findTranslation(array('language' => $langcode,  'source' => $source3->source, 'context' => $source3->context));
     $this->assertTrue($found && $found->lid == $source3->lid && !isset($found->translation) && $found->isNew(), 'Translation not found but source string found.');
 
     // Load all translations. For next queries we'll be loading only translated strings.    $only_translated = array('untranslated' => FALSE);
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php
index f1c44dc..23d201d 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php
@@ -74,7 +74,7 @@ function testStringTranslation() {
     // Add string.
     t($name, array(), array('langcode' => $langcode));
     // Reset locale cache.
-    locale_reset();
+    $this->container->get('locale.translation')->reset();
     $this->assertRaw('"edit-languages-' . $langcode .'-weight"', t('Language code found.'));
     $this->assertText(t($name), t('Test language added.'));
     $this->drupalLogout();
@@ -153,7 +153,7 @@ function testStringTranslation() {
     // Refresh the locale() cache to get fresh data from t() below. We are in
     // the same HTTP request and therefore t() is not refreshed by saving the
     // translation above.
-    locale_reset();
+    $this->container->get('locale.translation')->reset();
     // Now we should get the proper fresh translation from t().
     $this->assertTrue($name != $translation_to_en && t($name, array(), array('langcode' => 'en')) == $translation_to_en, t('t() works for English.'));
     $this->assertTrue(t($name, array(), array('langcode' => LANGUAGE_SYSTEM)) == $name, t('t() works for LANGUAGE_SYSTEM.'));
@@ -369,7 +369,7 @@ function testStringSearch() {
     // Add string.
     t($name, array(), array('langcode' => $langcode));
     // Reset locale cache.
-    locale_reset();
+    $this->container->get('locale.translation')->reset();
     $this->drupalLogout();
 
     // Search for the name.
@@ -484,13 +484,13 @@ function testUICustomizedStrings(){
     language_save($language);
 
     // Create test source string
-    $string = locale_storage()->createString(array(
+    $string = $this->container->get('locale.storage')->createString(array(
       'source' => $this->randomName(100),
       'context' => $this->randomName(20),
     ))->save();
 
     // Create translation for new string and save it as non-customized.
-    $translation = locale_storage()->createTranslation(array(
+    $translation = $this->container->get('locale.storage')->createTranslation(array(
       'lid' => $string->lid,
       'language' => 'de',
       'translation' => $this->randomName(100),
@@ -498,7 +498,7 @@ function testUICustomizedStrings(){
     ))->save();
 
     // Reset locale cache.
-    locale_reset();
+    $this->container->get('locale.translation')->reset();
 
     // Ensure non-customized translation string does appear if searching Non-customized translation.
     $search = array(
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
index ee2b0c3..adf8f32 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
@@ -73,7 +73,7 @@ function testUninstallProcess() {
     $this->drupalLogin($user);
     $this->drupalGet('admin/config/regional/translate/translate');
     // Get any of the javascript strings to translate.
-    $js_strings = locale_storage()->getStrings(array('type' => 'javascript'));
+    $js_strings = $this->container->get('locale.storage')->getStrings(array('type' => 'javascript'));
     $string = reset($js_strings);
     $edit = array('string' => $string->source);
     $this->drupalPost('admin/config/regional/translate', $edit, t('Filter'));
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateTest.php
index e5dccbd..feb11c8 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateTest.php
@@ -226,9 +226,12 @@ private function setCurrentTranslations() {
       'June' => 'Juni',
     );
     foreach ($non_customized_translations as $source => $translation) {
-      $string = locale_storage()->createString(array('source' => $source, 'context' => $context))
+      $string = $this->container->get('locale.storage')->createString(array(
+          'source' => $source,
+          'context' => $context,
+        ))
         ->save();
-      $target = locale_storage()->createTranslation(array(
+      $target = $this->container->get('locale.storage')->createTranslation(array(
         'lid' => $string->getId(),
         'language' => $langcode,
         'translation' => $translation,
@@ -243,9 +246,12 @@ private function setCurrentTranslations() {
       'May' => 'Mai_customized',
     );
     foreach ($customized_translations as $source => $translation) {
-      $string = locale_storage()->createString(array('source' => $source, 'context' => $context))
-        ->save();
-      $target = locale_storage()->createTranslation(array(
+      $string = $this->container->get('locale.storage')->createString(array(
+        'source' => $source,
+        'context' => $context,
+      ))
+      ->save();
+      $target = $this->container->get('locale.storage')->createTranslation(array(
         'lid' => $string->getId(),
         'language' => $langcode,
         'translation' => $translation,
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 880e335..8e1b286 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -313,7 +313,7 @@ function locale_language_update($language) {
  */
 function locale_language_delete($language) {
   // Remove translations.
-  locale_storage()->deleteTranslations(array('language' => $language->langcode));
+  Drupal::service('locale.storage')->deleteTranslations(array('language' => $language->langcode));
 
   // Remove interface translation files.
   module_load_include('inc', 'locale', 'locale.bulk');
@@ -344,79 +344,6 @@ function locale_translatable_language_list() {
 }
 
 /**
- * Provides interface translation services.
- *
- * This function is called from t() to translate a string if needed.
- *
- * @param $string
- *   A string to look up translation for. If omitted, all the
- *   cached strings will be returned in all languages already
- *   used on the page.
- * @param $context
- *   The context of this string.
- * @param $langcode
- *   Language code to use for the lookup.
- */
-function locale($string = NULL, $context = NULL, $langcode = NULL) {
-  $language_interface = language(LANGUAGE_TYPE_INTERFACE);
-
-  // Use the advanced drupal_static() pattern, since this is called very often.
-  static $drupal_static_fast;
-  if (!isset($drupal_static_fast)) {
-    $drupal_static_fast['locale'] = &drupal_static(__FUNCTION__, array('cache' => array(), 'exists' => NULL));
-  }
-  $locale_t = &$drupal_static_fast['locale']['cache'];
-  $locale_exists = &$drupal_static_fast['locale']['exists'];
-
-  // Check whether Locale module is actually installed and operational.
-  // The mere existence of locale() does not imply that Locale module is
-  // actually enabled and its database tables are installed. Since PHP code
-  // cannot be unloaded, this is typically the case in the environment that
-  // is executing a test.
-  if (!isset($locale_exists)) {
-    $locale_exists = function_exists('module_exists') && module_exists('locale');
-  }
-  if (!$locale_exists) {
-    return $string;
-  }
-
-  if (!isset($string)) {
-    // Return all cached strings if no string was specified
-    return $locale_t;
-  }
-
-  $langcode = isset($langcode) ? $langcode : $language_interface->langcode;
-
-  // Strings are cached by langcode, context and roles, using instances of the
-  // LocaleLookup class to handle string lookup and caching.
-  if (!isset($locale_t[$langcode][$context]) && isset($language_interface)) {
-    $locale_t[$langcode][$context] = new LocaleLookup($langcode, $context, locale_storage());
-  }
-  return ($locale_t[$langcode][$context][$string] === TRUE ? $string : $locale_t[$langcode][$context][$string]);
-}
-
-/**
- * Reset static variables used by locale().
- */
-function locale_reset() {
-  drupal_static_reset('locale');
-}
-
-/**
- * Gets the locale storage controller class .
- *
- * @return Drupal\locale\StringStorageInterface
- */
-function locale_storage() {
-  $storage = &drupal_static(__FUNCTION__);
-  if (!isset($storage)) {
-    $options = array('target' => 'default');
-    $storage = new StringDatabaseStorage(Database::getConnection($options['target']), $options);
-  }
-  return $storage;
-}
-
-/**
  * Returns plural form index for a specific number.
  *
  * The index is computed from the formula of this language.
@@ -699,12 +626,12 @@ function locale_library_info_alter(&$libraries, $module) {
 function locale_form_language_admin_overview_form_alter(&$form, &$form_state) {
   $languages = $form['languages']['#languages'];
 
-  $total_strings = locale_storage()->countStrings();
+  $total_strings = Drupal::service('locale.storage')->countStrings();
   $stats = array_fill_keys(array_keys($languages), array());
 
   // If we have source strings, count translations and calculate progress.
   if (!empty($total_strings)) {
-    $translations = locale_storage()->countTranslations();
+    $translations = Drupal::service('locale.storage')->countTranslations();
     foreach ($translations as $langcode => $translated) {
       $stats[$langcode]['translated'] = $translated;
       if ($translated > 0) {
@@ -1042,7 +969,7 @@ function _locale_refresh_translations($langcodes, $lids = array()) {
   if (!empty($langcodes)) {
     // Update javascript translations if any of the strings has a javascript
     // location, or if no string ids were provided, update all languages.
-    if (empty($lids) || ($strings = locale_storage()->getStrings(array('lid' => $lids, 'type' => 'javascript')))) {
+    if (empty($lids) || ($strings = Drupal::service('locale.storage')->getStrings(array('lid' => $lids, 'type' => 'javascript')))) {
       array_map('_locale_invalidate_js', $langcodes);
     }
   }
@@ -1143,11 +1070,11 @@ function _locale_parse_js_file($filepath) {
     $string =  implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($match['string'], 1, -1)));
     $context = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($match['context'], 1, -1)));
 
-    $source = locale_storage()->findString(array('source' => $string, 'context' => $context));
+    $source = Drupal::service('locale.storage')->findString(array('source' => $string, 'context' => $context));
 
     if (!$source) {
       // We don't have the source string yet, thus we insert it into the database.
-      $source = locale_storage()->createString(array(
+      $source = Drupal::service('locale.storage')->createString(array(
         'source'    => $string,
         'context'   => $context,
       ));
@@ -1216,7 +1143,7 @@ function _locale_rebuild_js($langcode = NULL) {
     'translated' => TRUE,
   );
   $translations = array();
-  foreach (locale_storage()->getTranslations($conditions) as $data) {
+  foreach (Drupal::service('locale.storage')->getTranslations($conditions) as $data) {
     $translations[$data->context][$data->source] = $data->translation;
   }
 
diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc
index fc68a51..1c5c33c 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -57,7 +57,7 @@ function locale_translate_filter_load_strings() {
     }
   }
 
-  return locale_storage()->getTranslations($conditions, $options);
+  return Drupal::service('locale.storage')->getTranslations($conditions, $options);
 }
 
 /**
@@ -402,7 +402,7 @@ function locale_translate_edit_form_submit($form, &$form_state) {
   // Preload all translations for strings in the form.
   $lids = array_keys($form_state['values']['strings']);
   $existing_translation_objects = array();
-  foreach (locale_storage()->getTranslations(array('lid' => $lids, 'language' => $langcode, 'translated' => TRUE)) as $existing_translation_object) {
+  foreach (Drupal::service('locale.storage')->getTranslations(array('lid' => $lids, 'language' => $langcode, 'translated' => TRUE)) as $existing_translation_object) {
     $existing_translation_objects[$existing_translation_object->lid] = $existing_translation_object;
   }
 
@@ -431,7 +431,7 @@ function locale_translate_edit_form_submit($form, &$form_state) {
 
     if ($is_changed) {
       // Only update or insert if we have a value to use.
-      $target = isset($existing_translation_objects[$lid]) ? $existing_translation_objects[$lid] : locale_storage()->createTranslation(array('lid' => $lid, 'language' => $langcode));
+      $target = isset($existing_translation_objects[$lid]) ? $existing_translation_objects[$lid] : Drupal::service('locale.storage')->createTranslation(array('lid' => $lid, 'language' => $langcode));
       $target->setPlurals($new_translation['translations'])
         ->setCustomized()
         ->save();
diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml
index 012a7d9..f0627e7 100644
--- a/core/modules/locale/locale.services.yml
+++ b/core/modules/locale/locale.services.yml
@@ -4,3 +4,12 @@ services:
     tags:
       - { name: event_subscriber }
     arguments: ['@language_manager', '@config.context']
+  locale.storage:
+    class: Drupal\locale\StringDatabaseStorage
+    arguments: ['@database']
+  locale.translation.lookup:
+    class: Drupal\locale\LocaleTranslation
+    arguments: ['@locale.storage']
+  locale.translation:
+    class: Drupal\Core\Translation\TranslationManager
+    arguments: ['@language_manager', ['@locale.translation.custom', '@locale.translation.lookup']]
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index a67d091..cd84e7d 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\simpletest;
 
+use Symfony\Component\DependencyInjection\Reference;
 use Drupal\Core\Database\Database;
 use Drupal\Component\Utility\Settings;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
@@ -904,6 +905,13 @@ protected function prepareEnvironment() {
 
     // Reset and create a new service container.
     $this->container = new ContainerBuilder();
+     // @todo Remove this once this class has no calls to t() and format_plural()
+    $this->container->register('language_manager', 'Drupal\Core\Language\LanguageManager');
+    $this->container->register('locale.translation.custom', 'Drupal\Core\Translation\CustomStrings');
+    $this->container->register('locale.translation', 'Drupal\Core\Translation\TranslationManager')
+      ->addArgument(new Reference('language_manager'))
+      ->addArgument(array(new Reference('locale.translation.custom')));
+
     \Drupal::setContainer($this->container);
 
     // Unset globals.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerLanguageTest.php b/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerLanguageTest.php
index 6e0d37b..c4a0113 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerLanguageTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerLanguageTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Tests\Installer;
 
 use Drupal\simpletest\WebTestBase;
+use Drupal\Core\Translation\FileTranslation;
 
 /**
  * Tests installer language detection.
@@ -34,8 +35,6 @@ function setUp() {
    * Tests that the installer can find translation files.
    */
   function testInstallerTranslationFiles() {
-    include_once DRUPAL_ROOT . '/core/includes/install.core.inc';
-
     // Different translation files would be found depending on which language
     // we are looking for.
     $expected_translation_files = array(
@@ -45,8 +44,9 @@ function testInstallerTranslationFiles() {
       'it' => array(),
     );
 
+    $file_translation = new FileTranslation($GLOBALS['conf']['locale.settings']['translation.path']);
     foreach ($expected_translation_files as $langcode => $files_expected) {
-      $files_found = install_find_translation_files($langcode);
+      $files_found = $file_translation->findTranslationFiles($langcode);
       $this->assertTrue(count($files_found) == count($files_expected), format_string('@count installer languages found.', array('@count' => count($files_expected))));
       foreach ($files_found as $file) {
         $this->assertTrue(in_array($file->filename, $files_expected), format_string('@file found.', array('@file' => $file->filename)));
