diff --git a/core/includes/common.inc b/core/includes/common.inc
index 9f50506..7d94453 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -163,6 +163,15 @@ const DRUPAL_CACHE_PER_PAGE = 0x0004;
 const DRUPAL_CACHE_GLOBAL = 0x0008;
 
 /**
+ * The delimiter used to split plural strings.
+ *
+ * This is the ETX (End of text) character and is used as a minimal means to
+ * separate singular and plural variants in source and translation text. It
+ * was found to be the most compatible delimiter for the supported databases.
+ */
+const LOCALE_PLURAL_DELIMITER = "\03";
+
+/**
  * Adds content to a specified region.
  *
  * @param $region
@@ -1719,27 +1728,34 @@ function format_xml_elements($array) {
  */
 function format_plural($count, $singular, $plural, array $args = array(), array $options = array()) {
   $args['@count'] = $count;
+  // Join both forms to search a translation.
+  $tranlatable_string = implode(LOCALE_PLURAL_DELIMITER, array($singular, $plural));
+  // Translate as usual.
+  $translated_strings = t($tranlatable_string, $args, $options);
+  // Split joined translation strings into array.
+  $translated_array = explode(LOCALE_PLURAL_DELIMITER, $translated_strings);
+
   if ($count == 1) {
-    return t($singular, $args, $options);
+    return $translated_array[0];
   }
 
   // Get the plural index through the gettext formula.
+  // @todo implement static variable to minimize function_exists() usage.
   $index = (function_exists('locale_get_plural')) ? locale_get_plural($count, isset($options['langcode']) ? $options['langcode'] : NULL) : -1;
-  // If the index cannot be computed, use the plural as a fallback (which
-  // allows for most flexiblity with the replaceable @count value).
-  if ($index < 0) {
-    return t($plural, $args, $options);
+  if ($index == 0) {
+    // Singular form.
+    return $translated_array[0];
   }
   else {
-    switch ($index) {
-      case "0":
-        return t($singular, $args, $options);
-      case "1":
-        return t($plural, $args, $options);
-      default:
-        unset($args['@count']);
-        $args['@count[' . $index . ']'] = $count;
-        return t(strtr($plural, array('@count' => '@count[' . $index . ']')), $args, $options);
+    if (isset($translated_array[$index])) {
+      // N-th plural form.
+      return $translated_array[$index];
+    }
+    else {
+      // If the index cannot be computed or there's no translation, use
+      // the second plural form as a fallback (which allows for most flexiblity
+      // with the replaceable @count value).
+      return $translated_array[1];
     }
   }
 }
diff --git a/core/includes/gettext.inc b/core/includes/gettext.inc
index 95e84cf..395376c 100644
--- a/core/includes/gettext.inc
+++ b/core/includes/gettext.inc
@@ -173,7 +173,7 @@ function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL) {
       }
 
       // Append the plural form to the current entry.
-      $current['msgid'] .= "\0" . $quoted;
+      $current['msgid'] .= LOCALE_PLURAL_DELIMITER . $quoted;
 
       $context = 'MSGID_PLURAL';
     }
@@ -390,8 +390,10 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL
 
     // Store the string we got in the database.
     case 'db-store':
-      // We got header information.
+
       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 (($mode != LOCALE_IMPORT_KEEP) || empty($locale_plurals[$lang]['plurals'])) {
           // Since we only need to parse the header if we ought to update the
@@ -413,32 +415,25 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL
       }
 
       else {
-        // Some real string to import.
+        // Found a string to store, clean up and prepare the data.
         $comments = _locale_import_shorten_comments(empty($value['#']) ? array() : $value['#']);
 
-        if (strpos($value['msgid'], "\0")) {
-          // This string has plural versions.
-          $english = explode("\0", $value['msgid'], 2);
-          $entries = array_keys($value['msgstr']);
-          for ($i = 3; $i <= count($entries); $i++) {
-            $english[] = $english[1];
-          }
-          $translation = array_map('_locale_import_append_plural', $value['msgstr'], $entries);
-          $english = array_map('_locale_import_append_plural', $english, $entries);
-          foreach ($translation as $key => $trans) {
-            if ($key == 0) {
-              $plid = 0;
-            }
-            $plid = _locale_import_one_string_db($report, $lang, isset($value['msgctxt']) ? $value['msgctxt'] : '', $english[$key], $trans, $comments, $mode, $plid, $key);
-          }
+        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']);
         }
 
-        else {
-          // A simple string to import.
-          $english = $value['msgid'];
-          $translation = $value['msgstr'];
-          _locale_import_one_string_db($report, $lang, isset($value['msgctxt']) ? $value['msgctxt'] : '', $english, $translation, $comments, $mode);
-        }
+        _locale_import_one_string_db(
+          $report,
+          $lang,
+          isset($value['msgctxt']) ? $value['msgctxt'] : '',
+          $value['msgid'],
+          $value['msgstr'],
+          $comments,
+          $mode
+        );
       }
   } // end of db-store operation
 }
