From c607d319f16eada816a3d200c9a43658f376ab9b Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Mon, 24 Apr 2023 10:10:02 +0200
Subject: [PATCH] [TASK] Deprecate various language-related methods and
 properties

The following methods and properties have been
deprecated:

* Argument $alternativeLanguageKeys in LocalizationUtility::translate()
* <f:translate> ViewHelper argument "alternativeLanguageKeys"
* LanguageService->getLL()

The method "LanguageService->includeLLFile()" is
marked as internal.

This is done in order to streamline future development
in label-related areas.

Resolves: #100721
Releases: main
Change-Id: I1a0bc2eab61ec807cb43082dfef2361dbe6b185f
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/78799
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: core-ci <typo3@b13.com>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
Tested-by: Oliver Bartsch <bo@cedev.de>
Tested-by: Oliver Klee <typo3-coding@oliverklee.de>
---
 .../Classes/RecordList/DatabaseRecordList.php |   4 +-
 .../Classes/Localization/LanguageService.php  |   3 +
 ...100721-LabelRelatedMethodsAndArguments.rst |  74 ++++++++++++
 .../Classes/Utility/LocalizationUtility.php   |  20 ++--
 .../Utility/LocalizationUtilityTest.php       |   9 --
 .../Utility/LocalizationUtilityTest.php       | 108 ++++++++++++++++++
 .../ViewHelpers/TranslateViewHelper.php       |   5 +-
 .../MethodArgumentDroppedStaticMatcher.php    |   6 +
 .../Php/MethodCallMatcher.php                 |   7 ++
 9 files changed, 216 insertions(+), 20 deletions(-)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/12.4/Deprecation-100721-LabelRelatedMethodsAndArguments.rst
 create mode 100644 typo3/sysext/extbase/Tests/FunctionalDeprecated/Utility/LocalizationUtilityTest.php

diff --git a/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php b/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php
index 6663e20092c1..ab741ff11074 100644
--- a/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php
+++ b/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php
@@ -622,11 +622,11 @@ class DatabaseRecordList
             $icon = $this->table // @todo separate table header from contract/expand link
                 ? $this->iconFactory
                     ->getIcon('actions-view-table-collapse', Icon::SIZE_SMALL)
-                    ->setTitle($lang->getLL('contractView'))
+                    ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:contractView'))
                     ->render()
                 : $this->iconFactory
                     ->getIcon('actions-view-table-expand', Icon::SIZE_SMALL)
-                    ->setTitle($lang->getLL('expandView'))
+                    ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:expandView'))
                     ->render();
             $theData[$titleCol] = $this->linkWrapTable($table, $tableTitle . ' (<span class="t3js-table-total-items">' . $totalItems . '</span>) ' . $icon);
         }
diff --git a/typo3/sysext/core/Classes/Localization/LanguageService.php b/typo3/sysext/core/Classes/Localization/LanguageService.php
index 6ca4b861028d..27ca8dc18aa6 100644
--- a/typo3/sysext/core/Classes/Localization/LanguageService.php
+++ b/typo3/sysext/core/Classes/Localization/LanguageService.php
@@ -128,9 +128,11 @@ class LanguageService
      *
      * @param string $index Label key
      * @return string
+     * @deprecated will be removed in TYPO3 v13.0. Use sL() instead.
      */
     public function getLL($index)
     {
+        trigger_error('Calling LanguageService->getLL() will be removed in TYPO3 v13.0. Use LanguageService->sL() instead.', E_USER_DEPRECATED);
         return $this->getLLL($index, $this->labels);
     }
 
@@ -216,6 +218,7 @@ class LanguageService
      *
      * @param string $fileRef $fileRef is a file-reference
      * @return array returns the loaded label file
