diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityApiTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityApiTest.php
index 5eb245f81e8778ad72b236df4c2b92e75d5ea5c8..a7ac1a99a5c1e858dca8c66e25aa7249433a78dd 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityApiTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityApiTest.php
@@ -42,8 +42,8 @@ function testCRUD() {
 
     $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test')));
 
-    $this->assertEqual($entities[0]->name, 'test', 'Created and loaded entity.');
-    $this->assertEqual($entities[1]->name, 'test', 'Created and loaded entity.');
+    $this->assertEqual($entities[0]->get('name'), 'test', 'Created and loaded entity.');
+    $this->assertEqual($entities[1]->get('name'), 'test', 'Created and loaded entity.');
 
     // Test loading a single entity.
     $loaded_entity = entity_test_load($entity->id);
@@ -57,10 +57,10 @@ function testCRUD() {
 
     // Test updating an entity.
     $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test')));
-    $entities[0]->name = 'test3';
+    $entities[0]->set('name', 'test3');
     $entities[0]->save();
     $entity = entity_test_load($entities[0]->id);
-    $this->assertEqual($entity->name, 'test3', 'Entity updated.');
+    $this->assertEqual($entity->get('name'), 'test3', 'Entity updated.');
 
     // Try deleting multiple test entities by deleting all.
     $ids = array_keys(entity_test_load_multiple(FALSE));
@@ -78,10 +78,10 @@ function testEntityGettersSetters() {
     $this->assertNull($entity->get('uid'), 'Property is not set.');
 
     $entity->set('uid', $GLOBALS['user']->uid);
-    $this->assertEqual($entity->uid, $GLOBALS['user']->uid, 'Property has been set.');
+    $this->assertEqual($entity->get('uid'), $GLOBALS['user']->uid, 'Property has been set.');
 
     $value = $entity->get('uid');
-    $this->assertEqual($value, $entity->uid, 'Property has been retrieved.');
+    $this->assertEqual($value, $entity->get('uid'), 'Property has been retrieved.');
 
     // Make sure setting/getting translations boils down to setting/getting the
     // regular value as the entity and property are not translatable.
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityTranslationTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityTranslationTest.php
index 99842e5056dfd867a8ce26ea0dd294c8f0af8bdf..6f9ec7a523ce1db4d8464f8d31eff8f34363fe91 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityTranslationTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityTranslationTest.php
@@ -7,6 +7,9 @@
 
 namespace Drupal\entity\Tests;
 
+use Exception;
+use InvalidArgumentException;
+
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -14,6 +17,8 @@
  */
 class EntityTranslationTest extends WebTestBase {
 
+  protected $langcodes;
+
   public static function getInfo() {
     return array(
       'name' => 'Entity Translation',
@@ -90,7 +95,7 @@ function testEntityLanguageMethods() {
 
     // Now, make the entity language-specific by assigning a language and test
     // translating it.
-    $entity->langcode = $this->langcodes[0];
+    $entity->setLangcode($this->langcodes[0]);
     $entity->{$this->field_name} = array();
     $this->assertEqual($entity->language(), language_load($this->langcodes[0]), 'Entity language retrieved.');
     $this->assertFalse($entity->translations(), 'No translations are available');
@@ -115,5 +120,108 @@ function testEntityLanguageMethods() {
     // Try to get a not available translation.
     $value = $entity->get($this->field_name, $this->langcodes[2]);
     $this->assertNull($value, 'A translation that is not available is NULL.');
+
+    // Try to get a value using an invalid language code.
+    $value = $entity->get($this->field_name, 'invalid');
+    $this->assertNull($value, 'A translation for an invalid language is NULL.');
+
+    // Try to set a value using an invalid language code.
+    $message = "An exception is thrown when trying to set an invalid translation.";
+    try {
+      $entity->set($this->field_name, NULL, 'invalid');
+      // This line is not expected to be executed unless something goes wrong.
+      $this->fail($message);
+    }
+    catch (Exception $e) {
+      $this->assertTrue($e instanceof InvalidArgumentException, $message);
+    }
+  }
+
+  /**
+   * Tests multilingual properties.
+   */
+  function testMultilingualProperties() {
+    $name = $this->randomName();
+    $uid = mt_rand(0, 127);
+    $langcode = $this->langcodes[0];
+
+    // Create a language neutral entity and check that properties are stored
+    // as language neutral.
+    $entity = entity_create('entity_test', array('name' => $name, 'uid' => $uid));
+    $entity->save();
+    $entity = entity_test_load($entity->id());
+    $this->assertEqual($entity->language()->langcode, LANGUAGE_NOT_SPECIFIED, 'Entity created as language neutral.');
+    $this->assertEqual($name, $entity->get('name', LANGUAGE_NOT_SPECIFIED), 'The entity name has been correctly stored as language neutral.');
+    $this->assertEqual($uid, $entity->get('uid', LANGUAGE_NOT_SPECIFIED), 'The entity author has been correctly stored as language neutral.');
+    $this->assertNull($entity->get('name', $langcode), 'The entity name is not available as a language-aware property.');
+    $this->assertNull($entity->get('uid', $langcode), 'The entity author is not available as a language-aware property.');
+    $this->assertEqual($name, $entity->get('name'), 'The entity name can be retrieved without specifying a language.');
+    $this->assertEqual($uid, $entity->get('uid'), 'The entity author can be retrieved without specifying a language.');
+
+    // Create a language-aware entity and check that properties are stored
+    // as language-aware.
+    $entity = entity_create('entity_test', array('name' => $name, 'uid' => $uid, 'langcode' => $langcode));
+    $entity->save();
+    $entity = entity_test_load($entity->id());
+    $this->assertEqual($entity->language()->langcode, $langcode, 'Entity created as language specific.');
+    $this->assertEqual($name, $entity->get('name', $langcode), 'The entity name has been correctly stored as a language-aware property.');
+    $this->assertEqual($uid, $entity->get('uid', $langcode), 'The entity author has been correctly stored as a language-aware property.');
+    $this->assertNull($entity->get('name', LANGUAGE_NOT_SPECIFIED), 'The entity name is not available as a language neutral property.');
+    $this->assertNull($entity->get('uid', LANGUAGE_NOT_SPECIFIED), 'The entity author is not available as a language neutral property.');
+    $this->assertEqual($name, $entity->get('name'), 'The entity name can be retrieved without specifying a language.');
+    $this->assertEqual($uid, $entity->get('uid'), 'The entity author can be retrieved without specifying a language.');
+
+    // Create property translations.
+    $properties = array();
+    $default_langcode = $langcode;
+    foreach ($this->langcodes as $langcode) {
+      if ($langcode != $default_langcode) {
+        $properties[$langcode] = array(
+          'name' => $this->randomName(),
+          'uid' => mt_rand(0, 127),
+        );
+      }
+      else {
+        $properties[$langcode] = array(
+          'name' => $name,
+          'uid' => $uid,
+        );
+      }
+      $entity->setProperties($properties[$langcode], $langcode);
+    }
+    $entity->save();
+
+    // Check that property translation were correctly stored.
+    $entity = entity_test_load($entity->id());
+    foreach ($this->langcodes as $langcode) {
+      $args = array('%langcode' => $langcode);
+      $this->assertEqual($properties[$langcode]['name'], $entity->get('name', $langcode), format_string('The entity name has been correctly stored for language %langcode.', $args));
+      $this->assertEqual($properties[$langcode]['uid'], $entity->get('uid', $langcode), format_string('The entity author has been correctly stored for language %langcode.', $args));
+    }
+
+    // Test query conditions (cache is reset at each call).
+    $translated_id = $entity->id();
+    // Create an additional entity with only the uid set. The uid for the
+    // original language is the same of one used for a translation.
+    $langcode = $this->langcodes[1];
+    entity_create('entity_test', array('uid' => $properties[$langcode]['uid']))->save();
+    $entities = entity_test_load_multiple(FALSE, array(), TRUE);
+    $this->assertEqual(count($entities), 3, 'Three entities were created.');
+    $entities = entity_test_load_multiple(array($translated_id), array(), TRUE);
+    $this->assertEqual(count($entities), 1, 'One entity correctly loaded by id.');
+    $entities = entity_test_load_multiple(array(), array('name' => $name), TRUE);
+    $this->assertEqual(count($entities), 2, 'Two entities correctly loaded by name.');
+    // @todo The default language condition should go away in favor of an
+    // explicit parameter.
+    $entities = entity_test_load_multiple(array(), array('name' => $properties[$langcode]['name'], 'default_langcode' => 0), TRUE);
+    $this->assertEqual(count($entities), 1, 'One entity correctly loaded by name translation.');
+    $entities = entity_test_load_multiple(array(), array('langcode' => $default_langcode, 'name' => $name), TRUE);
+    $this->assertEqual(count($entities), 1, 'One entity correctly loaded by name and language.');
+    $entities = entity_test_load_multiple(array(), array('langcode' => $langcode, 'name' => $properties[$langcode]['name']), TRUE);
+    $this->assertEqual(count($entities), 0, 'No entity loaded by name translation specifying the translation language.');
+    $entities = entity_test_load_multiple(array(), array('langcode' => $langcode, 'name' => $properties[$langcode]['name'], 'default_langcode' => 0), TRUE);
+    $this->assertEqual(count($entities), 1, 'One entity loaded by name translation and language specifying to look for translations.');
+    $entities = entity_test_load_multiple(array(), array('uid' => $properties[$langcode]['uid'], 'default_langcode' => NULL), TRUE);
+    $this->assertEqual(count($entities), 2, 'Two entities loaded by uid without caring about property translatability.');
   }
 }
diff --git a/core/modules/entity/tests/modules/entity_test/entity_test.install b/core/modules/entity/tests/modules/entity_test/entity_test.install
index 1bef3908b144da8da7e45346e480bbfa4c09700d..3621eb6bce4c596ab3029c8a37eb9564dbd76644 100644
--- a/core/modules/entity/tests/modules/entity_test/entity_test.install
+++ b/core/modules/entity/tests/modules/entity_test/entity_test.install
@@ -49,6 +49,41 @@ function entity_test_schema() {
         'length' => 128,
         'not null' => FALSE,
       ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of the original variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+  $schema['entity_test_property_data'] = array(
+    'description' => 'Stores entity_test item properties.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {entity_test}.id of the test entity.',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'default_langcode' => array(
+        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
       'name' => array(
         'description' => 'The name of the test entity.',
         'type' => 'varchar',
@@ -63,24 +98,15 @@ function entity_test_schema() {
         'default' => NULL,
         'description' => "The {users}.uid of the associated user.",
       ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of the test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
     ),
     'indexes' => array(
       'uid' => array('uid'),
     ),
     'foreign keys' => array(
       'uid' => array('users' => 'uid'),
+      'id' => array('entity_test' => 'id'),
     ),
-    'primary key' => array('id'),
-    'unique keys' => array(
-      'uuid' => array('uuid'),
-    ),
+    'primary key' => array('id', 'langcode'),
   );
   return $schema;
 }
diff --git a/core/modules/entity/tests/modules/entity_test/entity_test.module b/core/modules/entity/tests/modules/entity_test/entity_test.module
index 67c3085f37e3a3cc3ae1f812641fe6fdd10f3087..088706dc9e9b0601eb11fa1d1686685ac3763a58 100644
--- a/core/modules/entity/tests/modules/entity_test/entity_test.module
+++ b/core/modules/entity/tests/modules/entity_test/entity_test.module
@@ -11,8 +11,8 @@
 function entity_test_entity_info() {
   $items['entity_test'] = array(
     'label' => t('Test entity'),
-    'entity class' => 'Drupal\entity\Entity',
-    'controller class' => 'Drupal\entity\DatabaseStorageController',
+    'entity class' => 'Drupal\entity_test\EntityTest',
+    'controller class' => 'Drupal\entity_test\EntityTestStorageController',
     'base table' => 'entity_test',
     'fieldable' => TRUE,
     'entity keys' => array(
@@ -35,7 +35,7 @@ function entity_test_entity_info() {
  * @param bool $reset
  *   A boolean indicating that the internal cache should be reset.
  *
- * @return Drupal\entity\Entity
+ * @return Drupal\entity_test\EntityTest
  *   The loaded entity object, or FALSE if the entity cannot be loaded.
  */
 function entity_test_load($id, $reset = FALSE) {