---
title: "Localization in PHP"
manual: "TYPO3 Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3coreapi:extension-localization-php@main"
source: "ExtensionArchitecture/HowTo/Localization/Php.rst"
rendered: "2026-09-19T06:56:03+00:00"
---

# Localization in PHP {#extension-localization-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](https://docs.typo3.org/permalink/t3coreapi:localization-in-plain-php@main)
-   [Localization in Extbase](https://docs.typo3.org/permalink/t3coreapi:localization-in-extbase@main)
-   [Example: provide localized strings via JSON by a middleware](https://docs.typo3.org/permalink/t3coreapi:example-provide-localized-strings-via-json-by-a-middleware@main)

## Localization in plain PHP {#extension-localization-php-plain}

> [!NOTE]
> The global variable `$GLOBALS['LANG']` is not available in all contexts
> so it is best not to rely on it. Use
> `LanguageServiceFactory` instead.

The `TranslatorInterface` objects are
available if a backend user has been initialized, in particular in the
following contexts:

-   frontend: only if there is a logged-in backend user
-   backend: always, except in **System** modules (for example within
    an upgrade wizard in the backend)
-   install tool / install tool modules in backend (e.g. Upgrade Wizard): no
-   in cli: only if a backend user was initialized, e.g. by
    `TYPO3CMSCoreCoreBootstrap::initializeBackendUser()`

The `LanguageServiceFactory` can be used
to instantiate. Please see the examples below.

[The methods provided by the instantiated TranslatorInterface](https://docs.typo3.org/permalink/t3coreapi:translator-api@main)
class then be used to translate texts using the language keys of XLIFF language
files.

### Localization in frontend context {#extension-localization-php-frontend}

In plain PHP use the class [LanguageServiceFactory](https://docs.typo3.org/permalink/t3coreapi:languageservicefactory-api@main)
to create a [TranslatorInterface](https://docs.typo3.org/permalink/t3coreapi:translator-api@main) from the current
site language:

**EXT:my_extension/Classes/UserFunction/MyUserFunction.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Backend;

use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
use TYPO3\CMS\Core\Localization\TranslatorInterface;

final class MyUserFunction
{
  private TranslatorInterface $translator;

  public function __construct(
    private readonly LanguageServiceFactory $languageServiceFactory,
  ) {}

  private function getTranslator(
    ServerRequestInterface $request,
  ): TranslatorInterface {
    return $this->languageServiceFactory->createFromSiteLanguage(
      $request->getAttribute('language')
        ?? $request->getAttribute('site')->getDefaultLanguage(),
    );
  }

  public function main(
    string $content,
    array $conf,
    ServerRequestInterface $request,
  ): string {
    $this->translator = $this->getTranslator($request);
    return $this->translator->label('my_extension.messages:something');
  }
}

```

[Dependency injection](https://docs.typo3.org/permalink/t3coreapi:dependencyinjection@main) 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 {#extension-localization-php-backend}

In the backend context you should use the
[LanguageServiceFactory](https://docs.typo3.org/permalink/t3coreapi:languageservicefactory-api@main)
to create the required [TranslatorInterface](https://docs.typo3.org/permalink/t3coreapi:translator-api@main).

**EXT:my_extension/Classes/Backend/MyBackendClass.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Backend;

use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
use TYPO3\CMS\Core\Localization\TranslatorInterface;

final class MyBackendClass
{
  public function __construct(
    private readonly LanguageServiceFactory $languageServiceFactory,
  ) {}

  private function translateSomething(string $input): string
  {
    return $this->getTranslator()->label($input);
  }

  private function getTranslator(): TranslatorInterface
  {
    return $this->languageServiceFactory
        ->createFromUserPreferences($this->getBackendUserAuthentication());
  }

  private function getBackendUserAuthentication(): BackendUserAuthentication
  {
    return $GLOBALS['BE_USER'];
  }

  // ...
}

```

> [!WARNING]
> **Attention**
>
> During development you are usually logged into the backend. So the global
> variable `$GLOBALS['LANG']` might be available in the frontend. Once
> logged out it is usually not available. **Never** depend on
> `$GLOBALS['LANG']` in the frontend unless you know what you are doing.

### Localization without context {#extension-localization-php-without}

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:

**EXT:my_extension/Classes/Utility/MyUtility.php**

```php
<?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 $labelKey): 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'];
    $translator = $languageServiceFactory->createFromSiteLanguage(
      $request->getAttribute('language')
        ?? $request->getAttribute('site')->getDefaultLanguage(),
    );
    return $translator->label($labelKey);
  }
}

```

## Localization in Extbase {#extension-localization-extbase}

In [Extbase](https://docs.typo3.org/permalink/t3coreapi:extbase-extension-framework@main) context you can use the method
[\\TYPO3\\CMS\\Extbase\\Utility\\LocalizationUtility::translate($key, $extensionName)](https://docs.typo3.org/permalink/t3coreapi:extbase-localization-utility-api@main).

This method requires the localization key as the first and the extension's
name as optional second parameter. For all available parameters
see [below](https://docs.typo3.org/permalink/t3coreapi:extbase-localization-utility-api@main). Then the corresponding
text in the current language will be loaded from this extension's
`locallang.xlf` file.

The method `translate()` takes translation overrides from TypoScript into
account. See [Changing localized terms using TypoScript](https://docs.typo3.org/permalink/t3coreapi:localization-typoscript-local-lang@main).

### Example: translate a flash message in an Extbase controller {#extension-localization-extbase-example}

In this example the content of the flash message to be displayed in the backend
will be translated:

**Class MyVendor\\MyExtension\\Controller\\ModuleController**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use MyVendor\MyExtension\Service\TableInformationService;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;

class ModuleController extends ActionController
{
  public function __construct(TableInformationService $tableInformationService) {}

  /**
   * Adds a count of entries to the flash message
   */
  public function countAction(string $tablename = 'pages'): ResponseInterface
  {
    $count = $this->tableInformationService->countRecords($tablename);

    $message = LocalizationUtility::translate(
      'record_count_message',
      'examples',
      [$count, $tablename],
    );

    $this->addFlashMessage(
      $message ?? '',
      'Information',
      ContextualFeedbackSeverity::INFO,
    );
    return $this->redirect('flash');
  }
}

```

The string in the translation file is defined like this:

**EXT:my_extension/Resources/Private/Language/locallang.xlf**

```xml
<?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](https://www.php.net/manual/en/function.sprintf.php).

This behaviour is the same like in a
[Fluid translate ViewHelper with arguments](https://docs.typo3.org/permalink/t3coreapi:extension-localization-fluid-arguments@main).

## Example: provide localized strings via JSON by a middleware {#example-localization-middleware}

In the following example we use the [Translator API](https://docs.typo3.org/permalink/t3coreapi:translator-api@main)
to provide a list of localized season names. This list could then be loaded in
the frontend via Ajax.

You can find the complete example in
[GitHub, EXT:examples and HaikuSeasonList](https://github.com/TYPO3-Documentation/t3docs-examples/blob/main/Classes/Middleware/HaikuSeasonList.php).

As we do not need a full frontend context with TypoScript the JSON is returned
by [PSR-15 middleware](https://docs.typo3.org/permalink/t3coreapi:request-handling@main).

Beside other factories needed by our response, we inject the
[LanguageServiceFactory](https://docs.typo3.org/permalink/t3coreapi:languageservicefactory-api@main) with
[constructor dependency injection](https://docs.typo3.org/permalink/t3coreapi:constructor-injection@main).

**Class T3docs\\Examples\\Middleware\\HaikuSeasonList**

```php
<?php

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Server\MiddlewareInterface;
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 readonly class HaikuSeasonList implements MiddlewareInterface
{
  public function __construct(
    private LanguageServiceFactory $languageServiceFactory,
    private ResponseFactoryInterface $responseFactory,
    private StreamFactoryInterface $streamFactory,
  ) {}
}

```

The main method `process()` is called with a
`ServerRequestInterface` argument that can be used to detect the
current language and is passed on to the private method `getSeasons()`
to do the actual translation:

**Class T3docs\\Examples\\Middleware\\HaikuSeasonList**

```php
<?php

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

final readonly 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\CMS\Core\Localization\LanguageServiceFactory`
create an object of type
`TranslatorInterface` from the language
in the request, falling back to the default language of the site.

The `TranslatorInterface` object can
then be queried for the localized strings:

**Class T3docs\\Examples\\Middleware\\HaikuSeasonList**

```php
<?php

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;

final readonly class HaikuSeasonList implements MiddlewareInterface
{
  private const SEASONS = ['spring', 'summer', 'autumn', 'winter', 'theFifthSeason'];
  private const TRANSLATION_PATH = 'LLL:examples.plugin_haiku.messages:season.';

  /**
   * @return array<string, string>
   */
  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;
  }
}

```
