1<?php
2
3namespace Drupal\Tests\language\Functional;
4
5use Drupal\Component\Render\FormattableMarkup;
6use Drupal\Core\Language\LanguageInterface;
7use Drupal\entity_test\Entity\EntityTest;
8use Drupal\language\Entity\ConfigurableLanguage;
9use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity;
10use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
11use Drupal\Tests\BrowserTestBase;
12use Symfony\Cmf\Component\Routing\RouteObjectInterface;
13use Symfony\Component\HttpFoundation\Request;
14use Symfony\Component\Routing\Route;
15
16/**
17 * Tests language negotiation with the language negotiator content entity.
18 *
19 * @group language
20 */
21class LanguageNegotiationContentEntityTest extends BrowserTestBase {
22
23  /**
24   * Modules to enable.
25   *
26   * @var array
27   */
28  public static $modules = [
29    'language',
30    'language_test',
31    'entity_test',
32    'system',
33  ];
34
35  /**
36   * {@inheritdoc}
37   */
38  protected $defaultTheme = 'stark';
39
40  /**
41   * The entity being used for testing.
42   *
43   * @var \Drupal\Core\Entity\ContentEntityInterface
44   */
45  protected $entity;
46
47  /**
48   * {@inheritdoc}
49   */
50  protected function setUp() {
51    parent::setUp();
52
53    ConfigurableLanguage::create(['id' => 'es'])->save();
54    ConfigurableLanguage::create(['id' => 'fr'])->save();
55
56    // In order to reflect the changes for a multilingual site in the container
57    // we have to rebuild it.
58    $this->rebuildContainer();
59
60    $this->createTranslatableEntity();
61
62    $user = $this->drupalCreateUser(['view test entity']);
63    $this->drupalLogin($user);
64  }
65
66  /**
67   * Tests default with content language remaining same as interface language.
68   */
69  public function testDefaultConfiguration() {
70    $translation = $this->entity;
71    $this->drupalGet($translation->toUrl());
72    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
73    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
74    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
75    $this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
76
77    $translation = $this->entity->getTranslation('es');
78    $this->drupalGet($translation->toUrl());
79    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
80    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
81    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
82    $this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
83
84    $translation = $this->entity->getTranslation('fr');
85    $this->drupalGet($translation->toUrl());
86    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
87    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
88    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
89    $this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
90  }
91
92  /**
93   * Tests enabling the language negotiator language_content_entity.
94   */
95  public function testEnabledLanguageContentNegotiator() {
96    // Define the method language-url with a higher priority than
97    // language-content-entity. This configuration should match the default one,
98    // where the language-content-entity is turned off.
99    $config = $this->config('language.types');
100    $config->set('configurable', [LanguageInterface::TYPE_INTERFACE, LanguageInterface::TYPE_CONTENT]);
101    $config->set('negotiation.language_content.enabled', [
102      LanguageNegotiationUrl::METHOD_ID => 0,
103      LanguageNegotiationContentEntity::METHOD_ID => 1,
104    ]);
105    $config->save();
106
107    // In order to reflect the changes for a multilingual site in the container
108    // we have to rebuild it.
109    $this->rebuildContainer();
110
111    // The tests for the default configuration should still pass.
112    $this->testDefaultConfiguration();
113
114    // Define the method language-content-entity with a higher priority than
115    // language-url.
116    $config->set('negotiation.language_content.enabled', [
117      LanguageNegotiationContentEntity::METHOD_ID => 0,
118      LanguageNegotiationUrl::METHOD_ID => 1,
119    ]);
120    $config->save();
121
122    // In order to reflect the changes for a multilingual site in the container
123    // we have to rebuild it.
124    $this->rebuildContainer();
125
126    // The method language-content-entity should run before language-url and
127    // append query parameter for the content language and prevent language-url
128    // from overwriting the URL.
129    $default_site_langcode = $this->config('system.site')->get('default_langcode');
130
131    // Now switching to an entity route, so that the URL links are generated
132    // while being on an entity route.
133    $this->setCurrentRequestForRoute('/entity_test/{entity_test}', 'entity.entity_test.canonical');
134
135    $translation = $this->entity;
136    $this->drupalGet($translation->toUrl());
137    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
138    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
139    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
140    $this->assertTrue(($last_interface_language == $default_site_langcode) && ($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), 'Interface language and Content language are the same as the default translation language of the entity.');
141    $this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
142    $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
143
144    $translation = $this->entity->getTranslation('es');
145    $this->drupalGet($translation->toUrl());
146    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
147    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
148    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
149    $this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
150    $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
151
152    $translation = $this->entity->getTranslation('fr');
153    $this->drupalGet($translation->toUrl());
154    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
155    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
156    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
157    $this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
158    $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
159  }
160
161  /**
162   * Creates a translated entity.
163   */
164  protected function createTranslatableEntity() {
165    $this->entity = EntityTest::create();
166    $this->entity->addTranslation('es', ['name' => 'name spanish']);
167    $this->entity->addTranslation('fr', ['name' => 'name french']);
168    $this->entity->save();
169  }
170
171  /**
172   * Sets the current request to a specific path with the corresponding route.
173   *
174   * @param string $path
175   *   The path for which the current request should be created.
176   * @param string $route_name
177   *   The route name for which the route object for the request should be
178   *   created.
179   */
180  protected function setCurrentRequestForRoute($path, $route_name) {
181    $request = Request::create($path);
182    $request->attributes->set(RouteObjectInterface::ROUTE_NAME, $route_name);
183    $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route($path));
184    $this->container->get('request_stack')->push($request);
185  }
186
187}
188