Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\language\Plugin\LanguageNegotiation;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Core\Language\LanguageInterface;
|
Chris@0
|
6 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
|
Chris@0
|
7 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
|
Chris@0
|
8 use Drupal\Core\Render\BubbleableMetadata;
|
Chris@0
|
9 use Drupal\Core\Url;
|
Chris@0
|
10 use Drupal\language\LanguageNegotiationMethodBase;
|
Chris@0
|
11 use Drupal\language\LanguageSwitcherInterface;
|
Chris@0
|
12 use Symfony\Component\HttpFoundation\Request;
|
Chris@0
|
13
|
Chris@0
|
14 /**
|
Chris@0
|
15 * Class for identifying language via URL prefix or domain.
|
Chris@0
|
16 *
|
Chris@0
|
17 * @LanguageNegotiation(
|
Chris@0
|
18 * id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID,
|
Chris@0
|
19 * types = {\Drupal\Core\Language\LanguageInterface::TYPE_INTERFACE,
|
Chris@0
|
20 * \Drupal\Core\Language\LanguageInterface::TYPE_CONTENT,
|
Chris@0
|
21 * \Drupal\Core\Language\LanguageInterface::TYPE_URL},
|
Chris@0
|
22 * weight = -8,
|
Chris@0
|
23 * name = @Translation("URL"),
|
Chris@0
|
24 * description = @Translation("Language from the URL (Path prefix or domain)."),
|
Chris@0
|
25 * config_route_name = "language.negotiation_url"
|
Chris@0
|
26 * )
|
Chris@0
|
27 */
|
Chris@0
|
28 class LanguageNegotiationUrl extends LanguageNegotiationMethodBase implements InboundPathProcessorInterface, OutboundPathProcessorInterface, LanguageSwitcherInterface {
|
Chris@0
|
29
|
Chris@0
|
30 /**
|
Chris@0
|
31 * The language negotiation method id.
|
Chris@0
|
32 */
|
Chris@0
|
33 const METHOD_ID = 'language-url';
|
Chris@0
|
34
|
Chris@0
|
35 /**
|
Chris@0
|
36 * URL language negotiation: use the path prefix as URL language indicator.
|
Chris@0
|
37 */
|
Chris@0
|
38 const CONFIG_PATH_PREFIX = 'path_prefix';
|
Chris@0
|
39
|
Chris@0
|
40 /**
|
Chris@0
|
41 * URL language negotiation: use the domain as URL language indicator.
|
Chris@0
|
42 */
|
Chris@0
|
43 const CONFIG_DOMAIN = 'domain';
|
Chris@0
|
44
|
Chris@0
|
45 /**
|
Chris@0
|
46 * {@inheritdoc}
|
Chris@0
|
47 */
|
Chris@0
|
48 public function getLangcode(Request $request = NULL) {
|
Chris@0
|
49 $langcode = NULL;
|
Chris@0
|
50
|
Chris@0
|
51 if ($request && $this->languageManager) {
|
Chris@0
|
52 $languages = $this->languageManager->getLanguages();
|
Chris@0
|
53 $config = $this->config->get('language.negotiation')->get('url');
|
Chris@0
|
54
|
Chris@0
|
55 switch ($config['source']) {
|
Chris@0
|
56 case LanguageNegotiationUrl::CONFIG_PATH_PREFIX:
|
Chris@0
|
57 $request_path = urldecode(trim($request->getPathInfo(), '/'));
|
Chris@0
|
58 $path_args = explode('/', $request_path);
|
Chris@0
|
59 $prefix = array_shift($path_args);
|
Chris@0
|
60
|
Chris@0
|
61 // Search prefix within added languages.
|
Chris@0
|
62 $negotiated_language = FALSE;
|
Chris@0
|
63 foreach ($languages as $language) {
|
Chris@0
|
64 if (isset($config['prefixes'][$language->getId()]) && $config['prefixes'][$language->getId()] == $prefix) {
|
Chris@0
|
65 $negotiated_language = $language;
|
Chris@0
|
66 break;
|
Chris@0
|
67 }
|
Chris@0
|
68 }
|
Chris@0
|
69
|
Chris@0
|
70 if ($negotiated_language) {
|
Chris@0
|
71 $langcode = $negotiated_language->getId();
|
Chris@0
|
72 }
|
Chris@0
|
73 break;
|
Chris@0
|
74
|
Chris@0
|
75 case LanguageNegotiationUrl::CONFIG_DOMAIN:
|
Chris@0
|
76 // Get only the host, not the port.
|
Chris@0
|
77 $http_host = $request->getHost();
|
Chris@0
|
78 foreach ($languages as $language) {
|
Chris@0
|
79 // Skip the check if the language doesn't have a domain.
|
Chris@0
|
80 if (!empty($config['domains'][$language->getId()])) {
|
Chris@0
|
81 // Ensure that there is exactly one protocol in the URL when
|
Chris@0
|
82 // checking the hostname.
|
Chris@0
|
83 $host = 'http://' . str_replace(['http://', 'https://'], '', $config['domains'][$language->getId()]);
|
Chris@0
|
84 $host = parse_url($host, PHP_URL_HOST);
|
Chris@0
|
85 if ($http_host == $host) {
|
Chris@0
|
86 $langcode = $language->getId();
|
Chris@0
|
87 break;
|
Chris@0
|
88 }
|
Chris@0
|
89 }
|
Chris@0
|
90 }
|
Chris@0
|
91 break;
|
Chris@0
|
92 }
|
Chris@0
|
93 }
|
Chris@0
|
94
|
Chris@0
|
95 return $langcode;
|
Chris@0
|
96 }
|
Chris@0
|
97
|
Chris@0
|
98 /**
|
Chris@0
|
99 * {@inheritdoc}
|
Chris@0
|
100 */
|
Chris@0
|
101 public function processInbound($path, Request $request) {
|
Chris@0
|
102 $config = $this->config->get('language.negotiation')->get('url');
|
Chris@0
|
103
|
Chris@0
|
104 if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
|
Chris@0
|
105 $parts = explode('/', trim($path, '/'));
|
Chris@0
|
106 $prefix = array_shift($parts);
|
Chris@0
|
107
|
Chris@0
|
108 // Search prefix within added languages.
|
Chris@0
|
109 foreach ($this->languageManager->getLanguages() as $language) {
|
Chris@0
|
110 if (isset($config['prefixes'][$language->getId()]) && $config['prefixes'][$language->getId()] == $prefix) {
|
Chris@0
|
111 // Rebuild $path with the language removed.
|
Chris@0
|
112 $path = '/' . implode('/', $parts);
|
Chris@0
|
113 break;
|
Chris@0
|
114 }
|
Chris@0
|
115 }
|
Chris@0
|
116 }
|
Chris@0
|
117
|
Chris@0
|
118 return $path;
|
Chris@0
|
119 }
|
Chris@0
|
120
|
Chris@0
|
121 /**
|
Chris@0
|
122 * {@inheritdoc}
|
Chris@0
|
123 */
|
Chris@0
|
124 public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
|
Chris@0
|
125 $url_scheme = 'http';
|
Chris@0
|
126 $port = 80;
|
Chris@0
|
127 if ($request) {
|
Chris@0
|
128 $url_scheme = $request->getScheme();
|
Chris@0
|
129 $port = $request->getPort();
|
Chris@0
|
130 }
|
Chris@0
|
131 $languages = array_flip(array_keys($this->languageManager->getLanguages()));
|
Chris@0
|
132 // Language can be passed as an option, or we go for current URL language.
|
Chris@0
|
133 if (!isset($options['language'])) {
|
Chris@0
|
134 $language_url = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_URL);
|
Chris@0
|
135 $options['language'] = $language_url;
|
Chris@0
|
136 }
|
Chris@0
|
137 // We allow only added languages here.
|
Chris@0
|
138 elseif (!is_object($options['language']) || !isset($languages[$options['language']->getId()])) {
|
Chris@0
|
139 return $path;
|
Chris@0
|
140 }
|
Chris@0
|
141 $config = $this->config->get('language.negotiation')->get('url');
|
Chris@0
|
142 if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
|
Chris@0
|
143 if (is_object($options['language']) && !empty($config['prefixes'][$options['language']->getId()])) {
|
Chris@0
|
144 $options['prefix'] = $config['prefixes'][$options['language']->getId()] . '/';
|
Chris@0
|
145 if ($bubbleable_metadata) {
|
Chris@0
|
146 $bubbleable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL]);
|
Chris@0
|
147 }
|
Chris@0
|
148 }
|
Chris@0
|
149 }
|
Chris@0
|
150 elseif ($config['source'] == LanguageNegotiationUrl::CONFIG_DOMAIN) {
|
Chris@0
|
151 if (is_object($options['language']) && !empty($config['domains'][$options['language']->getId()])) {
|
Chris@0
|
152
|
Chris@0
|
153 // Save the original base URL. If it contains a port, we need to
|
Chris@0
|
154 // retain it below.
|
Chris@0
|
155 if (!empty($options['base_url'])) {
|
Chris@0
|
156 // The colon in the URL scheme messes up the port checking below.
|
Chris@0
|
157 $normalized_base_url = str_replace(['https://', 'http://'], '', $options['base_url']);
|
Chris@0
|
158 }
|
Chris@0
|
159
|
Chris@0
|
160 // Ask for an absolute URL with our modified base URL.
|
Chris@0
|
161 $options['absolute'] = TRUE;
|
Chris@0
|
162 $options['base_url'] = $url_scheme . '://' . $config['domains'][$options['language']->getId()];
|
Chris@0
|
163
|
Chris@0
|
164 // In case either the original base URL or the HTTP host contains a
|
Chris@0
|
165 // port, retain it.
|
Chris@0
|
166 if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
|
Chris@0
|
167 list(, $port) = explode(':', $normalized_base_url);
|
Chris@0
|
168 $options['base_url'] .= ':' . $port;
|
Chris@0
|
169 }
|
Chris@0
|
170 elseif (($url_scheme == 'http' && $port != 80) || ($url_scheme == 'https' && $port != 443)) {
|
Chris@0
|
171 $options['base_url'] .= ':' . $port;
|
Chris@0
|
172 }
|
Chris@0
|
173
|
Chris@0
|
174 if (isset($options['https'])) {
|
Chris@0
|
175 if ($options['https'] === TRUE) {
|
Chris@0
|
176 $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
|
Chris@0
|
177 }
|
Chris@0
|
178 elseif ($options['https'] === FALSE) {
|
Chris@0
|
179 $options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
|
Chris@0
|
180 }
|
Chris@0
|
181 }
|
Chris@0
|
182
|
Chris@0
|
183 // Add Drupal's subfolder from the base_path if there is one.
|
Chris@0
|
184 $options['base_url'] .= rtrim(base_path(), '/');
|
Chris@0
|
185 if ($bubbleable_metadata) {
|
Chris@0
|
186 $bubbleable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL, 'url.site']);
|
Chris@0
|
187 }
|
Chris@0
|
188 }
|
Chris@0
|
189 }
|
Chris@0
|
190 return $path;
|
Chris@0
|
191 }
|
Chris@0
|
192
|
Chris@0
|
193 /**
|
Chris@0
|
194 * {@inheritdoc}
|
Chris@0
|
195 */
|
Chris@0
|
196 public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
|
Chris@0
|
197 $links = [];
|
Chris@0
|
198 $query = $request->query->all();
|
Chris@0
|
199
|
Chris@0
|
200 foreach ($this->languageManager->getNativeLanguages() as $language) {
|
Chris@0
|
201 $links[$language->getId()] = [
|
Chris@0
|
202 // We need to clone the $url object to avoid using the same one for all
|
Chris@0
|
203 // links. When the links are rendered, options are set on the $url
|
Chris@0
|
204 // object, so if we use the same one, they would be set for all links.
|
Chris@0
|
205 'url' => clone $url,
|
Chris@0
|
206 'title' => $language->getName(),
|
Chris@0
|
207 'language' => $language,
|
Chris@0
|
208 'attributes' => ['class' => ['language-link']],
|
Chris@0
|
209 'query' => $query,
|
Chris@0
|
210 ];
|
Chris@0
|
211 }
|
Chris@0
|
212
|
Chris@0
|
213 return $links;
|
Chris@0
|
214 }
|
Chris@0
|
215
|
Chris@0
|
216 }
|