From d047811e93cabdc371a918c5f3be60ba9dde71fe Mon Sep 17 00:00:00 2001 From: Benni Mack <benni@typo3.org> Date: Tue, 17 Jan 2023 17:14:11 +0100 Subject: [PATCH] [TASK] Clean up Language-related functionality This change cleans up some tests, and also streamlines some usages (not all of them) of $TYPO3_CONF_VARS['EXTCONF']['lang']['availableLanguages']. Resolves: #99578 Releases: main Change-Id: I52b6f43260d08abf59d6078498511084469b2308 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/77423 Reviewed-by: Oliver Hader <oliver.hader@typo3.org> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: core-ci <typo3@b13.com> Tested-by: Nikita Hovratov <nikita.h@live.de> Reviewed-by: Nikita Hovratov <nikita.h@live.de> --- .../core/Classes/Localization/CacheWarmer.php | 12 +++------ .../core/Classes/Localization/Locales.php | 12 +++++++++ .../TcaSystemLanguageCollector.php | 2 +- .../Classes/Utility/LocalizationUtility.php | 19 +++++-------- .../Utility/LocalizationUtilityTest.php | 13 ++------- .../ViewHelpers/TranslateViewHelper.php | 4 +-- .../Classes/Service/TranslationService.php | 27 +++++-------------- .../Service/TranslationServiceTest.php | 7 +++-- 8 files changed, 36 insertions(+), 60 deletions(-) diff --git a/typo3/sysext/core/Classes/Localization/CacheWarmer.php b/typo3/sysext/core/Classes/Localization/CacheWarmer.php index b3360e6057a7..9dcd39a795bd 100644 --- a/typo3/sysext/core/Classes/Localization/CacheWarmer.php +++ b/typo3/sysext/core/Classes/Localization/CacheWarmer.php @@ -25,21 +25,17 @@ use TYPO3\CMS\Core\Package\PackageManager; */ class CacheWarmer { - protected PackageManager $packageManager; - protected LocalizationFactory $localizationFactory; - public function __construct( - PackageManager $packageManager, - LocalizationFactory $localizationFactory + protected PackageManager $packageManager, + protected Locales $locales, + protected LocalizationFactory $localizationFactory ) { - $this->packageManager = $packageManager; - $this->localizationFactory = $localizationFactory; } public function warmupCaches(CacheWarmupEvent $event): void { if ($event->hasGroup('system')) { - $languages = array_merge(['default'], $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lang']['availableLanguages'] ?? []); + $languages = $this->locales->getActiveLanguages(); $packages = $this->packageManager->getActivePackages(); foreach ($packages as $package) { $dir = $package->getPackagePath() . 'Resources/Private/Language'; diff --git a/typo3/sysext/core/Classes/Localization/Locales.php b/typo3/sysext/core/Classes/Localization/Locales.php index 9233e100f5a9..837e4c6a46c2 100644 --- a/typo3/sysext/core/Classes/Localization/Locales.php +++ b/typo3/sysext/core/Classes/Localization/Locales.php @@ -199,6 +199,18 @@ class Locales implements SingletonInterface return $this->languages; } + /** + * Returns a list of all ISO codes / TYPO3 languages that have active language packs, but also includes "default". + * @return array<int, non-empty-string> + */ + public function getActiveLanguages(): array + { + return array_merge( + ['default'], + array_filter(array_values($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lang']['availableLanguages'] ?? [])) + ); + } + /** * Returns the mapping between TYPO3 (old) language codes and ISO codes. * diff --git a/typo3/sysext/core/Classes/Localization/TcaSystemLanguageCollector.php b/typo3/sysext/core/Classes/Localization/TcaSystemLanguageCollector.php index 3e3192f9965c..58c476911950 100644 --- a/typo3/sysext/core/Classes/Localization/TcaSystemLanguageCollector.php +++ b/typo3/sysext/core/Classes/Localization/TcaSystemLanguageCollector.php @@ -44,7 +44,7 @@ class TcaSystemLanguageCollector $languageItems = $this->locales->getLanguages(); unset($languageItems['default']); asort($languageItems); - $installedLanguages = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lang']['availableLanguages'] ?? []; + $installedLanguages = $this->locales->getActiveLanguages(); $availableLanguages = []; $unavailableLanguages = []; foreach ($languageItems as $typo3Language => $name) { diff --git a/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php b/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php index fa8aa15024d5..b0f003ea0c6c 100644 --- a/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php +++ b/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php @@ -47,12 +47,12 @@ class LocalizationUtility * * @param string $key The key from the LOCAL_LANG array for which to return the value. * @param string|null $extensionName The name of the extension - * @param array $arguments The arguments of the extension, being passed over to sprintf - * @param string $languageKey The language key or null for using the current language from the system + * @param array|null $arguments The arguments of the extension, being passed over to sprintf + * @param string|null $languageKey The language key or null for using the current language from the system * @param string[] $alternativeLanguageKeys The alternative language keys if no translation was found. * @return string|null The value from LOCAL_LANG or null if no translation was found. */ - public static function translate(string $key, ?string $extensionName = null, array $arguments = null, string $languageKey = null, array $alternativeLanguageKeys = null): ?string + public static function translate(string $key, ?string $extensionName = null, array $arguments = null, string $languageKey = null, array $alternativeLanguageKeys = []): ?string { if ($key === '') { // Early return guard: returns null if the key was empty, because the key may be a dynamic value @@ -76,7 +76,7 @@ class LocalizationUtility if ($languageKey === null) { $languageKey = static::getLanguageKey(); } - $languageService = static::initializeLocalization($languageFilePath, $languageKey, $alternativeLanguageKeys ?? [], $extensionName); + $languageService = static::initializeLocalization($languageFilePath, $languageKey, $alternativeLanguageKeys, $extensionName); $resolvedLabel = $languageService->sL('LLL:' . $languageFilePath . ':' . $key); $value = $resolvedLabel !== '' ? $resolvedLabel : null; @@ -99,7 +99,7 @@ class LocalizationUtility /** * Loads local-language values by looking for a "locallang.xlf" file in the plugin resources directory and if found includes it. - * Also locallang values set in the TypoScript property "_LOCAL_LANG" are merged onto the values found in the "locallang.xlf" file. + * Locallang values set in the TypoScript property "_LOCAL_LANG" are merged onto the values found in the "locallang.xlf" file. * * @param string[] $alternativeLanguageKeys */ @@ -156,8 +156,6 @@ class LocalizationUtility } } elseif (!empty($GLOBALS['BE_USER']->user['lang'])) { return $GLOBALS['BE_USER']->user['lang']; - } elseif (!empty(static::getLanguageService()->lang)) { - return static::getLanguageService()->lang; } return 'default'; } @@ -244,16 +242,11 @@ class LocalizationUtility protected static function getCurrentSiteLanguage(): ?SiteLanguage { if ($GLOBALS['TYPO3_REQUEST'] instanceof ServerRequestInterface) { - return $GLOBALS['TYPO3_REQUEST']->getAttribute('language', null); + return $GLOBALS['TYPO3_REQUEST']->getAttribute('language'); } return null; } - protected static function getLanguageService(): LanguageService - { - return $GLOBALS['LANG']; - } - protected static function getRuntimeCache(): FrontendInterface { return GeneralUtility::makeInstance(CacheManager::class)->getCache('runtime'); diff --git a/typo3/sysext/extbase/Tests/Functional/Utility/LocalizationUtilityTest.php b/typo3/sysext/extbase/Tests/Functional/Utility/LocalizationUtilityTest.php index be9ca2bbda89..cf917caa477b 100644 --- a/typo3/sysext/extbase/Tests/Functional/Utility/LocalizationUtilityTest.php +++ b/typo3/sysext/extbase/Tests/Functional/Utility/LocalizationUtilityTest.php @@ -19,8 +19,6 @@ namespace TYPO3\CMS\Extbase\Tests\Functional\Utility; use PHPUnit\Framework\MockObject\MockObject; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; -use TYPO3\CMS\Core\Localization\LanguageServiceFactory; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; @@ -149,9 +147,8 @@ class LocalizationUtilityTest extends FunctionalTestCase ->with('Framework', 'label_test', null) ->willReturn([]); - $backendUserAuthentication = new BackendUserAuthentication(); - $backendUserAuthentication->user = ['lang' => $languageKey]; - $GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageServiceFactory::class)->createFromUserPreferences($backendUserAuthentication); + $GLOBALS['BE_USER'] = new BackendUserAuthentication(); + $GLOBALS['BE_USER']->user = ['lang' => $languageKey]; self::assertSame($expected, LocalizationUtility::translate($key, 'label_test', $arguments, alternativeLanguageKeys: $altLanguageKeys)); } @@ -172,7 +169,6 @@ class LocalizationUtilityTest extends FunctionalTestCase ->with('Framework', 'label_test', null) ->willReturn([]); - $GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageServiceFactory::class)->create('default'); self::assertSame($expected, LocalizationUtility::translate($key, 'label_test', $arguments, $languageKey, $altLanguageKeys)); } @@ -205,7 +201,6 @@ class LocalizationUtilityTest extends FunctionalTestCase ], ]); - $GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageServiceFactory::class)->create('default'); self::assertSame('key1 value from TS core', LocalizationUtility::translate('key1', 'label_test', languageKey: 'da')); // Label from XLF file, no override self::assertSame('English label for key2', LocalizationUtility::translate('key2', 'label_test', languageKey: 'da')); @@ -232,8 +227,6 @@ class LocalizationUtilityTest extends FunctionalTestCase ], ]); - $GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageServiceFactory::class)->create('default'); - $result = LocalizationUtility::translate('key1', 'label_test', languageKey: 'da'); self::assertSame('', $result); @@ -269,8 +262,6 @@ class LocalizationUtilityTest extends FunctionalTestCase ->with($configurationType, 'core', null) ->willReturn($typoScriptLocalLang); - $GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageServiceFactory::class)->create('default'); - $result = LocalizationUtility::translate('key1', 'core', languageKey: 'da'); self::assertSame('I am a new key and there is no xlf file', $result); diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php index a1dc258b7a0d..2d8a98af9525 100644 --- a/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php +++ b/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php @@ -122,7 +122,7 @@ final class TranslateViewHelper extends AbstractViewHelper $this->registerArgument('default', 'string', 'If the given locallang key could not be found, this value is used. If this argument is not set, child nodes will be used to render the default'); $this->registerArgument('arguments', 'array', 'Arguments to be replaced in the resulting string'); $this->registerArgument('extensionName', 'string', 'UpperCamelCased extension key (for example BlogExample)'); - $this->registerArgument('languageKey', 'string', 'Language key ("dk" for example) or "default" to use. If empty, use current language. Ignored in non-extbase context.'); + $this->registerArgument('languageKey', 'string', 'Language key ("da" for example) or "default" to use. If empty, use current language. Ignored in non-extbase context.'); $this->registerArgument('alternativeLanguageKeys', 'array', 'Alternative language keys if no translation does exist. Ignored in non-extbase context.'); } @@ -191,7 +191,7 @@ final class TranslateViewHelper extends AbstractViewHelper // overloads from _LOCAL_LANG extbase TypoScript settings if specified. // Not this triggers TypoScript parsing via extbase ConfigurationManager // and should be avoided in backend context! - $value = LocalizationUtility::translate($id, $extensionName, $translateArguments, $arguments['languageKey'], $arguments['alternativeLanguageKeys']); + $value = LocalizationUtility::translate($id, $extensionName, $translateArguments, $arguments['languageKey'], $arguments['alternativeLanguageKeys'] ?? []); } catch (\InvalidArgumentException $e) { // @todo: Switch to more specific Exceptions here - for instance those thrown when a package was not found, see #95957 $value = null; diff --git a/typo3/sysext/form/Classes/Service/TranslationService.php b/typo3/sysext/form/Classes/Service/TranslationService.php index 775d3cbceae5..fdf1b13e68d1 100644 --- a/typo3/sysext/form/Classes/Service/TranslationService.php +++ b/typo3/sysext/form/Classes/Service/TranslationService.php @@ -22,6 +22,7 @@ use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; use TYPO3\CMS\Core\Http\ApplicationType; use TYPO3\CMS\Core\Localization\LanguageService; use TYPO3\CMS\Core\Localization\LanguageServiceFactory; +use TYPO3\CMS\Core\Localization\Locales; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\CMS\Core\Utility\ArrayUtility; @@ -50,9 +51,10 @@ class TranslationService implements SingletonInterface protected string $languageKey = ''; public function __construct( - protected ConfigurationManagerInterface $configurationManager, - protected LanguageServiceFactory $languageServiceFactory, - protected FrontendInterface $runtimeCache, + protected readonly ConfigurationManagerInterface $configurationManager, + protected readonly LanguageServiceFactory $languageServiceFactory, + protected readonly FrontendInterface $runtimeCache, + protected readonly Locales $locales ) { } @@ -159,7 +161,7 @@ class TranslationService implements SingletonInterface $result = []; $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFiles); - foreach ($this->getAllTypo3BackendLanguages() as $language) { + foreach ($this->locales->getActiveLanguages() as $language) { $result[$language] = $key; foreach ($translationFiles as $translationFile) { $translatedValue = $this->translate($key, $arguments, $translationFile, $language, $key); @@ -519,8 +521,6 @@ class TranslationService implements SingletonInterface $this->languageKey = $this->getCurrentSiteLanguage()->getTypo3Language(); } elseif (!empty($GLOBALS['BE_USER']->user['lang'])) { $this->languageKey = $GLOBALS['BE_USER']->user['lang']; - } elseif (!empty($this->getLanguageService()->lang)) { - $this->languageKey = $this->getLanguageService()->lang; } } } @@ -635,19 +635,4 @@ class TranslationService implements SingletonInterface } return null; } - - protected function getAllTypo3BackendLanguages(): array - { - $languages = array_merge( - ['default'], - array_values($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lang']['availableLanguages'] ?? []) - ); - - return $languages; - } - - protected function getLanguageService(): LanguageService - { - return $GLOBALS['LANG']; - } } diff --git a/typo3/sysext/form/Tests/Functional/Service/TranslationServiceTest.php b/typo3/sysext/form/Tests/Functional/Service/TranslationServiceTest.php index 969b35e0e1b4..15453f109266 100644 --- a/typo3/sysext/form/Tests/Functional/Service/TranslationServiceTest.php +++ b/typo3/sysext/form/Tests/Functional/Service/TranslationServiceTest.php @@ -18,7 +18,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Form\Tests\Functional\Service; use TYPO3\CMS\Core\Localization\LanguageServiceFactory; -use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Localization\Locales; use TYPO3\CMS\Extbase\Configuration\ConfigurationManager; use TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement; use TYPO3\CMS\Form\Domain\Model\FormElements\Page; @@ -40,10 +40,9 @@ class TranslationServiceTest extends FunctionalTestCase $this->subject = new TranslationService( $configurationManager, $this->get(LanguageServiceFactory::class), - $this->get('cache.runtime') + $this->get('cache.runtime'), + new Locales() ); - - $GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageServiceFactory::class)->create('default'); } /** -- GitLab