For coders TYPO3 Tech Corner

[PHP] Redirect to the best fitting language based on the browserlanguage

[PHP] Redirect to the best fitting language based on the browserlanguage

The codezero/browser-locale package helps us to identify the visitor's browser language. You can load this e.g. via composer.json:

{ "name": "in2code/in2template", "description": "Sitepackage extension", "type": "typo3-cms-extension", "homepage": "https://www.in2code.de", "require": { "typo3/cms-core": "^10.4", "codezero/browser-locale": "^3.0" }, "autoload": { "psr-4": { "In2code\\In2template\\": "Classes/" } } }

Logic is based in LanguageRedirect.php for the redirect:

<?php declare(strict_types=1); namespace In2code\In2template\Middleware; use CodeZero\BrowserLocale\BrowserLocale; use CodeZero\BrowserLocale\Filters\LanguageFilter; use In2code\In2template\Utility\ObjectUtility; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; use TYPO3\CMS\Core\Http\RedirectResponse; use TYPO3\CMS\Core\Routing\PageArguments; use TYPO3\CMS\Core\Site\Entity\Site; use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Object\Exception; /** * Class LanguageRedirect * to redirect users to additional language of home site for browsers that do not support default language */ class LanguageRedirect implements MiddlewareInterface { /** * @var ServerRequestInterface */ protected $request = null; /** * @param ServerRequestInterface $request * @param RequestHandlerInterface $handler * @return ResponseInterface * @throws Exception */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $this->request = $request; if ($this->isRedirectNeeded()) { return $this->redirectToSecondaryLanguage(); } return $handler->handle($request); } /** * Redirect only if: * - Home page * - Referrer is not current domain (to enable a language switch on the website) * - If is default language * - Is no bot (do not redirect if a bot is watching your home page) * - Browser does not support default language * - Is additional language existing * * @return bool */ protected function isRedirectNeeded(): bool { return $this->isHomepage() && $this->isExternalReferrer() && $this->isDefaultLanguage() && $this->isNoBot() && $this->isDefaultLanguageNotSupportedInVisitorBrowser() && $this->isAdditionalLanguageExisting(); } /** * @return bool */ protected function isNoBot(): bool { $bots = [ 'google', 'bingbot', 'slurp', 'duckduck', 'baidu', 'yandex', 'sogou', 'exabot', 'lighthouse' ]; $userAgent = GeneralUtility::getIndpEnv('HTTP_USER_AGENT'); if (empty($userAgent)) { // no useragent - crawler, caretaker or bot? return false; } foreach ($bots as $bot) { if (stristr($userAgent, $bot) !== false) { return false; } } return true; } /** * @return ResponseInterface * @throws Exception */ protected function redirectToSecondaryLanguage(): ResponseInterface { $configuration = [ 'parameter' => $this->getHomePageIdentifier(), 'language' => $this->getSecondaryLanguageIdentifier(), 'forceAbsoluteUrl' => true ]; $url = ObjectUtility::getContentObject()->typoLink_URL($configuration); return new RedirectResponse($url, 302); } /** * @return bool */ protected function isHomepage(): bool { /** @var PageArguments $pageArguments */ $pageArguments = $this->request->getAttribute('routing'); return $pageArguments->getPageId() === $this->getHomePageIdentifier(); } /** * Check if referrer is not within current domain (also empty referrer is used) * * @return bool */ protected function isExternalReferrer(): bool { $referrer = GeneralUtility::getIndpEnv('HTTP_REFERER'); $domain = GeneralUtility::getIndpEnv('HTTP_HOST'); return stristr($referrer, $domain) === false; } /** * @return bool */ protected function isDefaultLanguageNotSupportedInVisitorBrowser(): bool { try { $browser = new BrowserLocale(GeneralUtility::getIndpEnv('HTTP_ACCEPT_LANGUAGE')); $languages = $browser->filter(new LanguageFilter()); return in_array($this->getDefaultLanguageCode(), $languages) === false; } catch (\Exception $exception) { return false; } } /** * @return bool */ protected function isAdditionalLanguageExisting(): bool { $languages = $this->request->getAttribute('site')->getLanguages(); return count($languages) > 1; } /** * @return bool */ protected function isDefaultLanguage(): bool { /** @var SiteLanguage $language */ $language = $this->request->getAttribute('language'); return $language->getLanguageId() === $this->getDefaultLanguageIdentifier(); } /** * Return language code (like "de") from default language configuration in site configuration * * @return string */ protected function getDefaultLanguageCode(): string { /** @var SiteLanguage $defaultLanguage */ $defaultLanguage = $this->request->getAttribute('site')->getDefaultLanguage(); return $defaultLanguage->getTwoLetterIsoCode(); } /** * @return int */ protected function getDefaultLanguageIdentifier(): int { /** @var SiteLanguage $defaultLanguage */ $defaultLanguage = $this->request->getAttribute('site')->getDefaultLanguage(); return $defaultLanguage->getLanguageId(); } /** * Get secondary language id from site configuration * * @return int */ protected function getSecondaryLanguageIdentifier(): int { $languages = $this->request->getAttribute('site')->getLanguages(); if (count($languages) > 1) { /** @var SiteLanguage $secondaryLanguage */ $secondaryLanguage = $languages[1]; return $secondaryLanguage->getLanguageId(); } return 0; } /** * Get page id of the homepage from site configuration * * @return int */ protected function getHomePageIdentifier(): int { /** @var Site $site */ $site = $this->request->getAttribute('site'); return $site->getRootPageId(); } }

RequestMiddlewares.php:

<?php return [ 'frontend' => [ 'in2template-languageredirect' => [ 'target' => \In2code\In2template\Middleware\LanguageRedirect::class, 'after' => [ 'typo3/cms-frontend/tsfe', 'typo3/cms-frontend/page-resolver', ] ] ] ];

Back

"Code faster, look at the time" - does this sound familiar to you?

How about time and respect for code quality? Working in a team? Automated tests?

Join us