diff --git a/core/modules/language/tests/src/Unit/ConfigurableLanguageManagerTest.php b/core/modules/language/tests/src/Unit/ConfigurableLanguageManagerTest.php
new file mode 100644
index 0000000..11d8b48
--- /dev/null
+++ b/core/modules/language/tests/src/Unit/ConfigurableLanguageManagerTest.php
@@ -0,0 +1,198 @@
+<?php
+
+namespace Drupal\Tests\language\Unit;
+
+use Drupal\Core\Config\Config;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Language\LanguageDefault;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\language\Config\LanguageConfigFactoryOverrideInterface;
+use Drupal\language\ConfigurableLanguageManager;
+use Drupal\language\LanguageNegotiatorInterface;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * @coversDefaultClass \Drupal\language\ConfigurableLanguageManager
+ *
+ * @group language
+ */
+class ConfigurableLanguageManagerTest extends UnitTestCase {
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $configFactory;
+
+  /**
+   * The language configuration override.
+   *
+   * @var \Drupal\language\Config\LanguageConfigFactoryOverrideInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $configFactoryOverride;
+
+  /**
+   * The default language.
+   *
+   * @var \Drupal\Core\Language\LanguageDefault|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $defaultLanguage;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $moduleHandler;
+
+  /**
+   * The system under test.
+   *
+   * @var \Drupal\language\ConfigurableLanguageManager
+   */
+  protected $languageManager;
+
+  /**
+   * The language negotiator.
+   *
+   * @var \Drupal\language\LanguageNegotiatorInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $negotiator;
+
+  /**
+   * The request object.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $requestStack;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->configFactory = $this->prophesize(ConfigFactoryInterface::class);
+
+    $this->configFactoryOverride = $this->prophesize(LanguageConfigFactoryOverrideInterface::class);
+
+    $this->defaultLanguage = $this->prophesize(LanguageDefault::class);
+
+    $this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
+
+    $this->negotiator = $this->prophesize(LanguageNegotiatorInterface::class);
+
+    $this->requestStack = $this->prophesize(RequestStack::class);
+
+    $this->languageManager = new ConfigurableLanguageManager($this->defaultLanguage->reveal(), $this->configFactory->reveal(), $this->moduleHandler->reveal(), $this->configFactoryOverride->reveal(), $this->requestStack->reveal());
+  }
+
+  /**
+   * Test that multiple language types can be initialized simultaneously.
+   *
+   * When a language type (e.g. the interface language) is first requested, an
+   * initialization phase is started. During this phase we might make more
+   * requests for language types, and if we request the same type as the one
+   * we're initializing, we risk an infinite loop.
+   *
+   * To prevent this, the language manager keeps track of which language types
+   * it is currently initializing, and if asked for the language for one of
+   * these types it will return the default language.
+   *
+   * Language types are not necessarily interdependent, and it should thus be
+   * possible to fully initialize one while another is being initialized. In
+   * other words each language type should have its own initialization status.
+   *
+   * This test initializes two language types simultaneously by making an
+   * "outer" language types initialization dependent on an inner language type.
+   *
+   * @covers ::getCurrentLanguage
+   */
+  public function testCurrentLanguageShouldSupportRecursion() {
+    // Set optional dependencies.
+    $this->languageManager->setNegotiator($this->negotiator->reveal());
+
+    // Mock the config for one configurable language, the actual language does
+    // not matter, we just need at least two to be able to verify that we're not
+    // getting the default language when requesting the inner language type.
+    $config_id = 'foo_bar';
+    $config = $this->prophesize(Config::class);
+    $config->get()->willReturn([
+      'id' => $config_id,
+      'label' => 'Foo Bar',
+      'locked' => FALSE,
+    ]);
+
+    // Mock finding the config that represents configurable language we've just
+    // set up.
+    $config_ids = [$config_id];
+    $this->configFactory->listAll('language.entity.')->willReturn($config_ids);
+    $this->configFactory->loadMultiple($config_ids)->willReturn([$config]);
+
+    $language_negotiation_method_id = $config_id;
+
+    // Mock the default language. Note that we have to use PHPUnit mocks for the
+    // languages, as Prophecy's method mocks will change their own state. During
+    // a custom array sort, this would trigger PHP "Array was modified by the
+    // user comparison function" errors.
+    $default_language = $this->getMock(LanguageInterface::class);
+    $this->defaultLanguage->get()->willReturn($default_language);
+
+    // These are the two language types we will initialize. We will set up the
+    // test so that initialization of the outer language type triggers an
+    // initialization of the inner. Thus setting up a situation where we test
+    // whether the initialization-status of the outer type affects the inner.
+    $outer_language_type = LanguageInterface::TYPE_INTERFACE;
+    $inner_language_type = LanguageInterface::TYPE_URL;
+
+    // Setup the initialization of the inner language type. We just return the
+    // type straight away.
+    $expected_inner_language = $this->getMock(LanguageInterface::class);
+    $this->negotiator->initializeType($inner_language_type)->willReturn([
+      $language_negotiation_method_id => $expected_inner_language,
+    ]);
+
+    // Setup initialization of the outer language type.
+    // When initialization is requested we turn to the Language Manager and
+    // ask it to initialize the inner language type. This will initialize the
+    // language type fully as we just return the result straight away in the
+    // above mocking. The Language Manager then caches the result and will from
+    // now on return it when the type is requested.
+    // We then return the expected outer language type.
+    $expected_outer_language = $this->getMock(LanguageInterface::class);
+    $language_manager = $this->languageManager;
+    $this->negotiator->initializeType($outer_language_type)->will(
+      function ($type) use (
+        $language_manager,
+        $language_negotiation_method_id,
+        $expected_outer_language,
+        $inner_language_type
+      ) {
+        $language_manager->getCurrentLanguage($inner_language_type);
+        return [
+          $language_negotiation_method_id => $expected_outer_language,
+        ];
+      }
+    );
+
+    // Request the outer language type from the language manager. This will
+    // initialize the inner language as a side-effect as described above.
+    $actual_outer_language = $this->languageManager->getCurrentLanguage(
+      $outer_language_type
+    );
+    $this->assertSame($expected_outer_language, $actual_outer_language);
+
+    // This is the actual test: request the inner language type from the
+    // language manager. If it was able to initialize the language while the
+    // outer language type was being initialized, we get the expected language.
+    // If not, we will get the default language.
+    $actual_inner_language = $this->languageManager->getCurrentLanguage(
+      $inner_language_type
+    );
+    $this->assertSame($expected_inner_language, $actual_inner_language);
+  }
+
+}
