diff --git a/core/modules/locale/lib/Drupal/locale/LocaleLookup.php b/core/modules/locale/lib/Drupal/locale/LocaleLookup.php
index 2003e4e..277c161 100644
--- a/core/modules/locale/lib/Drupal/locale/LocaleLookup.php
+++ b/core/modules/locale/lib/Drupal/locale/LocaleLookup.php
@@ -2,14 +2,15 @@
 
 /**
  * @file
- * Contains \Drupal\locale\Locale\Lookup.
+ * Contains \Drupal\locale\LocaleLookup.
  */
 
 namespace Drupal\locale;
 
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Cache\CacheCollector;
-use Drupal\Core\DestructableInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Lock\LockBackendInterface;
 
 /**
@@ -53,6 +54,20 @@ class LocaleLookup extends CacheCollector {
   protected $lock;
 
   /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
    * Constructs a LocaleLookup object.
    *
    * @param string $langcode
@@ -65,11 +80,17 @@ class LocaleLookup extends CacheCollector {
    *   The cache backend.
    * @param \Drupal\Core\Lock\LockBackendInterface $lock
    *   The lock backend.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
    */
-  public function __construct($langcode, $context, StringStorageInterface $string_storage, CacheBackendInterface $cache, LockBackendInterface $lock) {
+  public function __construct($langcode, $context, StringStorageInterface $string_storage, CacheBackendInterface $cache, LockBackendInterface $lock, ConfigFactoryInterface $config_factory, LanguageManagerInterface $language_manager) {
     $this->langcode = $langcode;
     $this->context = (string) $context;
     $this->stringStorage = $string_storage;
+    $this->configFactory = $config_factory;
+    $this->languageManager = $language_manager;
 
     // Add the current user's role IDs to the cache key, this ensures that, for
     // example, strings for admin menu items and settings forms are not cached
@@ -99,18 +120,46 @@ protected function resolveCacheMiss($offset) {
         'source' => $offset,
         'context' => $this->context,
         'version' => \Drupal::VERSION
-      ))->addLocation('path', request_uri())->save();
+      ))->addLocation('path', $this->requestUri())->save();
       $value = TRUE;
     }
+
+    // If there is no translation available for the current language then use
+    // language fallback to try other translations.
+    if ($value === TRUE) {
+      $fallbacks = $this->languageManager->getFallbackCandidates($this->langcode, array('operation' => 'locale_lookup', 'data' => $offset));
+      if (!empty($fallbacks)) {
+        foreach($fallbacks as $langcode) {
+          $translation = $this->stringStorage->findTranslation(array(
+            'language' => $langcode,
+            'source' => $offset,
+            'context' => $this->context,
+          ));
+
+          if ($translation && !empty($translation->translation)) {
+            $value = $translation->translation;
+            break;
+          }
+        }
+      }
+    }
+
     $this->storage[$offset] = $value;
     // Disabling the usage of string caching allows a module to watch for
     // the exact list of strings used on a page. From a performance
     // perspective that is a really bad idea, so we have no user
     // interface for this. Be careful when turning this option off!
-    if (\Drupal::config('locale.settings')->get('cache_strings')) {
+    if ($this->configFactory->get('locale.settings')->get('cache_strings')) {
       $this->persist($offset);
     }
     return $value;
   }
 
+  /**
+   * Wraps request_uri().
+   */
+  protected function requestUri($omit_query_string = FALSE) {
+    return request_uri($omit_query_string);
+  }
+
 }
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleTranslation.php b/core/modules/locale/lib/Drupal/locale/LocaleTranslation.php
index 2c4590f..b79894b 100644
--- a/core/modules/locale/lib/Drupal/locale/LocaleTranslation.php
+++ b/core/modules/locale/lib/Drupal/locale/LocaleTranslation.php
@@ -11,11 +11,9 @@
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\DestructableInterface;
 use Drupal\Core\Language\Language;
-use Drupal\Core\Lock\LockBackendAbstract;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Lock\LockBackendInterface;
 use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
-use Drupal\locale\StringStorageInterface;
-use Drupal\locale\LocaleLookup;
 
 /**
  * String translator using the locale module.
@@ -63,6 +61,13 @@ class LocaleTranslation implements TranslatorInterface, DestructableInterface {
   protected $lock;
 
   /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
    * Constructs a translator using a string storage.
    *
    * @param \Drupal\locale\StringStorageInterface $storage
@@ -73,12 +78,15 @@ class LocaleTranslation implements TranslatorInterface, DestructableInterface {
    *   The lock backend.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The config factory.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
    */
