diff --git a/typo3/sysext/core/Classes/Localization/Locales.php b/typo3/sysext/core/Classes/Localization/Locales.php index 0c8c4c913bc9b79a007e515ddf33d277ddc1a5e0..977248bb4f96d20988852bd06c64b000125db9fc 100644 --- a/typo3/sysext/core/Classes/Localization/Locales.php +++ b/typo3/sysext/core/Classes/Localization/Locales.php @@ -14,6 +14,8 @@ namespace TYPO3\CMS\Core\Localization; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Core\Log\LogManager; +use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\CMS\Core\Utility\ArrayUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -256,4 +258,41 @@ class Locales implements \TYPO3\CMS\Core\SingletonInterface } return $selectedLanguage; } + + /** + * Setting locale based on a SiteLanguage's defined locale. + * Used for frontend rendering, previously set within TSFE->settingLocale + * + * @param SiteLanguage $siteLanguage + * @return bool whether the locale was found on the system (and could be set properly) or not + */ + public static function setSystemLocaleFromSiteLanguage(SiteLanguage $siteLanguage): bool + { + $locale = $siteLanguage->getLocale(); + // No locale was given, so return false; + if (!$locale) { + return false; + } + $availableLocales = GeneralUtility::trimExplode(',', $locale, true); + // If LC_NUMERIC is set e.g. to 'de_DE' PHP parses float values locale-aware resulting in strings with comma + // as decimal point which causes problems with value conversions - so we set all locale types except LC_NUMERIC + // @see https://bugs.php.net/bug.php?id=53711 + $locale = setlocale(LC_COLLATE, ...$availableLocales); + if ($locale) { + // As str_* methods are locale aware and turkish has no upper case I + // Class autoloading and other checks depending on case changing break with turkish locale LC_CTYPE + // @see http://bugs.php.net/bug.php?id=35050 + if (strpos($locale, 'tr') !== 0) { + setlocale(LC_CTYPE, ...$availableLocales); + } + setlocale(LC_MONETARY, ...$availableLocales); + setlocale(LC_TIME, ...$availableLocales); + } else { + GeneralUtility::makeInstance(LogManager::class) + ->getLogger(__CLASS__) + ->error('Locale "' . htmlspecialchars($siteLanguage->getLocale()) . '" not found.'); + return false; + } + return true; + } } diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-88473-TypoScriptFrontendController-settingLocale.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-88473-TypoScriptFrontendController-settingLocale.rst new file mode 100644 index 0000000000000000000000000000000000000000..b94c28beab2d0b69be332d7da2ea119aabd67048 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-88473-TypoScriptFrontendController-settingLocale.rst @@ -0,0 +1,40 @@ +.. include:: ../../Includes.txt + +================================================================= +Deprecation: #88473 - TypoScriptFrontendController->settingLocale +================================================================= + +See :issue:`88473` + +Description +=========== + +Due to Site Handling, setting the locale information (:php:`setlocale`) can be handled +much earlier without any dependencies on the global TSFE object. + +The functionality of the method :php:`TypoScriptFrontendController->settingLocale()` has +been moved into :php:`Locales::setSystemLocaleFromSiteLanguage()`. The former method +has been marked as deprecated. + + +Impact +====== + +Calling :php:`TypoScriptFrontendController->settingLocale()` will trigger a PHP :php:`E_USER_DEPRECATED` error. + + +Affected Installations +====================== + +Any TYPO3 installation with a third party extension booting up a custom Frontend system and +explicitly call the method above. + + +Migration +========= + +Migrate the existing PHP code to :php:`Locales::setSystemLocaleFromSiteLanguage()` or ensure +that the SiteResolver middleware for Frontend Requests is executed where the locale is now +set automatically. + +.. index:: Frontend, PHP-API, FullyScanned \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Unit/Localization/LocalesTest.php b/typo3/sysext/core/Tests/Unit/Localization/LocalesTest.php index 9733eabf78e2a5884d4858de1292853cf5d5647d..4f044e5a43530e0a6289439f211b5e5911d70d76 100644 --- a/typo3/sysext/core/Tests/Unit/Localization/LocalesTest.php +++ b/typo3/sysext/core/Tests/Unit/Localization/LocalesTest.php @@ -14,7 +14,9 @@ namespace TYPO3\CMS\Core\Tests\Unit\Localization; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Core\Http\Uri; use TYPO3\CMS\Core\Localization\Locales; +use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; /** @@ -22,6 +24,28 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase; */ class LocalesTest extends UnitTestCase { + protected $resetSingletonInstances = true; + + /** + * @var string + */ + protected $originalLocale; + + protected function setUp(): void + { + parent::setUp(); + $this->originalLocale = setlocale(LC_COLLATE, 0); + } + + protected function tearDown(): void + { + // Restore original locale + setlocale(LC_COLLATE, $this->originalLocale); + setlocale(LC_MONETARY, $this->originalLocale); + setlocale(LC_TIME, $this->originalLocale); + parent::tearDown(); + } + /** * @return array */ @@ -61,4 +85,45 @@ class LocalesTest extends UnitTestCase ); $this->assertSame($expected, $detectedLanguage); } + + /** + * @test + */ + public function setSystemLocaleFromSiteLanguageWithoutLocaleDoesNotSetLocale(): void + { + $language = new SiteLanguage(0, '', new Uri('/'), []); + $result = Locales::setSystemLocaleFromSiteLanguage($language); + static::assertFalse($result); + $currentLocale = setlocale(LC_COLLATE, 0); + // Check that the locale was not overridden + static::assertEquals($this->originalLocale, $currentLocale); + } + + /** + * @test + */ + public function setSystemLocaleFromSiteLanguageWithProperLocaleSetsLocale(): void + { + $locale = 'en_US'; + $language = new SiteLanguage(0, $locale, new Uri('/'), []); + $result = Locales::setSystemLocaleFromSiteLanguage($language); + static::assertTrue($result); + $currentLocale = setlocale(LC_COLLATE, 0); + // Check that the locale was overridden + static::assertEquals($locale, $currentLocale); + } + + /** + * @test + */ + public function setSystemLocaleFromSiteLanguageWithInvalidLocaleDoesNotSetLocale(): void + { + $locale = 'af_EUR'; + $language = new SiteLanguage(0, $locale, new Uri('/'), []); + $result = Locales::setSystemLocaleFromSiteLanguage($language); + static::assertFalse($result); + $currentLocale = setlocale(LC_COLLATE, 0); + // Check that the locale was not overridden + static::assertEquals($this->originalLocale, $currentLocale); + } } diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php index 56feba241f0f8f403cc9fd5fa778048838226857..c815de099d6ad81caece7562c360466365c4045c 100644 --- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php +++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php @@ -2079,29 +2079,14 @@ class TypoScriptFrontendController implements LoggerAwareInterface /** * Setting locale for frontend rendering + * @deprecated will be removed in TYPO3 v11.0. Use Locales::setSystemLocaleFromSiteLanguage() instead. */ public function settingLocale() { + trigger_error('TSFE->settingLocale() will be removed in TYPO3 v11.0. Use Locales::setSystemLocaleFromSiteLanguage() instead, as this functionality is independent of TSFE.', E_USER_DEPRECATED); $siteLanguage = $this->getCurrentSiteLanguage(); - $locale = $siteLanguage->getLocale(); - if ($locale) { - $availableLocales = GeneralUtility::trimExplode(',', $locale, true); - // If LC_NUMERIC is set e.g. to 'de_DE' PHP parses float values locale-aware resulting in strings with comma - // as decimal point which causes problems with value conversions - so we set all locale types except LC_NUMERIC - // @see https://bugs.php.net/bug.php?id=53711 - $locale = setlocale(LC_COLLATE, ...$availableLocales); - if ($locale) { - // As str_* methods are locale aware and turkish has no upper case I - // Class autoloading and other checks depending on case changing break with turkish locale LC_CTYPE - // @see http://bugs.php.net/bug.php?id=35050 - if (strpos($locale, 'tr') !== 0) { - setlocale(LC_CTYPE, ...$availableLocales); - } - setlocale(LC_MONETARY, ...$availableLocales); - setlocale(LC_TIME, ...$availableLocales); - } else { - $this->getTimeTracker()->setTSlogMessage('Locale "' . htmlspecialchars($locale) . '" not found.', 3); - } + if ($siteLanguage->getLocale() && !Locales::setSystemLocaleFromSiteLanguage($siteLanguage)) { + $this->getTimeTracker()->setTSlogMessage('Locale "' . htmlspecialchars($siteLanguage->getLocale()) . '" not found.', 3); } } diff --git a/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php b/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php index 2e754e618278a79b90ff14c8c2b91a8a66b7ee6a..e0f903950f9907982b2de10947809d5492503ff8 100644 --- a/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php +++ b/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php @@ -71,9 +71,8 @@ class PrepareTypoScriptFrontendRendering implements MiddlewareInterface $this->controller->getConfigArray(); // Setting language and locale - $this->timeTracker->push('Setting language and locale'); + $this->timeTracker->push('Setting language'); $this->controller->settingLanguage(); - $this->controller->settingLocale(); $this->timeTracker->pull(); // Convert POST data to utf-8 for internal processing if metaCharset is different diff --git a/typo3/sysext/frontend/Classes/Middleware/SiteResolver.php b/typo3/sysext/frontend/Classes/Middleware/SiteResolver.php index 8423e557af4437311889e4f1d582c5fef3a9793b..dc5e5de53afbe81ec567e677aa48af51a9fdc14e 100644 --- a/typo3/sysext/frontend/Classes/Middleware/SiteResolver.php +++ b/typo3/sysext/frontend/Classes/Middleware/SiteResolver.php @@ -19,8 +19,10 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use TYPO3\CMS\Core\Localization\Locales; use TYPO3\CMS\Core\Routing\SiteMatcher; use TYPO3\CMS\Core\Routing\SiteRouteResult; +use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -60,6 +62,9 @@ class SiteResolver implements MiddlewareInterface $request = $request->withAttribute('site', $routeResult->getSite()); $request = $request->withAttribute('language', $routeResult->getLanguage()); $request = $request->withAttribute('routing', $routeResult); + if ($routeResult->getLanguage() instanceof SiteLanguage) { + Locales::setSystemLocaleFromSiteLanguage($routeResult->getLanguage()); + } return $handler->handle($request); } } diff --git a/typo3/sysext/frontend/Tests/Unit/Middleware/SiteResolverTest.php b/typo3/sysext/frontend/Tests/Unit/Middleware/SiteResolverTest.php index 68a75c986b5699ebd56316ef4fddbb4d0e41eafb..5d2fe6672a7a8baf1cf2de98c3a3d650c587f8be 100644 --- a/typo3/sysext/frontend/Tests/Unit/Middleware/SiteResolverTest.php +++ b/typo3/sysext/frontend/Tests/Unit/Middleware/SiteResolverTest.php @@ -93,6 +93,8 @@ class SiteResolverTest extends UnitTestCase { // restore locale to original setting setlocale(LC_COLLATE, $this->originalLocale); + setlocale(LC_MONETARY, $this->originalLocale); + setlocale(LC_TIME, $this->originalLocale); parent::tearDown(); } diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php index 7fe89fe8a565248f4f4f45d3a79c3adfb60e1a31..101b3cee10a62ef1eb7a973cd957f60952c8fe71 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php @@ -4154,4 +4154,11 @@ return [ 'Deprecation-88406-SetCacheHashnoCacheHashOptionsInViewHelpersAndUriBuilder.rst' ], ], + 'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->settingLocale' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-88473-TypoScriptFrontendController-settingLocale.rst' + ], + ], ]; diff --git a/typo3/sysext/redirects/Classes/Service/RedirectService.php b/typo3/sysext/redirects/Classes/Service/RedirectService.php index 5a7afc0164f7578ebf34221112a65a338622d9bd..47abfde6f5482f0baded11fd653d0e0e269d4f58 100644 --- a/typo3/sysext/redirects/Classes/Service/RedirectService.php +++ b/typo3/sysext/redirects/Classes/Service/RedirectService.php @@ -282,7 +282,6 @@ class RedirectService implements LoggerAwareInterface $controller->calculateLinkVars($queryParams); $controller->getConfigArray(); $controller->settingLanguage(); - $controller->settingLocale(); $controller->newCObj(); return $controller; }