Localization outside the frontend context 

When rendering a typical frontend request, TYPO3 resolves the active site and language aspect before any Extbase code executes. However, backend modules, CLI commands, and custom middleware operate outside this standard pipeline and frequently lack a language context. This guide outlines how localization behaves in non-frontend environments and how to explicitly manage languages in your Extbase code.

Understanding the default behavior 

If no language context is explicitly provided, Extbase queries the Context API and falls back to a default fallback state:

  • Fallback Target: An initialized LanguageAspect with an ID of 0.
  • Overlay Mode: OVERLAYS_ON_WITH_FLOATING.

The Impact: Your backend modules or CLI commands will behave as if they are executing against a site configured with fallbackType: strict in the default language. Only default language records will be returned.

While acceptable for simple tasks, this default behavior breaks features like CLI commands that send localized email alerts, generate localized reports, or export multi-language datasets.

Setting the language explicitly 

To query records in a specific locale, you must manually pass the language aspect to your query settings.

The following CLI command demonstrates how to fetch a frontend user's preferred language from their record, resolve it against the site configuration, and query the repository using that specific context. The command sends a reminder about upcoming conferences to all frontend users, in the language stored on their user record.

EXT:my_extension/Classes/Command/ConferenceReminderCommand.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Command;

use MyVendor\MyExtension\Domain\Repository\ConferenceRepository;
use MyVendor\MyExtension\Domain\Repository\FrontendUserRepository;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Context\LanguageAspectFactory;
use TYPO3\CMS\Core\Site\SiteFinder;

#[AsCommand(
  name: 'myextension:conferencereminder',
  description: 'Mail all frontend users a reminder in their own language',
)]
class ConferenceReminderCommand extends Command
{
  public function __construct(
    protected readonly ConferenceRepository $conferenceRepository,
    protected readonly FrontendUserRepository $frontendUserRepository,
    protected readonly SiteFinder $siteFinder,
  ) {
    parent::__construct();
  }

  protected function execute(
    InputInterface $input,
    OutputInterface $output,
  ): int {
    // There is no site in a command, so the site the mails belong to has
    // to be named explicitly.
    $site = $this->siteFinder->getSiteByIdentifier('my-site');

    foreach ($this->frontendUserRepository->findAll() as $user) {
      // for sake of this example the user record provides a chosen language
      // in line with the site configuration
      $siteLanguage = $site->getLanguageById($user->getLanguageId());

      // The language's own configuration, exactly as the frontend of
      // this site would apply it.
      $languageAspect = LanguageAspectFactory::createFromSiteLanguage(
        $siteLanguage,
      );

      $conferences = $this->conferenceRepository
          ->findAllForLanguageAspect($languageAspect);

      // Send the mail, using $conferences and $siteLanguage->getLocale()
    }

    return Command::SUCCESS;
  }
}
Copied!

Because this method relies on findAllForLanguageAspect() , the exact same repository logic remains reusable across both the frontend (where the aspect is provided natively) and CLI commands (where you supply it manually). See also Deciding the language per query.

Backend modules 

How you manage localization in backend modules depends entirely on whether your module interacts with the page tree.

Global modules (no page tree) 

Modules that manage global records independently of specific pages (for example, global settings, system reports, or job queues) do not have a page and so do not have a site configuration to take a fallbackType from. They default to the primary language aspect. You must explicitly specify target languages in your query settings.

Page-bound modules (with page tree) 

If your module utilizes the backend page tree navigation component, TYPO3 knows exactly which page the editor is viewing. Because pages map directly to sites, you can automatically inherit the site's full localization rules—including the site's languages, language titles for the language selector, and fallback types.

To match the exact translation behavior your visitors see on the live frontend, resolve the site language from the active page and generate the aspect using the factory. Then hand the aspect to findAllForLanguageAspect() (see Setting a language aspect) to add it to the query settings and execute the query.

EXT:my_extension/Classes/Controller/ConferenceModuleController.php
<?php

namespace MyVendor\MyExtension\Controller;

use MyVendor\MyExtension\Domain\Repository\ConferenceRepository;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
use TYPO3\CMS\Core\Context\LanguageAspectFactory;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class ConferenceModuleController extends ActionController
{
  public function __construct(
    protected readonly ModuleTemplateFactory $moduleTemplateFactory,
    protected readonly ConferenceRepository $conferenceRepository,
    protected readonly SiteFinder $siteFinder,
  ) {}

  public function indexAction(
    int $pageUid = 0,
    int $languageId = 0,
  ): ResponseInterface {
    // The page selected in the page tree determines the site, and with it
    // the available languages and their fallback configuration.
    $site = $this->siteFinder->getSiteByPageId($pageUid);
    $siteLanguage = $site->getLanguageById($languageId);

    // The same aspect the frontend would use for this site language.
    $languageAspect = LanguageAspectFactory::createFromSiteLanguage(
      $siteLanguage,
    );

    $moduleTemplate = $this->moduleTemplateFactory->create($this->request);
    $moduleTemplate->assign('languages', $site->getLanguages());
    $moduleTemplate->assign('selectedLanguage', $siteLanguage);
    $moduleTemplate->assign(
      'conferences',
      $this->conferenceRepository->findAllForLanguageAspect($languageAspect),
    );

    return $moduleTemplate->renderResponse('Conference/Index');
  }
}
Copied!

Using LanguageAspectFactory::createFromSiteLanguage() ensures your backend module's data listings stay perfectly synchronized with the frontend site configuration.

How relations are handled 

Relational fields follow the exact same translation mapping rules used throughout TYPO3. They are automatically resolved based on the specific language aspect assigned to the parent query.

If you rely on the fallback environment (the default language aspect), any relational child records fetched inside your commands will resolve to the default language. If you explicitly fetch a parent record in a localized language, its underlying relations will automatically match that localized language context.