diff --git a/core/modules/locale/lib/Drupal/locale/LocaleLookup.php b/core/modules/locale/lib/Drupal/locale/LocaleLookup.php
index 897eaa1..3fd4ff6 100644
--- a/core/modules/locale/lib/Drupal/locale/LocaleLookup.php
+++ b/core/modules/locale/lib/Drupal/locale/LocaleLookup.php
@@ -8,6 +8,8 @@
 namespace Drupal\locale;
 
 use Drupal\Core\Utility\CacheArray;
+use Drupal\locale\LocaleSource;
+use Drupal\locale\LocaleTranslation;
 
 /**
  * Extends CacheArray to allow for dynamic building of the locale cache.
@@ -44,36 +46,22 @@ class LocaleLookup extends CacheArray {
    * Overrides DrupalCacheArray::resolveCacheMiss().
    */
   protected function resolveCacheMiss($offset) {
-    $translation = db_query("SELECT s.lid, t.translation, s.version 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(
-      ':language' => $this->langcode,
-      ':source' => $offset,
-      ':context' => $this->context,
-    ))->fetchObject();
+    $translation = LocaleTranslation::loadBySource($this->langcode, $offset, $this->context, array('lid', 'version', 'translation'));
     if ($translation) {
-      if ($translation->version != VERSION) {
-        // This is the first use of this string under current Drupal version.
-        // Update the {locales_source} table to indicate the string is current.
-        db_update('locales_source')
-          ->fields(array('version' => VERSION))
-          ->condition('lid', $translation->lid)
-          ->execute();
-      }
+      $translation->checkVersion(VERSION);
       $value = !empty($translation->translation) ? $translation->translation : TRUE;
     }
     else {
       // We don't have the source string, update the {locales_source} table to
       // indicate the string is not translated.
-      db_merge('locales_source')
-        ->insertFields(array(
-          'location' => request_uri(),
-          'version' => VERSION,
-        ))
-        ->key(array(
-          'source' => $offset,
-          'context' => $this->context,
-        ))
-        ->execute();
-        $value = TRUE;
+      $source = new LocaleSource(array(
+        'source' => $offset,
+        'context' => $this->context,
+        'location' => request_uri(),
+        'version' => VERSION
+      ));
+      $source->save();
+      $value = TRUE;
     }
     $this->storage[$offset] = $value;
     // Disabling the usage of string caching allows a module to watch for
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleSource.php b/core/modules/locale/lib/Drupal/locale/LocaleSource.php
new file mode 100644
index 0000000..09082ab
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/LocaleSource.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\locale\LocaleSource.
+ */
+
+namespace Drupal\locale;
+
+use Drupal\locale\LocaleString;
+use PDO;
+
+/**
+ * Defines the locale source class.
+ */
+class LocaleSource extends LocaleString {
+
+  /**
+   * Load by locale id.
+   */
+  public static function loadById($lid) {
+    $strings = self::loadMultiple(array('lid' => $lid));
+    return reset($strings);
+  }
+
+  /**
+   * Load by context, source.
+   */
+  public static function loadBySource($source, $context = '') {
+    $strings = self::loadMultiple(array('source' => $source, 'context' => $context));
+    return reset($strings);
+  }
+
+  /**
+   * Load by multiple conditions.
+   */
+  public static function loadMultiple($conditions) {
+    $query = db_select('locales_source', 's')
+      ->fields('s');
+    foreach ($conditions as $field => $value) {
+      $query->conditions('s.' . $field, $value);
+    }
+    $result =$query->execute();
+    $result->setFetchMode(PDO::FETCH_CLASS, 'Drupal\locale\LocaleSource');
+    return $result->fetchAll();
+  }
+
+  /**
+   * Implementation of LocaleString::getSource().
+   */
+  public function getSource() {
+    return $this;
+  }
+
+  /**
+   * Implementation of LocaleString::getString().
+   */
+  public function getString() {
+    return $this->source;
+  }
+
+  /**
+   * Implementation of LocaleString::save().
+   */
+  public function save() {
+    $key_fields = isset($this->lid) ? 'lid' : array();
+    drupal_write_record('locales_source', $this, $key_fields);
+    return $this;
+  }
+
+  /**
+   * Implementation of LocaleString::delete().
+   */
+  public function delete() {
+    if (isset($this->lid)) {
+      $query = db_delete('locales_source')
+        ->condition('lid', $this->lid)
+        ->execute();
+      unset($this->lid);
+    }
+    return $this;
+  }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleString.php b/core/modules/locale/lib/Drupal/locale/LocaleString.php
new file mode 100644
index 0000000..b1dedf9
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/LocaleString.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\locale\LocaleString.
+ */
+
+namespace Drupal\locale;
+
+/**
+ * Defines the locale string class.
+ *
+ * This is the base class for LocaleSource and LocaleTranslation.
+ */
+abstract class LocaleString {
+  /**
+   * The locale ID.
+   *
+   * @var integer
+   */
+  public $lid;
+
+  /**
+   * The string location.
+   *
+   * @var string
+   */
+  public $location = '';
+
+  /**
+   * The source string.
+   *
+   * @var string
+   */
+  public $source;
+
+  /**
+   * The string context.
+   *
+   * @var string
+   */
+  public $context = '';
+
+  /**
+   * The string version.
+   *
+   * @var string
+   */
+  public $version;
+
+  /**
+   * Constructs a new locale source object.
+   *
+   * @param $values
+   *   Object or array with initial values.
+   */
+  public function __construct($values = array()) {
+    // Set initial values.
+    $this->setValues((array)$values);
+  }
+
+  /**
+   * Constructs a new locale source object.
+   */
+  public function setValues(array $values = array()) {
+    foreach ($values as $key => $value) {
+      if (property_exists($this, $key)) {
+        $this->$key = $value;
+      }
+    }
+    return $this;
+  }
+
+  /**
+   * Check whether this string version matches a given version.
+   */
+  public function checkVersion($version) {
+    if ($this->version != $version) {
+      // This is the first use of this string under current Drupal version.
+      // Update the {locales_source} table to indicate the string is current.
+      $source = $this->getSource();
+      $source->version = $version;
+      $source->save();
+    }
+  }
+
+  /**
+   * Save string object to database.
+   */
+  public abstract function save();
+
+  /**
+   * Delete string object from database.
+   */
+  public abstract function delete();
+
+  /**
+   * Split string to work with plural values.
+   */
+  public function getPlurals() {
+    return explode(LOCALE_PLURAL_DELIMITER, $this->getString());
+  }
+
+  /**
+   * Helper function to explode plurals.
+   */
+
+  /**
+   * Get source string object.
+   */
+  public abstract function getSource();
+
+  /**
+   * Get plain string contained in this object.
+   */
+  public abstract function getString();
+
+}
\ No newline at end of file
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleTranslation.php b/core/modules/locale/lib/Drupal/locale/LocaleTranslation.php
new file mode 100644
index 0000000..88283d2
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/LocaleTranslation.php
@@ -0,0 +1,221 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\locale\LocaleTranslation.
+ */
+
+namespace Drupal\locale;
+
+use Drupal\locale\LocaleString;
+use PDO;
+
+/**
+ * Defines the locale translation class.
+ */
+class LocaleTranslation extends LocaleString {
+  /**
+   * The language code.
+   *
+   * @var string
+   */
+  public $language;
+
+  /**
+   * The string translation.
+   *
+   * @var string
+   */
+  public $translation;
+
+  /**
+   * Boolean indicating whether this string is customized.
+   */
+  public $customized;
+
+  /**
+   * Boolean indicating whether the target object exists.
+   */
+  protected $is_new = TRUE;
+
+  /**
+   * Load by context, source, fast query.
+   */
+  public static function loadBySource($langcode, $source, $context = '', $fields = array()) {
+    $conditions = array('language' => $langcode, 'source' => $source, 'context' => $context);
+    $translations = self::loadMultiple($fields, $conditions);
+    return reset($translations);
+  }
+
+  /**
+   * Load by context, source, fast query.
+   */
+  public static function loadById($langcode, $lid, $fields = array()) {
+    $conditions = array('language' => $langcode, 'lid' => $lid);
+    $translations = self::loadMultiple($fields, $conditions);
+    return reset($translations);
+  }
+
+  /**
+   * Load multiple, fast query.
+   *
+   * In order to produce a query as fast as possible we must
+   * pass the exact fields we need to load only that ones.
+   *
+   * @param $conditions
+   *   Array with simple field conditions.
+   * @param $fields
+   *   Fields to load, defaults to all.
+   */
+  public static function loadMultiple($fields, $conditions = array(), $options = array()) {
+    // We get any of the target fields to check later whether this actually has translation.
+    $check_field = 'translation';
+    foreach ($fields as $field_name) {
+      if (self::isTargetField($field_name)) {
+        $check_field = $field_name;
+        break;
+      }
+    }
+    reset($fields);
+    $query = self::buildQuery($fields, $conditions, $options);
+    $result = $query->execute();
+    $result->setFetchMode(PDO::FETCH_CLASS, 'Drupal\locale\LocaleTranslation');
+    $translations = $result->fetchAll();
+    // Set values we already know, they were used as conditions.
+    // We get any of the target fields to check whether this actually has translation.
+    foreach ($translations as $translation) {
+      $translation->setValues($conditions);
+      $translation->setNew(!isset($translation->$check_field));
+    }
+
+    return $translations;
+  }
+
+  /**
+   * Build query with multiple conditions and fields.
+   *
+   * The query uses both locales_source and locales_target tables.
+   *
+   * @returns SelectQuery
+   *
+  */
+  public static function buildQuery($fields = array(), $conditions = array(), $options = array()) {
+    $target_fields = $source_fields = array();
+    foreach ($fields as $field_name) {
+      if (self::isTargetField($field_name)) {
+        $target_fields[] = $field_name;
+      }
+      else {
+        $source_fields[] = $field_name;
+      }
+    }
+    return self::getBaseQuery($conditions, $options)
+      ->fields('s', $source_fields)
+      ->fields('t', $target_fields);
+  }
+
+  /**
+   * Build base query with multiple conditions.
+   *
+   * The query uses both locales_source and locales_target tables.
+   *
+   * @param $conditions
+   *   Array with simple field conditions.
+   * @param $options
+   *   Array of options that may contain the following values.
+   *   - 'not_translated', whether to load not translated strings too (use left join).
+   *   Defaults to TRUE.
+   *   - 'string_filter', filter to apply to both source and target string.
+   *   - 'pager_limit', Use pager and set this limit value.
+   * @returns SelectQuery
+   */
+  public static function getBaseQuery($conditions = array(), $options = array()) {
+    // Add default options and see which kind of join we need.
+    $options += array('not_translated' => TRUE, );
+    // Left join to keep untranslated strings in, inner join to filter for only translations.
+    $join = $options['not_translated'] ? 'leftJoin' : 'innerJoin';
+
+    $query = db_select('locales_source', 's');
+    if (isset($conditions['language'])) {
+      // If we've got a language condition, we use it for the join.
+      $query->$join('locales_target', 't', "t.lid = s.lid AND t.language = :langcode", array(':langcode' => $conditions['language']));
+      unset($conditions['language']);
+    }
+    else {
+      // Since we don't have a language, join with locale id only.
+      $query->$join('locales_target', 't', "t.lid = s.lid");
+    }
+    foreach ($conditions as $field => $value) {
+      $field_alias = (self::isTargetField($field) ? 't.' : 's.') . $field;
+      // Handle NULL condtions too.
+      if (is_null($value)) {
+        $query->isNull($field_alias);
+      }
+      else {
+        $query->condition($field_alias, $value);
+      }
+    }
+    // Process other options
+    if (!empty($options['string_filter'])) {
+      $query->condition(db_or()
+          ->condition('s.source', '%' . db_like($options['string_filter']) . '%', 'LIKE')
+          ->condition('t.translation', '%' . db_like($options['string_filter']) . '%', 'LIKE')
+      );
+    }
+    if (!empty($options['pager_limit'])) {
+      $query = $query->extend('Drupal\Core\Database\Query\PagerSelectExtender')->limit($options['pager_limit']);
+    }
+    return $query;
+  }
+
+  /**
+   * Check whether a field is a target field.
+   */
+  public static function isTargetField($field_name) {
+    return in_array($field_name, array('language', 'translation', 'customized'));
+  }
+  /**
+   * Implementation of LocaleString::getSource().
+   */
+  public function getSource() {
+    return LocaleSource::loadById($this->lid);
+  }
+
+  /**
+   * Implementation of LocaleString::getString().
+   */
+  public function getString() {
+    return $this->translation;
+  }
+  /**
+   * Mar the string as new.
+   */
+  public function setNew($is_new = TRUE) {
+    $this->is_new = $is_new;
+    return $this;
+  }
+
+  /**
+   * Implementation of LocaleString::save().
+   */
+  public function save() {
+    $key_fields = $this->is_new ? array() : array('lid', 'language');
+    drupal_write_record('locales_target', $this, $key_fields);
+    $this->setNew(FALSE);
+    return $this;
+  }
+
+  /**
+   * Implementation of LocaleString::delete().
+   */
+  public function delete() {
+    if (!$this->is_new) {
+      $query = db_delete('locales_target')
+        ->condition('lid', $this->lid)
+        ->condition('language', $this->language)
+        ->execute();
+      $this->setNew();
+    }
+    return $this;
+  }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php b/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php
index c0dfc2f..a6d35d0 100644
--- a/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php
+++ b/core/modules/locale/lib/Drupal/locale/PoDatabaseReader.php
@@ -10,6 +10,8 @@ namespace Drupal\locale;
 use Drupal\Component\Gettext\PoHeader;
 use Drupal\Component\Gettext\PoItem;
 use Drupal\Component\Gettext\PoReaderInterface;
+use Drupal\locale\LocaleTranslation;
+use PDO;
 
 /**
  * Gettext PO reader working with the locale module database.
@@ -109,49 +111,43 @@ class PoDatabaseReader implements PoReaderInterface {
   private function buildQuery() {
     $langcode = $this->_langcode;
     $options = $this->_options;
+    $fields = array('lid', 'source', 'context', 'location');
 
     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));
-      }
+      $conditions = array('language' => $langcode);
+      $fields[] = 'translation';
+      // Translate some options into field conditions.
       if ($options['customized']) {
         if (!$options['not_customized']) {
           // Filter for customized strings only.
-          $query->condition('t.customized', LOCALE_CUSTOMIZED);
+          $conditions['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);
+          $conditions['customized'] = LOCALE_NOT_CUSTOMIZED;
         }
         else {
           // Filter for strings without translation.
-          $query->isNull('t.translation');
+          $conditions['translation'] = NULL;
         }
       }
-      $query->fields('t', array('translation'));
-    }
-    else {
-      $query->leftJoin('locales_target', 't', 's.lid = t.lid');
     }
-    $query->fields('s', array('lid', 'source', 'context', 'location'));
+    // @todo Changed behavior with query builder: if no language condition get all target fields too.
+    // This shouldn't be a problem, just a minor performance glitch.
 
-    $this->_result = $query->execute();
+    // User our query builder with fields and conditions.
+    $result = LocaleTranslation::buildQuery($fields, $conditions, $options)->execute();
+    //$result->setFetchMode(PDO::FETCH_CLASS, 'Drupal\locale\LocaleTranslation');
+    $this->_result = $result;
   }
 
   /**
diff --git a/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php b/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
index 33f05d9..74e8e5d 100644
--- a/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
+++ b/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
@@ -11,6 +11,8 @@ use Drupal\Component\Gettext\PoHeader;
 use Drupal\Component\Gettext\PoItem;
 use Drupal\Component\Gettext\PoReaderInterface;
 use Drupal\Component\Gettext\PoWriterInterface;
+use Drupal\locale\LocaleSource;
+use Drupal\locale\LocaleTranslation;
 
 /**
  * Gettext PO writer working with the locale module database.
@@ -226,12 +228,7 @@ class PoDatabaseWriter implements PoWriterInterface {
 
 
     // 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' => $this->_langcode,
-        ))
-        ->fetchObject();
+    $string = LocaleTranslation::loadBySource($this->_langcode, $source, $context, array('lid', 'customized'));
 
     if (!empty($translation)) {
       // Skip this string unless it passes a check for dangerous code.
@@ -243,27 +240,19 @@ class PoDatabaseWriter implements PoWriterInterface {
       elseif (isset($string->lid)) {
         if (!isset($string->customized)) {
           // No translation in this language.
-          db_insert('locales_target')
-            ->fields(array(
-              'lid' => $string->lid,
-              'language' => $this->_langcode,
+          $string->setValues(array(
               'translation' => $translation,
               'customized' => $customized,
-            ))
-            ->execute();
+          ))->save();
 
           $this->_report['additions']++;
         }
         elseif ($overwrite_options[$string->customized ? 'customized' : 'not_customized']) {
           // Translation exists, only overwrite if instructed.
-          db_update('locales_target')
-            ->fields(array(
+          $string->setValues(array(
               'translation' => $translation,
               'customized' => $customized,
-            ))
-            ->condition('language', $this->_langcode)
-            ->condition('lid', $string->lid)
-            ->execute();
+          ))->save();
 
           $this->_report['updates']++;
         }
@@ -271,33 +260,22 @@ class PoDatabaseWriter implements PoWriterInterface {
       }
       else {
         // No such source string in the database yet.
-        $lid = db_insert('locales_source')
-          ->fields(array(
-            'source' => $source,
-            'context' => $context,
-          ))
-          ->execute();
-
-        db_insert('locales_target')
-          ->fields(array(
-            'lid' => $lid,
-            'language' => $this->_langcode,
-            'translation' => $translation,
-            'customized' => $customized,
-          ))
-          ->execute();
+        $source = new LocaleSource(array('source' => $source, 'context' => $context));
+        $source->save();
+        $target = new LocaleTranslation($source);
+        $target->setValues(array(
+          'language' => $this->_langcode,
+          'translation' => $translation,
+          'customized' => $customized,
+        ))->save();
 
         $this->_report['additions']++;
-        return $lid;
+        return $source->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();
-
+      $string->delete();
       $this->_report['deletes']++;
       return $string->lid;
     }
diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc
index 5fdba76..228e601 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -5,6 +5,8 @@
  * Interface translation summary, editing and deletion user interfaces.
  */
 
+use Drupal\locale\LocaleSource;
+use Drupal\locale\LocaleTranslation;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
@@ -18,42 +20,37 @@ function locale_translate_page() {
 }
 
 /**
- * Build a string search query.
+ * Build a string search query and return array of string objects.
  */
-function locale_translate_query() {
+function locale_translate_query_load_strings() {
   $filter_values = locale_translate_filter_values();
 
-  $sql_query = db_select('locales_source', 's');
   // Language is sanitized to be one of the possible options in
   // locale_translate_filter_values().
-  $sql_query->leftJoin('locales_target', 't', "t.lid = s.lid AND t.language = :langcode", array(':langcode' => $filter_values['langcode']));
-  $sql_query->fields('s', array('source', 'location', 'context', 'lid'));
-  $sql_query->fields('t', array('translation', 'language', 'customized'));
+  $fields = array('source', 'location', 'context', 'lid', 'translation', 'language', 'customized');
+  $conditions = array('language' => $filter_values['langcode']);
+  $options = array('pager_limit' => 30);
 
   if (!empty($filter_values['string'])) {
-    $sql_query->condition(db_or()
-      ->condition('s.source', '%' . db_like($filter_values['string']) . '%', 'LIKE')
-      ->condition('t.translation', '%' . db_like($filter_values['string']) . '%', 'LIKE')
-    );
+    $options['string_filter'] = $filter_values['string'];
   }
 
   // Add translation status conditions.
   switch ($filter_values['translation']) {
     case 'translated':
-      $sql_query->isNotNull('t.translation');
+      $options['not_translated'] = FALSE;
       if ($filter_values['customized'] != 'all') {
-        $sql_query->condition('t.customized', $filter_values['customized']);
+        $conditions['customized'] = $filter_values['customized'];
       }
       break;
 
     case 'untranslated':
-      $sql_query->isNull('t.translation');
+      $conditions['translation'] = NULL;
       break;
 
   }
 
-  $sql_query = $sql_query->extend('Drupal\Core\Database\Query\PagerSelectExtender')->limit(30);
-  return $sql_query->execute();
+  return LocaleTranslation::loadMultiple($fields, $conditions, $options);
 }
 
 /**
@@ -270,14 +267,16 @@ function locale_translate_edit_form($form, &$form_state) {
   );
 
   if (isset($langcode)) {
-    $strings = locale_translate_query();
+    $strings = locale_translate_query_load_strings();
 
     $plural_formulas = variable_get('locale_translation_plurals', array());
 
     foreach ($strings as $string) {
+      // Cast into source string, will do for our purposes.
+      $source = new LocaleSource($string);
       // Split source to work with plural values.
-      $source_array = explode(LOCALE_PLURAL_DELIMITER, $string->source);
-      $translation_array = explode(LOCALE_PLURAL_DELIMITER, $string->translation);
+      $source_array = $source->getPlurals();
+      $translation_array = $string->getPlurals();
       if (count($source_array) == 1) {
         // Add original string value and mark as non-plural.
         $form['strings'][$string->lid]['plural'] = array(
@@ -391,8 +390,9 @@ function locale_translate_edit_form_submit($form, &$form_state) {
   $langcode = $form_state['values']['langcode'];
   foreach ($form_state['values']['strings'] as $lid => $translations) {
     // Serialize plural variants in one string by LOCALE_PLURAL_DELIMITER.
+    $target = LocaleTranslation::loadById($langcode, $lid, array('translation'));
     $translation_new = implode(LOCALE_PLURAL_DELIMITER, $translations['translations']);
-    $translation_old = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $langcode))->fetchField();
+    $translation_old = $target->translation;
     // No translation when all strings are empty.
     $has_translation = FALSE;
     foreach ($translations['translations'] as $string) {
@@ -403,33 +403,15 @@ function locale_translate_edit_form_submit($form, &$form_state) {
     }
     if ($has_translation) {
       // Only update or insert if we have a value to use.
-      if (!empty($translation_old) && $translation_old != $translation_new) {
-        db_update('locales_target')
-          ->fields(array(
-            'translation' => $translation_new,
-            'customized' => LOCALE_CUSTOMIZED,
-          ))
-          ->condition('lid', $lid)
-          ->condition('language', $langcode)
-          ->execute();
-      }
-      if (empty($translation_old)) {
-        db_insert('locales_target')
-          ->fields(array(
-            'lid' => $lid,
-            'translation' => $translation_new,
-            'language' => $langcode,
-            'customized' => LOCALE_CUSTOMIZED,
-          ))
-          ->execute();
-      }
+      $target->setValues(array(
+        'translation' => $translation_new,
+        'customized' => LOCALE_CUSTOMIZED
+      ))
+      ->save();
     }
     elseif (!empty($translation_old)) {
       // Empty translation entered: remove existing entry from database.
-      db_delete('locales_target')
-        ->condition('lid', $lid)
-        ->condition('language', $langcode)
-        ->execute();
+      $target->delete();
     }
 
   }
