config = $config; $this->storeCodeMapper = $storeCodeMapper; $this->storeViewRegistry = $storeViewRegistry; $this->storeManager = $storeManager; } public function toLanguageUrl(string $url, string $language): string { $parts = parse_url($url); if ($parts === false) { return $url; } if ($this->isExternalUrl($parts)) { return $url; } $path = $parts['path'] ?? '/'; $path = $path === '' ? '/' : $path; $basePath = $this->getBasePath(); $relativePath = $this->stripBasePath($path, $basePath); if ($this->config->shouldSkipPath($relativePath)) { return $url; } $parts['path'] = $this->prependBasePath( $basePath, $this->convertRelativePath($relativePath, strtolower($language)) ); return $this->buildUrl($parts); } /** * @param mixed[] $parts */ private function isExternalUrl(array $parts): bool { if (!isset($parts['host'])) { return false; } $baseParts = parse_url($this->storeManager->getStore()->getBaseUrl(UrlInterface::URL_TYPE_WEB)); $baseHost = isset($baseParts['host']) ? strtolower($baseParts['host']) : ''; return strtolower((string)$parts['host']) !== $baseHost; } private function convertRelativePath(string $path, string $language): string { $path = '/' . ltrim($path, '/'); $segments = explode('/', trim($path, '/'), 2); $firstSegment = $segments[0] ?? ''; $remainingPath = $segments[1] ?? ''; if ($this->storeCodeMapper->isRealStoreCodeInScope($firstSegment)) { $language = $this->storeCodeMapper->languageFromStoreCode($firstSegment) ?? $language; $path = $remainingPath === '' ? '/' : '/' . ltrim($remainingPath, '/'); } if ($this->storeCodeMapper->isLanguageAllowed(strtolower($firstSegment))) { $path = $remainingPath === '' ? '/' : '/' . ltrim($remainingPath, '/'); } if ($this->storeCodeMapper->isDefaultLanguage($language)) { return $path; } return $path === '/' ? '/' . $language . '/' : '/' . $language . $path; } private function getBasePath(): string { $baseParts = parse_url($this->storeManager->getStore()->getBaseUrl(UrlInterface::URL_TYPE_WEB)); $basePath = isset($baseParts['path']) ? rtrim($baseParts['path'], '/') : ''; return $basePath !== '' ? $basePath : '/'; } private function stripBasePath(string $path, string $basePath): string { if ($basePath === '/' || $basePath === '') { return '/' . ltrim($path, '/'); } if ($path === $basePath) { return '/'; } if (strpos($path, $basePath . '/') === 0) { return '/' . ltrim(substr($path, strlen($basePath)), '/'); } return '/' . ltrim($path, '/'); } private function prependBasePath(string $basePath, string $path): string { $path = '/' . ltrim($path, '/'); if ($basePath === '/' || $basePath === '') { return $path; } return rtrim($basePath, '/') . $path; } /** * @param mixed[] $parts */ private function buildUrl(array $parts): string { $url = ''; if (isset($parts['host'])) { $url .= isset($parts['scheme']) ? $parts['scheme'] . '://' : '//'; if (isset($parts['user'])) { $url .= $parts['user']; if (isset($parts['pass'])) { $url .= ':' . $parts['pass']; } $url .= '@'; } $url .= $parts['host']; if (isset($parts['port'])) { $url .= ':' . $parts['port']; } } $url .= $parts['path'] ?? '/'; if (isset($parts['query']) && $parts['query'] !== '') { $url .= '?' . $parts['query']; } if (isset($parts['fragment']) && $parts['fragment'] !== '') { $url .= '#' . $parts['fragment']; } return $url; } }