@@ -791,27 +786,6 @@ function _locale_import_tokenize_formula($formula) {
 }
 
 /**
- * Adds count indices to a string.
- *
- * Callback for array_map() within _locale_import_one_string().
- *
- * @param $entry
- *   An array element.
- * @param $key
- *   Index of the array element.
- */
-function _locale_import_append_plural($entry, $key) {
-  // No modifications for 0, 1
-  if ($key == 0 || $key == 1) {
-    return $entry;
-  }
-
-  // First remove any possibly false indices, then add new ones
-  $entry = preg_replace('/(@count)\[[0-9]\]/', '\\1', $entry);
-  return preg_replace('/(@count)/', "\\1[$key]", $entry);
-}
-
-/**
  * Generates a short, one-string version of the passed comment array.
  *
  * @param $comment
@@ -872,28 +846,19 @@ function _locale_import_parse_quoted($string) {
  */
 function _locale_export_get_strings($language = NULL) {
   if (isset($language)) {
-    $result = db_query("SELECT s.lid, s.source, s.context, s.location, t.translation, t.plid, t.plural FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language ORDER BY t.plid, t.plural", array(':language' => $language->langcode));
+    $result = db_query("SELECT s.lid, s.source, s.context, s.location, t.translation FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language", array(':language' => $language->langcode));
   }
   else {
-    $result = db_query("SELECT s.lid, s.source, s.context, s.location, t.plid, t.plural FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid ORDER BY t.plid, t.plural");
+    $result = db_query("SELECT s.lid, s.source, s.context, s.location FROM {locales_source} s");
   }
   $strings = array();
   foreach ($result as $child) {
-    $string = array(
+    $strings[$child->lid] = array(
       'comment'     => $child->location,
       'source'      => $child->source,
       'context'     => $child->context,
       'translation' => isset($child->translation) ? $child->translation : '',
     );
-    if ($child->plid) {
-      // Has a parent lid. Since we process in the order of plids,
-      // we already have the parent in the array, so we can add the
-      // lid to the next plural version to it. This builds a linked
-      // list of plurals.
-      $string['child'] = TRUE;
-      $strings[$child->plid]['plural'] = $child->lid;
-    }
-    $strings[$child->lid] = $string;
   }
   return $strings;
 }
@@ -933,6 +898,12 @@ function _locale_export_po_generate($language = NULL, $strings = array(), $heade
       $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 {
@@ -956,41 +927,38 @@ function _locale_export_po_generate($language = NULL, $strings = array(), $heade
   $output = $header . "\n";
 
   foreach ($strings as $lid => $string) {
-    // Only process non-children, children are output below their parent.
-    if (!isset($string['child'])) {
-      if ($string['comment']) {
-        $output .= '#: ' . $string['comment'] . "\n";
-      }
-      if (!empty($string['context'])) {
-        $output .= 'msgctxt ' . _locale_export_string($string['context']);
-      }
-      $output .= 'msgid ' . _locale_export_string($string['source']);
-      if (!empty($string['plural'])) {
-        $plural = $string['plural'];
-        $output .= 'msgid_plural ' . _locale_export_string($strings[$plural]['source']);
-        if (isset($language)) {
-          $translation = $string['translation'];
-          for ($i = 0; $i < $locale_plurals[$language->langcode]['plurals']; $i++) {
-            $output .= 'msgstr[' . $i . '] ' . _locale_export_string($translation);
-            if ($plural) {
-              $translation = _locale_export_remove_plural($strings[$plural]['translation']);
-              $plural = isset($strings[$plural]['plural']) ? $strings[$plural]['plural'] : 0;
-            }
-            else {
-              $translation = '';
-            }
+    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 .= 'msgstr ' . _locale_export_string($string['translation']);
+        $output .= 'msgstr[0] ""' . "\n";
+        $output .= 'msgstr[1] ""' . "\n";
       }
-      $output .= "\n";
     }
+    else {
+      $output .= 'msgid ' . _locale_export_string($string['source']);
+      $output .= 'msgstr ' . _locale_export_string($string['translation']);
+    }
+    $output .= "\n";
   }
   return $output;
 }
@@ -1087,12 +1055,5 @@ function _locale_export_wrap($str, $len) {
 }
 
 /**
- * Removes plural index information from a string.
- */
-function _locale_export_remove_plural($entry) {
-  return preg_replace('/(@count)\[[0-9]\]/', '\\1', $entry);
-}
-
-/**
  * @} End of "locale-api-import-export"
  */
diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc
index e41bae5..5d98758 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -278,13 +278,37 @@ function locale_translate_edit_form($form, &$form_state, $lid) {
     drupal_set_message(t('String not found.'), 'error');
     drupal_goto('admin/config/regional/translate/translate');
   }
-
-  // Add original text to the top and some values for form altering.
-  $form['original'] = array(
-    '#type'  => 'item',
-    '#title' => t('Original text'),
-    '#markup' => check_plain(wordwrap($source->source, 0)),
-  );
+  // Split source to work with plural values.
+  $source_array = explode(LOCALE_PLURAL_DELIMITER, $source->source);
+  if (count($source_array) == 1) {
+    // Add original text value and mark as non-plural.
+    $form['plural'] = array(
+      '#type' => 'value',
+      '#value' => 0
+    );
+    $form['original'] = array(
+      '#type'  => 'item',
+      '#title' => t('Original text'),
+      '#markup' => check_plain($source_array[0]),
+    );
+  }
+  else {
+    // Add original text value and mark as plural.
+    $form['plural'] = array(
+      '#type' => 'value',
+      '#value' => 1
+    );
+    $form['original_singular'] = array(
+      '#type'  => 'item',
+      '#title' => t('Original singular form'),
+      '#markup' => check_plain($source_array[0]),
+    );
+    $form['original_plural'] = array(
+      '#type'  => 'item',
+      '#title' => t('Original plural form'),
+      '#markup' => check_plain($source_array[1]),
+    );
+  }
   if (!empty($source->context)) {
     $form['context'] = array(
       '#type' => 'item',
@@ -307,22 +331,68 @@ function locale_translate_edit_form($form, &$form_state, $lid) {
   if (!locale_translate_english()) {
     unset($languages['en']);
   }
-  $form['translations'] = array('#tree' => TRUE);
+  // Store languages to iterate for validation and submission of the form.
+  $form_state['langcodes'] = array_keys($languages);
+  $plural_formulas = variable_get('locale_translation_plurals', array());
+
+  $form['translations'] = array(
+    '#type' => 'vertical_tabs',
+    '#tree' => TRUE
+  );
+
   // Approximate the number of rows to use in the default textarea.
-  $rows = min(ceil(str_word_count($source->source) / 12), 10);
+  $rows = min(ceil(str_word_count($source_array[0]) / 12), 10);
   foreach ($languages as $langcode => $language) {
     $form['translations'][$langcode] = array(
-      '#type' => 'textarea',
+      '#type' => 'fieldset',
       '#title' => $language->name,
-      '#rows' => $rows,
-      '#default_value' => '',
     );
+    if (empty($form['plural']['#value'])) {
+      $form['translations'][$langcode][0] = array(
+        '#type' => 'textarea',
+        '#title' => $language->name,
+        '#rows' => $rows,
+        '#default_value' => '',
+      );
+    }
+    else {
+      // Dealing with plural strings.
+      if (isset($plural_formulas[$langcode]['plurals']) && $plural_formulas[$langcode]['plurals'] > 1) {
+        // Add a textarea for each plural variant.
+        for ($i = 0; $i < $plural_formulas[$langcode]['plurals']; $i++) {
+          $form['translations'][$langcode][$i] = array(
+            '#type' => 'textarea',
+            '#title' => ($i == 0 ? t('Sigular form') : format_plural($i, 'First plural form', '@count. plural form')),
+            '#rows' => $rows,
+            '#default_value' => '',
+          );
+        }
+      }
+      else {
+        // Fallback for unknow number of plurals.
+        $form['translations'][$langcode][0] = array(
+          '#type' => 'textarea',
+          '#title' => t('Sigular form'),
+          '#rows' => $rows,
+          '#default_value' => '',
+        );
+        $form['translations'][$langcode][1] = array(
+          '#type' => 'textarea',
+          '#title' => t('Plural form'),
+          '#rows' => $rows,
+          '#default_value' => '',
+        );
+      }
+    }
   }
 
   // Fetch translations and fill in default values in the form.
   $result = db_query("SELECT DISTINCT translation, language FROM {locales_target} WHERE lid = :lid", array(':lid' => $lid));
   foreach ($result as $translation) {
-    $form['translations'][$translation->language]['#default_value'] = $translation->translation;
+    $translation_array = explode(LOCALE_PLURAL_DELIMITER, $translation->translation);
+    for ($i = 0; $i < count($translation_array); $i++) {
+      $form['translations'][$translation->language][$i]['#default_value'] = $translation_array[$i];
+    }
   }
 
   $form['actions'] = array('#type' => 'actions');
@@ -334,10 +404,12 @@ function locale_translate_edit_form($form, &$form_state, $lid) {
  * Validate string editing form submissions.
  */
 function locale_translate_edit_form_validate($form, &$form_state) {
-  foreach ($form_state['values']['translations'] as $key => $value) {
-    if (!locale_string_is_safe($value)) {
-      form_set_error('translations', t('The submitted string contains disallowed HTML: %string', array('%string' => $value)));
-      watchdog('locale', 'Attempted submission of a translation string with disallowed HTML: %string', array('%string' => $value), WATCHDOG_WARNING);
+  foreach ($form_state['langcodes'] as $langcode) {
+    foreach ($form_state['values']['translations'][$langcode] as $key => $value) {
+      if (!locale_string_is_safe($value)) {
+        form_set_error("translations][$langcode][$key", t('The submitted string contains disallowed HTML: %string', array('%string' => $value)));
+        watchdog('locale', 'Attempted submission of a translation string with disallowed HTML: %string', array('%string' => $value), WATCHDOG_WARNING);
+      }
     }
   }
 }
@@ -349,9 +421,19 @@ function locale_translate_edit_form_validate($form, &$form_state) {
  */
 function locale_translate_edit_form_submit($form, &$form_state) {
   $lid = $form_state['values']['lid'];
-  foreach ($form_state['values']['translations'] as $key => $value) {
-    $translation = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $key))->fetchField();
-    if (!empty($value)) {
+  foreach ($form_state['langcodes'] as $langcode) {
+    // Serialize plural variants in one string by LOCALE_PLURAL_DELIMITER.
+    $value = implode(LOCALE_PLURAL_DELIMITER, $form_state['values']['translations'][$langcode]);
+    $translation = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $langcode))->fetchField();
+    // No translation when all strings are empty.
+    $has_translation = FALSE;
+    foreach ($form_state['values']['translations'][$langcode] as $string) {
+      if (!empty($string)) {
+        $has_translation = TRUE;
+        break;
+      }
+    }
+    if ($has_translation) {
       // Only update or insert if we have a value to use.
       if (!empty($translation)) {
         db_update('locales_target')
@@ -359,7 +441,7 @@ function locale_translate_edit_form_submit($form, &$form_state) {
             'translation' => $value,
           ))
           ->condition('lid', $lid)
-          ->condition('language', $key)
+          ->condition('language', $langcode)
           ->execute();
       }
       else {
@@ -367,7 +449,7 @@ function locale_translate_edit_form_submit($form, &$form_state) {
           ->fields(array(
             'lid' => $lid,
             'translation' => $value,
-            'language' => $key,
+            'language' => $langcode,
           ))
           ->execute();
       }
@@ -376,12 +458,12 @@ function locale_translate_edit_form_submit($form, &$form_state) {
       // Empty translation entered: remove existing entry from database.
       db_delete('locales_target')
         ->condition('lid', $lid)
-        ->condition('language', $key)
+        ->condition('language', $langcode)
         ->execute();
     }
 
     // Force JavaScript translation file recreation for this language.
-    _locale_invalidate_js($key);
+    _locale_invalidate_js($langcode);
   }
 
   drupal_set_message(t('The string has been saved.'));
diff --git a/core/modules/locale/locale.test b/core/modules/locale/locale.test
index 208c5e6..3f257a4 100644
--- a/core/modules/locale/locale.test
+++ b/core/modules/locale/locale.test
@@ -267,8 +267,8 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
     $this->clickLink(t('edit'));
     $string_edit_url = $this->getUrl();
     $edit = array(
-      "translations[$langcode]" => $translation,
-      'translations[en]' => $translation_to_en,
+      "translations[$langcode][0]" => $translation,
+      'translations[en][0]' => $translation_to_en,
     );
     $this->drupalPost(NULL, $edit, t('Save translations'));
     $this->assertText(t('The string has been saved.'), t('The string has been saved.'));
@@ -367,7 +367,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
     $query->addExpression('min(l.lid)', 'lid');
     $result = $query->condition('l.location', '%.js%', 'LIKE')->execute();
     $url = 'admin/config/regional/translate/edit/' . $result->fetchObject()->lid;
-    $edit = array('translations['. $langcode .']' => $this->randomName());
+    $edit = array('translations['. $langcode .'][0]' => $this->randomName());
     $this->drupalPost($url, $edit, t('Save translations'));
 
     // Trigger JavaScript translation parsing and building.
@@ -434,7 +434,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
     $path = $matches[0];
     foreach ($bad_translations as $key => $translation) {
       $edit = array(
-        "translations[$langcode]" => $translation,
+        "translations[$langcode][0]" => $translation,
       );
       $this->drupalPost($path, $edit, t('Save translations'));
       // Check for a form error on the textarea.
@@ -521,7 +521,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
     preg_match('!admin/config/regional/translate/edit/(\d)+!', $this->getUrl(), $matches);
     $lid = $matches[1];
     $edit = array(
-      "translations[$langcode]" => $translation,
+      "translations[$langcode][0]" => $translation,
     );
     $this->drupalPost(NULL, $edit, t('Save translations'));
 
@@ -792,8 +792,8 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
     // The import should automatically create the corresponding language.
     $this->assertRaw(t('The language %language has been created.', array('%language' => 'French')), t('The language has been automatically created.'));
 
-    // The import should have created 7 strings.
-    $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 9, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
+    // The import should have created 8 strings.
+    $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 8, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
 
     // This import should have saved plural forms to have 2 variants.
     $locale_plurals = variable_get('locale_translation_plurals', array());
@@ -1284,7 +1284,7 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase {
     $string = db_query('SELECT min(lid) AS lid FROM {locales_source} WHERE location LIKE :location', array(
       ':location' => '%.js%',
     ))->fetchObject();
-    $edit = array('translations[fr]' => 'french translation');
+    $edit = array('translations[fr][0]' => 'french translation');
     $this->drupalPost('admin/config/regional/translate/edit/' . $string->lid, $edit, t('Save translations'));
     _locale_rebuild_js('fr');
     $locale_javascripts = variable_get('locale_translation_javascript', array());
@@ -2212,8 +2212,8 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
     // Should find the string and now click edit to post translated string.
     $this->clickLink('edit');
     $edit = array(
-      "translations[$langcode_browser_fallback]" => $language_browser_fallback_string,
-      "translations[$langcode]" => $language_string,
+      "translations[$langcode_browser_fallback][0]" => $language_browser_fallback_string,
+      "translations[$langcode][0]" => $language_string,
     );
     $this->drupalPost(NULL, $edit, t('Save translations'));
 
