Use multilanguage in middlewares

get the correct position

  • 9 LTS
  • 10-dev

What is a middleware?

Since TYPO3 9.2 we´re able to use PSR-15 middlewares to manipulate the order things get loaded and returned.

Please take a look at the article below if you´re new to middleware. It´s well explained and will answer you many questions.

PSR-15 Middlewares in TYPO3 (usetypo3.com)

The order is that important

The goal is to use the language service in our middleware or in a service class that has been instantiated because of our middleware. The problem is that the language service get´s initialized much later than our good old eID position. Those helper methods inside TYPO3\CMS\Frontend\Utility\EidUtility are deprecated since TYPO3 9.4 and should no longer be used.

Let´s take a look at the initLanguage() method:

 

/**
 * Initializes $GLOBALS['LANG'] for use in eID scripts.
 *
 * @param string $language TYPO3 language code
 * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0. Instantiate the LanguageService by yourself instead.
 */
public static function initLanguage($language = 'default')
{
    trigger_error('EidUtility::initLanguage() will be removed in TYPO3 v10.0. Ensure to intantiate the LanguageService by yourself.', E_USER_DEPRECATED);
    if (!is_object($GLOBALS['LANG'])) {
        $GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageService::class);
        $GLOBALS['LANG']->init($language);
    }
}

 

They´re telling us to instantiate the service by ourselves but do we really want this? My problem with this way is that we need to care about the language. If you wan´t to pass the language key by yourself then you can just copy those lines and pass the language key using a post variable or something similar. I made something like this using eID in TYPO3 7 (take a look).

PrepareTypoScriptFrontendRendering

I´m using the middleware typo3/cms-frontend/prepare-tsfe-rendering for this case. The language service is initialized and uses the language of the current domain so we can send ajax requests using the current url and for example a post request.

Register the middleware (Configuration/RequestMiddlewares.php):

 

<?php
return [
    'frontend' => [
        'tutorial-product' => [
            'target' => \Kronovanet\Tutorial\Middleware\ProductMiddleware::class,
            'after' => ['typo3/cms-frontend/prepare-tsfe-rendering']
        ]
    ]
];

 

Little example of a middleware using the tsfe-rendering (Example 1):

 

<?php
declare(strict_types=1);
namespace Kronovanet\Tutorial\Middleware;

use Kronovanet\Tutorial\Domain\Model\Product;
use Kronovanet\Tutorial\Domain\Repository\ProductRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Fluid\View\StandaloneView;

/**
 * Product ajax middleware
 */
class ProductMiddleware implements MiddlewareInterface
{
    /**
     * @var StandaloneView
     */
    protected $standaloneView;

    public function __construct()
    {
        $this->standaloneView = GeneralUtility::makeInstance(StandaloneView::class);
        $this->initializeStandaloneView();
    }

    private function initializeStandaloneView(): void
    {
        $this->standaloneView->getRenderingContext()->setControllerName('Ajax');
        $this->standaloneView->getRenderingContext()->setControllerAction('Product');
        $this->standaloneView->getRequest()->setControllerExtensionName('tutorial');
    }

    /**
     * Process an incoming server request and return a response, optionally delegating
     * response creation to a handler.
     *
     * @param ServerRequestInterface $request
     * @param RequestHandlerInterface $handler
     * @return ResponseInterface
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $action = $this->getParamFromRequest($request, 'myExtAjaxAction');

        // this can be replaced by a switch case or so if you wan´t more than one action
        if ($action !== 'myAction') {
            return $handler->handle($request);
        }

        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
        $productRepository = $objectManager->get(ProductRepository::class);
        $productUid = (int)$this->getParamFromRequest($request, 'product');
        $product = $productRepository->findByUid($productUid);

        if (!$product instanceof Product) {
            throw new \InvalidArgumentException(
                'Could not find a product with uid "' . $productUid . '".',
                1555576709
            );
        }

        $startDate = new \DateTime($this->getParamFromRequest($request, 'startDate'));
        /** @var \DateTime $endDate */
        $duration = (int)$this->getParamFromRequest($request, 'duration');
        $isAvailable = $product->checkAvailability($startDate, $duration, $endDate);

        $this->standaloneView->assignMultiple([
            'isAvailable' => $isAvailable,
            'startDate' => $startDate->getTimestamp(),
            'endDate' => $endDate->getTimestamp(),
            'price' => $product->calculatePrice($duration, $startDate),
            'product' => $product
        ]);

        return new HtmlResponse($this->standaloneView->render());
    }

    /**
     * @param ServerRequestInterface $request
     * @param string $name
     * @return mixed
     */
    protected function getParamFromRequest(ServerRequestInterface $request, string $name)
    {
        return $request->getParsedBody()[$name] ?? $request->getQueryParams()[$name] ?? null;
    }
}

 

Before you ask: The f:translate ViewHelper will be used inside the fluid templates of that example.

If you just want to use the LanguageService (Example 2):

 

<?php
declare(strict_types=1);
namespace Kronovanet\Tutorial\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Http\HtmlResponse;

/**
 * Just for demo purpose
 */
class TranslationMiddleware implements MiddlewareInterface
{
    /**
     * Process an incoming server request and return a response, optionally delegating
     * response creation to a handler.
     *
     * @param ServerRequestInterface $request
     * @param RequestHandlerInterface $handler
     * @return ResponseInterface
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $action = $this->getParamFromRequest($request, 'myExtAjaxAction');

        // this can be replaced by a switch case or so if you wan´t more than one action
        if ($action !== 'myAction') {
            return $handler->handle($request);
        }

        $translation = $GLOBALS['LANG']->sL('LLL:EXT:tutorial/Resources/Private/Language/locallang.xlf:my_label');

        return new HtmlResponse($translation);
    }

    /**
     * @param ServerRequestInterface $request
     * @param string $name
     * @return mixed
     */
    protected function getParamFromRequest(ServerRequestInterface $request, string $name)
    {
        return $request->getParsedBody()[$name] ?? $request->getQueryParams()[$name] ?? null;
    }
}