class LanguageNegotiationUrl

Same name and namespace in other branches
  1. 9 core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl
  2. 8.9.x core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl
  3. 10 core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl

Class for identifying language via URL prefix or domain.

Hierarchy

Expanded class hierarchy of LanguageNegotiationUrl

16 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 19

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']) || $options['language'] instanceof LanguageInterface && $options['language']->getId() == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
            $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) && str_contains($normalized_base_url, ':')) {
                    [
                        ,
                        $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 = [];
        parse_str($request->getQueryString() ?? '', $query);
        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

Title Sort descending Modifiers Object type Summary Overriden Title 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

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.