diff --git a/core/includes/gettext.inc b/core/includes/gettext.inc
new file mode 100644
index 0000000..4e3b221
--- /dev/null
+++ b/core/includes/gettext.inc
@@ -0,0 +1,1131 @@
+<?php
+
+/**
+ * @file
+ * Gettext parsing and generating API.
+ *
+ * @todo Decouple these functions from Locale API and put to gettext_ namespace.
+ */
+
+/**
+ * @defgroup locale-api-import-export Translation import/export API.
+ * @{
+ * Functions to import and export translations.
+ *
+ * These functions provide the ability to import translations from
+ * external files and to export translations and translation templates.
+ */
+
+/**
+ * Parses Gettext Portable Object information and inserts it into the database.
+ *
+ * @param $file
+ *   Drupal file object corresponding to the PO file to import.
+ * @param $langcode
+ *   Language code.
+ * @param $overwrite_options
+ *   An associative array indicating what data should be overwritten, if any.
+ *   - not_customized: strings marked not customized should be overwritten.
+ *   - customized: strings marked customized should be overwritten.
+ * @param $customized
+ *   Whether the strings being imported should be saved as customized.
+ *   Use LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED. All strings in the file
+ *   will be saved with this customization flag.
+ */
+function _locale_import_po($file, $langcode, $overwrite_options, $customized = LOCALE_NOT_CUSTOMIZED) {
+  // Try to allocate enough time to parse and import the data.
+  drupal_set_time_limit(240);
+
+  // Check if we have the language already in the database.
+  if (!language_load($langcode)) {
+    drupal_set_message(t('The language selected for import is not supported.'), 'error');
+    return FALSE;
+  }
+
+  // Get strings from file (returns on failure after a partial import, or on success)
+  $status = _locale_import_read_po('db-store', $file, $overwrite_options, $langcode, $customized);
+  if ($status === FALSE) {
+    // Error messages are set in _locale_import_read_po().
+    return FALSE;
+  }
+
+  // Get status information on import process.
+  list($header_done, $additions, $updates, $deletes, $skips) = _locale_import_one_string('db-report');
+
+  if (!$header_done) {
+    drupal_set_message(t('The translation file %filename appears to have a missing or malformed header.', array('%filename' => $file->filename)), 'error');
+  }
+
+  // Clear cache and force refresh of JavaScript translations.
+  _locale_invalidate_js($langcode);
+  cache()->deletePrefix('locale:');
+
+  // Rebuild the menu, strings may have changed.
+  menu_router_rebuild();
+
+  drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes)));
+  watchdog('locale', 'Imported %file into %locale: %number new strings added, %update updated and %delete removed.', array('%file' => $file->filename, '%locale' => $langcode, '%number' => $additions, '%update' => $updates, '%delete' => $deletes));
+  if ($skips) {
+    if (module_exists('dblog')) {
+      $skip_message = format_plural($skips, 'A translation string was skipped because of disallowed or malformed HTML. <a href="@url">See the log</a> for details.', '@count translation strings were skipped because of disallowed or malformed HTML. <a href="@url">See the log</a> for details.', array('@url' => url('admin/reports/dblog')));
+    }
+    else {
+      $skip_message = format_plural($skips, 'A translation string was skipped because of disallowed or malformed HTML. See the log for details.', '@count translation strings were skipped because of disallowed or malformed HTML. See the log for details.');
+    }
+    drupal_set_message($skip_message, 'error');
+    watchdog('locale', '@count disallowed HTML string(s) in %file', array('@count' => $skips, '%file' => $file->uri), WATCHDOG_WARNING);
+  }
+  return TRUE;
+}
+
+/**
+ * Parses a Gettext Portable Object file into an array.
+ *
+ * @param $op
+ *   Storage operation type: db-store or mem-store.
+ * @param $file
+ *   Drupal file object corresponding to the PO file to import.
+ * @param $overwrite_options
+ *   An associative array indicating what data should be overwritten, if any.
+ *   - not_customized: not customized strings should be overwritten.
+ *   - customized: customized strings should be overwritten.
+ * @param $lang
+ *   Language code.
+ * @param $customized
+ *   Whether the strings being imported should be saved as customized.
+ *   Use LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
+ */
+function _locale_import_read_po($op, $file, $overwrite_options = NULL, $lang = NULL, $customized = LOCALE_NOT_CUSTOMIZED) {
+
+  // The file will get closed by PHP on returning from this function.
+  $fd = fopen($file->uri, 'rb');
+  if (!$fd) {
+    _locale_import_message('The translation import failed because the file %filename could not be read.', $file);
+    return FALSE;
+  }
+
+  /*
+   * The parser context. Can be:
+   *  - 'COMMENT' (#)
+   *  - 'MSGID' (msgid)
+   *  - 'MSGID_PLURAL' (msgid_plural)
+   *  - 'MSGCTXT' (msgctxt)
+   *  - 'MSGSTR' (msgstr or msgstr[])
+   *  - 'MSGSTR_ARR' (msgstr_arg)
+   */
+  $context = 'COMMENT';
+
+  // Current entry being read.
+  $current = array();
+
+  // Current plurality for 'msgstr[]'.
+  $plural = 0;
+
+  // Current line.
+  $lineno = 0;
+
+  while (!feof($fd)) {
+    // A line should not be longer than 10 * 1024.
+    $line = fgets($fd, 10 * 1024);
+
+    if ($lineno == 0) {
+      // The first line might come with a UTF-8 BOM, which should be removed.
+      $line = str_replace("\xEF\xBB\xBF", '', $line);
+    }
+
+    $lineno++;
+
+    // Trim away the linefeed.
+    $line = trim(strtr($line, array("\\\n" => "")));
+
+    if (!strncmp('#', $line, 1)) {
+      // Lines starting with '#' are comments.
+
+      if ($context == 'COMMENT') {
+        // Already in comment token, insert the comment.
+        $current['#'][] = substr($line, 1);
+      }
+      elseif (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
+        // We are currently in string token, close it out.
+        _locale_import_one_string($op, $current, $overwrite_options, $lang, $file, $customized);
+
+        // Start a new entry for the comment.
+        $current         = array();
+        $current['#'][]  = substr($line, 1);
+
+        $context = 'COMMENT';
+      }
+      else {
+        // A comment following any other token is a syntax error.
+        _locale_import_message('The translation file %filename contains an error: "msgstr" was expected but not found on line %line.', $file, $lineno);
+        return FALSE;
+      }
+    }
+    elseif (!strncmp('msgid_plural', $line, 12)) {
+      // A plural form for the current message.
+
+      if ($context != 'MSGID') {
+        // A plural form cannot be added to anything else but the id directly.
+        _locale_import_message('The translation file %filename contains an error: "msgid_plural" was expected but not found on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      // Remove 'msgid_plural' and trim away whitespace.
+      $line = trim(substr($line, 12));
+      // At this point, $line should now contain only the plural form.
+
+      $quoted = _locale_import_parse_quoted($line);
+      if ($quoted === FALSE) {
+        // The plural form must be wrapped in quotes.
+        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      // Append the plural form to the current entry.
+      $current['msgid'] .= LOCALE_PLURAL_DELIMITER . $quoted;
+
+      $context = 'MSGID_PLURAL';
+    }
+    elseif (!strncmp('msgid', $line, 5)) {
+      // Starting a new message.
+
+      if (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
+        // We are currently in a message string, close it out.
+        _locale_import_one_string($op, $current, $overwrite_options, $lang, $file, $customized);
+
+        // Start a new context for the id.
+        $current = array();
+      }
+      elseif ($context == 'MSGID') {
+        // We are currently already in the context, meaning we passed an id with no data.
+        _locale_import_message('The translation file %filename contains an error: "msgid" is unexpected on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      // Remove 'msgid' and trim away whitespace.
+      $line = trim(substr($line, 5));
+      // At this point, $line should now contain only the message id.
+
+      $quoted = _locale_import_parse_quoted($line);
+      if ($quoted === FALSE) {
+        // The message id must be wrapped in quotes.
+        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      $current['msgid'] = $quoted;
+      $context = 'MSGID';
+    }
+    elseif (!strncmp('msgctxt', $line, 7)) {
+      // Starting a new context.
+
+      if (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
+        // We are currently in a message, start a new one.
+        _locale_import_one_string($op, $current, $overwrite_options, $lang, $file, $customized);
+        $current = array();
+      }
+      elseif (!empty($current['msgctxt'])) {
+        // A context cannot apply to another context.
+        _locale_import_message('The translation file %filename contains an error: "msgctxt" is unexpected on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      // Remove 'msgctxt' and trim away whitespaces.
+      $line = trim(substr($line, 7));
+      // At this point, $line should now contain the context.
+
+      $quoted = _locale_import_parse_quoted($line);
+      if ($quoted === FALSE) {
+        // The context string must be quoted.
+        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      $current['msgctxt'] = $quoted;
+
+      $context = 'MSGCTXT';
+    }
+    elseif (!strncmp('msgstr[', $line, 7)) {
+      // A message string for a specific plurality.
+
+      if (($context != 'MSGID') && ($context != 'MSGCTXT') && ($context != 'MSGID_PLURAL') && ($context != 'MSGSTR_ARR')) {
+        // Message strings must come after msgid, msgxtxt, msgid_plural, or other msgstr[] entries.
+        _locale_import_message('The translation file %filename contains an error: "msgstr[]" is unexpected on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      // Ensure the plurality is terminated.
+      if (strpos($line, ']') === FALSE) {
+        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      // Extract the plurality.
+      $frombracket = strstr($line, '[');
+      $plural = substr($frombracket, 1, strpos($frombracket, ']') - 1);
+
+      // Skip to the next whitespace and trim away any further whitespace, bringing $line to the message data.
+      $line = trim(strstr($line, " "));
+
+      $quoted = _locale_import_parse_quoted($line);
+      if ($quoted === FALSE) {
+        // The string must be quoted.
+        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      $current['msgstr'][$plural] = $quoted;
+
+      $context = 'MSGSTR_ARR';
+    }
+    elseif (!strncmp("msgstr", $line, 6)) {
+      // A string for the an id or context.
+
+      if (($context != 'MSGID') && ($context != 'MSGCTXT')) {
+        // Strings are only valid within an id or context scope.
+        _locale_import_message('The translation file %filename contains an error: "msgstr" is unexpected on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      // Remove 'msgstr' and trim away away whitespaces.
+      $line = trim(substr($line, 6));
+      // At this point, $line should now contain the message.
+
+      $quoted = _locale_import_parse_quoted($line);
+      if ($quoted === FALSE) {
+        // The string must be quoted.
+        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      $current['msgstr'] = $quoted;
+
+      $context = 'MSGSTR';
+    }
+    elseif ($line != '') {
+      // Anything that is not a token may be a continuation of a previous token.
+
+      $quoted = _locale_import_parse_quoted($line);
+      if ($quoted === FALSE) {
+        // The string must be quoted.
+        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        return FALSE;
+      }
+
+      // Append the string to the current context.
+      if (($context == 'MSGID') || ($context == 'MSGID_PLURAL')) {
+        $current['msgid'] .= $quoted;
+      }
+      elseif ($context == 'MSGCTXT') {
+        $current['msgctxt'] .= $quoted;
+      }
+      elseif ($context == 'MSGSTR') {
+        $current['msgstr'] .= $quoted;
+      }
+      elseif ($context == 'MSGSTR_ARR') {
+        $current['msgstr'][$plural] .= $quoted;
+      }
+      else {
+        // No valid context to append to.
+        _locale_import_message('The translation file %filename contains an error: there is an unexpected string on line %line.', $file, $lineno);
+        return FALSE;
+      }
+    }
+  }
+
+  // End of PO file, closed out the last entry.
+  if (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
+    _locale_import_one_string($op, $current, $overwrite_options, $lang, $file, $customized);
+  }
+  elseif ($context != 'COMMENT') {
+    _locale_import_message('The translation file %filename ended unexpectedly at line %line.', $file, $lineno);
+    return FALSE;
+  }
+}
+
+/**
+ * Sets an error message if an error occurred during locale file parsing.
+ *
+ * @param $message
+ *   The message to be translated.
+ * @param $file
+ *   Drupal file object corresponding to the PO file to import.
+ * @param $lineno
+ *   An optional line number argument.
+ */
+function _locale_import_message($message, $file, $lineno = NULL) {
+  $vars = array('%filename' => $file->filename);
+  if (isset($lineno)) {
+    $vars['%line'] = $lineno;
+  }
+  $t = get_t();
+  drupal_set_message($t($message, $vars), 'error');
+}
+
+/**
+ * Performs the specified operation for one string.
+ *
+ * @param $op
+ *   Operation to perform: 'db-store', 'db-report', 'mem-store' or 'mem-report'.
+ * @param $value
+ *   Details of the string stored.
+ * @param $overwrite_options
+ *   An associative array indicating what data should be overwritten, if any.
+ *   - not_customized: not customized strings should be overwritten.
+ *   - customized: customized strings should be overwritten.
+ * @param $lang
+ *   Language to store the string in.
+ * @param $file
+ *   Object representation of file being imported, only required when op is
+ *   'db-store'.
+ * @param $customized
+ *   Whether the strings being imported should be saved as customized.
+ *   Use LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
+ */
+function _locale_import_one_string($op, $value = NULL, $overwrite_options = NULL, $lang = NULL, $file = NULL, $customized = LOCALE_NOT_CUSTOMIZED) {
+  $report = &drupal_static(__FUNCTION__, array('additions' => 0, 'updates' => 0, 'deletes' => 0, 'skips' => 0));
+  $header_done = &drupal_static(__FUNCTION__ . ':header_done', FALSE);
+  $strings = &drupal_static(__FUNCTION__ . ':strings', array());
+
+  switch ($op) {
+    // Return stored strings
+    case 'mem-report':
+      return $strings;
+
+    // Store string in memory (only supports single strings)
+    case 'mem-store':
+      $strings[isset($value['msgctxt']) ? $value['msgctxt'] : ''][$value['msgid']] = $value['msgstr'];
+      return;
+
+    // Called at end of import to inform the user
+    case 'db-report':
+      return array($header_done, $report['additions'], $report['updates'], $report['deletes'], $report['skips']);
+
+    // Store the string we got in the database.
+    case 'db-store':
+
+      if ($value['msgid'] == '') {
+        // If 'msgid' is empty, it means we got values for the header of the
+        // file as per the structure of the Gettext format.
+        $locale_plurals = variable_get('locale_translation_plurals', array());
+        if (array_sum($overwrite_options) || empty($locale_plurals[$lang]['plurals'])) {
+          // Since we only need to parse the header if we ought to update the
+          // plural formula, only run this if we don't need to keep existing
+          // data untouched or if we don't have an existing plural formula.
+          $header = _locale_import_parse_header($value['msgstr']);
+
+          // Get and store the plural formula if available.
+          if (isset($header["Plural-Forms"]) && $p = _locale_import_parse_plural_forms($header["Plural-Forms"], $file->uri)) {
+            list($nplurals, $formula) = $p;
+            $locale_plurals[$lang] = array(
+              'plurals' => $nplurals,
+              'formula' => $formula,
+            );
+            variable_set('locale_translation_plurals', $locale_plurals);
+          }
+        }
+        $header_done = TRUE;
+      }
+
+      else {
+        // Found a string to store, clean up and prepare the data.
+        $comments = _locale_import_shorten_comments(empty($value['#']) ? array() : $value['#']);
+
+        if (is_array($value['msgstr'])) {
+          // Sort plural variants by their form index.
+          ksort($value['msgstr']);
+          // Serialize plural variants in one string by LOCALE_PLURAL_DELIMITER.
+          $value['msgstr'] = implode(LOCALE_PLURAL_DELIMITER, $value['msgstr']);
+        }
+
+        _locale_import_one_string_db(
+          $report,
+          $lang,
+          isset($value['msgctxt']) ? $value['msgctxt'] : '',
+          $value['msgid'],
+          $value['msgstr'],
+          $comments,
+          $overwrite_options,
+          $customized
+        );
+      }
+  } // end of db-store operation
+}
+
+/**
+ * Imports one string into the database.
+ *
+ * @param $report
+ *   Report array summarizing the number of changes done in the form:
+ *   array(inserts, updates, deletes).
+ * @param $langcode
+ *   Language code to import string into.
+ * @param $context
+ *   The context of this string.
+ * @param $source
+ *   Source string.
+ * @param $translation
+ *   Translation to language specified in $langcode.
+ * @param $location
+ *   Location value to save with source string.
+ * @param $overwrite_options
+ *   An associative array indicating what data should be overwritten, if any.
+ *   - not_customized: not customized strings should be overwritten.
+ *   - customized: customized strings should be overwritten.
+ * @param $customized
+ *   (optional) Whether the strings being imported should be saved as customized.
+ *   Use LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
+ *
+ * @return
+ *   The string ID of the existing string modified or the new string added.
+ */
+function _locale_import_one_string_db(&$report, $langcode, $context, $source, $translation, $location, $overwrite_options, $customized = LOCALE_NOT_CUSTOMIZED) {
+
+  // Initialize overwrite options if not set.
+  $overwrite_options += array(
+    'not_customized' => FALSE,
+    'customized' => FALSE,
+  );
+
+  // Look up the source string and any existing translation.
+  $string = db_query("SELECT s.lid, t.customized FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.source = :source AND s.context = :context", array(
+    ':source' => $source,
+    ':context' => $context,
+    ':language' => $langcode,
+    ))
+    ->fetchObject();
+
+  if (!empty($translation)) {
+    // Skip this string unless it passes a check for dangerous code.
+    if (!locale_string_is_safe($translation)) {
+      watchdog('locale', 'Import of string "%string" was skipped because of disallowed or malformed HTML.', array('%string' => $translation), WATCHDOG_ERROR);
+      $report['skips']++;
+      return 0;
+    }
+    elseif (isset($string->lid)) {
+      // We have this source string saved already.
+      db_update('locales_source')
+        ->fields(array(
+          'location' => $location,
+        ))
+        ->condition('lid', $string->lid)
+        ->execute();
+
+      if (!isset($string->customized)) {
+        // No translation in this language.
+        db_insert('locales_target')
+          ->fields(array(
+            'lid' => $string->lid,
+            'language' => $langcode,
+            'translation' => $translation,
+            'customized' => $customized,
+          ))
+          ->execute();
+
+        $report['additions']++;
+      }
+      elseif ($overwrite_options[$string->customized ? 'customized' : 'not_customized']) {
+        // Translation exists, only overwrite if instructed.
+        db_update('locales_target')
+          ->fields(array(
+            'translation' => $translation,
+            'customized' => $customized,
+          ))
+          ->condition('language', $langcode)
+          ->condition('lid', $string->lid)
+          ->execute();
+
+        $report['updates']++;
+      }
+      return $string->lid;
+    }
+    else {
+      // No such source string in the database yet.
+      $lid = db_insert('locales_source')
+        ->fields(array(
+          'location' => $location,
+          'source' => $source,
+          'context' => (string) $context,
+        ))
+        ->execute();
+
+      db_insert('locales_target')
+        ->fields(array(
+           'lid' => $lid,
+           'language' => $langcode,
+           'translation' => $translation,
+           'customized' => $customized,
+        ))
+        ->execute();
+
+      $report['additions']++;
+      return $lid;
+    }
+  }
+  elseif (isset($string->lid) && isset($string->customized) && $overwrite_options[$string->customized ? 'customized' : 'not_customized']) {
+    // Empty translation, remove existing if instructed.
+    db_delete('locales_target')
+      ->condition('language', $langcode)
+      ->condition('lid', $string->lid)
+      ->execute();
+
+    $report['deletes']++;
+    return $string->lid;
+  }
+}
+
+/**
+ * Parses a Gettext Portable Object file header.
+ *
+ * @param $header
+ *   A string containing the complete header.
+ *
+ * @return
+ *   An associative array of key-value pairs.
+ */
+function _locale_import_parse_header($header) {
+  $header_parsed = array();
+  $lines = array_map('trim', explode("\n", $header));
+  foreach ($lines as $line) {
+    if ($line) {
+      list($tag, $contents) = explode(":", $line, 2);
+      $header_parsed[trim($tag)] = trim($contents);
+    }
+  }
+  return $header_parsed;
+}
+
+/**
+ * Parses a Plural-Forms entry from a Gettext Portable Object file header.
+ *
+ * @param $pluralforms
+ *   A string containing the Plural-Forms entry.
+ * @param $filepath
+ *   A string containing the filepath.
+ *
+ * @return
+ *   An array containing the number of plurals and a
+ *   formula in PHP for computing the plural form.
+ */
+function _locale_import_parse_plural_forms($pluralforms, $filepath) {
+  // First, delete all whitespace
+  $pluralforms = strtr($pluralforms, array(" " => "", "\t" => ""));
+
+  // Select the parts that define nplurals and plural
+  $nplurals = strstr($pluralforms, "nplurals=");
+  if (strpos($nplurals, ";")) {
+    $nplurals = substr($nplurals, 9, strpos($nplurals, ";") - 9);
+  }
+  else {
+    return FALSE;
+  }
+  $plural = strstr($pluralforms, "plural=");
+  if (strpos($plural, ";")) {
+    $plural = substr($plural, 7, strpos($plural, ";") - 7);
+  }
+  else {
+    return FALSE;
+  }
+
+  // Get PHP version of the plural formula
+  $plural = _locale_import_parse_arithmetic($plural);
+
+  if ($plural !== FALSE) {
+    return array($nplurals, $plural);
+  }
+  else {
+    drupal_set_message(t('The translation file %filepath contains an error: the plural formula could not be parsed.', array('%filepath' => $filepath)), 'error');
+    return FALSE;
+  }
+}
+
+/**
+ * Parses and sanitizes an arithmetic formula into a PHP expression.
+ *
+ * While parsing, we ensure, that the operators have the right
+ * precedence and associativity.
+ *
+ * @param $string
+ *   A string containing the arithmetic formula.
+ *
+ * @return
+ *   The PHP version of the formula.
+ */
+function _locale_import_parse_arithmetic($string) {
+  // Operator precedence table
+  $precedence = array("(" => -1, ")" => -1, "?" => 1, ":" => 1, "||" => 3, "&&" => 4, "==" => 5, "!=" => 5, "<" => 6, ">" => 6, "<=" => 6, ">=" => 6, "+" => 7, "-" => 7, "*" => 8, "/" => 8, "%" => 8);
+  // Right associativity
+  $right_associativity = array("?" => 1, ":" => 1);
+
+  $tokens = _locale_import_tokenize_formula($string);
+
+  // Parse by converting into infix notation then back into postfix
+  // Operator stack - holds math operators and symbols
+  $operator_stack = array();
+  // Element Stack - holds data to be operated on
+  $element_stack = array();
+
+  foreach ($tokens as $token) {
+    $current_token = $token;
+
+    // Numbers and the $n variable are simply pushed into $element_stack
+    if (is_numeric($token)) {
+      $element_stack[] = $current_token;
+    }
+    elseif ($current_token == "n") {
+      $element_stack[] = '$n';
+    }
+    elseif ($current_token == "(") {
+      $operator_stack[] = $current_token;
+    }
+    elseif ($current_token == ")") {
+      $topop = array_pop($operator_stack);
+      while (isset($topop) && ($topop != "(")) {
+        $element_stack[] = $topop;
+        $topop = array_pop($operator_stack);
+      }
+    }
+    elseif (!empty($precedence[$current_token])) {
+      // If it's an operator, then pop from $operator_stack into $element_stack until the
+      // precedence in $operator_stack is less than current, then push into $operator_stack
+      $topop = array_pop($operator_stack);
+      while (isset($topop) && ($precedence[$topop] >= $precedence[$current_token]) && !(($precedence[$topop] == $precedence[$current_token]) && !empty($right_associativity[$topop]) && !empty($right_associativity[$current_token]))) {
+        $element_stack[] = $topop;
+        $topop = array_pop($operator_stack);
+      }
+      if ($topop) {
+        $operator_stack[] = $topop;   // Return element to top
+      }
+      $operator_stack[] = $current_token;      // Parentheses are not needed
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  // Flush operator stack
+  $topop = array_pop($operator_stack);
+  while ($topop != NULL) {
+    $element_stack[] = $topop;
+    $topop = array_pop($operator_stack);
+  }
+
+  // Now extract formula from stack
+  $previous_size = count($element_stack) + 1;
+  while (count($element_stack) < $previous_size) {
+    $previous_size = count($element_stack);
+    for ($i = 2; $i < count($element_stack); $i++) {
+      $op = $element_stack[$i];
+      if (!empty($precedence[$op])) {
+        $f = "";
+        if ($op == ":") {
+          $f = $element_stack[$i - 2] . "):" . $element_stack[$i - 1] . ")";
+        }
+        elseif ($op == "?") {
+          $f = "(" . $element_stack[$i - 2] . "?(" . $element_stack[$i - 1];
+        }
+        else {
+          $f = "(" . $element_stack[$i - 2] . $op . $element_stack[$i - 1] . ")";
+        }
+        array_splice($element_stack, $i - 2, 3, $f);
+        break;
+      }
+    }
+  }
+
+  // If only one element is left, the number of operators is appropriate
+  if (count($element_stack) == 1) {
+    return $element_stack[0];
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Provides backward-compatible formula parsing for token_get_all().
+ *
+ * @param $string
+ *   A string containing the arithmetic formula.
+ *
+ * @return
+ *   The PHP version of the formula.
+ */
+function _locale_import_tokenize_formula($formula) {
+  $formula = str_replace(" ", "", $formula);
+  $tokens = array();
+  for ($i = 0; $i < strlen($formula); $i++) {
+    if (is_numeric($formula[$i])) {
+      $num = $formula[$i];
+      $j = $i + 1;
+      while ($j < strlen($formula) && is_numeric($formula[$j])) {
+        $num .= $formula[$j];
+        $j++;
+      }
+      $i = $j - 1;
+      $tokens[] = $num;
+    }
+    elseif ($pos = strpos(" =<>!&|", $formula[$i])) { // We won't have a space
+      $next = $formula[$i + 1];
+      switch ($pos) {
+        case 1:
+        case 2:
+        case 3:
+        case 4:
+          if ($next == '=') {
+            $tokens[] = $formula[$i] . '=';
+            $i++;
+          }
+          else {
+            $tokens[] = $formula[$i];
+          }
+          break;
+        case 5:
+          if ($next == '&') {
+            $tokens[] = '&&';
+            $i++;
+          }
+          else {
+            $tokens[] = $formula[$i];
+          }
+          break;
+        case 6:
+          if ($next == '|') {
+            $tokens[] = '||';
+            $i++;
+          }
+          else {
+            $tokens[] = $formula[$i];
+          }
+          break;
+      }
+    }
+    else {
+      $tokens[] = $formula[$i];
+    }
+  }
+  return $tokens;
+}
+
+/**
+ * Generates a short, one-string version of the passed comment array.
+ *
+ * @param $comment
+ *   An array of strings containing a comment.
+ *
+ * @return
+ *   Short one-string version of the comment.
+ */
+function _locale_import_shorten_comments($comment) {
+  $comm = '';
+  while (count($comment)) {
+    $test = $comm . substr(array_shift($comment), 1) . ', ';
+    if (strlen($comm) < 130) {
+      $comm = $test;
+    }
+    else {
+      break;
+    }
+  }
+  return trim(substr($comm, 0, -2));
+}
+
+/**
+ * Parses a string in quotes.
+ *
+ * @param $string
+ *   A string specified with enclosing quotes.
+ *
+ * @return
+ *   The string parsed from inside the quotes.
+ */
+function _locale_import_parse_quoted($string) {
+  if (substr($string, 0, 1) != substr($string, -1, 1)) {
+    return FALSE;   // Start and end quotes must be the same
+  }
+  $quote = substr($string, 0, 1);
+  $string = substr($string, 1, -1);
+  if ($quote == '"') {        // Double quotes: strip slashes
+    return stripcslashes($string);
+  }
+  elseif ($quote == "'") {  // Simple quote: return as-is
+    return $string;
+  }
+  else {
+    return FALSE;             // Unrecognized quote
+  }
+}
+
+/**
+ * Generates a structured array of all translated strings for the language.
+ *
+ * @param $language
+ *   Language object to generate the output for, or NULL if generating
+ *   translation template.
+ * @param $options
+ *   (optional) An associative array specifying what to include in the output:
+ *   - customized: include customized strings (if TRUE)
+ *   - uncustomized: include non-customized string (if TRUE)
+ *   - untranslated: include untranslated source strings (if TRUE)
+ *   Ignored if $language is NULL.
+ *
+ * @return
+ *   An array of translated strings that can be used to generate an export.
+ */
+function _locale_export_get_strings($language = NULL, $options = array()) {
+
+  // Assume FALSE for all options if not provided by the API.
+  $options += array(
+    'customized' => FALSE,
+    'not_customized' => FALSE,
+    'not_translated' => FALSE,
+  );
+  if (array_sum($options) == 0) {
+    // If user asked to not include anything in the translation files,
+    // that would not make sense, so just fall back on providing a template.
+    $language = NULL;
+  }
+
+  // Build and execute query to collect source strings and translations.
+  $query = db_select('locales_source', 's');
+  if (!empty($language)) {
+    if ($options['not_translated']) {
+      // Left join to keep untranslated strings in.
+      $query->leftJoin('locales_target', 't', 's.lid = t.lid AND t.language = :language', array(':language' => $language->langcode));
+    }
+    else {
+      // Inner join to filter for only translations.
+      $query->innerJoin('locales_target', 't', 's.lid = t.lid AND t.language = :language', array(':language' => $language->langcode));
+    }
+    if ($options['customized']) {
+      if (!$options['not_customized']) {
+        // Filter for customized strings only.
+        $query->condition('t.customized', LOCALE_CUSTOMIZED);
+      }
+      // Else no filtering needed in this case.
+    }
+    else {
+      if ($options['not_customized']) {
+        // Filter for non-customized strings only.
+        $query->condition('t.customized', LOCALE_NOT_CUSTOMIZED);
+      }
+      else {
+        // Filter for strings without translation.
+        $query->isNull('t.translation');
+      }
+    }
+    $query->fields('t', array('translation'));
+  }
+  else {
+    $query->leftJoin('locales_target', 't', 's.lid = t.lid');
+  }
+  $query->fields('s', array('lid', 'source', 'context', 'location'));
+  $result = $query->execute();
+
+  // Structure results in an array with metainformation on the strings.
+  $strings = array();
+  foreach ($result as $child) {
+    $strings[$child->lid] = array(
+      'comment'     => $child->location,
+      'source'      => $child->source,
+      'context'     => $child->context,
+      'translation' => isset($child->translation) ? $child->translation : '',
+    );
+  }
+  return $strings;
+}
+
+/**
+ * Generates the PO(T) file contents for the given strings.
+ *
+ * @param $language
+ *   Language object to generate the output for, or NULL if generating
+ *   translation template.
+ * @param $strings
+ *   Array of strings to export. See _locale_export_get_strings()
+ *   on how it should be formatted.
+ * @param $header
+ *   The header portion to use for the output file. Defaults
+ *   are provided for PO and POT files.
+ */
+function _locale_export_po_generate($language = NULL, $strings = array(), $header = NULL) {
+  global $user;
+
+  $locale_plurals = variable_get('locale_translation_plurals', array());
+
+  if (!isset($header)) {
+    if (isset($language)) {
+      $header = '# ' . $language->name . ' translation of ' . variable_get('site_name', 'Drupal') . "\n";
+      $header .= '# Generated by ' . $user->name . ' <' . $user->mail . ">\n";
+      $header .= "#\n";
+      $header .= "msgid \"\"\n";
+      $header .= "msgstr \"\"\n";
+      $header .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n";
+      $header .= "\"POT-Creation-Date: " . date("Y-m-d H:iO") . "\\n\"\n";
+      $header .= "\"PO-Revision-Date: " . date("Y-m-d H:iO") . "\\n\"\n";
+      $header .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n";
+      $header .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n";
+      $header .= "\"MIME-Version: 1.0\\n\"\n";
+      $header .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
+      $header .= "\"Content-Transfer-Encoding: 8bit\\n\"\n";
+      if (!empty($locale_plurals[$language->langcode]['formula'])) {
+        $header .= "\"Plural-Forms: nplurals=" . $locale_plurals[$language->langcode]['plurals'] . "; plural=" . strtr($locale_plurals[$language->langcode]['formula'], array('$' => '')) . ";\\n\"\n";
+        // Remember number of plural variants to optimize the export.
+        $nplurals = $locale_plurals[$language->langcode]['plurals'];
+      }
+      else {
+        // Remember we did not have a plural number for the export.
+        $nplurals = 0;
+      }
+    }
+    else {
+      $header = "# LANGUAGE translation of PROJECT\n";
+      $header .= "# Copyright (c) YEAR NAME <EMAIL@ADDRESS>\n";
+      $header .= "#\n";
+      $header .= "msgid \"\"\n";
+      $header .= "msgstr \"\"\n";
+      $header .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n";
+      $header .= "\"POT-Creation-Date: " . date("Y-m-d H:iO") . "\\n\"\n";
+      $header .= "\"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\\n\"\n";
+      $header .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n";
+      $header .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n";
+      $header .= "\"MIME-Version: 1.0\\n\"\n";
+      $header .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
+      $header .= "\"Content-Transfer-Encoding: 8bit\\n\"\n";
+      $header .= "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n";
+    }
+  }
+
+  $output = $header . "\n";
+
+  foreach ($strings as $lid => $string) {
+    if ($string['comment']) {
+      $output .= '#: ' . $string['comment'] . "\n";
+    }
+    if (!empty($string['context'])) {
+      $output .= 'msgctxt ' . _locale_export_string($string['context']);
+    }
+    if (strpos($string['source'], LOCALE_PLURAL_DELIMITER) !== FALSE) {
+      // Export plural string.
+      $export_array = explode(LOCALE_PLURAL_DELIMITER, $string['source']);
+      $output .= 'msgid ' . _locale_export_string($export_array[0]);
+      $output .= 'msgid_plural ' . _locale_export_string($export_array[1]);
+      if (isset($language)) {
+        $export_array = explode(LOCALE_PLURAL_DELIMITER, $string['translation']);
+        for ($i = 0; $i < $nplurals; $i++) {
+          if (isset($export_array[$i])) {
+            $output .= 'msgstr[' . $i . '] ' . _locale_export_string($export_array[$i]);
+          }
+          else {
+            $output .= 'msgstr[' . $i . '] ""' . "\n";
+          }
+        }
+      }
+      else {
+        $output .= 'msgstr[0] ""' . "\n";
+        $output .= 'msgstr[1] ""' . "\n";
+      }
+    }
+    else {
+      $output .= 'msgid ' . _locale_export_string($string['source']);
+      $output .= 'msgstr ' . _locale_export_string($string['translation']);
+    }
+    $output .= "\n";
+  }
+  return $output;
+}
+
+/**
+ * Writes a generated PO or POT file to the output.
+ *
+ * @param $language
+ *   Language object to generate the output for, or NULL if generating
+ *   translation template.
+ * @param $output
+ *   The PO(T) file to output as a string. See _locale_export_generate_po()
+ *   on how it can be generated.
+ */
+function _locale_export_po($language = NULL, $output = NULL) {
+  // Log the export event.
+  if (isset($language)) {
+    $filename = $language->langcode . '.po';
+    watchdog('locale', 'Exported %locale translation file: %filename.', array('%locale' => $language->name, '%filename' => $filename));
+  }
+  else {
+    $filename = 'drupal.pot';
+    watchdog('locale', 'Exported translation file: %filename.', array('%filename' => $filename));
+  }
+  // Download the file for the client.
+  header("Content-Disposition: attachment; filename=$filename");
+  header("Content-Type: text/plain; charset=utf-8");
+  print $output;
+  drupal_exit();
+}
+
+/**
+ * Prints a string on multiple lines.
+ */
+function _locale_export_string($str) {
+  $stri = addcslashes($str, "\0..\37\\\"");
+  $parts = array();
+
+  // Cut text into several lines
+  while ($stri != "") {
+    $i = strpos($stri, "\\n");
+    if ($i === FALSE) {
+      $curstr = $stri;
+      $stri = "";
+    }
+    else {
+      $curstr = substr($stri, 0, $i + 2);
+      $stri = substr($stri, $i + 2);
+    }
+    $curparts = explode("\n", _locale_export_wrap($curstr, 70));
+    $parts = array_merge($parts, $curparts);
+  }
+
+  // Multiline string
+  if (count($parts) > 1) {
+    return "\"\"\n\"" . implode("\"\n\"", $parts) . "\"\n";
+  }
+  // Single line string
+  elseif (count($parts) == 1) {
+    return "\"$parts[0]\"\n";
+  }
+  // No translation
+  else {
+    return "\"\"\n";
+  }
+}
+
+/**
+ * Wraps text for Portable Object (Template) files.
+ */
+function _locale_export_wrap($str, $len) {
+  $words = explode(' ', $str);
+  $return = array();
+
+  $cur = "";
+  $nstr = 1;
+  while (count($words)) {
+    $word = array_shift($words);
+    if ($nstr) {
+      $cur = $word;
+      $nstr = 0;
+    }
+    elseif (strlen("$cur $word") > $len) {
+      $return[] = $cur . " ";
+      $cur = $word;
+    }
+    else {
+      $cur = "$cur $word";
+    }
+  }
+  $return[] = $cur;
+
+  return implode("\n", $return);
+}
+
+/**
+ * @} End of "defgroup locale-api-import-export".
+ */
diff --git a/core/includes/gettext.sketch.inc b/core/includes/gettext.sketch.inc
deleted file mode 100644
index fdaa2c5..0000000
--- a/core/includes/gettext.sketch.inc
+++ /dev/null
@@ -1,195 +0,0 @@
-<?php
-
-/**
- * @file
- * Experimental "sketchy" code for gettext parsing and generating API.
- * For a world without failures, exceptions and certainly without users ;)
- */
-
-use Drupal\Core\Gettext\BatchStateInterface;
-
-use Drupal\Core\Gettext\PoFileWriter;
-use Drupal\Core\Gettext\PoFileReader;
-
-use Drupal\Core\Gettext\PoDatabaseWriter;
-use Drupal\Core\Gettext\PoDatabaseReader;
-
-/**
- * Example code to import a po file into the database.
- *
- * Import a po file into the Database.
- * TODO: xml ?!?
- * The file may be local, remote (via stream wrapper) or even XML.
- * The import has a small memory footprint.
- * The import may be split up to enable batch handling.
- */
-$langcode = 'nl';
-
-$reader = new PoFileReader();
-$reader->setURI('public://source.po.txt');
-$reader->setLangcode($langcode);
-$reader->open();
-
-// Get header
-$header = $reader->getHeader();
-
-$writer = new PoDatabaseWriter();
-$writer->setLangcode($langcode);
-
-// Write 10 items
-$writer->writeItems($reader, 10);
-$state = $reader->getState();
-// Store state in Batch
-$reader->setState($state);
-
-// Write remaining items
-$writer->writeItems($reader);
-
-// Invoke it through batch
-gettext_transfer($reader, $writer);
-
-/**
- * Example code to export translations to a po file.
- *
- * Export gettext data to a PO file.
- * The file may be local, remote (via stream wrapper) or even XML.
- * The import has a small memory footprint.
- * The import may be split up to enable batch handling.
- */
-
-$reader = new PoDatabaseReader;
-$reader->setLangcode($langcode);
-$destination = 'public://destination.po.txt';
-$writer = new PoFileWriter();
-$writer->setURI($destination);
-$writer->setLangcode($langcode);
-$writer->setHeader($reader->getHeader());
-
-
-$writer->writeItems($reader);
-
-/**
- * Transfers gettext data from source to destination.
- */
-function gettext_transfer($reader, $writer) {
-  // Use batch processing if both source and destination support it and the source
-  // is large enough. If not, process in once.
-  if ($reader instanceof BatchStateInterface && $writer instanceof BatchStateInterface) {
-    // Transfer data in batches.
-    // Built and execute batch.
-    $batch = gettext_transfer_batch_setup($reader, $writer);
-    batch_set($batch);
-  }
-  else {
-    $writer->writeItems($reader);
-  }
-}
-
-/**
- * Set up a batch process to transfer Gettext data.
- */
-function gettext_transfer_batch_setup($reader, $writer) {
-  $batch = array(
-    'operations' => array(
-      // TODO: can this be objects?
-      // Shouldn't we use $reader->getState() / $writer->getState();
-      array('gettext_transfer_batch_op', array($reader, $writer)),
-    ),
-    'finished' => 'gettext_transfer_batch_finished',
-  );
-  return $batch;
-}
-
-/**
- * Batch operation transferring gettext data.
- *
- * @todo Add source filter conditions (context, language(?)) as parameter.
- */
-function gettext_transfer_batch_op($source, $destination) {
-  // Initialize sandbox for batch reading.
-  if (empty($context['sandbox'])) {
-    $context['sandbox']['transfer'] = array(
-      'chunk' => min($source->chunkSize(), $destination->chunkSize()),
-    );
-  }
-
-  // Execute one transfer cycle, which will process bite size 'chunks'.
-  $success = gettext_transfer_execute($source, $destination, $context['sandbox']['transfer']);
-
-  // After a number of cycles executing gettext_transfer_execute() the transfer
-  // is completed.
-  // See gettextapi_import_batch_op() for details.
-  if ($context['sandbox']['transfer']['finished']) {
-    $context['finished'] = 1;
-
-    if ($success) {
-      // Collect statistics from $context['sandbox']['transfer']
-      // and tell the world about what great work we did.
-    }
-  }
-}
-
-/**
- * Batch wrap-up for gettext data transfer.
- */
-function gettext_transfer_batch_finished($success, $results, $operations) {
-  if ($success) {
-    // Call post processing handler defined by the destination object.
-    // Can we get the callback and arguments via $results?
-    $post_process_callback = $results['post_process_callback'];
-    $arguments = $results['post_process_arguments'];
-    $post_process_callback($arguments);
-
-    // We did it, Magoo! :)
-  }
-  else {
-    // Sadly reporting where it went wrong.
-  }
-}
-
-/**
- * Post processing for gettext language import into the database.
- *
- * To be called after successfull (batch) import.
- */
-function gettext_post_process_import($langcode) {
-  // After succesfull import we refresh all affected parts of the system.
-  _locale_invalidate_js($langcode);
-  cache_clear_all('locale:', 'cache', TRUE);
-  menu_rebuild();
-}
-
-/**
- * Transfer gettext data from source to destination in bite size chunks.
- */
-function gettext_transfer_execute($source, $destination, &$transfer) {
-  // If transfer is not yet started we set the header data.
-  // The header (if supported) is written before the first translation is written.
-  if (!$destination->inProgress()) {
-    $destination->setHeader($source->getHeader());
-  }
-
-  //TODO: this is not yet rewriten to PoFile|Database|Reader|Writer
-  $chunk = $transfer['chunk'];
-  // Transfer translations as long as valid data is available and
-  // the bite size chunk is not yet swallowed.
-  while ($source->valid() && $chunk > 0) {
-    // Get one translation from source, write it to destination.
-    $translation = $source->read();
-    $destination->write($translation);
-    $chunk--;
-  }
-
-  // Report the percentage of completion for progress reporting.
-  // Ai, user interface. Why do we bother ;)
-  $transfer['state']['percentage_of_completion'] = $source->poc();
-
-  // Close connections when we are done and report the results.
-  if ($source->finished()) {
-    $destination->finish();
-    $transfer['finished'] = TRUE;
-  }
-
-  // Report statistics including errors and error log.
-  $transfer['result'] = $destination->statistics();
-}
diff --git a/core/includes/install.inc b/core/includes/install.inc
index fbd279e..96e84b3 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -2,7 +2,6 @@
 
 use Drupal\Core\Database\Database;
 use Drupal\Core\Config\FileStorage;
-use Drupal\Core\Gettext\Gettext;
 
 /**
  * Indicates that a module has not been installed yet.
@@ -718,7 +717,13 @@ function st($string, array $args = array(), array $options = array()) {
       // that multiple files end with the same extension, even if unlikely.
       $files = install_find_translation_files($install_state['parameters']['langcode']);
       if (!empty($files)) {
-        $strings = Gettext::filesToArray($install_state['parameters']['langcode'], $files);
+        // Include cross-dependent code from locale module and gettext.inc.
+        require_once DRUPAL_ROOT . '/core/modules/locale/locale.module';
+        require_once DRUPAL_ROOT . '/core/includes/gettext.inc';
+        foreach ($files as $file) {
+          _locale_import_read_po('mem-store', $file);
+        }
+        $strings = _locale_import_one_string('mem-report');
       }
     }
   }
diff --git a/core/lib/Drupal/Component/Gettext/BatchStateInterface.php b/core/lib/Drupal/Component/Gettext/BatchStateInterface.php
deleted file mode 100644
index 081ac24..0000000
--- a/core/lib/Drupal/Component/Gettext/BatchStateInterface.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-namespace Drupal\Component\Gettext;
-
-/**
- * Add state to an object to manage continue after a previous batch call.
- *
- * The class implementing this must make sure to pass all state.
- * It's constructor must be empty as the batch interface needs to recreate
- * it without constructor arguments.
- *
- * @TODO: https://drupal.org/node/1637348
- *
- * Example:
- * <code>
- * class MyReader implements BatchStateInterface {
- *   function __construct(){
- *     // empty
- *   }
- *   ...
- *   function getState() {
- *     return array(
- *       '__CLASS__' => __CLASS__,
- *       'my_key' => 'my value',
- *     );
- *   }
- * }
- * </code>
- */
-interface BatchStateInterface {
-
-  /**
-   * Returns the current state used for resetting state later on.
-   *
-   * The state is used to reconstruct the state of the object by calling
-   * setState().
-   *
-   * The Class implemeting this interface must have an empty constructor.
-   *
-   * @return array
-   *   key/value pairs of which one must be __CLASS__
-   */
-  function getState();
-
-  /**
-   * Sets the object ready to roll.
-   *
-   * After calling setState it is assumed the object is ready to do it's work.
-   */
-  function setState(array $state);
-}
diff --git a/core/lib/Drupal/Component/Gettext/PoFileReader.php b/core/lib/Drupal/Component/Gettext/PoFileReader.php
deleted file mode 100644
index a6d972f..0000000
--- a/core/lib/Drupal/Component/Gettext/PoFileReader.php
+++ /dev/null
@@ -1,556 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Component\Gettext\PoFileReader.
- *
- * TODOs
- * - constructor needs a state
- */
-
-namespace Drupal\Component\Gettext;
-
-use Drupal\Component\Gettext\BatchStateInterface;
-use Drupal\Component\Gettext\POReader;
-use Drupal\Component\Gettext\PoHeader;
-
-/**
- * Defines a Gettext file reader for PO format.
- *
- * According to http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
- * a PO file may contain the following
- *
- * white-space
- * #  translator-comments
- * #. extracted-comments
- * #: reference...
- * #, flag...
- * #| msgid previous-untranslated-string
- * msgid untranslated-string
- * msgstr translated-string
- *
- * TODOs:
- * Current implementation has a simple white-space fall-thru. This should
- * be improved
- *
- * Current implementation misses the special comments
- * #.
- * #:
- * #|
- */
-class PoFileReader implements BatchStateInterface, PoStreamInterface, PoReaderInterface {
-
-  /**
-   * Source line number being parsed.
-   *
-   * @var int
-   */
-  private $lineno = 0;
-
-  /**
-   * The context of the translation being parsed.
-   *
-   * @var string
-   */
-  private $context = 'COMMENT';
-
-  /**
-   * Current entry being read.
-   *
-   * @var array
-   */
-  private $current = array();
-
-  /**
-   * URI of the PO file that is being read.
-   *
-   * @var string
-   */
-  private $_uri = '';
-
-  /**
-   * Language code.
-   *
-   * @var string
-   */
-  private $_langcode = NULL;
-  private $_size;
-  private $_fd;
-
-  /**
-   * The PO file header.
-   *
-   * @var POHeader
-   */
-  private $_header;
-
-  private $translation;
-  private $finished;
-
-  /**
-   * @see BatchStateInterface
-   */
-  public function __construct() {
-    // empty
-  }
-
-  public function getURI() {
-    return $this->_uri;
-  }
-
-  public function setURI($uri) {
-    $this->_uri = $uri;
-  }
-
-  public function getLangcode() {
-    return $this->_langcode;
-  }
-
-  public function setLangcode($langcode) {
-    $this->_langcode = $langcode;
-  }
-
-
-  public function open() {
-    if (!empty($this->_uri)) {
-      $this->_fd = fopen($this->_uri, 'rb');
-      $this->_size = ftell($this->_fd);
-      // We immediately read the header as we are at BOF
-      $this->readHeader();
-    }
-    else {
-      throw new \Exception("Cannot open without URI set");
-    }
-  }
-
-  public function close() {
-    if ($this->_fd) {
-      fclose($this->_fd);
-    }
-  }
-
-
-  public function setState(array $state) {
-    $this->setURI($state['uri']);
-    $this->setLangcode($state['langcode']);
-    // Make sure to (re)read the PoHeader
-    $this->open();
-    // Move to last read position.
-    if (isset($state['seekpos'])) {
-      fseek($this->_fd, $state['seekpos']);
-    }
-    if (isset($state['lineno'])) {
-      $this->lineno = $state['lineno'];
-    }
-  }
-
-  public function getState() {
-    return array(
-      'class' => __CLASS__,
-      'uri' => $this->_uri,
-      'langcode' => $this->_langcode,
-      'seekpos' => ftell($this->_fd),
-      'lineno' => $this->lineno,
-    );
-  }
-
-  /**
-   * Return a translation object (singular or plural)
-   *
-   * @todo Define a translation object for this purpose?
-   *       Or use a standard class for better performance?
-   */
-  public function readItem() {
-    $this->readTranslation();
-    return $this->translation;
-  }
-
-  private function readTranslation() {
-    $this->translation = NULL;
-    while (!$this->finished && is_null($this->translation)) {
-      $this->readLine();
-    }
-    return $this->translation;
-  }
-
-  public function getHeader() {
-    return $this->_header;
-  }
-
-  public function setHeader(PoHeader $header) {
-    // TODO : throw exception?
-  }
-
-  /**
-   * Reads the header from the given input stream.
-   *
-   * We need to read the optional first COMMENT
-   * Next read a MSGID and a MSGSTR
-   */
-  private function readHeader() {
-    $translation = $this->readTranslation();
-    $header = new PoHeader;
-    $header->setFromString(trim($translation->translation));
-    $this->_header = $header;
-  }
-
-  /**
-   * Reads a line from a PO file.
-   *
-   * While reading a line it's content is processed according to current
-   * context.
-   *
-   * The parser context. Can be:
-   *  - 'COMMENT' (#)
-   *  - 'MSGID' (msgid)
-   *  - 'MSGID_PLURAL' (msgid_plural)
-   *  - 'MSGCTXT' (msgctxt)
-   *  - 'MSGSTR' (msgstr or msgstr[])
-   *  - 'MSGSTR_ARR' (msgstr_arg)
-   *
-   * @return boolean FALSE or NULL
-   */
-  private function readLine() {
-    // a string or boolean FALSE
-    $line = fgets($this->_fd);
-    $this->finished = ($line === FALSE);
-    if (!$this->finished) {
-
-      if ($this->lineno == 0) {
-        // The first line might come with a UTF-8 BOM, which should be removed.
-        $line = str_replace("\xEF\xBB\xBF", '', $line);
-        // Current plurality for 'msgstr[]'.
-        $this->plural = 0;
-      }
-
-      $this->lineno++;
-
-      // Trim away the linefeed.
-      $line = trim(strtr($line, array("\\\n" => "")));
-
-      if (!strncmp('#', $line, 1)) {
-        // Lines starting with '#' are comments.
-
-        if ($this->context == 'COMMENT') {
-          // Already in comment token, insert the comment.
-          $this->current['#'][] = substr($line, 1);
-        }
-        elseif (($this->context == 'MSGSTR') || ($this->context == 'MSGSTR_ARR')) {
-          // We are currently in string token, close it out.
-          $this->saveOneString();
-
-          // Start a new entry for the comment.
-          $this->current = array();
-          $this->current['#'][] = substr($line, 1);
-
-          $this->context = 'COMMENT';
-          return TRUE;
-        }
-        else {
-          // A comment following any other token is a syntax error.
-          $this->log('The translation file %filename contains an error: "msgstr" was expected but not found on line %line.', $this->lineno);
-          return FALSE;
-        }
-        return;
-      }
-      elseif (!strncmp('msgid_plural', $line, 12)) {
-        // A plural form for the current message.
-
-        if ($this->context != 'MSGID') {
-          // A plural form cannot be added to anything else but the id directly.
-          $this->log('The translation file %filename contains an error: "msgid_plural" was expected but not found on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        // Remove 'msgid_plural' and trim away whitespace.
-        $line = trim(substr($line, 12));
-        // At this point, $line should now contain only the plural form.
-
-        $quoted = $this->parseQuoted($line);
-        if ($quoted === FALSE) {
-          // The plural form must be wrapped in quotes.
-          $this->log('The translation file %filename contains a syntax error on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        // Append the plural form to the current entry.
-        if (is_string($this->current['msgid'])) {
-          // The first value was stored as string. Now we know the context is
-          // plural, it is converted to array.
-          $this->current['msgid'] = array($this->current['msgid']);
-        }
-        $this->current['msgid'][] = $quoted;
-
-        $this->context = 'MSGID_PLURAL';
-        return;
-      }
-      elseif (!strncmp('msgid', $line, 5)) {
-        // Starting a new message.
-
-        if (($this->context == 'MSGSTR') || ($this->context == 'MSGSTR_ARR')) {
-          // We are currently in a message string, close it out.
-          $this->saveOneString();
-
-          // Start a new context for the id.
-          $this->current = array();
-        }
-        elseif ($this->context == 'MSGID') {
-          // We are currently already in the context, meaning we passed an id with no data.
-          $this->log('The translation file %filename contains an error: "msgid" is unexpected on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        // Remove 'msgid' and trim away whitespace.
-        $line = trim(substr($line, 5));
-        // At this point, $line should now contain only the message id.
-
-        $quoted = $this->parseQuoted($line);
-        if ($quoted === FALSE) {
-          // The message id must be wrapped in quotes.
-          $this->log('The translation file %filename contains a syntax error on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        $this->current['msgid'] = $quoted;
-        $this->context = 'MSGID';
-        return;
-      }
-      elseif (!strncmp('msgctxt', $line, 7)) {
-        // Starting a new context.
-
-        if (($this->context == 'MSGSTR') || ($this->context == 'MSGSTR_ARR')) {
-          // We are currently in a message, start a new one.
-          $this->saveOneString($this->current);
-          $this->current = array();
-        }
-        elseif (!empty($this->current['msgctxt'])) {
-          // A context cannot apply to another context.
-          $this->log('The translation file %filename contains an error: "msgctxt" is unexpected on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        // Remove 'msgctxt' and trim away whitespaces.
-        $line = trim(substr($line, 7));
-        // At this point, $line should now contain the context.
-
-        $quoted = $this->parseQuoted($line);
-        if ($quoted === FALSE) {
-          // The context string must be quoted.
-          $this->log('The translation file %filename contains a syntax error on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        $this->current['msgctxt'] = $quoted;
-
-        $this->context = 'MSGCTXT';
-        return;
-      }
-      elseif (!strncmp('msgstr[', $line, 7)) {
-        // A message string for a specific plurality.
-
-        if (($this->context != 'MSGID') && ($this->context != 'MSGCTXT') && ($this->context != 'MSGID_PLURAL') && ($this->context != 'MSGSTR_ARR')) {
-          // Message strings must come after msgid, msgxtxt, msgid_plural, or other msgstr[] entries.
-          $this->log('The translation file %filename contains an error: "msgstr[]" is unexpected on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        // Ensure the plurality is terminated.
-        if (strpos($line, ']') === FALSE) {
-          $this->log('The translation file %filename contains a syntax error on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        // Extract the plurality.
-        $frombracket = strstr($line, '[');
-        $this->plural = substr($frombracket, 1, strpos($frombracket, ']') - 1);
-
-        // Skip to the next whitespace and trim away any further whitespace, bringing $line to the message data.
-        $line = trim(strstr($line, " "));
-
-        $quoted = $this->parseQuoted($line);
-        if ($quoted === FALSE) {
-          // The string must be quoted.
-          $this->log('The translation file %filename contains a syntax error on line %line.', $this->lineno);
-          return FALSE;
-        }
-        if (!isset($this->current['msgstr']) || !is_array($this->current['msgstr'])) {
-          $this->current['msgstr'] = array();
-        }
-
-        $this->current['msgstr'][$this->plural] = $quoted;
-
-        $this->context = 'MSGSTR_ARR';
-        return;
-      }
-      elseif (!strncmp("msgstr", $line, 6)) {
-        // A string for the an id or context.
-
-        if (($this->context != 'MSGID') && ($this->context != 'MSGCTXT')) {
-          // Strings are only valid within an id or context scope.
-          $this->log('The translation file %filename contains an error: "msgstr" is unexpected on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        // Remove 'msgstr' and trim away away whitespaces.
-        $line = trim(substr($line, 6));
-        // At this point, $line should now contain the message.
-
-        $quoted = $this->parseQuoted($line);
-        if ($quoted === FALSE) {
-          // The string must be quoted.
-          $this->log('The translation file %filename contains a syntax error on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        $this->current['msgstr'] = $quoted;
-
-        $this->context = 'MSGSTR';
-        return;
-      }
-      elseif ($line != '') {
-        // Anything that is not a token may be a continuation of a previous token.
-
-        $quoted = $this->parseQuoted($line);
-        if ($quoted === FALSE) {
-          // The string must be quoted.
-          $this->log('The translation file %filename contains a syntax error on line %line.', $this->lineno);
-          return FALSE;
-        }
-
-        // Append the string to the current context.
-        if (($this->context == 'MSGID') || ($this->context == 'MSGID_PLURAL')) {
-          if (is_array($this->current['msgid'])) {
-            // Add string to last array element.
-            $last_index = count($this->current['msgid']) - 1;
-            $this->current['msgid'][$last_index] .= $quoted;
-          }
-          else {
-            $this->current['msgid'] .= $quoted;
-          }
-        }
-        elseif ($this->context == 'MSGCTXT') {
-          $this->current['msgctxt'] .= $quoted;
-        }
-        elseif ($this->context == 'MSGSTR') {
-          $this->current['msgstr'] .= $quoted;
-        }
-        elseif ($this->context == 'MSGSTR_ARR') {
-          $this->current['msgstr'][$this->plural] .= $quoted;
-        }
-        else {
-          // No valid context to append to.
-          $this->log('The translation file %filename contains an error: there is an unexpected string on line %line.', $this->lineno);
-          return FALSE;
-        }
-        return;
-      }
-    }
-
-    // Empty line read or EOF of PO file, closed out the last entry.
-    if (($this->context == 'MSGSTR') || ($this->context == 'MSGSTR_ARR')) {
-      $this->saveOneString($this->current);
-      $this->current = array();
-    }
-    elseif ($this->context != 'COMMENT') {
-      $this->log('The translation file %filename ended unexpectedly at line %line.', $this->lineno);
-      return FALSE;
-    }
-  }
-
-  /**
-   * Sets an error message if an error occurred during locale file parsing.
-   *
-   * @param $message
-   *   The message to be translated.
-   * @param $lineno
-   *   An optional line number argument.
-   */
-  protected function log($message, $lineno = NULL) {
-    if (isset($lineno)) {
-      $vars['%line'] = $lineno;
-    }
-    $t = get_t();
-    $this->errorLog[] = $t($message, $vars);
-  }
-
-  /**
-   * Store the parsed values as translation object.
-   */
-  public function saveOneString() {
-    $value = $this->current;
-    $plural = FALSE;
-
-    $comments = '';
-    if (isset($value['#'])) {
-      $comments = $this->shortenComments($value['#']);
-    }
-
-    if (is_array($value['msgstr'])) {
-      // Sort plural variants by their form index.
-      ksort($value['msgstr']);
-      $plural = TRUE;
-    }
-
-    $translation = new PoItem;
-    $translation->context = isset($value['msgctxt']) ? $value['msgctxt'] : '';
-    $translation->source = $value['msgid'];
-    $translation->translation = $value['msgstr'];
-    $translation->plural = $plural;
-    $translation->comment = $comments;
-    $translation->langcode = $this->getLangcode();
-
-    $this->translation = $translation;
-
-    $this->context = 'COMMENT';
-  }
-
-  /**
-   * Parses a string in quotes.
-   *
-   * @param $string
-   *   A string specified with enclosing quotes.
-   *
-   * @return
-   *   The string parsed from inside the quotes.
-   */
-  function parseQuoted($string) {
-    if (substr($string, 0, 1) != substr($string, -1, 1)) {
-      return FALSE;   // Start and end quotes must be the same
-    }
-    $quote = substr($string, 0, 1);
-    $string = substr($string, 1, -1);
-    if ($quote == '"') {        // Double quotes: strip slashes
-      return stripcslashes($string);
-    }
-    elseif ($quote == "'") {  // Simple quote: return as-is
-      return $string;
-    }
-    else {
-      return FALSE;             // Unrecognized quote
-    }
-  }
-
-  /**
-   * Generates a short, one-string version of the passed comment array.
-   *
-   * @param $comment
-   *   An array of strings containing a comment.
-   *
-   * @return
-   *   Short one-string version of the comment.
-   */
-  private function shortenComments($comment) {
-    $comm = '';
-    while (count($comment)) {
-      $test = $comm . substr(array_shift($comment), 1) . ', ';
-      if (strlen($comm) < 130) {
-        $comm = $test;
-      }
-      else {
-        break;
-      }
-    }
-    return trim(substr($comm, 0, -2));
-  }
-
-}
diff --git a/core/lib/Drupal/Component/Gettext/PoFileWriter.php b/core/lib/Drupal/Component/Gettext/PoFileWriter.php
deleted file mode 100644
index 182fcb8..0000000
--- a/core/lib/Drupal/Component/Gettext/PoFileWriter.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Component\Gettext\PoFileWriter.
- */
-
-namespace Drupal\Component\Gettext;
-
-use Drupal\Component\Gettext\PoHeader;
-use Drupal\Component\Gettext\BatchStateInterface;
-
-/**
- * Defines a Gettext writer.
- */
-class PoFileWriter implements PoStreamInterface, PoWriterInterface, BatchStateInterface {
-
-  private $_uri;
-  private $_header;
-  private $_fd;
-  private $_seekpos;
-  private $_open = FALSE;
-
-  /**
-   * @see BatchStateInterface
-   */
-  function __construct() {
-    // empty
-  }
-
-  public function getHeader() {
-    return $this->_header;
-  }
-
-  public function setHeader(PoHeader $header) {
-    $this->_header = $header;
-  }
-
-  public function getLangcode() {
-    return $this->_langcode;
-  }
-
-  public function setLangcode($langcode) {
-    $this->_langcode = $langcode;
-  }
-
-  public function open() {
-    // Open in append mode
-    $this->_fd = fopen($this->getURI(), 'a');
-    $this->_seekpos = ftell($this->_fd);
-    if ($this->_seekpos == 0) {
-      // If file is new position == 0
-      $this->writeHeader();
-    }
-    else {
-      $reader = new PoFileReader($this->uri);
-      $this->_header = $reader->getHeader();
-    }
-  }
-
-  public function close() {
-    fclose($this->_fd);
-  }
-
-  public function setState(array $state) {
-    $this->_uri = $state['uri'];
-    $this->open();
-  }
-
-  public function getState() {
-    return array(
-      'uri' => $this->_uri,
-      'seekpos' => ftell($this->_fd),
-    );
-  }
-
-  private function write($data) {
-    $result = fputs($this->_fd, $data);
-    if ($result === FALSE) {
-      // TODO: better context for message
-      throw new \Exception("Unable to write data : " . substr($data, 0, 20));
-    }
-    $this->_seekpos = ftell($this->_fd);
-  }
-
-  private function writeHeader() {
-    $this->write($this->_header);
-  }
-
-  public function writeItem(PoItem $item) {
-    $this->write($item);
-  }
-
-  public function writeItems(PoReaderInterface $reader, $count = -1) {
-    $forever = $count == -1;
-    while (($count-- > 0 || $forever) && ($item = $reader->readItem())) {
-      $this->writeItem($item);
-    }
-  }
-
-  public function getURI() {
-    if (empty($this->_uri)) {
-      throw new \Exception("Empty URI");
-    }
-    return $this->_uri;
-  }
-
-  public function setURI($uri) {
-    $this->_uri = $uri;
-  }
-
-}
diff --git a/core/lib/Drupal/Component/Gettext/PoHeader.php b/core/lib/Drupal/Component/Gettext/PoHeader.php
deleted file mode 100644
index 724f108..0000000
--- a/core/lib/Drupal/Component/Gettext/PoHeader.php
+++ /dev/null
@@ -1,405 +0,0 @@
-<?php
-
-namespace Drupal\Component\Gettext;
-
-/**
- * Description of PoHeader
- *
- * http://www.gnu.org/software/gettext/manual/gettext.html#Header-Entry
- *
- * Project-Id-Version
- * Report-Msgid-Bugs-To
- * Project-Id-Version
- * POT-Creation-Date
- * PO-Revision-Date
- * Last-Translator
- * Language-Team
- * Language
- * Content-Type
- * Content-Transfer-Encoding
- * Plural-Forms
- *
- * TODOs
- * - There is no MIME-version in the specs
- *
- *  Example header
- *
- * "Project-Id-Version: Drupal core (7.11)\n"
- * "POT-Creation-Date: 2012-02-12 22:59+0000\n"
- * "PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
- * "Language-Team: Catalan\n"
- * "MIME-Version: 1.0\n"
- * "Content-Type: text/plain; charset=utf-8\n"
- * "Content-Transfer-Encoding: 8bit\n"
- * "Plural-Forms: nplurals=2; plural=(n>1);\n"
-
- * @author clemens
- */
-class PoHeader {
-
-  private $_langcode;
-  private $_projectIdVersion;
-  private $_potCreationDate;
-  private $_poRevisionDate;
-  private $_languageTeam;
-  private $_mimeVersion;
-  private $_contentType;
-  private $_contentTransferEncoding;
-  private $_pluralForms;
-  private $_authors;
-  private $_po_date;
-  private $_languageName;
-  private $_projectName;
-
-  /**
-   * Creates a PoHeader with default values set.
-   *
-   * @param type $langcode
-   */
-  public function __construct($langcode = NULL) {
-    $this->_langcode = $langcode;
-    $this->setDefaults();
-  }
-
-  static public function mapping() {
-    return array(
-      'Project-Id-Version' => '_projectIdVersion',
-      // * Report-Msgid-Bugs-To
-      'POT-Creation-Date' => '_potCreationDate',
-      'PO-Revision-Date' => '_poRevisionDate',
-      // * Last-Translator
-      'Language-Team' => '_languageTeam',
-      'MIME-Version' => '_mimeVersion',
-      // * Language
-      'Content-Type' => '_contentType',
-      'Content-Transfer-Encoding' => '_contentTransferEncoding',
-      'Plural-Forms' => '_pluralForms',
-    );
-  }
-
-  function getPlural() {
-    return $this->_pluralForms;
-  }
-
-  function setLanguageName($languageName) {
-    $this->_languageName = $languageName;
-  }
-
-  function getLanguageName() {
-    return $this->_languageName;
-  }
-
-  function setProjectName($projectName) {
-    $this->_projectName = $projectName;
-  }
-
-  function getProjectName() {
-    return $this->_projectName;
-  }
-
-  /**
-   * Compile the PO header.
-   */
-  private function compileHeader() {
-    $output = '';
-
-    $isTemplate = $this->_languageName == '';
-
-    $output .= '# ' . ($isTemplate ? 'LANGUAGE' : $this->_languageName) . ' translation of ' . ($isTemplate ? 'PROJECT' : $this->_projectName) . "\n";
-    if (!empty($this->_authors)) {
-      $output .= '# Generated by ' . implode("\n# ", $this->_authors) . "\n";
-    }
-    $output .= "#\n";
-
-    // Add the actual header information.
-    $output .= "msgid \"\"\n";
-    $output .= "msgstr \"\"\n";
-    $output .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n";
-    $output .= "\"POT-Creation-Date: " . $this->_po_date . "\\n\"\n";
-    $output .= "\"PO-Revision-Date: " . $this->_po_date . "\\n\"\n";
-    $output .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n";
-    $output .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n";
-    $output .= "\"MIME-Version: 1.0\\n\"\n";
-    $output .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
-    $output .= "\"Content-Transfer-Encoding: 8bit\\n\"\n";
-    $output .= "\"Plural-Forms: " . $this->_pluralForms . "\\n\"\n";
-    $output .= "\n";
-
-    return $output;
-  }
-
-  /**
-   * Stores a given PO Header string
-   *
-   * TODO: the header string is cleaned by the parser :(
-   *   we need to accept unclean version too
-   *
-   * @param type $header
-   */
-  public function setFromString($header) {
-    $values = $this->parseHeader($header);
-
-    $this->setDefaults($values);
-  }
-
-  /**
-   * TODO: compare with Symfony::setDefaults()
-   *
-   * @param type $values
-   */
-  public function setDefaults($values = array()) {
-    $defaults = array(
-      'POT-Creation-Date' => date("Y-m-d H:iO"),
-      'Plural-Forms' => 'nplurals=2; plural=(n > 1);',
-    );
-    foreach ($defaults as $key => $value) {
-      if (empty($values[$key])) {
-        $values[$key] = $value;
-      }
-    }
-    $mapping = self::mapping();
-    foreach ($mapping as $key => $var) {
-      if (isset($values[$key])) {
-        $this->{$var} = $values[$key];
-      }
-    }
-  }
-
-  public function __toString() {
-    $result = $this->compileHeader() . "\n";
-    return $result;
-  }
-
-  /**
-   * Parses a Plural-Forms entry from a Gettext Portable Object file header.
-   *
-   * @param $pluralforms
-   *   A string containing the Plural-Forms entry.
-   * @param $filepath
-   *   A string containing the filepath.
-   *
-   * @return
-   *   An array containing the number of plurals and a
-   *   formula in PHP for computing the plural form.
-   */
-  function parsePluralForms($pluralforms, $filepath) {
-    // First, delete all whitespace
-    $pluralforms = strtr($pluralforms, array(" " => "", "\t" => ""));
-
-    // Select the parts that define nplurals and plural
-    $nplurals = strstr($pluralforms, "nplurals=");
-    if (strpos($nplurals, ";")) {
-      $nplurals = substr($nplurals, 9, strpos($nplurals, ";") - 9);
-    }
-    else {
-      return FALSE;
-    }
-    $plural = strstr($pluralforms, "plural=");
-    if (strpos($plural, ";")) {
-      $plural = substr($plural, 7, strpos($plural, ";") - 7);
-    }
-    else {
-      return FALSE;
-    }
-
-    // Get PHP version of the plural formula
-    $plural = $this->parseArithmetic($plural);
-
-    if ($plural !== FALSE) {
-      return array($nplurals, $plural);
-    }
-    else {
-      throw new Exception("The plural formula could not be parsed.");
-    }
-  }
-
-  /**
-   * Parses a Gettext Portable Object file header.
-   *
-   * @param $header
-   *   A string containing the complete header.
-   *
-   * @return
-   *   An associative array of key-value pairs.
-   */
-  private function parseHeader($header) {
-    $header_parsed = array();
-    $lines = array_map('trim', explode("\n", $header));
-    foreach ($lines as $line) {
-      if ($line) {
-        list($tag, $contents) = explode(":", $line, 2);
-        $header_parsed[trim($tag)] = trim($contents);
-      }
-    }
-    return $header_parsed;
-  }
-
-  /**
-   * Parses and sanitizes an arithmetic formula into a PHP expression.
-   *
-   * While parsing, we ensure, that the operators have the right
-   * precedence and associativity.
-   *
-   * @param $string
-   *   A string containing the arithmetic formula.
-   *
-   * @return
-   *   The PHP version of the formula.
-   */
-  private function parseArithmetic($string) {
-    // Operator precedence table
-    $precedence = array("(" => -1, ")" => -1, "?" => 1, ":" => 1, "||" => 3, "&&" => 4, "==" => 5, "!=" => 5, "<" => 6, ">" => 6, "<=" => 6, ">=" => 6, "+" => 7, "-" => 7, "*" => 8, "/" => 8, "%" => 8);
-    // Right associativity
-    $right_associativity = array("?" => 1, ":" => 1);
-
-    $tokens = $this->tokenizeFormula($string);
-
-    // Parse by converting into infix notation then back into postfix
-    // Operator stack - holds math operators and symbols
-    $operator_stack = array();
-    // Element Stack - holds data to be operated on
-    $element_stack = array();
-
-    foreach ($tokens as $token) {
-      $current_token = $token;
-
-      // Numbers and the $n variable are simply pushed into $element_stack
-      if (is_numeric($token)) {
-        $element_stack[] = $current_token;
-      }
-      elseif ($current_token == "n") {
-        $element_stack[] = '$n';
-      }
-      elseif ($current_token == "(") {
-        $operator_stack[] = $current_token;
-      }
-      elseif ($current_token == ")") {
-        $topop = array_pop($operator_stack);
-        while (isset($topop) && ($topop != "(")) {
-          $element_stack[] = $topop;
-          $topop = array_pop($operator_stack);
-        }
-      }
-      elseif (!empty($precedence[$current_token])) {
-        // If it's an operator, then pop from $operator_stack into $element_stack until the
-        // precedence in $operator_stack is less than current, then push into $operator_stack
-        $topop = array_pop($operator_stack);
-        while (isset($topop) && ($precedence[$topop] >= $precedence[$current_token]) && !(($precedence[$topop] == $precedence[$current_token]) && !empty($right_associativity[$topop]) && !empty($right_associativity[$current_token]))) {
-          $element_stack[] = $topop;
-          $topop = array_pop($operator_stack);
-        }
-        if ($topop) {
-          $operator_stack[] = $topop;   // Return element to top
-        }
-        $operator_stack[] = $current_token;      // Parentheses are not needed
-      }
-      else {
-        return FALSE;
-      }
-    }
-
-    // Flush operator stack
-    $topop = array_pop($operator_stack);
-    while ($topop != NULL) {
-      $element_stack[] = $topop;
-      $topop = array_pop($operator_stack);
-    }
-
-    // Now extract formula from stack
-    $previous_size = count($element_stack) + 1;
-    while (count($element_stack) < $previous_size) {
-      $previous_size = count($element_stack);
-      for ($i = 2; $i < count($element_stack); $i++) {
-        $op = $element_stack[$i];
-        if (!empty($precedence[$op])) {
-          $f = "";
-          if ($op == ":") {
-            $f = $element_stack[$i - 2] . "):" . $element_stack[$i - 1] . ")";
-          }
-          elseif ($op == "?") {
-            $f = "(" . $element_stack[$i - 2] . "?(" . $element_stack[$i - 1];
-          }
-          else {
-            $f = "(" . $element_stack[$i - 2] . $op . $element_stack[$i - 1] . ")";
-          }
-          array_splice($element_stack, $i - 2, 3, $f);
-          break;
-        }
-      }
-    }
-
-    // If only one element is left, the number of operators is appropriate
-    if (count($element_stack) == 1) {
-      return $element_stack[0];
-    }
-    else {
-      return FALSE;
-    }
-  }
-
-  /**
-   * Provides backward-compatible formula parsing for token_get_all().
-   *
-   * @param $string
-   *   A string containing the arithmetic formula.
-   *
-   * @return
-   *   The PHP version of the formula.
-   */
-  private function tokenizeFormula($formula) {
-    $formula = str_replace(" ", "", $formula);
-    $tokens = array();
-    for ($i = 0; $i < strlen($formula); $i++) {
-      if (is_numeric($formula[$i])) {
-        $num = $formula[$i];
-        $j = $i + 1;
-        while ($j < strlen($formula) && is_numeric($formula[$j])) {
-          $num .= $formula[$j];
-          $j++;
-        }
-        $i = $j - 1;
-        $tokens[] = $num;
-      }
-      elseif ($pos = strpos(" =<>!&|", $formula[$i])) { // We won't have a space
-        $next = $formula[$i + 1];
-        switch ($pos) {
-          case 1:
-          case 2:
-          case 3:
-          case 4:
-            if ($next == '=') {
-              $tokens[] = $formula[$i] . '=';
-              $i++;
-            }
-            else {
-              $tokens[] = $formula[$i];
-            }
-            break;
-          case 5:
-            if ($next == '&') {
-              $tokens[] = '&&';
-              $i++;
-            }
-            else {
-              $tokens[] = $formula[$i];
-            }
-            break;
-          case 6:
-            if ($next == '|') {
-              $tokens[] = '||';
-              $i++;
-            }
-            else {
-              $tokens[] = $formula[$i];
-            }
-            break;
-        }
-      }
-      else {
-        $tokens[] = $formula[$i];
-      }
-    }
-    return $tokens;
-  }
-
-}
diff --git a/core/lib/Drupal/Component/Gettext/PoInterface.php b/core/lib/Drupal/Component/Gettext/PoInterface.php
deleted file mode 100644
index 8c01ee7..0000000
--- a/core/lib/Drupal/Component/Gettext/PoInterface.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Component\Gettext\PoReader.
- *
- * TODOs
- * - constructor needs a state
- * - add getState
- * - gettextInterface should have a readLine method
- */
-
-namespace Drupal\Component\Gettext;
-
-use Drupal\Component\Gettext\Reader;
-use Drupal\Component\Gettext\PoHeader;
-
-/**
- * Defines PO / gettext related must haves.
- *
- * @see PoReaderInterface
- * @see PoWriterInterface
- */
-interface PoInterface {
-  function setLangcode($langcode);
-  function getLangcode();
-
-  function getHeader();
-  function setHeader(PoHeader $header);
-
-}
diff --git a/core/lib/Drupal/Component/Gettext/PoItem.php b/core/lib/Drupal/Component/Gettext/PoItem.php
deleted file mode 100644
index e53418e..0000000
--- a/core/lib/Drupal/Component/Gettext/PoItem.php
+++ /dev/null
@@ -1,147 +0,0 @@
-<?php
-
-namespace Drupal\Component\Gettext;
-
-/**
- * PoItem allows for some transformation on it's data.
- *
- * TODO: we need to check memory constraints for using PoItem versus plain array
- * structure. A possible structure could be
- *   array(
- *     'source' => 'home',
- *     'translation' => 'thuis',
- *     'plural' => 0,
- *     'context' => '',
- *     'comment' => '',
- *   ),
- * In case we want using just arrays we can make these methods static.
- *
- * @author clemens
- * @see \Drupal\Component\Gettext\Gettext
- */
-class PoItem {
-
-  /**
-   * The context this translation belongs to.
-   *
-   * The default context should be an empty string
-   * @see PoMemoryWriter.writeItem()
-   * @var string
-   */
-  public $context = '';
-  public $source;
-  public $plural;
-  public $comment;
-  public $translation;
-
-  static public function mapping() {
-    return array(
-      'msgctxt' => 'context',
-      'msgid' => 'source',
-      'msgstr' => 'translation',
-      '#' => 'comment',
-    );
-  }
-
-  public function fromArray(array $values = array()) {
-    foreach ($values as $key => $value) {
-      $this->{$key} = $value;
-    }
-    if (isset($this->source) && strpos($this->source, LOCALE_PLURAL_DELIMITER) !== FALSE) {
-      $this->source = explode(LOCALE_PLURAL_DELIMITER, $this->source);
-      $this->translation = explode(LOCALE_PLURAL_DELIMITER, $this->translation);
-      $this->plural = count($this->translation);
-    }
-  }
-
-  public function __toString() {
-    return $this->compileTranslation();
-  }
-
-  /**
-   * Compile PO translations strings from a translation object.
-   *
-   * Translation object consists of:
-   *   source       string (singular) or array of strings (plural)
-   *   translation  string (singular) or array of strings (plural)
-   *   plural       TRUE: source and translation are plurals
-   *   context      source context string
-   */
-  private function compileTranslation() {
-    $output = '';
-
-    // Format string context.
-    if (!empty($this->context)) {
-      $output .= 'msgctxt ' . $this->formatString($this->context);
-    }
-
-    // Format translation
-    if ($this->plural) {
-      $output .= $this->formatPlural();
-    }
-    else {
-      $output .= $this->formatSingular();
-    }
-
-    // Add one empty line to separate the translations.
-    $output .= "\n";
-
-    return $output;
-  }
-
-  /**
-   * Formats a plural translation.
-   */
-  private function formatPlural() {
-    $output = '';
-
-    // Format source strings.
-    $output .= 'msgid ' . $this->formatString($this->source[0]);
-    $output .= 'msgid_plural ' . $this->formatString($this->source[1]);
-
-    foreach ($this->translation as $i => $trans) {
-      if (isset($this->translation[$i])) {
-        $output .= 'msgstr[' . $i . '] ' . $this->formatString($trans);
-      }
-      else {
-        $output .= 'msgstr[' . $i . '] ""' . "\n";
-      }
-    }
-
-    return $output;
-  }
-
-  /**
-   * Formats a singular translation.
-   */
-  private function formatSingular() {
-    $output = '';
-    $output .= 'msgid ' . $this->formatString($this->source);
-    $output .= 'msgstr ' . (isset($this->translation) ? $this->formatString($this->translation) : '');
-    return $output;
-  }
-
-  /**
-   * Formats a string for output on multiple lines.
-   */
-  private function formatString($string) {
-    // Escape characters for processing.
-    $string = addcslashes($string, "\0..\37\\\"");
-
-    // Always include a line break after the explicit \n line breaks from
-    // the source string. Otherwise wrap at 70 chars to accommodate the extra
-    // format overhead too.
-    $parts = explode("\n", wordwrap(str_replace('\n', "\\n\n", $string), 70, " \n"));
-
-    // Multiline string should be exported starting with a "" and newline to
-    // have all lines aligned on the same column.
-    if (count($parts) > 1) {
-      return "\"\"\n\"" . implode("\"\n\"", $parts) . "\"\n";
-    }
-    // Single line strings are output on the same line.
-    else {
-      return "\"$parts[0]\"\n";
-    }
-  }
-
-}
diff --git a/core/lib/Drupal/Component/Gettext/PoMemoryWriter.php b/core/lib/Drupal/Component/Gettext/PoMemoryWriter.php
deleted file mode 100644
index c64524e..0000000
--- a/core/lib/Drupal/Component/Gettext/PoMemoryWriter.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Component\Gettext\PoMemoryWriter.
- */
-
-namespace Drupal\Component\Gettext;
-
-use Drupal\Component\Gettext\PoHeader;
-use Drupal\Component\Gettext\BatchStateInterface;
-
-/**
- * Defines a Gettext memory writer.
- *
- * This writer is used by the installer.
- *
- * TODO: do we need a BatchStateInterface? Yes https://drupal.org/node/1637348
- */
-class PoMemoryWriter implements PoWriterInterface, BatchStateInterface {
-
-  private $_header;
-  private $_items;
-
-  function __construct() {
-    $this->_items = array();
-  }
-
-  public function setState(array $state) {
-    // nothing to do?
-  }
-
-  public function getState() {
-    return array();
-  }
-
-  /**
-   * Stores values into memory.
-   *
-   * The structure is context dependent.
-   * TODO: where is this structure documented?
-   * - array[context][source] = translation
-   *
-   * @param PoItem $item
-   */
-  public function writeItem(PoItem $item) {
-    if (is_array($item->source)) {
-      $item->source = implode(LOCALE_PLURAL_DELIMITER, $item->source);
-      $item->translation = implode(LOCALE_PLURAL_DELIMITER, $item->translation);
-    }
-    $this->_items[isset($item->context) ? $item->context : ''][$item->source] = $item->translation;
-  }
-
-  public function writeItems(PoReaderInterface $reader, $count = -1) {
-    $forever = $count == -1;
-    while (($count-- > 0 || $forever) && ($item = $reader->readItem())) {
-      $this->writeItem($item);
-    }
-  }
-
-  public function getHeader() {
-    // TODO: what
-  }
-
-  public function getLangcode() {
-    // TODO: what
-  }
-
-  public function setHeader(PoHeader $header) {
-    // TODO: what
-  }
-
-  public function setLangcode($langcode) {
-    // TODO: what
-  }
-
-  public function getData() {
-    return $this->_items;
-  }
-}
diff --git a/core/lib/Drupal/Component/Gettext/PoReaderInterface.php b/core/lib/Drupal/Component/Gettext/PoReaderInterface.php
deleted file mode 100644
index 9977767..0000000
--- a/core/lib/Drupal/Component/Gettext/PoReaderInterface.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Component\Gettext\PoReader.
- *
- * TODOs
- * - constructor needs a state
- * - add getState
- * - gettextInterface should have a readLine method
- */
-
-namespace Drupal\Component\Gettext;
-
-use Drupal\Component\Gettext\PoInterface;
-
-/**
- * Defines a Gettext reader for PO format.
- */
-interface PoReaderInterface extends PoInterface {
-  function readItem();
-}
diff --git a/core/lib/Drupal/Component/Gettext/PoStreamInterface.php b/core/lib/Drupal/Component/Gettext/PoStreamInterface.php
deleted file mode 100644
index af0b909..0000000
--- a/core/lib/Drupal/Component/Gettext/PoStreamInterface.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Component\Gettext\PoReader.
- *
- * TODOs
- * - constructor needs a state
- * - add getState
- * - gettextInterface should have a readLine method
- */
-
-namespace Drupal\Component\Gettext;
-
-/**
- * Defines PO / gettext related must haves.
- *
- * @see PoReaderInterface
- * @see PoWriterInterface
- */
-interface PoStreamInterface {
-  function open();
-  function close();
-
-  function getURI();
-  function setURI($uri);
-}
diff --git a/core/lib/Drupal/Component/Gettext/PoWriterInterface.php b/core/lib/Drupal/Component/Gettext/PoWriterInterface.php
deleted file mode 100644
index f3cbee0..0000000
--- a/core/lib/Drupal/Component/Gettext/PoWriterInterface.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Component\Gettext\PoWriterInterface.
- */
-
-namespace Drupal\Component\Gettext;
-
-use Drupal\Component\Gettext\PoInterface;
-use Drupal\Component\Gettext\PoItem;
-
-/**
- * Defines a Gettext writer.
- */
-interface PoWriterInterface extends PoInterface {
-  /**
-   * Writes the given item.
-   *
-   * @param PoItem $item
-   */
-  function writeItem(PoItem $item);
-
-  /**
-   * Writes all or the given amout of items.
-   *
-   * @param PoReaderInterface $reader
-   * @param $count
-   *   When $count == -1 all items are read from the $reader.
-   */
-  function writeItems(PoReaderInterface $reader, $count = -1);
-}
diff --git a/core/lib/Drupal/Component/Gettext/testGettext.php b/core/lib/Drupal/Component/Gettext/testGettext.php
deleted file mode 100644
index 3cf236e..0000000
--- a/core/lib/Drupal/Component/Gettext/testGettext.php
+++ /dev/null
@@ -1,692 +0,0 @@
-<?php
-
-$cmd = __FILE__;
-$_SERVER['HTTP_HOST'] = 'default';
-$_SERVER['PHP_SELF'] = '/index.php';
-$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
-$_SERVER['SERVER_SOFTWARE'] = NULL;
-$_SERVER['REQUEST_METHOD'] = 'GET';
-$_SERVER['QUERY_STRING'] = '';
-$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = '/';
-$_SERVER['HTTP_USER_AGENT'] = 'console';
-
-define('DRUPAL_ROOT', getcwd());
-
-include_once DRUPAL_ROOT . '/core/includes/bootstrap.inc';
-drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
-
-/**
- * Run this like:
- * drush @drupal.d8 php-script core/lib/Drupal/Component/Gettext/testGettext.php
- */
-use Drupal\Component\Gettext\PoHeader;
-use Drupal\Component\Gettext\PoItem;
-use Drupal\Component\Gettext\PoFileReader;
-use Drupal\Component\Gettext\PoFileWriter;
-use Drupal\Component\Gettext\PoMemoryWriter;
-use Drupal\locale\PoDatabaseWriter;
-use Drupal\locale\PoDatabaseReader;
-
-function logLine($string, $type = '-') {
-  echo str_repeat($type, 50) . "\n";
-  echo str_repeat(" ", 0) . $string . "\n";
-  if ($type != '-') {
-    echo str_repeat('-', 50) . "\n";
-  }
-}
-
-/**
- * Creates a test PO stucture
- *
- * TODO: the object structure is for .po so we miss the .pot format
- *
- * @param type $langcode
- * @return array of objects
- */
-function gettext_struct($langcode = 'nl') {
-  $src = array(
-    array(
-      'source' => 'home',
-      'translation' => 'thuis',
-      'plural' => 0,
-      'context' => '',
-    ),
-    array(
-      'source' => 'delete',
-      'translation' => 'verwijderen',
-      'plural' => 0,
-      'context' => '',
-    ),
-    array(
-      'source' => array('1 day', '@count days'),
-      'translation' => array('1 dag', '@count dagen'),
-      'plural' => 1,
-      'context' => '',
-    ),
-  );
-  $items = array();
-  foreach ($src as $values) {
-    $item = new PoItem();
-    $item->fromArray($values);
-    $items[] = $item;
-  }
-  $result = array(
-    'langcode' => $langcode,
-    'items' => $items,
-  );
-
-  return $result;
-}
-
-function testWriter($uri = '') {
-  if (empty($uri)) {
-    $uri = getPublicUri(__FUNCTION__, $langcode);
-  }
-  zapUri($uri);
-  logLine(__FUNCTION__);
-  $po = gettext_struct();
-  $langcode = $po['langcode'];
-  $items = $po['items'];
-
-  $writer = new PoFileWriter();
-
-  $writer->setLangcode($langcode);
-  $writer->setHeader(new PoHeader($langcode));
-  $writer->setURI($uri);
-  $writer->open();
-  foreach ($items as $item) {
-    $writer->writeItem($item);
-  }
-  $writer->close();
-  return $uri;
-}
-
-function testBatchState() {
-  logLine(__FUNCTION__, '=');
-  $langcode = 'nl';
-  $uri = remoteToPublic($langcode);
-
-  $reader = new PoFileReader();
-  $reader->setLangcode($langcode);
-  $reader->setURI($uri);
-
-  echo "Not yet opened $uri\n";
-  dumpState($reader->getState());
-
-  echo "Opening stream $uri\n";
-  $reader->open();
-  dumpState($reader->getState());
-
-  echo "Reading 1 items\n";
-  $reader->readItem();
-  dumpState($reader->getState());
-
-  echo "Saving state\n";
-  $state = $reader->getState();
-
-  $item = $reader->readItem();
-  echo "Item read after state $item";
-
-  echo "Dropping \$reader\n";
-  $reader = NULL;
-
-  $reader = new PoFileReader();
-  $reader->setLangcode($langcode);
-  $reader->setURI($uri);
-
-  // This is an implicit open!
-  echo "Setting state simulating Batch\n";
-  $reader->setState($state);
-  dumpState($reader->getState());
-  $sameItem = $reader->readItem();
-  echo "These should be the same\n\n$item\n$sameItem\n";
-}
-
-function getReadStream($uri) {
-  logLine(__FUNCTION__, '=');
-  $s = new GettextFileInterface($uri);
-  return $s;
-}
-
-function testPoReader() {
-  logLine(__FUNCTION__, '=');
-
-  $uri = 'public://test.po.txt';
-
-  logLine("Reading : $uri", '=');
-  logLine("File contents first 500 bytes");
-  $contents = file_get_contents($uri);
-  echo substr($contents, 0, 500) . "\n";
-
-  logLine("Using PoFileReader");
-  $reader = new PoFileReader($uri);
-  echo $reader->getHeader();
-
-  $i = 0;
-  while (($item = $reader->readItem()) && $i++ < 4) {
-    printItem($item, $i);
-  }
-}
-
-function testHeader() {
-  logLine(__FUNCTION__, '=');
-  $h = new PoHeader();
-
-  echo "----------------\n";
-  $h->setFromString('');
-  echo "empty header\n";
-  echo $h;
-
-  echo "----------------\n";
-  $h->setFromString('"Project-Id-Version: ' . __FILE__ . '\n"');
-  echo "-- one item -- \n";
-  echo $h;
-}
-
-function testFileToDb() {
-  logLine(__FUNCTION__, '=');
-  $uri = getPublicUri(__FUNCTION__, 'nl');
-  testWriter($uri);
-  logLine("Reading : $uri");
-  logLine("File contents first 500 bytes");
-  $contents = file_get_contents($uri);
-  echo substr($contents, 0, 500) . "\n";
-
-  logLine("POFileReader");
-  $reader = new PoFileReader();
-  $reader->setURI($uri);
-  $reader->open();
-  $header = $reader->getHeader();
-  logLine("Header read");
-  logLine($header);
-
-  $langcode = 'ca';
-  logLine("PoDatabaseWriter");
-  $writer = new PoDatabaseWriter();
-  $writer->setLangcode($langcode);
-  $writer->setHeader($header);
-
-  $i = 0;
-  while (($item = $reader->readItem()) && $i < 4) {
-    logLine($item);
-    $writer->writeItem($item);
-    $i++;
-  }
-  logLine(__FUNCTION__ . " #$i items written.");
-}
-
-function testDbDump() {
-  logLine(__FUNCTION__, '=');
-  $reader = new PoDatabaseReader('en');
-  echo $reader->getHeader() . "\n";
-
-  $i = 0;
-  while (($item = $reader->readItem()) && $i < 4) {
-    printItem($item, $i++);
-  }
-
-  echo "Saving state to simulate a batch\n";
-  $state = $reader->getState();
-
-  echo "Create a new PoDatabaseReader so simulate a batch\n";
-  $reader = new PoDatabaseReader('en');
-
-  // Set the state
-  $reader->setState($state);
-  $i = 0;
-  while (($item = $reader->readItem()) && $i < 4) {
-    printItem($item, $i++);
-  }
-}
-
-function printItem($item, $context = 0) {
-  logLine(__FUNCTION__, '=');
-  if ($item) {
-    logLine("$context : $item->lid");
-    print_r($item);
-  }
-}
-
-function readItem($reader, $context = 0) {
-  $item = $reader->readItem();
-  if ($item) {
-    printItem($item);
-  }
-}
-
-function dumpState($state) {
-  logLine(__FUNCTION__, '=');
-  print_r($state);
-}
-
-function testPOFileReader() {
-  logLine(__FUNCTION__, '=');
-  $uri = 'public://nl-nl.po';
-
-  $reader = new PoFileReader($uri);
-  dumpState($reader->getState());
-  echo $reader->getHeader() . "\n";
-  dumpState($reader->getState());
-  $i = 0;
-  while (($item = readItem($reader)) && $i < 4) {
-    printItem($item, $i++);
-  }
-  dumpState($reader->getState());
-}
-
-function getLanguages($langcode = NULL) {
-  logLine(__FUNCTION__, '=');
-  if (!is_null($langcode)) {
-    return array($langcode);
-  }
-  return array(
-    'nl',
-    'ar',
-    'ca',
-    'en', // does not exists on d.o (should it be?)
-    'NOP', // does really not exists on d.o
-  );
-}
-
-function getRemoteUris($langcode = NULL) {
-  logLine(__FUNCTION__, '=');
-  $langcodes = getLanguages($langcode);
-  $uris = array();
-  foreach ($langcodes as $langcode) {
-    $uri = "http://ftp.drupal.org/files/translations/7.x/drupal/drupal-7.11.$langcode.po";
-    $uris[$langcode] = $uri;
-  }
-  return $uris;
-}
-
-function getPublicUri($name, $langcode) {
-  $result = "public://$name-$langcode.po";
-  logLine($result);
-  return $result;
-}
-
-function getRemoteUri($langcode) {
-  $uris = getRemoteUris($langcode);
-  return $uris[$langcode];
-}
-
-function testRemotePOPumper() {
-  logLine(__FUNCTION__, '=');
-  $uris = getRemoteUris();
-  foreach ($uris as $langcode => $uri) {
-    logLine("langcode: $langcode");
-    $reader = new PoFileReader($uri);
-    $writer = new PoDatabaseWriter($langcode);
-    $writer->writeItems($reader, 10);
-  }
-}
-
-function testPOFileWriter() {
-  logLine(__FUNCTION__, '=');
-  $src = "http://ftp.drupal.org/files/translations/7.x/drupal/drupal-7.11.ar.po";
-
-  $reader = new PoFileReader();
-  $reader->setURI($src);
-  $reader->open();
-  $header = $reader->getHeader();
-
-  $dst = 'public://drupal-7.11.ar.po';
-  zapUri($dst);
-  $writer = new PoFileWriter();
-  $writer->setURI($dst);
-  $writer->setHeader($header);
-  $writer->open();
-
-  $i = 0;
-  while (($item = $reader->readItem()) && $i < 4) {
-    printItem($item, $i);
-    $i++;
-    $writer->writeItem($item);
-    dumpState($writer->getState());
-  }
-
-  $writer->close();
-}
-
-function testDbToFile() {
-  logLine(__FUNCTION__, '=');
-  $langcode = 'ca';
-  $reader = new PoDatabaseReader();
-  $reader->setLangcode($langcode);
-
-  $dst = 'public://drupal-7.11.dummy.po';
-  $header = $reader->getHeader();
-
-  $writer = new PoFileWriter();
-  $writer->setURI($dst);
-  $writer->setHeader($header);
-  $writer->open();
-
-  $i = 0;
-  while (($item = $reader->readItem()) && $i < 4) {
-    printItem($item, $i);
-    $i++;
-    $writer->writeItem($item);
-    dumpState($writer->getState());
-  }
-
-  $writer->writeItems($reader, 10);
-
-  $writer->close();
-}
-
-function testBatchSimulation() {
-  logLine(__FUNCTION__, '=');
-
-  // Grab first langcode
-  $uris = getRemoteUris();
-  $langcode = key($uris);
-  $src = current($uris);
-
-  logLine("Opening $langcode : $src");
-  $reader = new PoFileReader();
-  $reader->setURI($src);
-  $reader->open();
-
-  $header = $reader->getHeader();
-  logLine($header);
-
-  $dst = getPublicUri(__FUNCTION__, $langcode);
-
-  zapUri($dst);
-  logLine("Writing $langcode : $dst");
-  $writer = new PoFileWriter();
-  $writer->setURI($dst);
-  $writer->setHeader($header);
-  $writer->open();
-
-  logLine('Written header only', '=');
-  echo file_get_contents($dst);
-
-  processN($writer, $reader, 2);
-
-  dumpFileContents($dst);
-
-  $state = $reader->getState();
-  dumpState($state);
-
-  logLine('Replacing reader', '=');
-  $reader = new PoFileReader($src);
-
-  logLine('setting state back');
-  $reader->setState($state);
-  dumpState($state);
-
-  processN($writer, $reader, 3);
-  $reader->setState($state);
-  dumpState($state);
-
-  dumpFileContents($dst);
-}
-
-function testDBReaderState() {
-  logLine(__FUNCTION__);
-  $langcode = 'nl';
-  $reader = new PoDatabaseReader();
-  $reader->setLangcode($langcode);
-
-  logLine("Init PoDatabaseReader", '=');
-  $state = $reader->getState();
-  dumpState($state);
-
-  $header = $reader->getHeader();
-
-  $uri = getPublicUri(__FUNCTION__, $langcode);
-  zapUri($uri);
-  $writer = new PoFileWriter($uri, $header);
-  $writer->setHeader($header);
-  $writer->setURI($uri);
-  $writer->open();
-
-  processN($writer, $reader, 4);
-
-  logLine("Read some", '=');
-  $state = $reader->getState();
-  dumpState($state);
-
-  $reader = new PoDatabaseReader($langcode);
-  $reader->setState($state);
-  processN($writer, $reader, 4);
-  $state = $reader->getState();
-  dumpState($state);
-
-  logLine("File contents from $uri", '=');
-  echo file_get_contents($uri);
-}
-
-function zapUri($uri) {
-  logLine("Truncate $uri", '=');
-  ftruncate(fopen($uri, 'w'));
-}
-
-function processN($writer, $reader, $count = 10) {
-  if ($count == -1) {
-    logLine("processing items: __ALL__");
-  }
-  else {
-    logLine("processing items: $count");
-  }
-  $writer->writeItems($reader, $count);
-}
-
-function dumpFileContents($uri) {
-  logLine("Written: $uri", '=');
-  echo file_get_contents($uri);
-}
-
-function newPOFileReader($uri, $langcode = NULL) {
-  logLine("Reading from $uri using langcode: '$langcode'");
-  $reader = new PoFileReader();
-  $reader->setURI($uri);
-  $reader->setLangcode($langcode);
-  $reader->open();
-  return $reader;
-}
-
-function remoteToPublic($langcode) {
-  logLine(__FUNCTION__, '=');
-  $uri = getRemoteUri($langcode);
-
-  logLine("Reading from $uri");
-  $reader = newPoFileReader($uri, $langcode);
-  $header = $reader->getHeader();
-
-  $uri = getPublicUri($langcode, $langcode);
-  zapUri($uri);
-  logLine("Writing to $uri");
-  $writer = new PoFileWriter();
-  $writer->setURI($uri);
-  $writer->setHeader($header);
-  $writer->open();
-
-  processN($writer, $reader, -1);
-
-  return $uri;
-}
-
-function publicToDb($langcode) {
-  $uri = getPublicUri($langcode, $langcode);
-
-  logLine("Reading from $uri using langcode: '$langcode'");
-  $reader = new PoFileReader();
-  $reader->setURI($uri);
-  $reader->setLangcode($langcode);
-  $reader->open();
-
-  logLine("Writing to DB");
-  $writer = new PoDatabaseWriter();
-  $writer->setLangcode($langcode);
-  $writer->setHeader($reader->getHeader());
-
-  $locale_plurals = variable_get('locale_translation_plurals', array());
-  print_r(array("Should have $langcode" => $locale_plurals));
-
-  $options = $writer->getOptions();
-  print_r($writer->getOptions());
-  //$options['overwrite_options']['not_customized'] = TRUE;
-  $writer->setOptions($options);
-  print_r($writer->getOptions());
-  print_r($options);
-  processN($writer, $reader, -1);
-
-  print_r($writer->getReport());
-}
-
-function publicToMemory($langcode) {
-  $uri = getPublicUri($langcode, $langcode);
-
-  logLine("Reading from $uri using langcode: '$langcode'");
-  $reader = new PoFileReader();
-  $reader->setURI($uri);
-  $reader->open();
-
-  logLine("Writing to Memory");
-  $writer = new PoMemoryWriter();
-  $writer->setLangcode($langcode);
-  $writer->setHeader($reader->getHeader());
-
-  $locale_plurals = variable_get('locale_translation_plurals', array());
-  print_r(array("Should have $langcode" => $locale_plurals));
-
-  processN($writer, $reader, 5);
-
-  var_dump($writer);
-}
-
-function pumpAround($langcode) {
-  $uri = remoteToPublic($langcode);
-  publicToMemory($langcode);
-  publicToDb($langcode);
-
-  $reader = newPoFileReader($uri, $langcode);
-
-  logLine("Writing to DB");
-  $writer = new PoDatabaseWriter();
-  $writer->setLangcode($langcode);
-
-  processN($writer, $reader);
-
-  logLine("Reading from DB");
-  $reader = new PoDatabaseReader();
-  $reader->setLangcode($langcode);
-  $reader->setOptions(array());
-
-  var_dump($reader->getOptions());
-
-  $header = $reader->getHeader();
-
-  $uri = getPublicUri(__FUNCTION__ . '-db', $langcode);
-  zapUri($uri);
-  logLine("Writing to $uri");
-  $writer = new PoFileWriter();
-  $writer->setURI($uri);
-  $writer->setHeader($header);
-  $writer->open();
-  processN($writer, $reader, -1);
-}
-
-function runAll() {
-  // Make sure all test languages are around
-  testLanguages();
-  testWriter();
-  testDBReaderState();
-  testBatchSimulation();
-  testBatchState();
-  testPoReader();
-  testHeader();
-  testFileToDb();
-  testDbDump();
-  testPOFileReader();
-  testPOFileWriter();
-  testDbToFile();
-  testRemotePOPumper();
-
-  pumpAll();
-
-  testFileToDb();
-
-  testT();
-  testFormatPlural();
-}
-
-function pumpAll() {
-  $uris = getRemoteUris();
-  foreach ($uris as $langcode => $uri) {
-    pumpAround($langcode);
-  }
-}
-
-function testT() {
-  logLine(__FUNCTION__, '=');
-  $uris = getRemoteUris();
-  foreach ($uris as $langcode => $uri) {
-    // TODO create a DBReader with filter like plural / context / etc
-    $sentences = array(
-      'Home',
-      'May',
-      'Website',
-      'Drupal',
-      'One',
-      'Two',
-    );
-
-    foreach ($sentences as $sentence) {
-      $result = t($sentence, array(), array('langcode' => $langcode));
-      echo "$langcode: $sentence => $result\n";
-    }
-  }
-}
-
-function testFormatPlural() {
-  $uris = getRemoteUris();
-  foreach ($uris as $langcode => $uri) {
-    $plurals = array();
-    $plurals[] = array('1 day', '@count days');
-    $plurals[] = array('1 pending update', '@count pending updates');
-    foreach ($plurals as $plural) {
-      for ($i = 0; $i < 200; $i++) {
-        $result = format_plural($i, $plural[0], $plural[1], array('@count' => $i), array('langcode' => $langcode));
-        echo "$langcode: $plural[0] | $plural[1] => $result\n";
-        if ($i > 0) {
-          $i = $i * 3;
-        }
-      }
-    }
-  }
-}
-
-/**
- * Make sure languages are added to the Drupal install.
- */
-function testLanguages() {
-  $uris = (getRemoteUris());
-  unset($uris['NOP']);
-  $needed = array_keys($uris);
-  $list = language_list();
-  $missing = array_diff($needed, array_keys($list));
-  foreach ($missing as $langcode) {
-    $language = (object) array(
-      'langcode' => $langcode,
-      'default' => FALSE,
-    );
-    language_save($language);
-  }
-  if (count($missing)) {
-    echo "The following languages were added to your Drupal install: " . implode(", ", $missing) . "\n";
-  }
-}
-
-//testT();
-//testFormatPlural();
-//$langs = language_list();
-//var_dump($langs);
-//pumpAround('nl');
-runAll();
-//pumpAll();
-
-//testBatchState();
diff --git a/core/modules/locale/lib/Drupal/locale/Gettext.php b/core/modules/locale/lib/Drupal/locale/Gettext.php
deleted file mode 100644
index 76d9845..0000000
--- a/core/modules/locale/lib/Drupal/locale/Gettext.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-namespace Drupal\locale;
-
-use Drupal\Component\Gettext\PoFileReader;
-use Drupal\Component\Gettext\PoMemoryWriter;
-
-/**
- * The Gettext class provides for Gettext specific functionality.
- *
- * Most operations related to Gettext PO file handling are related to pumping
- * data from a source to a destination:
- * - Remote file http://*.po to memory
- * - Database to public://*.po
- *
- * This class adds static methods mostly.
- */
-class Gettext {
-
-  /**
-   * Reads the given PO files into a data structure.
-   *
-   * @param type $langcode
-   * @param array $files
-   * @return array
-   */
-  static function filesToArray($langcode, array $files) {
-    $writer = new PoMemoryWriter();
-    $writer->setLangcode($langcode);
-    foreach ($files as $file) {
-      $reader = new PoFileReader();
-      $reader->setURI($file->uri);
-      $reader->setLangcode($langcode);
-      $reader->open();
-      $writer->writeItems($reader, -1);
-    }
-    return $writer->getData();
-  }
-
-  /**
-   * Reads the given PO files into a data structure.
-   *
-   * @param type $file
-   * @param type $langcode
-   * @param array $files
-   * @return array
-   */
-  static function fileToDatabase($file, $langcode, $overwrite_options, $customized = LOCALE_NOT_CUSTOMIZED) {
-    $reader = new PoFileReader();
-    $reader->setLangcode($langcode);
-    $reader->setURI($file->uri);
-
-    try {
-      // When opening header is parsed immediately
-      $reader->open();
-    }
-    catch (Exception $exc) {
-      throw new $exc;
-    }
-
-    $header = $reader->getHeader();
-    if (!$header) {
-      throw new Exception('missing or malformed header.');
-    }
-
-    $writer = new PoDatabaseWriter();
-    $writer->setLangcode($langcode);
-    $options = array(
-      'overwrite_options' => $overwrite_options,
-      'customized' => $customized,
-    );
-    // It's vital options are set first.
-    // @TODO: this has to be fixed in https://drupal.org/node/1637334
-    $writer->setOptions($options);
-    $writer->setHeader($header);
-
-    try {
-      $writer->writeItems($reader, -1);
-    }
-    catch (Exception $exc) {
-      throw new $exc;
-    }
-
-    return $writer->getReport();
-  }
-}
diff --git a/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php b/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php
deleted file mode 100644
index e2cfbe2..0000000
--- a/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php
+++ /dev/null
@@ -1,185 +0,0 @@
-<?php
-
-namespace Drupal\locale;
-
-use Drupal\Component\Gettext\BatchStateInterface;
-use Drupal\Component\Gettext\PoHeader;
-use Drupal\Component\Gettext\PoItem;
-use Drupal\Component\Gettext\PoReaderInterface;
-
-class PoDatabaseReader implements BatchStateInterface, PoReaderInterface {
-  /*
-   * @param $overwrite_options
-   *   An associative array indicating what data should be overwritten, if any.
-   *   - not_customized: not customized strings should be overwritten.
-   *   - customized: customized strings should be overwritten.
-   * @param $customized
-   *   (optional) Whether the strings being imported should be saved as customized.
-   *   Use LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
-   */
-
-  private $_options;
-  private $_langcode;
-  private $_result;
-
-  /**
-   * lid of last read record
-   *
-   * This is used to manage state.
-   * TODO: state is not working yet ... see prepared statement
-   *
-   * @see PODatabaseReader::readItem()
-   * @see PODatabaseReader::buildQuery()
-   */
-  private $_lid = -1;
-
-  /**
-   * @see BatchStateInterface
-   */
-  function __construct() {
-    $this->setOptions(array());
-  }
-
-  public function getLangcode() {
-    return $this->_langcode;
-  }
-
-  public function setLangcode($langcode) {
-    $this->_langcode = $langcode;
-  }
-
-  function getOptions() {
-    return $this->_options;
-  }
-
-  function setOptions(array $options) {
-    if (!isset($options['override_options'])) {
-      $options['override_options'] = array();
-    }
-    if (!isset($options['customized'])) {
-      $options['customized'] = LOCALE_NOT_CUSTOMIZED;
-    }
-    $this->_options = array(
-      'override_options' => $options['override_options'],
-    );
-    $this->_options += $options;
-  }
-
-  function setState(array $state) {
-    $this->_lid = $state['lid'];
-    $this->setOptions($state['options']);
-    $this->buildQuery();
-  }
-
-  function getState() {
-    return array(
-      '__CLASS__' => __CLASS__,
-      'lid' => $this->_lid,
-      'options' => $this->_options,
-    );
-  }
-
-  function getHeader() {
-    return new PoHeader($this->getLangcode());
-  }
-
-  public function setHeader(PoHeader $header) {
-    // empty on purpose
-  }
-
-  /**
-   * Generates a structured array of all translated strings for the language.
-   *
-   * @param $language
-   *   Language object to generate the output for, or NULL if generating
-   *   translation template.
-   * @param $options
-   *   (optional) An associative array specifying what to include in the output:
-   *   - customized: include customized strings (if TRUE)
-   *   - uncustomized: include non-customized string (if TRUE)
-   *   - untranslated: include untranslated source strings (if TRUE)
-   *   Ignored if $language is NULL.
-   *
-   * @return
-   *   An array of translated strings that can be used to generate an export.
-   */
-  private function buildQuery() {
-    $langcode = $this->_langcode;
-    $options = $this->_options;
-
-    // Assume FALSE for all options if not provided by the API.
-    $options += array(
-      'customized' => FALSE,
-      'not_customized' => FALSE,
-      'not_translated' => FALSE,
-    );
-    if (array_sum($options) == 0) {
-      // If user asked to not include anything in the translation files,
-      // that would not make sense, so just fall back on providing a template.
-      $langcode = NULL;
-    }
-
-    // Build and execute query to collect source strings and translations.
-    $query = db_select('locales_source', 's');
-    if (!empty($langcode)) {
-      if ($options['not_translated']) {
-        // Left join to keep untranslated strings in.
-        $query->leftJoin('locales_target', 't', 's.lid = t.lid AND t.language = :language', array(':language' => $langcode));
-      }
-      else {
-        // Inner join to filter for only translations.
-        $query->innerJoin('locales_target', 't', 's.lid = t.lid AND t.language = :language', array(':language' => $langcode));
-      }
-      if ($options['customized']) {
-        if (!$options['not_customized']) {
-          // Filter for customized strings only.
-          $query->condition('t.customized', LOCALE_CUSTOMIZED);
-        }
-        // Else no filtering needed in this case.
-      }
-      else {
-        if ($options['not_customized']) {
-          // Filter for non-customized strings only.
-          $query->condition('t.customized', LOCALE_NOT_CUSTOMIZED);
-        }
-        else {
-          // Filter for strings without translation.
-          $query->isNull('t.translation');
-        }
-      }
-      $query->fields('t', array('translation'));
-    }
-    else {
-      $query->leftJoin('locales_target', 't', 's.lid = t.lid');
-    }
-    $query->fields('s', array('lid', 'source', 'context', 'location'));
-
-    // TODO: we need to order by lid
-    // This does not seem to work
-    $query->orderBy('s.lid');
-    $query->condition('s.lid', $this->_lid, '>');
-
-    $this->_result = $query->execute();
-    //echo "Executing: (lid = $this->_lid) : \n" . $this->_result->getQueryString() . "\n";
-  }
-
-  private function getResult() {
-    if (!isset($this->_result)) {
-      $this->buildQuery();
-    }
-    return $this->_result;
-  }
-
-  function readItem() {
-    $result = $this->getResult();
-    $values = $result->fetchAssoc();
-    if ($values) {
-      $poItem = new PoItem();
-      $poItem->fromArray($values);
-      // Manage state
-      $this->_lid = $values['lid'];
-      return $poItem;
-    }
-  }
-
-}
diff --git a/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php b/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
deleted file mode 100644
index 6386e46..0000000
--- a/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
+++ /dev/null
@@ -1,302 +0,0 @@
-<?php
-
-namespace Drupal\locale;
-
-use Drupal\Component\Gettext\BatchStateInterface;
-use Drupal\Component\Gettext\PoHeader;
-use Drupal\Component\Gettext\PoItem;
-use Drupal\Component\Gettext\PoReaderInterface;
-use Drupal\Component\Gettext\PoWriterInterface;
-
-
-class PoDatabaseWriter implements PoWriterInterface, BatchStateInterface {
-  /*
-   * @param $overwrite_options
-   *   An associative array indicating what data should be overwritten, if any.
-   *   - not_customized: not customized strings should be overwritten.
-   *   - customized: customized strings should be overwritten.
-   * @param $customized
-   *   (optional) Whether the strings being imported should be saved as customized.
-   *   Use LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
-   */
-
-  private $_options;
-  private $_langcode;
-  private $_header;
-
-  static function getDefaultState() {
-    return array(
-      'langcode' => NULL,
-      'report' => array(
-        'additions' => 0,
-        'updates' => 0,
-        'deletes' => 0,
-        'skips' => 0,
-        'ignored' => 0,
-      ),
-      'options' => array(
-        'overwrite_options' => array(
-          'not_customized' => FALSE,
-          'customized' => FALSE,
-        ),
-        'customized' => LOCALE_NOT_CUSTOMIZED,
-      ),
-    );
-  }
-
-  /**
-   * Report array summarizing the number of changes done in the form:
-   * array(additions, updates, deletes, skips, ignored).
-   *
-   * @var array
-   */
-  private $_report;
-
-  /**
-   * @see BatchStateInterface
-   */
-  function __construct() {
-    $this->setState(array());
-  }
-
-  public function getLangcode() {
-    return $this->_langcode;
-  }
-
-  public function setLangcode($langcode) {
-    $this->_langcode = $langcode;
-  }
-
-  public function getReport() {
-    return $this->_report;
-  }
-
-  function setReport($report) {
-    $report += array(
-      'additions' => 0,
-      'updates' => 0,
-      'deletes' => 0,
-      'skips' => 0,
-      'ignored' => 0,
-    );
-    $this->_report = $report;
-  }
-
-  function getOptions() {
-    return $this->_options;
-  }
-
-  function setOptions(array $options) {
-    $options['overwrite_options'] += array(
-      'not_customized' => FALSE,
-      'customized' => FALSE,
-    );
-    $options += array(
-      'customized' => LOCALE_NOT_CUSTOMIZED,
-    );
-    $this->_options = $options;
-  }
-
-  /**
-   * Implementation of BatchInterface::setState
-   *
-   * @param array $state
-   */
-  public function setState(array $state) {
-    $state += self::getDefaultState();
-    $this->_report = $state['report'];
-    $this->setLangcode($state['langcode']);
-    $this->setOptions($state['options']);
-  }
-
-  public function getState() {
-    return array(
-      'class' => __CLASS__,
-      'report' => $this->getReport(),
-      'langcode' => $this->getLangcode(),
-      'options' => $this->getOptions(),
-    );
-  }
-
-  function getHeader() {
-    return $this->_header;
-  }
-
-  /**
-   * Sets the header and configure drupal accordingly.
-   *
-   * Before being able to process the given header we need to know in what
-   * context this database write is done. For this the options must be set.
-   *
-   * A langcode is required to set the current headers PluralForm.
-   *
-   * @param PoHeader $header
-   * @throws Exception
-   */
-  function setHeader(PoHeader $header) {
-    $this->_header = $header;
-    $locale_plurals = variable_get('locale_translation_plurals', array());
-    // Check for options
-    $options = $this->getOptions();
-    if (empty($options)) {
-      throw new Exception("Options should be set before assigning a PoHeader");
-    }
-    $overwrite_options = $options['overwrite_options'];
-    // Check for langcode
-    $lang = $this->_langcode;
-    if (empty($lang)) {
-      throw new Exception("Langcode should be set before assigning a PoHeader");
-    }
-    if (array_sum($overwrite_options) || empty($locale_plurals[$lang]['plurals'])) {
-      // Get and store the plural formula if available.
-      $plural = $header->getPlural();
-      // TODO: this is a sloppy way to create a file name
-      // but parsePluralForms is also weird to me still
-      $filepath = __CLASS__ . "::" . __METHOD__;
-      if (isset($plural) && $p = $header->parsePluralForms($plural, $filepath)) {
-        list($nplurals, $formula) = $p;
-        $locale_plurals[$lang] = array(
-          'plurals' => $nplurals,
-          'formula' => $formula,
-        );
-        variable_set('locale_translation_plurals', $locale_plurals);
-      }
-    }
-  }
-
-  /**
-   * Write an item to the database.
-   *
-   * @param PoItem $item
-   */
-  function writeItem(PoItem $item) {
-    if ($item->plural) {
-      $item->source = join(LOCALE_PLURAL_DELIMITER, $item->source);
-      $item->translation = join(LOCALE_PLURAL_DELIMITER, $item->translation);
-    }
-    $this->importString($item, 'location');
-  }
-
-  /**
-   * Write next items from a reader to the database.
-   * If no number of items is specified, all of theme are being written.
-   *
-   * @param PoReaderInterface $reader
-   * @param $count
-   *  The number of items that will be wrote.
-   */
-  public function writeItems(PoReaderInterface $reader, $count = -1) {
-    $forever = $count == -1;
-    while (($count-- > 0 || $forever) && ($item = $reader->readItem())) {
-      $this->writeItem($item);
-    }
-  }
-
-  /**
-   * Imports one string into the database.
-   *
-   * @param PoItem $item
-   *  The item being imported.
-   * @param $location
-   *   Location value to save with source string.
-   *
-   * @return
-   *   The string ID of the existing string modified or the new string added.
-   */
-  private function importString($item, $location) {
-    // Initialize overwrite options if not set.
-    $this->_options['overwrite_options'] += array(
-      'not_customized' => FALSE,
-      'customized' => FALSE,
-    );
-    $overwrite_options = $this->_options['overwrite_options'];
-    $customized = $this->_options['customized'];
-
-    // Look up the source string and any existing translation.
-    $string = db_query("SELECT s.lid, t.customized FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.source = :source AND s.context = :context", array(
-      ':source' => $item->source,
-      ':context' => $item->context,
-      ':language' => $this->_langcode,
-        ))
-        ->fetchObject();
-
-    if (!empty($item->translation)) {
-      // Skip this string unless it passes a check for dangerous code.
-      if (!locale_string_is_safe($item->translation)) {
-        watchdog('locale', 'Import of string "%string" was skipped because of disallowed or malformed HTML.', array('%string' => $item->translation), WATCHDOG_ERROR);
-        $this->_report['skips']++;
-        return 0;
-      }
-      elseif (isset($string->lid)) {
-        // We have this source string saved already.
-        db_update('locales_source')
-            ->fields(array(
-              'location' => $location,
-            ))
-            ->condition('lid', $string->lid)
-            ->execute();
-
-        if (!isset($string->customized)) {
-          // No translation in this language.
-          db_insert('locales_target')
-              ->fields(array(
-                'lid' => $string->lid,
-                'language' => $this->_langcode,
-                'translation' => $item->translation,
-                'customized' => $customized,
-              ))
-              ->execute();
-
-          $this->_report['additions']++;
-        }
-        elseif ($overwrite_options[$string->customized ? 'customized' : 'not_customized']) {
-          // Translation exists, only overwrite if instructed.
-          db_update('locales_target')
-              ->fields(array(
-                'translation' => $item->translation,
-                'customized' => $customized,
-              ))
-              ->condition('language', $this->_langcode)
-              ->condition('lid', $string->lid)
-              ->execute();
-
-          $this->_report['updates']++;
-        }
-        return $string->lid;
-      }
-      else {
-        // No such source string in the database yet.
-        $lid = db_insert('locales_source')
-            ->fields(array(
-              'location' => $location,
-              'source' => $item->source,
-              'context' => (string) $item->context,
-            ))
-            ->execute();
-
-        db_insert('locales_target')
-            ->fields(array(
-              'lid' => $lid,
-              'language' => $this->_langcode,
-              'translation' => $item->translation,
-              'customized' => $customized,
-            ))
-            ->execute();
-
-        $this->_report['additions']++;
-        return $lid;
-      }
-    }
-    elseif (isset($string->lid) && isset($string->customized) && $overwrite_options[$string->customized ? 'customized' : 'not_customized']) {
-      // Empty translation, remove existing if instructed.
-      db_delete('locales_target')
-          ->condition('language', $this->_langcode)
-          ->condition('lid', $string->lid)
-          ->execute();
-
-      $this->_report['deletes']++;
-      return $string->lid;
-    }
-  }
-
-}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
index e55f356..6fe77c2 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
@@ -196,8 +196,7 @@ class LocaleImportFunctionalTest extends WebTestBase {
 
     // Ensure the translation file was automatically imported when language was
     // added.
-    $this->assertText(t('3 translation files imported.'), t('Language file automatically imported.'));
-    $this->assertText(t('A translation string was skipped because of disallowed or malformed HTML'), t('Language file automatically imported.'));
+    $this->assertText(t('One translation file imported.'), t('Language file automatically imported.'));
 
     // Ensure strings were successfully imported.
     $search = array(
@@ -207,25 +206,6 @@ class LocaleImportFunctionalTest extends WebTestBase {
     );
     $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
     $this->assertNoText(t('No strings available.'), t('String successfully imported.'));
-
-    // Ensure multiline string was imported.
-    $search = array(
-      'string' => 'HTTP Result Code: !status',
-      'langcode' => $langcode,
-      'translation' => 'all',
-    );
-    $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
-    $this->assertNoText(t('No strings available.'), t('String successfully imported.'));
-
-    // Ensure xyzzy was imported and xyzzy not.
-    $search = array(
-      'string' => 'xyzzy',
-      'langcode' => $langcode,
-      'translation' => 'all',
-    );
-    $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
-    $this->assertNoText(t('No strings available.'), t('String successfully imported.'));
-    $this->assertNoText('xyzzy2', t('String successfully imported.'));
   }
 
   /**
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index d96b40c..7a8a977 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -5,10 +5,7 @@
  * Mass import-export and batch import functionality for Gettext .po files.
  */
 
-use Drupal\Component\Gettext\PoFileWriter;
-use Drupal\locale\Gettext;
-use Drupal\locale\PoDatabaseReader;
-
+include_once DRUPAL_ROOT . '/core/includes/gettext.inc';
 
 /**
  * User interface for the translation import screen.
@@ -110,39 +107,7 @@ function locale_translate_import_form_submit($form, &$form_state) {
     $customized = $form_state['values']['customized'] ? LOCALE_CUSTOMIZED : LOCALE_NOT_CUSTOMIZED;
 
     // Now import strings into the language
-    try {
-      // Try to allocate enough time to parse and import the data.
-      drupal_set_time_limit(240);
-
-      $report = GetText::fileToDatabase($file, $language->langcode, $form_state['values']['overwrite_options'], $customized);
-      $additions = $report['additions'];
-      $updates = $report['updates'];
-      $deletes = $report['deletes'];
-      $skips = $report['skips'];
-
-      menu_router_rebuild();
-      // Clear cache and force refresh of JavaScript translations.
-      _locale_invalidate_js($language->langcode);
-      cache()->deletePrefix('locale:');
-
-      drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes)));
-      watchdog('locale', 'Imported %file into %locale: %number new strings added, %update updated and %delete removed.', array('%file' => $file->filename, '%locale' => $language->langcode, '%number' => $additions, '%update' => $updates, '%delete' => $deletes));
-      if ($skips) {
-        if (module_exists('dblog')) {
-          $skip_message = format_plural($skips, 'A translation string was skipped because of disallowed or malformed HTML. <a href="@url">See the log</a> for details.', '@count translation strings were skipped because of disallowed or malformed HTML. <a href="@url">See the log</a> for details.', array('@url' => url('admin/reports/dblog')));
-        }
-        else {
-          $skip_message = format_plural($skips, 'A translation string was skipped because of disallowed or malformed HTML. See the log for details.', '@count translation strings were skipped because of disallowed or malformed HTML. See the log for details.');
-        }
-        drupal_set_message($skip_message, 'error');
-        watchdog('locale', '@count disallowed HTML string(s) in %file', array('@count' => $skips, '%file' => $file->uri), WATCHDOG_WARNING);
-      }
-      $variables = array('%filename' => $file->filename);
-      drupal_set_message(t('The translation import of %filename is done.', $variables));
-      watchdog('locale', 'The translation import of %filename is done.', $variables);
-
-    } catch (Exception $exc) {
-      drupal_set_message(print_r($exc, TRUE));
+    if ($return = _locale_import_po($file, $language->langcode, $form_state['values']['overwrite_options'], $customized) == FALSE) {
       $variables = array('%filename' => $file->filename);
       drupal_set_message(t('The translation import of %filename failed.', $variables), 'error');
       watchdog('locale', 'The translation import of %filename failed.', $variables, WATCHDOG_ERROR);
@@ -242,42 +207,7 @@ function locale_translate_export_form_submit($form, &$form_state) {
     $language = NULL;
   }
   $content_options = isset($form_state['values']['content_options']) ? $form_state['values']['content_options'] : array();
-  $reader = new PoDatabaseReader();
-  $languageName = '';
-  if ($language != NULL) {
-    $reader->setLangcode($language->langcode);
-    $reader->setOptions($content_options);
-    $languages = language_list();
-    $languageName = isset($languages[$language->langcode]) ? $languages[$language->langcode]->name : '';
-    $filename = $language->langcode .'.po';
-  } else {
-    // Template required.
-    $filename = 'drupal.pot';
-  }
-  $item = $reader->readItem();
-  if (!empty($item)) {
-    $uri = tempnam('temporary://', 'po_');
-    $header = $reader->getHeader();
-    $header->setProjectName(variable_get('site_name', 'Drupal'));
-    $header->setLanguageName($languageName);
-
-    $writer = new PoFileWriter;
-    $writer->setUri($uri);
-    $writer->setHeader($header);
-
-    $writer->open();
-    $writer->writeItem($item);
-    $writer->writeItems($reader);
-    $writer->close();
-
-    header("Content-Disposition: attachment; filename=$filename");
-    header("Content-Type: text/plain; charset=utf-8");
-    print file_get_contents($uri);
-    drupal_exit();
-  }
-  else {
-    drupal_set_message('Nothing to export.');
-  }
+  _locale_export_po($language, _locale_export_po_generate($language, _locale_export_get_strings($language, $content_options)));
 }
 
 /**
@@ -358,12 +288,9 @@ function locale_translate_batch_build($files, $finish_feedback = FALSE) {
 /**
  * Perform interface translation import as a batch step.
  *
- * The given filepath is matched against ending with '{langcode}.po'. When
- * matched the filepath is added to batch context.
- *
  * @param $filepath
  *   Path to a file to import.
- * @param $context
+ * @param $results
  *   Contains a list of files imported.
  */
 function locale_translate_batch_import($filepath, &$context) {
@@ -371,15 +298,8 @@ function locale_translate_batch_import($filepath, &$context) {
   // we can extract the language code to use for the import from the end.
   if (preg_match('!(/|\.)([^\./]+)\.po$!', $filepath, $langcode)) {
     $file = entity_create('file', array('filename' => drupal_basename($filepath), 'uri' => $filepath));
-    // We need only the last match
-    $langcode = array_pop($langcode);
-    try {
-      $report = GetText::fileToDatabase($file, $langcode, array(), LOCALE_NOT_CUSTOMIZED);
-      $context['results']['files'][$filepath] = $filepath;
-      $context['results']['stats'][$filepath] = $report;
-    } catch (Exception $exc) {
-      drupal_set_message(print_r($exc, TRUE));
-    }
+    _locale_import_read_po('db-store', $file, array(), $langcode[2]);
+    $context['results'][] = $filepath;
   }
 }
 
@@ -388,29 +308,6 @@ function locale_translate_batch_import($filepath, &$context) {
  */
 function locale_translate_batch_finished($success, $results) {
   if ($success) {
-    $additions = $updates = $deletes = $skips = 0;
-    drupal_set_message(format_plural(count($results['files']), 'One translation file imported.', '@count translation files imported.'));
-    $skipped_files = array();
-    foreach ($results['stats'] as $filepath => $report) {
-      $additions += $report['additions'];
-      $updates += $report['updates'];
-      $deletes += $report['deletes'];
-      $skips += $report['skips'];
-      if ($report['skips'] > 0) {
-        $skipped_files[] = $filepath;
-      }
-    }
-    drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes)));
-    watchdog('locale', 'The translation was succesfully imported. %number new strings added, %update updated and %delete removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes));
-    if ($skips) {
-      if (module_exists('dblog')) {
-        $skip_message = format_plural($skips, 'A translation string was skipped because of disallowed or malformed HTML. <a href="@url">See the log</a> for details.', '@count translation strings were skipped because of disallowed or malformed HTML. <a href="@url">See the log</a> for details.', array('@url' => url('admin/reports/dblog')));
-      }
-      else {
-        $skip_message = format_plural($skips, 'A translation string was skipped because of disallowed or malformed HTML. See the log for details.', '@count translation strings were skipped because of disallowed or malformed HTML. See the log for details.');
-      }
-      drupal_set_message($skip_message, 'error');
-      watchdog('locale', '@count disallowed HTML string(s) in files: @files.', array('@count' => $skips, '@files' => implode(',', $skipped_files)), WATCHDOG_WARNING);
-    }
+    drupal_set_message(format_plural(count($results), 'One translation file imported.', '@count translation files imported.'));
   }
 }
diff --git a/core/modules/locale/tests/test2.xx.po b/core/modules/locale/tests/test2.xx.po
deleted file mode 100755
index 1733f69..0000000
--- a/core/modules/locale/tests/test2.xx.po
+++ /dev/null
@@ -1,15 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Drupal 7\\n"
-"MIME-Version: 1.0\\n"
-"Content-Type: text/plain; charset=UTF-8\\n"
-"Content-Transfer-Encoding: 8bit\\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
-
-msgid "xyzzy"
-msgstr "<strong>html</strong>"
-
-msgid "xyzzy2"
-msgstr "<script>html</script>"
-
-
diff --git a/core/modules/locale/tests/test3.xx.po b/core/modules/locale/tests/test3.xx.po
deleted file mode 100755
index 3840a0a..0000000
--- a/core/modules/locale/tests/test3.xx.po
+++ /dev/null
@@ -1,16 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Drupal 7\\n"
-"MIME-Version: 1.0\\n"
-"Content-Type: text/plain; charset=UTF-8\\n"
-"Content-Transfer-Encoding: 8bit\\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
-
-msgid "HTTP Result Code: !status"
-msgstr ""
-"a multiline test"
-"second\r\n"
-"third"
-
-
-
