Commit f3aeff7f authored by Benni Mack's avatar Benni Mack Committed by Susanne Moog
Browse files

[TASK] Instantiate $GLOBALS[LANG] via factory

LanguageService a.k.a. $GLOBALS[LANG] is used in various places,
not just for backend-labels anymore. The "init()" method (which stems
from the fact that PHP did not have constructors !!! back then), should
be covered - which is now done via factory methods.

The goal of the factory methods are to explicitly define the dependencies
where LanguageService is used, instead of calling "init()" in the emitters
code.

This change is a pre-cursor for TYPO3 to
- avoid accessing Bootstrap API for initializing $GLOBALS[LANG]
- streamline the various places where this API is instantiated
- have a first step to switch to "real" locales at _some_ point
- and eventually get rid of accessing LanguageService via globals.

In addition the PHP class comments are streamlined to reflect
the purpose of this class.

The next step is to adapt the testing framework to easily use this
API so our tests will not have to worry about $GLOBALS[LANG]
anymore.

My 2c: We should actually rename "LanguageService" to "LabelService"
or "LabelProvider" at some point.

Resolves: #90062
Releases: master
Change-Id: Ib1b6a8aacb2c2aecc3ab2931db04002dd02f0a99
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/62826


Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: default avatarJörg Bösche <typo3@joergboesche.de>
Tested-by: default avatarSascha Rademacher <sascha.rademacher+typo3@gmail.com>
Tested-by: Henning Liebe's avatarHenning Liebe <h.liebe@neusta.de>
Tested-by: Susanne Moog's avatarSusanne Moog <look@susi.dev>
Reviewed-by: default avatarJörg Bösche <typo3@joergboesche.de>
Reviewed-by: default avatarSascha Rademacher <sascha.rademacher+typo3@gmail.com>
Reviewed-by: Henning Liebe's avatarHenning Liebe <h.liebe@neusta.de>
Reviewed-by: Susanne Moog's avatarSusanne Moog <look@susi.dev>
parent e4225a8e
......@@ -24,6 +24,7 @@ use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\UserAspect;
use TYPO3\CMS\Core\Context\WorkspaceAspect;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
......@@ -73,7 +74,7 @@ class BackendUserAuthenticator implements MiddlewareInterface
Bootstrap::initializeBackendUser();
// @todo: once this logic is in this method, the redirect URL should be handled as response here
Bootstrap::initializeBackendAuthentication($this->isLoggedInBackendUserRequired($pathToRoute));
Bootstrap::initializeLanguageObject();
$GLOBALS['LANG'] = LanguageService::createFromUserPreferences($GLOBALS['BE_USER']);
// Register the backend user as aspect
$this->setBackendUserAspect($GLOBALS['BE_USER']);
......
......@@ -28,6 +28,7 @@ use TYPO3\CMS\Core\Core\ApplicationInterface;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
......@@ -75,7 +76,7 @@ class CommandApplication implements ApplicationInterface
Bootstrap::loadExtTables();
// create the BE_USER object (not logged in yet)
Bootstrap::initializeBackendUser(CommandLineUserAuthentication::class);
Bootstrap::initializeLanguageObject();
$GLOBALS['LANG'] = LanguageService::createFromUserPreferences($GLOBALS['BE_USER']);
// Make sure output is not buffered, so command-line output and interaction can take place
ob_clean();
......
......@@ -24,6 +24,7 @@ use TYPO3\CMS\Core\Authentication\CommandLineUserAuthentication;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
......@@ -64,7 +65,7 @@ class CommandRequestHandler implements RequestHandlerInterface
Bootstrap::loadExtTables();
// create the BE_USER object (not logged in yet)
Bootstrap::initializeBackendUser(CommandLineUserAuthentication::class);
Bootstrap::initializeLanguageObject();
$GLOBALS['LANG'] = LanguageService::createFromUserPreferences($GLOBALS['BE_USER']);
// Make sure output is not buffered, so command-line output and interaction can take place
ob_clean();
......
......@@ -32,6 +32,7 @@ use TYPO3\CMS\Core\DependencyInjection\ContainerBuilder;
use TYPO3\CMS\Core\Imaging\IconRegistry;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\IO\PharStreamWrapperInterceptor;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Package\FailsafePackageManager;
use TYPO3\CMS\Core\Package\PackageManager;
......@@ -618,7 +619,6 @@ class Bootstrap
public static function initializeLanguageObject()
{
/** @var $GLOBALS['LANG'] \TYPO3\CMS\Core\Localization\LanguageService */
$GLOBALS['LANG'] = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\LanguageService::class);
$GLOBALS['LANG']->init($GLOBALS['BE_USER']->uc['lang']);
$GLOBALS['LANG'] = LanguageService::createFromUserPreferences($GLOBALS['BE_USER']);
}
}
......@@ -16,7 +16,6 @@ namespace TYPO3\CMS\Core\Information;
*/
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Contains information and links, or copyright information for the project.
......@@ -41,8 +40,7 @@ class Typo3Information
} elseif ($GLOBALS['LANG'] instanceof LanguageService) {
$this->languageService = $GLOBALS['LANG'];
} else {
$this->languageService = GeneralUtility::makeInstance(LanguageService::class);
$this->languageService->init('default');
$this->languageService = LanguageService::create('default');
}
}
......
......@@ -14,16 +14,29 @@ namespace TYPO3\CMS\Core\Localization;
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Contains the TYPO3 Backend Language class
* Main API to fetch labels from XLF (label files) based on the current system
* language of TYPO3. It is able to resolve references to files + their pointers to the
* proper language. If you see something about "LLL", this class does the trick for you. It
* is not related for language handling of content, but rather of labels for plugins.
*
* Usually this is injected into $GLOBALS['LANG'] when in backend or CLI context, and
* populated by the current backend user. Don't rely on $GLOBAL['LANG'] in frontend, as it is only
* available in certain circumstances!
* In Frontend, this is also used to translate "labels", see TypoScriptFrontendController->sL()
* for that.
*
* As TYPO3 internally does not match the proper ISO locale standard, the "locale" here
* is actually a list of supported language keys, (see Locales class), whereas "english"
* has the language key "default".
*
* For detailed information about how localization is handled,
* please refer to the 'Inside TYPO3' document which describes this.
* This class is normally instantiated as the global variable $GLOBALS['LANG']
* It's only available in the backend and under certain circumstances in the frontend
* @see \TYPO3\CMS\Backend\Template\DocumentTemplate
*/
class LanguageService
{
......@@ -374,6 +387,7 @@ class LanguageService
* @param string $prefix Prefix to select the correct labels
* @param string $strip Sub-prefix to be removed from label names in the result
* @return array Processed labels
* @todo: deprecate
*/
public function getLabelsWithPrefix($prefix, $strip = '')
{
......@@ -394,4 +408,30 @@ class LanguageService
}
return $extraction;
}
/**
* Factory method to create a language service object.
*
* @param string $locale the locale (= the TYPO3-internal locale given)
* @return static
*/
public static function create(string $locale): self
{
$obj = GeneralUtility::makeInstance(LanguageService::class);
$obj->init($locale);
return $obj;
}
public static function createFromUserPreferences(?AbstractUserAuthentication $user): self
{
if ($user && ($user->uc['lang'] ?? false)) {
return static::create($user->uc['lang']);
}
return static::create('default');
}
public static function createFromSiteLanguage(SiteLanguage $language): self
{
return static::create($language->getTypo3Language());
}
}
......@@ -705,7 +705,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
$this->initPageRenderer();
$this->initCaches();
// Initialize LLL behaviour
$this->setOutputLanguage($this->language->getTypo3Language());
$this->setOutputLanguage();
}
/**
......@@ -3598,15 +3598,12 @@ class TypoScriptFrontendController implements LoggerAwareInterface
/**
* Sets all internal measures what language the page should be rendered.
* This is not for records, but rather the HTML / charset and the locallang labels
*
* @param string $language - usually set via Site Handling
*/
protected function setOutputLanguage($language = 'default')
protected function setOutputLanguage()
{
$this->languageService = GeneralUtility::makeInstance(LanguageService::class);
$this->languageService = LanguageService::createFromSiteLanguage($this->language);
// Always disable debugging for TSFE
$this->languageService->debugKey = false;
$this->languageService->init($language);
}
/**
......
......@@ -27,6 +27,7 @@ use TYPO3\CMS\Core\Context\UserAspect;
use TYPO3\CMS\Core\Context\WorkspaceAspect;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Http\NormalizedParams;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
......@@ -70,7 +71,7 @@ class BackendUserAuthenticator implements MiddlewareInterface
// like $GLOBALS['LANG'] for labels in the language of the BE User, the router, and ext_tables.php for all modules
// So things like Frontend Editing and Admin Panel can use this for generating links to the TYPO3 Backend.
if ($GLOBALS['BE_USER'] instanceof FrontendBackendUserAuthentication) {
Bootstrap::initializeLanguageObject();
$GLOBALS['LANG'] = LanguageService::createFromUserPreferences($GLOBALS['BE_USER']);
Bootstrap::initializeBackendRouter();
Bootstrap::loadExtTables();
$this->setBackendUserAspect($GLOBALS['BE_USER']);
......
......@@ -128,6 +128,7 @@ class TypoScriptFrontendControllerTest extends UnitTestCase
{
$string = StringUtility::getUniqueId();
$this->subject->page = [];
$this->subject->language = new SiteLanguage(0, 'fr', new Uri('/'), ['typo3Language' => 'fr']);
$this->subject->_call('setOutputLanguage');
self::assertEquals($string, $this->subject->sL($string));
}
......
......@@ -15,7 +15,6 @@ namespace TYPO3\CMS\IndexedSearch\Hook;
*/
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
......@@ -51,17 +50,6 @@ class CrawlerHook
*/
public $callBack = self::class;
/**
* The constructor
*/
public function __construct()
{
// To make sure the backend charset is available:
if (!is_object($GLOBALS['LANG'])) {
Bootstrap::initializeLanguageObject();
}
}
/**
* Initialization of crawler hook.
* This function is asked for each instance of the crawler and we must check if something is timed to happen and if so put entry(s) in the crawlers log to start processing.
......
......@@ -20,7 +20,6 @@ use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Configuration\ConfigurationManager;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
use TYPO3\CMS\Core\Database\Connection;
......@@ -28,6 +27,7 @@ use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
use TYPO3\CMS\Core\Package\PackageManager;
......@@ -377,7 +377,7 @@ class SettingsController extends AbstractController
public function extensionConfigurationGetContentAction(ServerRequestInterface $request): ResponseInterface
{
// Extension configuration needs initialized $GLOBALS['LANG']
Bootstrap::initializeLanguageObject();
$GLOBALS['LANG'] = LanguageService::create('default');
$extensionConfigurationService = new ExtensionConfigurationService();
$extensionsWithConfigurations = [];
$activePackages = GeneralUtility::makeInstance(PackageManager::class)->getActivePackages();
......
......@@ -37,7 +37,7 @@ class BrowseLinksController extends AbstractLinkBrowserController
/**
* TYPO3 language code of the content language
*
* @var int
* @var string
*/
protected $contentsLanguage;
......@@ -124,7 +124,7 @@ class BrowseLinksController extends AbstractLinkBrowserController
$this->contentsLanguage = $queryParameters['contentsLanguage'];
$this->RTEtsConfigParams = $queryParameters['RTEtsConfigParams'] ?? null;
$this->contentLanguageService->init($this->contentsLanguage);
$this->contentLanguageService = LanguageService::create($this->contentsLanguage);
$tcaFieldConf = ['enableRichtext' => true];
if (!empty($queryParameters['P']['richtextConfigurationName'])) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment