You are here

class LanguageNegotiationUrl in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl

Class for identifying language via URL prefix or domain.

Plugin annotation


@LanguageNegotiation(
  id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID,
  types = {\Drupal\Core\Language\LanguageInterface::TYPE_INTERFACE,
  \Drupal\Core\Language\LanguageInterface::TYPE_CONTENT,
  \Drupal\Core\Language\LanguageInterface::TYPE_URL},
  weight = -8,
  name = @Translation("URL"),
  description = @Translation("Language from the URL (Path prefix or domain)."),
  config_route_name = "language.negotiation_url"
)

Hierarchy

Expanded class hierarchy of LanguageNegotiationUrl

14 files declare their use of LanguageNegotiationUrl
BlockUiTest.php in core/modules/block/tests/src/Functional/BlockUiTest.php
BookMultilingualTest.php in core/modules/book/tests/src/Kernel/BookMultilingualTest.php
EntityUrlLanguageTest.php in core/modules/language/tests/src/Kernel/EntityUrlLanguageTest.php
language.module in core/modules/language/language.module
Add language handling functionality to Drupal.
LanguageNegotiationContentEntityTest.php in core/modules/language/tests/src/Functional/LanguageNegotiationContentEntityTest.php

... See full list

File

core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php, line 28

Namespace

Drupal\language\Plugin\LanguageNegotiation
View source
class LanguageNegotiationUrl extends LanguageNegotiationMethodBase implements InboundPathProcessorInterface, OutboundPathProcessorInterface, LanguageSwitcherInterface {

  /**
   * The language negotiation method id.
   */
  const METHOD_ID = 'language-url';

  /**
   * URL language negotiation: use the path prefix as URL language indicator.
   */
  const CONFIG_PATH_PREFIX = 'path_prefix';

  /**
   * URL language negotiation: use the domain as URL language indicator.
   */
  const CONFIG_DOMAIN = 'domain';

  /**
   * {@inheritdoc}
   */
  public function getLangcode(Request $request = NULL) {
    $langcode = NULL;
    if ($request && $this->languageManager) {
      $languages = $this->languageManager
        ->getLanguages();
      $config = $this->config
        ->get('language.negotiation')
        ->get('url');
      switch ($config['source']) {
        case LanguageNegotiationUrl::CONFIG_PATH_PREFIX:
          $request_path = urldecode(trim($request
            ->getPathInfo(), '/'));
          $path_args = explode('/', $request_path);
          $prefix = array_shift($path_args);

          // Search prefix within added languages.
          $negotiated_language = FALSE;
          foreach ($languages as $language) {
            if (isset($config['prefixes'][$language
              ->getId()]) && $config['prefixes'][$language
              ->getId()] == $prefix) {
              $negotiated_language = $language;
              break;
            }
          }
          if ($negotiated_language) {
            $langcode = $negotiated_language
              ->getId();
          }
          break;
        case LanguageNegotiationUrl::CONFIG_DOMAIN:

          // Get only the host, not the port.
          $http_host = $request
            ->getHost();
          foreach ($languages as $language) {

            // Skip the check if the language doesn't have a domain.
            if (!empty($config['domains'][$language
              ->getId()])) {

              // Ensure that there is exactly one protocol in the URL when
              // checking the hostname.
              $host = 'http://' . str_replace([
                'http://',
                'https://',
              ], '', $config['domains'][$language
                ->getId()]);
              $host = parse_url($host, PHP_URL_HOST);
              if ($http_host == $host) {
                $langcode = $language
                  ->getId();
                break;
              }
            }
          }
          break;
      }
    }
    return $langcode;
  }

  /**
   * {@inheritdoc}
   */
  public function processInbound($path, Request $request) {
    $config = $this->config
      ->get('language.negotiation')
      ->get('url');
    if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
      $parts = explode('/', trim($path, '/'));
      $prefix = array_shift($parts);

      // Search prefix within added languages.
      foreach ($this->languageManager
        ->getLanguages() as $language) {
        if (isset($config['prefixes'][$language
          ->getId()]) && $config['prefixes'][$language
          ->getId()] == $prefix) {

          // Rebuild $path with the language removed.
          $path = '/' . implode('/', $parts);
          break;
        }
      }
    }
    return $path;
  }

  /**
   * {@inheritdoc}
   */
  public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
    $url_scheme = 'http';
    $port = 80;
    if ($request) {
      $url_scheme = $request
        ->getScheme();
      $port = $request
        ->getPort();
    }
    $languages = array_flip(array_keys($this->languageManager
      ->getLanguages()));

    // Language can be passed as an option, or we go for current URL language.
    if (!isset($options['language'])) {
      $language_url = $this->languageManager
        ->getCurrentLanguage(LanguageInterface::TYPE_URL);
      $options['language'] = $language_url;
    }
    elseif (!is_object($options['language']) || !isset($languages[$options['language']
      ->getId()])) {
      return $path;
    }
    $config = $this->config
      ->get('language.negotiation')
      ->get('url');
    if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
      if (is_object($options['language']) && !empty($config['prefixes'][$options['language']
        ->getId()])) {
        $options['prefix'] = $config['prefixes'][$options['language']
          ->getId()] . '/';
        if ($bubbleable_metadata) {
          $bubbleable_metadata
            ->addCacheContexts([
            'languages:' . LanguageInterface::TYPE_URL,
          ]);
        }
      }
    }
    elseif ($config['source'] == LanguageNegotiationUrl::CONFIG_DOMAIN) {
      if (is_object($options['language']) && !empty($config['domains'][$options['language']
        ->getId()])) {

        // Save the original base URL. If it contains a port, we need to
        // retain it below.
        if (!empty($options['base_url'])) {

          // The colon in the URL scheme messes up the port checking below.
          $normalized_base_url = str_replace([
            'https://',
            'http://',
          ], '', $options['base_url']);
        }

        // Ask for an absolute URL with our modified base URL.
        $options['absolute'] = TRUE;
        $options['base_url'] = $url_scheme . '://' . $config['domains'][$options['language']
          ->getId()];

        // In case either the original base URL or the HTTP host contains a
        // port, retain it.
        if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
          list(, $port) = explode(':', $normalized_base_url);
          $options['base_url'] .= ':' . $port;
        }
        elseif ($url_scheme == 'http' && $port != 80 || $url_scheme == 'https' && $port != 443) {
          $options['base_url'] .= ':' . $port;
        }
        if (isset($options['https'])) {
          if ($options['https'] === TRUE) {
            $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
          }
          elseif ($options['https'] === FALSE) {
            $options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
          }
        }

        // Add Drupal's subfolder from the base_path if there is one.
        $options['base_url'] .= rtrim(base_path(), '/');
        if ($bubbleable_metadata) {
          $bubbleable_metadata
            ->addCacheContexts([
            'languages:' . LanguageInterface::TYPE_URL,
            'url.site',
          ]);
        }
      }
    }
    return $path;
  }

  /**
   * {@inheritdoc}
   */
  public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
    $links = [];
    $query = $request->query
      ->all();
    foreach ($this->languageManager
      ->getNativeLanguages() as $language) {
      $links[$language
        ->getId()] = [
        // We need to clone the $url object to avoid using the same one for all
        // links. When the links are rendered, options are set on the $url
        // object, so if we use the same one, they would be set for all links.
        'url' => clone $url,
        'title' => $language
          ->getName(),
        'language' => $language,
        'attributes' => [
          'class' => [
            'language-link',
          ],
        ],
        'query' => $query,
      ];
    }
    return $links;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
LanguageNegotiationMethodBase::$config protected property The configuration factory.
LanguageNegotiationMethodBase::$currentUser protected property The current active user.
LanguageNegotiationMethodBase::$languageManager protected property The language manager.
LanguageNegotiationMethodBase::persist public function Notifies the plugin that the language code it returned has been accepted. Overrides LanguageNegotiationMethodInterface::persist 1
LanguageNegotiationMethodBase::setConfig public function Injects the configuration factory. Overrides LanguageNegotiationMethodInterface::setConfig
LanguageNegotiationMethodBase::setCurrentUser public function Injects the current user. Overrides LanguageNegotiationMethodInterface::setCurrentUser
LanguageNegotiationMethodBase::setLanguageManager public function Injects the language manager. Overrides LanguageNegotiationMethodInterface::setLanguageManager
LanguageNegotiationUrl::CONFIG_DOMAIN constant URL language negotiation: use the domain as URL language indicator.
LanguageNegotiationUrl::CONFIG_PATH_PREFIX constant URL language negotiation: use the path prefix as URL language indicator.
LanguageNegotiationUrl::getLangcode public function Performs language negotiation. Overrides LanguageNegotiationMethodInterface::getLangcode
LanguageNegotiationUrl::getLanguageSwitchLinks public function Returns language switch links. Overrides LanguageSwitcherInterface::getLanguageSwitchLinks
LanguageNegotiationUrl::METHOD_ID constant The language negotiation method id.
LanguageNegotiationUrl::processInbound public function Processes the inbound path. Overrides InboundPathProcessorInterface::processInbound
LanguageNegotiationUrl::processOutbound public function Processes the outbound path. Overrides OutboundPathProcessorInterface::processOutbound