Localization in PHP
Sometimes you have to localize a string in PHP code, for example inside of a controller or a user function.
Which method of localization to use depends on the current context:
Localization in plain PHP
Localization in frontend context
In plain PHP use the class LanguageServiceFactory to create a LanguageService from the current site language:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Backend;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
final class MyUserFunction
{
private LanguageService $languageService;
public function __construct(
private readonly LanguageServiceFactory $languageServiceFactory,
) {}
private function getLanguageService(
ServerRequestInterface $request,
): LanguageService {
return $this->languageServiceFactory->createFromSiteLanguage(
$request->getAttribute('language')
?? $request->getAttribute('site')->getDefaultLanguage(),
);
}
public function main(
string $content,
array $conf,
ServerRequestInterface $request,
): string {
$this->languageService = $this->getLanguageService($request);
return $this->languageService->sL(
'LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:something',
);
}
}
Dependency injection should be available in most contexts where you need translations. Also the current request is available in entry point such as custom non-Extbase controllers, user functions, data processors etc.
Localization in backend context
In the backend context you should use the LanguageServiceFactory to create the required LanguageService.
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Backend;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
final class MyBackendClass
{
public function __construct(
private readonly LanguageServiceFactory $languageServiceFactory,
) {}
private function translateSomething(string $input): string
{
return $this->getLanguageService()->sL($input);
}
private function getLanguageService(): LanguageService
{
return $this->languageServiceFactory
->createFromUserPreferences($this->getBackendUserAuthentication());
}
private function getBackendUserAuthentication(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
// ...
}
Attention
During development you are usually logged into the backend. So the global
variable $GLOBALS
might be available in the frontend. Once
logged out it is usually not available. Never depend on
$GLOBALS
in the frontend unless you know what you are doing.
Localization without context
If you should happen to be in a context where none of these are available, for example a static function, you can still do translations:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Utility;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
final class MyUtility
{
private static function translateSomething(string $lll): string
{
$languageServiceFactory = GeneralUtility::makeInstance(
LanguageServiceFactory::class,
);
// As we are in a static context we cannot get the current request in
// another way this usually points to general flaws in your software-design
$request = $GLOBALS['TYPO3_REQUEST'];
$languageService = $languageServiceFactory->createFromSiteLanguage(
$request->getAttribute('language')
?? $request->getAttribute('site')->getDefaultLanguage(),
);
return $languageService->sL($lll);
}
}
Localization in Extbase
In Extbase context you can use the method \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($key, $extensionName).
This method requires the localization key as the first and the extension's
name as optional second parameter. For all available parameters
see below. Then the corresponding
text in the current language will be loaded from this extension's
locallang.
file.
The method translate
takes translation overrides from TypoScript into
account. See Changing localized terms using TypoScript.
Example
In this example the content of the flash message to be displayed in the backend gets translated:
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
class ModuleController extends ActionController implements LoggerAwareInterface
{
/**
* Adds a count of entries to the flash message
*/
public function countAction(string $tablename = 'pages'): ResponseInterface
{
$count = $this->tableInformationService->countRecords($tablename);
$message = LocalizationUtility::translate(
key: 'record_count_message',
extensionName: 'examples',
arguments: [$count, $tablename]
);
$this->addFlashMessage(
messageBody: $message,
messageTitle: 'Information',
severity: ContextualFeedbackSeverity::INFO
);
return $this->redirect('flash');
}
}
The string in the translation file is defined like this:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<!-- EXT:examples/Resources/Private/Language/locallang.xlf -->
<xliff version="1.0">
<file source-language="en" datatype="plaintext" original="messages" date="2013-03-09T18:44:59Z" product-name="examples">
<header />
<body>
<trans-unit id="new_relation" xml:space="preserve">
<source>Content element "%1$s" (uid: %2$d) has the following relations:</source>
</trans-unit>
</body>
</file>
</xliff>
The arguments
will be replaced in the localized strings by
the PHP function sprintf.
This behaviour is the same like in a Fluid translate ViewHelper with arguments.
Examples
Provide localized strings via JSON by a middleware
In the following example we use the language service API to provide a list of localized season names. This list could then be loaded in the frontend via Ajax.
You can finde the complete example on GitHub, EXT:examples, HaikuSeasonList.
As we do not need a full frontend context with TypoScript the JSON is returned by a PSR-15 middleware.
Beside other factories needed by our response, we inject the LanguageServiceFactory with constructor dependency injection.
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
/**
* This middleware can be used to retrieve a list of seasons with their according translation.
* To get the correct translation the URL must be within a base path defined in site
* handling. Some examples:
* "/en/haiku-season-list.json" for English translation (if /en is the configured base path)
* "/de/haiku-season-list.json" for German translation (if /de is the configured base path)
* If the base path is not available in the according site the default language will be used.
*/
final class HaikuSeasonList implements MiddlewareInterface
{
public function __construct(
private readonly LanguageServiceFactory $languageServiceFactory,
private readonly ResponseFactoryInterface $responseFactory,
private readonly StreamFactoryInterface $streamFactory
) {}
}
The main method process
is called with a
\Psr\
as argument that can be used to detect the
current language and is therefore passed on to the private method get
doing the
actual translation:
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
final class HaikuSeasonList implements MiddlewareInterface
{
private const URL_SEGMENT = '/haiku-season-list.json';
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (!str_contains($request->getUri()->getPath(), self::URL_SEGMENT)) {
return $handler->handle($request);
}
$seasons = json_encode($this->getSeasons($request), JSON_THROW_ON_ERROR);
return $this->responseFactory->createResponse()
->withHeader('Content-Type', 'application/json')
->withBody($this->streamFactory->createStream($seasons));
}
}
Now we can let the \TYPO3\
to
create a \TYPO3\
from the request's
language, falling back to the default language of the site.
The Language
can then be queried for the localized strings:
use Psr\Http\Message\ServerRequestInterface;
final class HaikuSeasonList implements MiddlewareInterface
{
private const SEASONS = ['spring', 'summer', 'autumn', 'winter', 'theFifthSeason'];
private const TRANSLATION_PATH = 'LLL:EXT:examples/Resources/Private/Language/PluginHaiku/locallang.xlf:season.';
private function getSeasons(ServerRequestInterface $request): array
{
$languageService = $this->languageServiceFactory->createFromSiteLanguage(
$request->getAttribute('language') ?? $request->getAttribute('site')->getDefaultLanguage()
);
$translatedSeasons = [];
foreach (self::SEASONS as $season) {
$translatedSeasons[$season] = $languageService->sL(self::TRANSLATION_PATH . $season);
}
return $translatedSeasons;
}
}