+     * @internal do not rely on this method as it is only used for internal purposes in TYPO3 v13.0.
      */
     public function includeLLFile(string $fileRef): array
     {
diff --git a/typo3/sysext/core/Documentation/Changelog/12.4/Deprecation-100721-LabelRelatedMethodsAndArguments.rst b/typo3/sysext/core/Documentation/Changelog/12.4/Deprecation-100721-LabelRelatedMethodsAndArguments.rst
new file mode 100644
index 000000000000..aaae92cbbf7b
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/12.4/Deprecation-100721-LabelRelatedMethodsAndArguments.rst
@@ -0,0 +1,74 @@
+.. include:: /Includes.rst.txt
+
+.. _deprecation-100721-1682333511:
+
+==========================================================
+Deprecation: #100721 - Label-related methods and arguments
+==========================================================
+
+See :issue:`100721`
+
+Description
+===========
+
+The method :php:`TYPO3\CMS\Core\Localization\LanguageService->getLL()` has been
+marked as deprecated.
+
+Along with the deprecation the method
+:php:`TYPO3\CMS\Core\Localization\LanguageService->includeLLFile()` has been
+marked as internal, as it is still used in TYPO3 core for backwards-compatibility
+internally, but not part of TYPO3's Core API anymore.
+
+With the introduction of Locales, it is also now not recommended anymore to use
+custom alternative language keys.
+
+For this reason the argument "alternativeLanguageKeys" of the
+:html:`<f:translate>` ViewHelper  has been deprecated as well, along with the
+method argument of the same name in
+:php:`TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate()`.
+
+
+Impact
+======
+
+Calling the method :php:`TYPO3\CMS\Core\Localization\LanguageService->getLL()`
+will trigger a PHP deprecation warning.
+
+Calling :php:`TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate()` with
+the argument "alternativeLanguageKeys" will also trigger a PHP deprecation warning,
+which is the underlying deprecation warning when using the argument
+"alternativeLanguageKeys" of the :html:`<f:translate>` ViewHelper.
+
+
+Affected installations
+======================
+
+TYPO3 installations within Backend modules using the method :php:`getLL()` or
+extensions or templates using the translate methods.
+
+The former usually happens in extensions which have been migrated from older
+TYPO3 versions with legacy functionality in Backend modules along
+with :php:`$GLOBALS['LANG']` as LanguageService object.
+
+
+Migration
+=========
+
+It is highly recommended to use the full path to a label file along
+with the :php:`sL()` method of :php:`TYPO3\CMS\Core\Localization\LanguageService`:
+
+Before:
+
+.. code-block:: php
+
+    $GLOBALS['LANG']->includeLLfile('EXT:my_extension/Resources/Private/Language/db.xlf');
+    $label = htmlspecialchars($GLOBALS['LANG']->getLL('my_label'));
+
+After:
+
+.. code-block:: php
+
+    $label = $GLOBALS['LANG']->sL('LLL:EXT:my_extension/Resources/Private/Language/db.xlf:my_label');
+    $label = htmlspecialchars($label);
+
+.. index:: PHP-API, PartiallyScanned, ext:core
diff --git a/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php b/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php
index 6c1147cf4a02..bdf27ea4a0e3 100644
--- a/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php
+++ b/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php
@@ -34,10 +34,7 @@ use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
  */
 class LocalizationUtility
 {
-    /**
-     * @var string
-     */
-    protected static $locallangPath = 'Resources/Private/Language/';
+    protected static string $locallangPath = 'Resources/Private/Language/';
 
     /**
      * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
@@ -50,11 +47,11 @@ 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|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[]|null $alternativeLanguageKeys The alternative language keys if no translation was found.
+     * @param Locale|string|null $languageKey The language key or null for using the current language from the system
+     * @param string[]|null $alternativeLanguageKeys The alternative language keys if no translation was found. @deprecated will be removed in TYPO3 v12.0
      * @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, Locale|string $languageKey = null, array $alternativeLanguageKeys = null): ?string
     {
         if ($key === '') {
             // Early return guard: returns null if the key was empty, because the key may be a dynamic value
@@ -78,6 +75,15 @@ class LocalizationUtility
         if ($languageKey === null) {
             $languageKey = static::getLanguageKey();
         }
+        if ($alternativeLanguageKeys !== null && $alternativeLanguageKeys !== []) {
+            trigger_error('Calling LocalizationUtility::translate() with the argument $alternativeLanguageKeys will be removed in TYPO3 v13.0. Use Locales instead.', E_USER_DEPRECATED);
+        }
+        if ($languageKey instanceof Locale) {
+            if ($alternativeLanguageKeys !== null && $alternativeLanguageKeys !== []) {
+                $alternativeLanguageKeys = $languageKey->getDependencies();
+            }
+            $languageKey = (string)$languageKey;
+        }
         $languageService = static::initializeLocalization($languageFilePath, $languageKey, $alternativeLanguageKeys, $extensionName);
         $resolvedLabel = $languageService->sL('LLL:' . $languageFilePath . ':' . $key);
         $value = $resolvedLabel !== '' ? $resolvedLabel : null;
diff --git a/typo3/sysext/extbase/Tests/Functional/Utility/LocalizationUtilityTest.php b/typo3/sysext/extbase/Tests/Functional/Utility/LocalizationUtilityTest.php
index bb880ce5c9a2..db9b90570a01 100644
--- a/typo3/sysext/extbase/Tests/Functional/Utility/LocalizationUtilityTest.php
+++ b/typo3/sysext/extbase/Tests/Functional/Utility/LocalizationUtilityTest.php
@@ -115,15 +115,6 @@ final class LocalizationUtilityTest extends FunctionalTestCase
 
             'placeholder and empty arguments in translation' =>
             ['keyWithPlaceholderAndNoArguments', 'da', '%d-%m-%Y', [], []],
-
-            'get translated key from primary language' =>
-            ['key1', 'da', 'Dansk label for key1', ['da_alt']],
-
-            'fallback to alternative language if translation is missing' =>
-            ['key2', 'da', 'Dansk alternative label for key2', ['da_alt']],
-
-            'fallback to English for label not translated in da and da_alt' =>
-            ['key3', 'da', 'English label for key3', ['da_alt']],
         ];
     }
 
diff --git a/typo3/sysext/extbase/Tests/FunctionalDeprecated/Utility/LocalizationUtilityTest.php b/typo3/sysext/extbase/Tests/FunctionalDeprecated/Utility/LocalizationUtilityTest.php
new file mode 100644
index 000000000000..dfde867cdd82
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/FunctionalDeprecated/Utility/LocalizationUtilityTest.php
@@ -0,0 +1,108 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Extbase\Tests\FunctionalDeprecated\Utility;
+
+use PHPUnit\Framework\MockObject\MockObject;
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
+use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
+use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
+
+final class LocalizationUtilityTest extends FunctionalTestCase
+{
+    protected ConfigurationManagerInterface&MockObject $configurationManagerInterfaceMock;
+
+    protected array $testExtensionsToLoad = ['typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/label_test'];
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        $reflectionClass = new \ReflectionClass(LocalizationUtility::class);
+        $this->configurationManagerInterfaceMock = $this->createMock(ConfigurationManagerInterface::class);
+        $property = $reflectionClass->getProperty('configurationManager');
+        $property->setValue($this->configurationManagerInterfaceMock);
+    }
+
+    /**
+     * Reset static properties
+     */
+    protected function tearDown(): void
+    {
+        $reflectionClass = new \ReflectionClass(LocalizationUtility::class);
+        $property = $reflectionClass->getProperty('configurationManager');
+        $property->setValue(null);
+        parent::tearDown();
+    }
+
+    public static function translateDataProvider(): array
+    {
+        return [
+            'get translated key from primary language' =>
+            ['key1', 'da', 'Dansk label for key1', ['da_alt']],
+
+            'fallback to alternative language if translation is missing' =>
+            ['key2', 'da', 'Dansk alternative label for key2', ['da_alt']],
+
+            'fallback to English for label not translated in da and da_alt' =>
+            ['key3', 'da', 'English label for key3', ['da_alt']],
+        ];
+    }
+
+    /**
+     * @dataProvider translateDataProvider
+     * @test
+     */
+    public function translateTestWithBackendUserLanguage(
+        string $key,
+        string $languageKey,
+        string $expected,
+        array $altLanguageKeys = [],
+        array $arguments = null
+    ): void {
+        // No TypoScript overrides
+        $this->configurationManagerInterfaceMock
+            ->method('getConfiguration')
+            ->with('Framework', 'label_test', null)
+            ->willReturn([]);
+
+        $GLOBALS['BE_USER'] = new BackendUserAuthentication();
+        $GLOBALS['BE_USER']->user = ['lang' => $languageKey];
+        self::assertSame($expected, LocalizationUtility::translate($key, 'label_test', $arguments, alternativeLanguageKeys: $altLanguageKeys));
+    }
+
+    /**
+     * @dataProvider translateDataProvider
+     * @test
+     */
+    public function translateTestWithExplicitLanguageParameters(
+        string $key,
+        string $languageKey,
+        string $expected,
+        array $altLanguageKeys = [],
+        array $arguments = null
+    ): void {
+        // No TypoScript overrides
+        $this->configurationManagerInterfaceMock
+            ->method('getConfiguration')
+            ->with('Framework', 'label_test', null)
+            ->willReturn([]);
+
+        self::assertSame($expected, LocalizationUtility::translate($key, 'label_test', $arguments, $languageKey, $altLanguageKeys));
+    }
+
+}
diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php
index 724d32b060fc..894e599453b9 100644
--- a/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php
+++ b/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php
@@ -123,7 +123,8 @@ final class TranslateViewHelper extends AbstractViewHelper
         $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 ("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.');
+        // @deprecated will be removed in TYPO3 v13.0. Deprecation is triggered in LocalizationUtility
+        $this->registerArgument('alternativeLanguageKeys', 'array', 'Alternative language keys if no translation does exist. Ignored in non-extbase context. Deprecated, will be removed in TYPO3 v13.0');
     }
 
     /**
@@ -196,7 +197,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'] ?? null);
         } 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/install/Configuration/ExtensionScanner/Php/MethodArgumentDroppedStaticMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodArgumentDroppedStaticMatcher.php
index a83d0e374eba..ae399d2f668b 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodArgumentDroppedStaticMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodArgumentDroppedStaticMatcher.php
@@ -95,4 +95,10 @@ return [
             'Breaking-98069-DebugConsoleRemoved.rst',
         ],
     ],
+    'TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate' => [
+        'maximumNumberOfArguments' => 4,
+        'restFiles' => [
+            'Deprecation-100721-LabelRelatedMethodsAndArguments.rst',
+        ],
+    ],
 ];
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
index 1f222e0b164a..01666eec3494 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
@@ -5715,4 +5715,11 @@ return [
             'Deprecation-100622-ExtbaseFeatureToggles.rst',
         ],
     ],
+    'TYPO3\CMS\Core\Localization\LanguageService->getLL' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 1,
+        'restFiles' => [
+            'Deprecation-100721-LabelRelatedMethodsAndArguments.rst',
+        ],
+    ],
 ];
-- 
GitLab