diff --git a/core/includes/gettext.inc b/core/includes/gettext.inc
index 95e84cf..9ed1441 100644
--- a/core/includes/gettext.inc
+++ b/core/includes/gettext.inc
@@ -26,8 +26,11 @@
  * @param $mode
  *   Should existing translations be replaced LOCALE_IMPORT_KEEP or
  *   LOCALE_IMPORT_OVERWRITE.
+ * @param $customized
+ *   Whether the strings being imported should be saved as customized.
+ *   Use LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
  */
-function _locale_import_po($file, $langcode, $mode) {
+function _locale_import_po($file, $langcode, $mode, $customized = LOCALE_NOT_CUSTOMIZED) {
   // Try to allocate enough time to parse and import the data.
   drupal_set_time_limit(240);
 
@@ -38,7 +41,7 @@ function _locale_import_po($file, $langcode, $mode) {
   }
 
   // Get strings from file (returns on failure after a partial import, or on success)
-  $status = _locale_import_read_po('db-store', $file, $mode, $langcode);
+  $status = _locale_import_read_po('db-store', $file, $mode, $langcode, $customized);
   if ($status === FALSE) {
     // Error messages are set in _locale_import_read_po().
     return FALSE;
@@ -85,8 +88,11 @@ function _locale_import_po($file, $langcode, $mode) {
  *   LOCALE_IMPORT_OVERWRITE.
  * @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, $mode = NULL, $lang = NULL) {
+function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL, $customized = LOCALE_NOT_CUSTOMIZED) {
 
   // The file will get closed by PHP on returning from this function.
   $fd = fopen($file->uri, 'rb');
@@ -138,7 +144,7 @@ function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL) {
       }
       elseif (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
         // We are currently in string token, close it out.
-        _locale_import_one_string($op, $current, $mode, $lang, $file);
+        _locale_import_one_string($op, $current, $mode, $lang, $file, $customized);
 
         // Start a new entry for the comment.
         $current         = array();
@@ -182,7 +188,7 @@ function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL) {
 
       if (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
         // We are currently in a message string, close it out.
-        _locale_import_one_string($op, $current, $mode, $lang, $file);
+        _locale_import_one_string($op, $current, $mode, $lang, $file, $customized);
 
         // Start a new context for the id.
         $current = array();
@@ -212,7 +218,7 @@ function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL) {
 
       if (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
         // We are currently in a message, start a new one.
-        _locale_import_one_string($op, $current, $mode, $lang, $file);
+        _locale_import_one_string($op, $current, $mode, $lang, $file, $customized);
         $current = array();
       }
       elseif (!empty($current['msgctxt'])) {
@@ -326,7 +332,7 @@ function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL) {
 
   // End of PO file, closed out the last entry.
   if (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
-    _locale_import_one_string($op, $current, $mode, $lang, $file);
+    _locale_import_one_string($op, $current, $mode, $lang, $file, $customized);
   }
   elseif ($context != 'COMMENT') {
     _locale_import_message('The translation file %filename ended unexpectedly at line %line.', $file, $lineno);
@@ -368,8 +374,11 @@ function _locale_import_message($message, $file, $lineno = NULL) {
  * @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, $mode = NULL, $lang = NULL, $file = NULL) {
+function _locale_import_one_string($op, $value = NULL, $mode = 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());
@@ -429,7 +438,7 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL
             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);
+            $plid = _locale_import_one_string_db($report, $lang, isset($value['msgctxt']) ? $value['msgctxt'] : '', $english[$key], $trans, $comments, $mode, $customized, $plid, $key);
           }
         }
 
@@ -437,7 +446,7 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL
           // 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'] : '', $english, $translation, $comments, $mode, $customized);
         }
       }
   } // end of db-store operation
@@ -461,6 +470,9 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL
  *   Location value to save with source string.
  * @param $mode
  *   Import mode to use, LOCALE_IMPORT_KEEP or LOCALE_IMPORT_OVERWRITE.
+ * @param $customized
+ *   Whether the strings being imported should be saved as customized.
+ *   Use LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
  * @param $plid
  *   Optional plural ID to use.
  * @param $plural
@@ -469,7 +481,7 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL
  * @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, $mode, $plid = 0, $plural = 0) {
+function _locale_import_one_string_db(&$report, $langcode, $context, $source, $translation, $location, $mode, $customized = LOCALE_NOT_CUSTOMIZED, $plid = 0, $plural = 0) {
   $lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = :context", array(':source' => $source, ':context' => $context))->fetchField();
 
   if (!empty($translation)) {
@@ -488,9 +500,11 @@ function _locale_import_one_string_db(&$report, $langcode, $context, $source, $t
         ->condition('lid', $lid)
         ->execute();
 
-      $exists = db_query("SELECT COUNT(lid) FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $langcode))->fetchField();
+      // See whether a translation exists already and is customized;
+      // we'll consider it customized if at least one of the plural forms is.
+      $existing = db_query("SELECT MAX(customized) FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $langcode))->fetchField();
 
-      if (!$exists) {
+      if (!isset($existing)) {
         // No translation in this language.
         db_insert('locales_target')
           ->fields(array(
@@ -499,18 +513,21 @@ function _locale_import_one_string_db(&$report, $langcode, $context, $source, $t
             'translation' => $translation,
             'plid' => $plid,
             'plural' => $plural,
+            'customized' => $customized,
           ))
           ->execute();
 
         $report['additions']++;
       }
-      elseif ($mode == LOCALE_IMPORT_OVERWRITE) {
+      elseif ($mode == LOCALE_IMPORT_OVERWRITE ||
+          ($mode == LOCALE_IMPORT_CHECK_CUSTOMIZED && $existing == $customized)) {
         // Translation exists, only overwrite if instructed.
         db_update('locales_target')
           ->fields(array(
             'translation' => $translation,
             'plid' => $plid,
             'plural' => $plural,
+            'customized' => $customized,
           ))
           ->condition('language', $langcode)
           ->condition('lid', $lid)
@@ -535,7 +552,8 @@ function _locale_import_one_string_db(&$report, $langcode, $context, $source, $t
            'language' => $langcode,
            'translation' => $translation,
            'plid' => $plid,
-           'plural' => $plural
+           'plural' => $plural,
+           'customized' => $customized,
         ))
         ->execute();
 
@@ -866,17 +884,35 @@ function _locale_import_parse_quoted($string) {
  * @param $language
  *   Language object to generate the output for, or NULL if generating
  *   translation template.
+ * @param $customized
+ *   Customization status of translation strings; if not NULL, only
+ *   translations with this status (LOCALE_NOT_CUSTOMIZED / LOCALE_CUSTOMIZED)
+ *   will be exported. 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) {
-  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));
+function _locale_export_get_strings($language = NULL, $customized = NULL) {
+
+  // Build and execute query to collect source strings and translations.
+  $query = db_select('locales_source', 's');
+  if (!empty($language)) {
+    $query->leftJoin('locales_target', 't', 's.lid = t.lid AND t.language = :language', array(':language' => $language->langcode));
+    if (isset($customized)) {
+      $query->condition('t.customized', $customized);
+    }
+    $query->fields('t', array('translation'));
   }
   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");
+    $query->leftJoin('locales_target', 't', 's.lid = t.lid');
   }
+  $query->fields('s', array('lid', 'source', 'context', 'location'))
+    ->fields('t', array('plid', 'plural'))
+    ->orderBy('t.plid')
+    ->orderBy('t.plural');
+  $result = $query->execute();
+
+  // Structure results in an array with metainformation on the strings.
   $strings = array();
   foreach ($result as $child) {
     $string = array(
diff --git a/core/includes/locale.inc b/core/includes/locale.inc
index 8a381ed..4047921 100644
--- a/core/includes/locale.inc
+++ b/core/includes/locale.inc
@@ -73,26 +73,50 @@ define('LOCALE_JS_OBJECT_CONTEXT', '
 ');
 
 /**
- * Translation import mode overwriting all existing translations
- * if new translated version available.
+ * Flag for locally not customized interface translation.
+ *
+ * Such translations are imported from .po files downloaded from
+ * localize.drupal.org for example.
+ */
+const LOCALE_NOT_CUSTOMIZED = 0;
+
+/**
+ * Flag for locally customized interface translation.
+ *
+ * Such translations are edited from their imported originals on the user
+ * interface or are imported as customized.
+ */
+const LOCALE_CUSTOMIZED = 1;
+
+/**
+ * Translation import mode not keeping existing translations.
+ *
+ * All translations are overwritten if a new translated version is available.
  */
 const LOCALE_IMPORT_OVERWRITE = 0;
 
 /**
- * Translation import mode keeping existing translations and only
- * inserting new strings.
+ * Translation import mode keeping existing translations.
+ *
+ * Only new translations (for previously untranslated strings) are inserted.
  */
 const LOCALE_IMPORT_KEEP = 1;
 
 /**
- * URL language negotiation: use the path prefix as URL language
- * indicator.
+ * Translation import mode keeping translations whose customized state differs.
+ *
+ * Translations are only overwritten if the 'customized' state of the
+ * new translated version matches that of the existing translation.
+ */
+const LOCALE_IMPORT_CHECK_CUSTOMIZED = 2;
+
+/**
+ * URL language negotiation: use the path prefix as URL language indicator.
  */
 const LANGUAGE_NEGOTIATION_URL_PREFIX = 0;
 
 /**
- * URL language negotiation: use the domain as URL language
- * indicator.
+ * URL language negotiation: use the domain as URL language indicator.
  */
 const LANGUAGE_NEGOTIATION_URL_DOMAIN = 1;
 
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index a4982e1..b277d0e 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -34,35 +34,49 @@ function locale_translate_import_form($form, &$form_state) {
   else {
     $default = key($existing_languages);
     $language_options = array(
-      t('Already added languages') => $existing_languages,
+      t('Existing languages') => $existing_languages,
       t('Languages not yet added') => language_admin_predefined_list()
     );
   }
 
-  $form['import'] = array('#type' => 'fieldset',
-    '#title' => t('Import translation'),
-  );
-  $form['import']['file'] = array('#type' => 'file',
-    '#title' => t('Language file'),
+  $form['file'] = array(
+    '#type' => 'file',
+    '#title' => t('Translation file'),
     '#size' => 50,
     '#description' => t('A Gettext Portable Object (<em>.po</em>) file.'),
   );
-  $form['import']['langcode'] = array('#type' => 'select',
-    '#title' => t('Import into'),
+  $form['langcode'] = array(
+    '#type' => 'select',
+    '#title' => t('Language'),
     '#options' => $language_options,
     '#default_value' => $default,
-    '#description' => t('Choose the language you want to add strings into. If you choose a language which is not yet set up, it will be added.'),
   );
-  $form['import']['mode'] = array('#type' => 'radios',
-    '#title' => t('Mode'),
-    '#default_value' => LOCALE_IMPORT_KEEP,
-    '#options' => array(
-      LOCALE_IMPORT_OVERWRITE => t('Strings in the uploaded file replace existing ones, new ones are added. The plural format is updated.'),
-      LOCALE_IMPORT_KEEP => t('Existing strings and the plural format are kept, only new strings are added.')
+  $form['customized'] = array(
+    '#title' => t('Import contains custom translations'),
+    '#type' => 'checkbox',
+  );
+  $form['mode_noncustomized'] = array(
+    '#title' => t('Overwrite existing non-customized translations'),
+    '#type' => 'checkbox',
+    '#default_value' => TRUE,
+    '#states' => array(
+      'checked' => array(
+         ':input[name=customized]' => array('checked' => TRUE),
+      ),
     ),
   );
-  $form['import']['submit'] = array('#type' => 'submit', '#value' => t('Import'));
+  $form['mode_customized'] = array(
+    '#title' => t('Overwrite existing customized translations'),
+    '#type' => 'checkbox',
+  );
 
+  $form['actions'] = array(
+    '#type' => 'actions'
+  );
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Import')
+  );
   return $form;
 }
 
@@ -87,9 +101,10 @@ function locale_translate_import_form_submit($form, &$form_state) {
       language_save($language);
       drupal_set_message(t('The language %language has been created.', array('%language' => t($predefined[$langcode][0]))));
     }
+    $customized = $form_state['values']['customized'] ? LOCALE_CUSTOMIZED : LOCALE_NOT_CUSTOMIZED;
 
     // Now import strings into the language
-    if ($return = _locale_import_po($file, $langcode, $form_state['values']['mode']) == FALSE) {
+    if ($return = _locale_import_po($file, $langcode, $form_state['values']['mode'], $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);
@@ -106,11 +121,9 @@ function locale_translate_import_form_submit($form, &$form_state) {
 }
 
 /**
- * User interface for the translation export screen.
+ * Builds form to export Gettext translation files.
  */
-function locale_translate_export_screen() {
-  // Get all enabled languages, except English, if we should not translate that.
-  drupal_static_reset('language_list');
+function locale_translate_export_form($form, &$form_state) {
   $languages = language_list(TRUE);
   $language_options = array();
   foreach ($languages as $langcode => $language) {
@@ -119,64 +132,54 @@ function locale_translate_export_screen() {
     }
   }
 
-  $output = '';
-  // Offer translation export if any language is set up.
-  if (!empty($language_options)) {
-    $elements = drupal_get_form('locale_translate_export_po_form', $language_options);
-    $output = drupal_render($elements);
-  }
-  $elements = drupal_get_form('locale_translate_export_pot_form');
-  $output .= drupal_render($elements);
-  return $output;
-}
-
-/**
- * Form to export PO files for the languages provided.
- *
- * @param $names
- *   An associate array with localized language names
- */
-function locale_translate_export_po_form($form, &$form_state, $names) {
-  $form['export_title'] = array('#type' => 'item',
-    '#title' => t('Export translation'),
+  $form['langcode'] = array(
+    '#type' => 'select',
+    '#title' => t('Language'),
+    '#options' => $language_options,
+    '#empty_option' => t('Source text only'),
+    '#empty_value' => LANGUAGE_SYSTEM,
+  );
+  $form['contents'] = array(
+    '#type' => 'container',
+    '#states' => array(
+      'invisible' => array(
+        ':input[name=langcode]' => array('value' => LANGUAGE_SYSTEM),
+      )
+    ),
   );
-  $form['langcode'] = array('#type' => 'select',
-    '#title' => t('Language name'),
-    '#options' => $names,
-    '#description' => t('Select the language to export in Gettext Portable Object (<em>.po</em>) format.'),
+  $form['contents']['customized'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Include customized translations'),
+    '#default_value' => TRUE,
+  );
+  $form['contents']['noncustomized'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Include non-customized translations'),
+    '#default_value' => TRUE,
   );
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Export'));
-  return $form;
-}
 
-/**
- * Translation template export form.
- */
-function locale_translate_export_pot_form() {
-  // Complete template export of the strings
-  $form['export_title'] = array('#type' => 'item',
-    '#title' => t('Export template'),
-    '#description' => t('Generate a Gettext Portable Object Template (<em>.pot</em>) file with all strings from the Drupal locale database.'),
+  $form['actions'] = array(
+    '#type' => 'actions'
+  );
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Export')
   );
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Export'));
-  // Reuse PO export submission callback.
-  $form['#submit'][] = 'locale_translate_export_po_form_submit';
   return $form;
 }
 
 /**
- * Process a translation (or template) export form submission.
+ * Processes a translation (or template) export form submission.
  */
-function locale_translate_export_po_form_submit($form, &$form_state) {
+function locale_translate_export_form_submit($form, &$form_state) {
   // If template is required, language code is not given.
   $language = NULL;
-  if (isset($form_state['values']['langcode'])) {
-    $languages = language_list();
-    $language = $languages[$form_state['values']['langcode']];
+  $customized = NULL;
+  if ($form_state['values']['langcode'] != LANGUAGE_SYTEM) {
+    $language = language_load($form_state['values']['langcode']);
+    $customized = ($form_state['values']['customized'] == -1) ? NULL : $form_state['values']['customized'];
   }
-  _locale_export_po($language, _locale_export_po_generate($language, _locale_export_get_strings($language)));
+  _locale_export_po($language, _locale_export_po_generate($language, _locale_export_get_strings($language, $customized)));
 }
 
 /**
diff --git a/core/modules/locale/locale.css b/core/modules/locale/locale.css
index 66de82b..96f08b9 100644
--- a/core/modules/locale/locale.css
+++ b/core/modules/locale/locale.css
@@ -6,7 +6,7 @@
 
 #locale-translation-filter-form .form-item-language,
 #locale-translation-filter-form .form-item-translation,
-#locale-translation-filter-form .form-item-group {
+#locale-translation-filter-form .form-item-customized {
   float: left; /* LTR */
   padding-right: .8em; /* LTR */
   margin: 0.1em;
diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install
index aad91ab..5a3c6d6 100644
--- a/core/modules/locale/locale.install
+++ b/core/modules/locale/locale.install
@@ -188,6 +188,11 @@ function locale_schema() {
         'default' => 0,
         'description' => 'Plural index number in case of plural strings.',
       ),
+      'customized' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0, // LOCALE_NOT_CUSTOMIZED
+      ),
     ),
     'primary key' => array('language', 'lid', 'plural'),
     'foreign keys' => array(
@@ -287,6 +292,27 @@ function locale_update_8001() {
 }
 
 /**
+ * Adds customized column to locales_target.
+ */
+function locale_update_8003() {
+  $spec = array(
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0, // LOCALE_NOT_CUSTOMIZED
+  );
+  db_add_field('locales_target', 'customized', $spec);
+
+  // If l10n_update module was installed retain its status data,
+  // but drop the old field from the table.
+  if (db_field_exists('locales_target', 'l10n_status')) {
+    db_update('locales_target')
+      ->expression('customized', 'l10n_status')
+      ->execute();
+    db_drop_field('locales_target', 'l10n_status');
+  }
+}
+
+/**
  * @} End of "addtogroup updates-7.x-to-8.x"
  * The next series of updates should start at 9000.
  */
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index a785317..fed3f7e 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -124,7 +124,8 @@ function locale_menu() {
   );
   $items['admin/config/regional/translate/export'] = array(
     'title' => 'Export',
-    'page callback' => 'locale_translate_export_screen',  // possibly multiple forms concatenated
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('locale_translate_export_form'),
     'access arguments' => array('translate interface'),
     'weight' => 30,
     'type' => MENU_LOCAL_TASK,
diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc
index e41bae5..6f4299f 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -29,6 +29,7 @@ function _locale_translate_seek() {
     $query = array(
       'translation' => 'all',
       'language' => 'all',
+      'customized' => 'all',
       'string' => '',
     );
   }
@@ -36,13 +37,16 @@ function _locale_translate_seek() {
   $sql_query = db_select('locales_source', 's');
   $sql_query->leftJoin('locales_target', 't', 't.lid = s.lid');
   $sql_query->fields('s', array('source', 'location', 'context', 'lid'));
-  $sql_query->fields('t', array('translation', 'language'));
+  $sql_query->fields('t', array('translation', 'language', 'customized'));
 
   // Compute LIKE section.
   switch ($query['translation']) {
     case 'translated':
       $sql_query->condition('t.translation', '%' . db_like($query['string']) . '%', 'LIKE');
       $sql_query->orderBy('t.translation', 'DESC');
+      if ($query['customized'] != 'all') {
+        $sql_query->condition('t.customized', $query['customized'], '=');
+      }
       break;
     case 'untranslated':
       $sql_query->condition(db_and()
@@ -134,7 +138,7 @@ function _locale_translate_seek_query() {
   $query = &drupal_static(__FUNCTION__);
   if (!isset($query)) {
     $query = array();
-    $fields = array('string', 'language', 'translation');
+    $fields = array('string', 'language', 'translation', 'customized');
     foreach ($fields as $field) {
       if (isset($_SESSION['locale_translation_filter'][$field])) {
         $query[$field] = $_SESSION['locale_translation_filter'][$field];
@@ -172,7 +176,25 @@ function locale_translation_filters() {
 
   $filters['translation'] = array(
     'title' => t('Search in'),
-    'options' => array('all' => t('Both translated and untranslated strings'), 'translated' => t('Only translated strings'), 'untranslated' => t('Only untranslated strings')),
+    'options' => array(
+      'all' => t('Both translated and untranslated strings'),
+      'translated' => t('Only translated strings'),
+      'untranslated' => t('Untranslated strings')
+    ),
+  );
+
+  $filters['customized'] = array(
+    'title' => t('Customized'),
+    'options' => array(
+      'all' => t('All'),
+      LOCALE_NOT_CUSTOMIZED => t('Non-customized only'),
+      LOCALE_CUSTOMIZED => t('Customized only'),
+    ),
+    'states' => array(
+      'visible' => array(
+        ':input[name=translation]' => array('value' => 'translated'),
+      )
+    ),
   );
 
   return $filters;
@@ -210,6 +232,9 @@ function locale_translation_filter_form() {
         '#size' => 0,
         '#options' => $filter['options'],
       );
+      if (isset($filter['states'])) {
+        $form['filters']['status'][$key]['#states'] = $filter['states'];
+      }
     }
     if (!empty($_SESSION['locale_translation_filter'][$key])) {
       $form['filters']['status'][$key]['#default_value'] = $_SESSION['locale_translation_filter'][$key];
@@ -353,21 +378,23 @@ function locale_translate_edit_form_submit($form, &$form_state) {
     $translation = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $key))->fetchField();
     if (!empty($value)) {
       // Only update or insert if we have a value to use.
-      if (!empty($translation)) {
+      if (!empty($translation) && ($translation != $value)) {
         db_update('locales_target')
           ->fields(array(
             'translation' => $value,
+            'customized' => LOCALE_CUSTOMIZED,
           ))
           ->condition('lid', $lid)
           ->condition('language', $key)
           ->execute();
       }
-      else {
+      if (empty($translation)) {
         db_insert('locales_target')
           ->fields(array(
             'lid' => $lid,
             'translation' => $value,
             'language' => $key,
+            'customized' => LOCALE_CUSTOMIZED,
           ))
           ->execute();
       }