-  public function __construct(StringStorageInterface $storage, CacheBackendInterface $cache, LockBackendInterface $lock, ConfigFactoryInterface $config_factory) {
+  public function __construct(StringStorageInterface $storage, CacheBackendInterface $cache, LockBackendInterface $lock, ConfigFactoryInterface $config_factory, LanguageManagerInterface $language_manager) {
     $this->storage = $storage;
     $this->cache = $cache;
     $this->lock = $lock;
     $this->configFactory = $config_factory;
+    $this->languageManager = $language_manager;
   }
 
   /**
@@ -93,7 +101,7 @@ public function getStringTranslation($langcode, $string, $context) {
     // Strings are cached by langcode, context and roles, using instances of the
     // LocaleLookup class to handle string lookup and caching.
     if (!isset($this->translations[$langcode][$context])) {
-      $this->translations[$langcode][$context] = new LocaleLookup($langcode, $context, $this->storage, $this->cache, $this->lock);
+      $this->translations[$langcode][$context] = new LocaleLookup($langcode, $context, $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager);
     }
     $translation = $this->translations[$langcode][$context]->get($string);
     return $translation === TRUE ? FALSE : $translation;
diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml
index b5f4cf4..b338644 100644
--- a/core/modules/locale/locale.services.yml
+++ b/core/modules/locale/locale.services.yml
@@ -12,7 +12,7 @@ services:
     arguments: ['@database']
   string_translator.locale.lookup:
     class: Drupal\locale\LocaleTranslation
-    arguments: ['@locale.storage', '@cache.cache', '@lock', '@config.factory']
+    arguments: ['@locale.storage', '@cache.cache', '@lock', '@config.factory', '@language_manager']
     tags:
       - { name: string_translator }
       - { name: needs_destruction }
diff --git a/core/modules/locale/tests/Drupal/locale/Tests/LocaleLookupTest.php b/core/modules/locale/tests/Drupal/locale/Tests/LocaleLookupTest.php
new file mode 100644
index 0000000..a62e40d
--- /dev/null
+++ b/core/modules/locale/tests/Drupal/locale/Tests/LocaleLookupTest.php
@@ -0,0 +1,274 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\locale\Tests\LocaleLookupTest.
+ */
+
+namespace Drupal\locale\Tests;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\locale\LocaleLookup;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests locale translation language fallback.
+ *
+ * @group Drupal
+ * @group Locale
+ *
+ * @coversDefaultClass \Drupal\locale\LocaleLookup
+ */
+class LocaleLookupTest extends UnitTestCase {
+
+  /**
+   * A mocked storage to use when instantiating LocaleTranslation objects.
+   *
+   * @var \Drupal\locale\StringStorageInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $storage;
+
+  /**
+   * A mocked cache object.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cache;
+
+  /**
+   * A mocked lock object.
+   *
+   * @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $lock;
+
+  /**
+   * A mocked user object built from AccountInterface.
+   *
+   * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $user;
+
+  /**
+   * A mocked config factory built with UnitTestCase::getConfigFactoryStub().
+   *
+   * @var \Drupal\Core\Config\ConfigFactory|\PHPUnit_Framework_MockObject_MockBuilder
+   */
+  protected $configFactory;
+
+  /**
+   * A mocked language manager built from LanguageManagerInterface.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $languageManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Locale language fallback tests',
+      'description' => 'Test locale module language fallback implementation.',
+      'group' => 'locale',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->storage = $this->getMock('Drupal\locale\StringStorageInterface');
+    $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
+    $this->lock->expects($this->never())
+      ->method($this->anything());
+
+    $this->user = $this->getMock('Drupal\Core\Session\AccountInterface');
+    $this->user->expects($this->any())
+      ->method('getRoles')
+      ->will($this->returnValue(array('anonymous')));
+
+    $this->configFactory = $this->getConfigFactoryStub(array('locale.settings' => array('cache_strings' => FALSE)));
+
+    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
+
+    $container = new ContainerBuilder();
+    $container->set('current_user', $this->user);
+    \Drupal::setContainer($container);
+  }
+
+  /**
+   * Tests locale lookups without fallback.
+   *
+   * @covers ::resolveCacheMiss()
+   */
+  public function testResolveCacheMissWithoutFallback() {
+    $args = array(
+      'language' => 'en',
+      'source' => 'test',
+      'context' => 'irrelevant',
+    );
+
+    $result = (object) array(
+      'translation' => 'test',
+    );
+
+    $this->cache->expects($this->once())
+      ->method('get')
+      ->with('locale:en:irrelevant:0', FALSE);
+
+    $this->storage->expects($this->once())
+      ->method('findTranslation')
+      ->with($this->equalTo($args))
+      ->will($this->returnValue($result));
+
+    $locale_lookup = $this->getMockBuilder('Drupal\locale\LocaleLookup')
+      ->setConstructorArgs(array('en', 'irrelevant', $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager))
+      ->setMethods(array('persist', 'requestUri'))
+      ->getMock();
+    $locale_lookup->expects($this->never())
+      ->method('persist');
+    $this->assertSame('test', $locale_lookup->get('test'));
+  }
+
+  /**
+   * Tests locale lookups with fallback.
+   *
+   * Note that context is irrelevant here. It is not used but it is required.
+   *
+   * @covers ::resolveCacheMiss()
+   *
+   * @dataProvider resolveCacheMissWithFallbackProvider
+   */
+  public function testResolveCacheMissWithFallback($langcode, $string, $context, $expected) {
+    // These are fake words!
+    $translations = array(
+      'en' => array(
+        'test' => 'test',
+        'fake' => 'fake',
+        'missing pl' => 'missing pl',
+        'missing cs' => 'missing cs',
+        'missing both' => 'missing both',
+      ),
+      'pl' => array(
+        'test' => 'test po polsku',
+        'fake' => 'ściema',
+        'missing cs' => 'zaginiony czech',
+      ),
+      'cs' => array(
+        'test' => 'test v české',
+        'fake' => 'falešný',
+        'missing pl' => 'chybějící pl',
+      ),
+    );
+    $this->storage->expects($this->any())
+      ->method('findTranslation')
+      ->will($this->returnCallback(function ($argument) use ($translations) {
+        if (isset($translations[$argument['language']][$argument['source']])) {
+          return (object) array('translation' => $translations[$argument['language']][$argument['source']]);
+        }
+        return TRUE;
+      }));
+
+    $this->languageManager->expects($this->any())
+      ->method('getFallbackCandidates')
+      ->will($this->returnCallback(function ($langcode) {
+        switch ($langcode) {
+          case 'pl':
+            return array('cs', 'en');
+          case 'cs':
+            return array('en');
+          default:
+            return array();
+        }
+      }));
+
+    $this->cache->expects($this->once())
+      ->method('get')
+      ->with('locale:' . $langcode . ':' . $context . ':0', FALSE);
+
+    $locale_lookup = new LocaleLookup($langcode, $context, $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager);
+    $this->assertSame($expected, $locale_lookup->get($string));
+  }
+
+  /**
+   * Provides test data for testResolveCacheMissWithFallback().
+   */
+  public function resolveCacheMissWithFallbackProvider() {
+    return array(
+      array('cs', 'test', 'irrelevant', 'test v české'),
+      array('cs', 'fake', 'irrelevant', 'falešný'),
+      array('cs', 'missing pl', 'irrelevant', 'chybějící pl'),
+      array('cs', 'missing cs', 'irrelevant', 'missing cs'),
+      array('cs', 'missing both', 'irrelevant', 'missing both'),
+
+      // Testing PL with fallback to cs, en.
+      array('pl', 'test', 'irrelevant', 'test po polsku'),
+      array('pl', 'fake', 'irrelevant', 'ściema'),
+      array('pl', 'missing pl', 'irrelevant', 'chybějící pl'),
+      array('pl', 'missing cs', 'irrelevant', 'zaginiony czech'),
+      array('pl', 'missing both', 'irrelevant', 'missing both'),
+    );
+  }
+
+  /**
+   * Tests locale lookups with persistent tracking.
+   *
+   * @covers ::resolveCacheMiss()
+   */
+  public function testResolveCacheMissWithPersist() {
+    $args = array(
+      'language' => 'en',
+      'source' => 'test',
+      'context' => 'irrelevant',
+    );
+
+    $result = (object) array(
+      'translation' => 'test',
+    );
+
+    $this->storage->expects($this->once())
+      ->method('findTranslation')
+      ->with($this->equalTo($args))
+      ->will($this->returnValue($result));
+
+    $this->configFactory = $this->getConfigFactoryStub(array('locale.settings' => array('cache_strings' => TRUE)));
+    $locale_lookup = $this->getMockBuilder('Drupal\locale\LocaleLookup')
+      ->setConstructorArgs(array('en', 'irrelevant', $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager))
+      ->setMethods(array('persist', 'requestUri'))
+      ->getMock();
+    $locale_lookup->expects($this->once())
+      ->method('persist');
+
+    $this->assertSame('test', $locale_lookup->get('test'));
+  }
+
+  /**
+   * Tests locale lookups without a found translation.
+   *
+   * @covers ::resolveCacheMiss()
+   */
+  public function testResolveCacheMissNoTranslation() {
+    $string = $this->getMock('Drupal\locale\StringInterface');
+    $string->expects($this->once())
+      ->method('addLocation')
+      ->will($this->returnSelf());
+    $this->storage->expects($this->once())
+      ->method('findTranslation')
+      ->will($this->returnValue(NULL));
+    $this->storage->expects($this->once())
+      ->method('createString')
+      ->will($this->returnValue($string));
+
+    $locale_lookup = $this->getMockBuilder('Drupal\locale\LocaleLookup')
+      ->setConstructorArgs(array('en', 'irrelevant', $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager))
+      ->setMethods(array('persist', 'requestUri'))
+      ->getMock();
+    $locale_lookup->expects($this->never())
+      ->method('persist');
+
+    $this->assertTrue($locale_lookup->get('test'));
+  }
+
+}
