diff --git a/core/includes/common.inc b/core/includes/common.inc
index 9d498c2..e454168 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -161,6 +161,11 @@ const DRUPAL_CACHE_PER_PAGE = 0x0004;
 const DRUPAL_CACHE_GLOBAL = 0x0008;
 
 /**
+ * The deliditer used to split plural strings.
+ */
+const LOCALE_PLURAL_DELIMITER = "\03";
+
+/**
  * Adds content to a specified region.
  *
  * @param $region
@@ -1717,27 +1722,35 @@ 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);
+  // @todo Check this against a single plural form languages. (Vietnamese?)
+  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..6642a08 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';
     }
@@ -416,29 +416,16 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL
         // Some real string to import.
         $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);
-          }
-        }
-
-        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);
+        if (is_array($value['msgstr'])) {
+          // Sort plurals by their form number.
+          ksort($value['msgstr']);
+          // Join plurals forms in one string.
+          $value['msgstr'] = implode(LOCALE_PLURAL_DELIMITER, $value['msgstr']);
         }
+        // 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);
       }
   } // end of db-store operation
 }
@@ -791,27 +778,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 +838,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 +890,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";
+        // Store nplurals to not use check in export loop.
+        $nplurals = $locale_plurals[$language->langcode]['plurals'];
+      }
+      else {
+        // Do not export plurals while there's no formula.
+        $nplurals = 0;
       }
     }
     else {
@@ -956,41 +919,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 +1047,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..eb20546 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -278,13 +278,31 @@ 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) {
+    $form['plural'] = array('#type' => 'value', '#value' => 0);
+    // Add original text to the top and some values for form altering.
+    $form['original'] = array(
+      '#type'  => 'item',
+      '#title' => t('Original text'),
+      '#markup' => check_plain($source_array[0]),
+    );
+  }
+  else {
+    // Dealing with plural string.
+    $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 +325,72 @@ 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 submition of the form.
+  $form_state['langcodes'] = array_keys($languages);
+  $plural_formulas = variable_get('locale_translation_plurals', array());
+  if (empty($form['plural']['#value'])) {
+    $form['translations'] = array('#tree' => TRUE);
+  }
+  else {
+    $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',
-      '#title' => $language->name,
-      '#rows' => $rows,
-      '#default_value' => '',
-    );
+    if (!empty($form['plural']['#value'])) {
+      // Display vertical tabs only for plural string translations.
+      $form['translations'][$langcode] = array(
+        '#type' => 'fieldset',
+        '#title' => $language->name,
+        '#collapsible' => TRUE,
+      );
+    }
+    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) {
+        // Build a textarea for each plural form.
+        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, '1 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 +402,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 +419,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) {
+    // Join all plural forms in one string.
+    $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 +439,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 +447,7 @@ function locale_translate_edit_form_submit($form, &$form_state) {
           ->fields(array(
             'lid' => $lid,
             'translation' => $value,
-            'language' => $key,
+            'language' => $langcode,
           ))
           ->execute();
       }
@@ -376,12 +456,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.'));
