From 44fc8221d30d5d8c3a69f08e62266948cfad2232 Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Tue, 17 Jan 2023 23:20:19 +0100
Subject: [PATCH] [BUGFIX] Avoid logging of invalid locales in site
 configuration

TYPO3's log is mostly covered with "Locale fr_FR.UTF-8 not found",
which can be simplified, if setlocale() is not working properly.
The POSIX Platform suffix "UTF-8" is then removed, and setlocale()
is called again with "fr_FR" instead of "fr_FR.UTF-8" thus avoiding
log flooding in some systems.

Resolves: #99591
Releases: main, 11.5, 10.4
Change-Id: I4609d453c29a306d448bcdc3277b51a344af28ae
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/77434
Tested-by: core-ci <typo3@b13.com>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
---
 .../core/Classes/Localization/Locales.php     | 34 ++++++++++++++++---
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/typo3/sysext/core/Classes/Localization/Locales.php b/typo3/sysext/core/Classes/Localization/Locales.php
index 2361be6d8d14..e436025e05f7 100644
--- a/typo3/sysext/core/Classes/Localization/Locales.php
+++ b/typo3/sysext/core/Classes/Localization/Locales.php
@@ -264,10 +264,25 @@ class Locales implements SingletonInterface
     public static function setSystemLocaleFromSiteLanguage(SiteLanguage $siteLanguage): bool
     {
         $locale = $siteLanguage->getLocale();
-        // No locale was given, so return false;
+        // No locale was given, so return false
         if (!$locale) {
             return false;
         }
+        return self::setLocale($locale, $locale);
+    }
+
+    /**
+     * Internal method, which calls itself again, in order to avoid multiple logging issues.
+     * The main reason for this method is that it calls itself again by trying again to set
+     * the locale. Due to sensible defaults, people used the locale "de_AT.utf-8" with the POSIX platform
+     * (see https://en.wikipedia.org/wiki/Locale_(computer_software)#POSIX_platforms) in their site configuration,
+     * even though the target system has "de_AT" and not "de_AT.UTF-8" defined.
+     * "setLocale()" is now called again without the POSIX platform suffix and is checked again if the locale
+     * is then available, and then logs the failed information.
+     */
+    protected static function setLocale(string $locale, string $localeStringForTrigger): bool
+    {
+        $incomingLocale = $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
@@ -283,9 +298,20 @@ class Locales implements SingletonInterface
             setlocale(LC_MONETARY, ...$availableLocales);
             setlocale(LC_TIME, ...$availableLocales);
         } else {
-            GeneralUtility::makeInstance(LogManager::class)
-                ->getLogger(__CLASS__)
-                ->error('Locale "' . htmlspecialchars($siteLanguage->getLocale()) . '" not found.');
+            // Retry again without the "utf-8" POSIX platform suffix if this is given.
+            if (str_contains($incomingLocale, '.')) {
+                [$localeWithoutModifier] = explode('.', $incomingLocale);
+                return self::setLocale($localeWithoutModifier, $incomingLocale);
+            }
+            if ($localeStringForTrigger === $locale) {
+                GeneralUtility::makeInstance(LogManager::class)
+                    ->getLogger(__CLASS__)
+                    ->error('Locale "' . htmlspecialchars($localeStringForTrigger) . '" not found.');
+            } else {
+                GeneralUtility::makeInstance(LogManager::class)
+                    ->getLogger(__CLASS__)
+                    ->error('Locale "' . htmlspecialchars($localeStringForTrigger) . '" and  "' . htmlspecialchars($incomingLocale) . '" not found.');
+            }
             return false;
         }
         return true;
-- 
GitLab