You are here

public function LanguageUILanguageNegotiationTest::testUILanguageNegotiation in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/language/tests/src/Functional/LanguageUILanguageNegotiationTest.php \Drupal\Tests\language\Functional\LanguageUILanguageNegotiationTest::testUILanguageNegotiation()

Tests for language switching by URL path.

File

core/modules/language/tests/src/Functional/LanguageUILanguageNegotiationTest.php, line 89

Class

LanguageUILanguageNegotiationTest
Tests the language UI for language switching.

Namespace

Drupal\Tests\language\Functional

Code

public function testUILanguageNegotiation() {

  // A few languages to switch to.
  // This one is unknown, should get the default lang version.
  $langcode_unknown = 'blah-blah';

  // For testing browser lang preference.
  $langcode_browser_fallback = 'vi';

  // For testing path prefix.
  $langcode = 'zh-hans';

  // For setting browser language preference to 'vi'.
  $http_header_browser_fallback = [
    "Accept-Language" => "{$langcode_browser_fallback};q=1",
  ];

  // For setting browser language preference to some unknown.
  $http_header_blah = [
    "Accept-Language" => "blah;q=1",
  ];

  // Create a private file for testing accessible by the admin user.
  \Drupal::service('file_system')
    ->mkdir($this->privateFilesDirectory . '/test');
  $filepath = 'private://test/private-file-test.txt';
  $contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data.";
  file_put_contents($filepath, $contents);
  $file = File::create([
    'uri' => $filepath,
    'uid' => $this->adminUser
      ->id(),
  ]);
  $file
    ->save();

  // Setup the site languages by installing two languages.
  // Set the default language in order for the translated string to be registered
  // into database when seen by t(). Without doing this, our target string
  // is for some reason not found when doing translate search. This might
  // be some bug.
  $default_language = \Drupal::languageManager()
    ->getDefaultLanguage();
  ConfigurableLanguage::createFromLangcode($langcode_browser_fallback)
    ->save();
  $this
    ->config('system.site')
    ->set('default_langcode', $langcode_browser_fallback)
    ->save();
  ConfigurableLanguage::createFromLangcode($langcode)
    ->save();

  // We will look for this string in the admin/config screen to see if the
  // corresponding translated string is shown.
  $default_string = 'Hide descriptions';

  // First visit this page to make sure our target string is searchable.
  $this
    ->drupalGet('admin/config');

  // Now the t()'ed string is in db so switch the language back to default.
  // This will rebuild the container so we need to rebuild the container in
  // the test environment.
  $this
    ->config('system.site')
    ->set('default_langcode', $default_language
    ->getId())
    ->save();
  $this
    ->config('language.negotiation')
    ->set('url.prefixes.en', '')
    ->save();
  $this
    ->rebuildContainer();

  // Translate the string.
  $language_browser_fallback_string = "In {$langcode_browser_fallback} In {$langcode_browser_fallback} In {$langcode_browser_fallback}";
  $language_string = "In {$langcode} In {$langcode} In {$langcode}";

  // Do a translate search of our target string.
  $search = [
    'string' => $default_string,
    'langcode' => $langcode_browser_fallback,
  ];
  $this
    ->drupalGet('admin/config/regional/translate');
  $this
    ->submitForm($search, 'Filter');
  $textarea = $this
    ->assertSession()
    ->elementExists('xpath', '//textarea');
  $lid = $textarea
    ->getAttribute('name');
  $edit = [
    $lid => $language_browser_fallback_string,
  ];
  $this
    ->drupalGet('admin/config/regional/translate');
  $this
    ->submitForm($edit, 'Save translations');
  $search = [
    'string' => $default_string,
    'langcode' => $langcode,
  ];
  $this
    ->drupalGet('admin/config/regional/translate');
  $this
    ->submitForm($search, 'Filter');
  $textarea = $this
    ->assertSession()
    ->elementExists('xpath', '//textarea');
  $lid = $textarea
    ->getAttribute('name');
  $edit = [
    $lid => $language_string,
  ];
  $this
    ->drupalGet('admin/config/regional/translate');
  $this
    ->submitForm($edit, 'Save translations');

  // Configure selected language negotiation to use zh-hans.
  $edit = [
    'selected_langcode' => $langcode,
  ];
  $this
    ->drupalGet('admin/config/regional/language/detection/selected');
  $this
    ->submitForm($edit, 'Save configuration');
  $test = [
    'language_negotiation' => [
      LanguageNegotiationSelected::METHOD_ID,
    ],
    'path' => 'admin/config',
    'expect' => $language_string,
    'expected_method_id' => LanguageNegotiationSelected::METHOD_ID,
    'http_header' => $http_header_browser_fallback,
    'message' => 'SELECTED: UI language is switched based on selected language.',
  ];
  $this
    ->doRunTest($test);

  // An invalid language is selected.
  $this
    ->config('language.negotiation')
    ->set('selected_langcode', NULL)
    ->save();
  $test = [
    'language_negotiation' => [
      LanguageNegotiationSelected::METHOD_ID,
    ],
    'path' => 'admin/config',
    'expect' => $default_string,
    'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
    'http_header' => $http_header_browser_fallback,
    'message' => 'SELECTED > DEFAULT: UI language is switched based on selected language.',
  ];
  $this
    ->doRunTest($test);

  // No selected language is available.
  $this
    ->config('language.negotiation')
    ->set('selected_langcode', $langcode_unknown)
    ->save();
  $test = [
    'language_negotiation' => [
      LanguageNegotiationSelected::METHOD_ID,
    ],
    'path' => 'admin/config',
    'expect' => $default_string,
    'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
    'http_header' => $http_header_browser_fallback,
    'message' => 'SELECTED > DEFAULT: UI language is switched based on selected language.',
  ];
  $this
    ->doRunTest($test);
  $tests = [
    // Default, browser preference should have no influence.
    [
      'language_negotiation' => [
        LanguageNegotiationUrl::METHOD_ID,
        LanguageNegotiationSelected::METHOD_ID,
      ],
      'path' => 'admin/config',
      'expect' => $default_string,
      'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
      'http_header' => $http_header_browser_fallback,
      'message' => 'URL (PATH) > DEFAULT: no language prefix, UI language is default and the browser language preference setting is not used.',
    ],
    // Language prefix.
    [
      'language_negotiation' => [
        LanguageNegotiationUrl::METHOD_ID,
        LanguageNegotiationSelected::METHOD_ID,
      ],
      'path' => "{$langcode}/admin/config",
      'expect' => $language_string,
      'expected_method_id' => LanguageNegotiationUrl::METHOD_ID,
      'http_header' => $http_header_browser_fallback,
      'message' => 'URL (PATH) > DEFAULT: with language prefix, UI language is switched based on path prefix',
    ],
    // Default, go by browser preference.
    [
      'language_negotiation' => [
        LanguageNegotiationUrl::METHOD_ID,
        LanguageNegotiationBrowser::METHOD_ID,
      ],
      'path' => 'admin/config',
      'expect' => $language_browser_fallback_string,
      'expected_method_id' => LanguageNegotiationBrowser::METHOD_ID,
      'http_header' => $http_header_browser_fallback,
      'message' => 'URL (PATH) > BROWSER: no language prefix, UI language is determined by browser language preference',
    ],
    // Prefix, switch to the language.
    [
      'language_negotiation' => [
        LanguageNegotiationUrl::METHOD_ID,
        LanguageNegotiationBrowser::METHOD_ID,
      ],
      'path' => "{$langcode}/admin/config",
      'expect' => $language_string,
      'expected_method_id' => LanguageNegotiationUrl::METHOD_ID,
      'http_header' => $http_header_browser_fallback,
      'message' => 'URL (PATH) > BROWSER: with language prefix, UI language is based on path prefix',
    ],
    // Default, browser language preference is not one of site's lang.
    [
      'language_negotiation' => [
        LanguageNegotiationUrl::METHOD_ID,
        LanguageNegotiationBrowser::METHOD_ID,
        LanguageNegotiationSelected::METHOD_ID,
      ],
      'path' => 'admin/config',
      'expect' => $default_string,
      'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
      'http_header' => $http_header_blah,
      'message' => 'URL (PATH) > BROWSER > DEFAULT: no language prefix and browser language preference set to unknown language should use default language',
    ],
  ];
  foreach ($tests as $test) {
    $this
      ->doRunTest($test);
  }

  // Unknown language prefix should return 404.
  $definitions = \Drupal::languageManager()
    ->getNegotiator()
    ->getNegotiationMethods();

  // Enable only methods, which are either not limited to a specific language
  // type or are supporting the interface language type.
  $language_interface_method_definitions = array_filter($definitions, function ($method_definition) {
    return !isset($method_definition['types']) || isset($method_definition['types']) && in_array(LanguageInterface::TYPE_INTERFACE, $method_definition['types']);
  });
  $this
    ->config('language.types')
    ->set('negotiation.' . LanguageInterface::TYPE_INTERFACE . '.enabled', array_flip(array_keys($language_interface_method_definitions)))
    ->save();
  $this
    ->drupalGet("{$langcode_unknown}/admin/config", [], $http_header_browser_fallback);
  $this
    ->assertSession()
    ->statusCodeEquals(404);

  // Set preferred langcode for user to NULL.
  $account = $this->loggedInUser;
  $account->preferred_langcode = NULL;
  $account
    ->save();
  $test = [
    'language_negotiation' => [
      LanguageNegotiationUser::METHOD_ID,
      LanguageNegotiationSelected::METHOD_ID,
    ],
    'path' => 'admin/config',
    'expect' => $default_string,
    'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
    'http_header' => [],
    'message' => 'USER > DEFAULT: no preferred user language setting, the UI language is default',
  ];
  $this
    ->doRunTest($test);

  // Set preferred langcode for user to default langcode.
  $account = $this->loggedInUser;
  $account->preferred_langcode = $default_language
    ->getId();
  $account
    ->save();
  $test = [
    'language_negotiation' => [
      LanguageNegotiationUser::METHOD_ID,
      LanguageNegotiationUrl::METHOD_ID,
    ],
    'path' => "{$langcode}/admin/config",
    'expect' => $default_string,
    'expected_method_id' => LanguageNegotiationUser::METHOD_ID,
    'http_header' => [],
    'message' => 'USER > URL: User has default language as preferred user language setting, the UI language is default',
  ];
  $this
    ->doRunTest($test);

  // Set preferred langcode for user to unknown language.
  $account = $this->loggedInUser;
  $account->preferred_langcode = $langcode_unknown;
  $account
    ->save();
  $test = [
    'language_negotiation' => [
      LanguageNegotiationUser::METHOD_ID,
      LanguageNegotiationSelected::METHOD_ID,
    ],
    'path' => 'admin/config',
    'expect' => $default_string,
    'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
    'http_header' => [],
    'message' => 'USER > DEFAULT: invalid preferred user language setting, the UI language is default',
  ];
  $this
    ->doRunTest($test);

  // Set preferred langcode for user to non default.
  $account->preferred_langcode = $langcode;
  $account
    ->save();
  $test = [
    'language_negotiation' => [
      LanguageNegotiationUser::METHOD_ID,
      LanguageNegotiationSelected::METHOD_ID,
    ],
    'path' => 'admin/config',
    'expect' => $language_string,
    'expected_method_id' => LanguageNegotiationUser::METHOD_ID,
    'http_header' => [],
    'message' => 'USER > DEFAULT: defined preferred user language setting, the UI language is based on user setting',
  ];
  $this
    ->doRunTest($test);

  // Set preferred admin langcode for user to NULL.
  $account->preferred_admin_langcode = NULL;
  $account
    ->save();
  $test = [
    'language_negotiation' => [
      LanguageNegotiationUserAdmin::METHOD_ID,
      LanguageNegotiationSelected::METHOD_ID,
    ],
    'path' => 'admin/config',
    'expect' => $default_string,
    'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
    'http_header' => [],
    'message' => 'USER ADMIN > DEFAULT: no preferred user admin language setting, the UI language is default',
  ];
  $this
    ->doRunTest($test);

  // Set preferred admin langcode for user to unknown language.
  $account->preferred_admin_langcode = $langcode_unknown;
  $account
    ->save();
  $test = [
    'language_negotiation' => [
      LanguageNegotiationUserAdmin::METHOD_ID,
      LanguageNegotiationSelected::METHOD_ID,
    ],
    'path' => 'admin/config',
    'expect' => $default_string,
    'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
    'http_header' => [],
    'message' => 'USER ADMIN > DEFAULT: invalid preferred user admin language setting, the UI language is default',
  ];
  $this
    ->doRunTest($test);

  // Set preferred admin langcode for user to non default.
  $account->preferred_admin_langcode = $langcode;
  $account
    ->save();
  $test = [
    'language_negotiation' => [
      LanguageNegotiationUserAdmin::METHOD_ID,
      LanguageNegotiationSelected::METHOD_ID,
    ],
    'path' => 'admin/config',
    'expect' => $language_string,
    'expected_method_id' => LanguageNegotiationUserAdmin::METHOD_ID,
    'http_header' => [],
    'message' => 'USER ADMIN > DEFAULT: defined preferred user admin language setting, the UI language is based on user setting',
  ];
  $this
    ->doRunTest($test);

  // Go by session preference.
  $language_negotiation_session_param = $this
    ->randomMachineName();
  $edit = [
    'language_negotiation_session_param' => $language_negotiation_session_param,
  ];
  $this
    ->drupalGet('admin/config/regional/language/detection/session');
  $this
    ->submitForm($edit, 'Save configuration');
  $tests = [
    [
      'language_negotiation' => [
        LanguageNegotiationSession::METHOD_ID,
      ],
      'path' => "admin/config",
      'expect' => $default_string,
      'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
      'http_header' => $http_header_browser_fallback,
      'message' => 'SESSION > DEFAULT: no language given, the UI language is default',
    ],
    [
      'language_negotiation' => [
        LanguageNegotiationSession::METHOD_ID,
      ],
      'path' => 'admin/config',
      'path_options' => [
        'query' => [
          $language_negotiation_session_param => $langcode,
        ],
      ],
      'expect' => $language_string,
      'expected_method_id' => LanguageNegotiationSession::METHOD_ID,
      'http_header' => $http_header_browser_fallback,
      'message' => 'SESSION > DEFAULT: language given, UI language is determined by session language preference',
    ],
  ];
  foreach ($tests as $test) {
    $this
      ->doRunTest($test);
  }
}