DigiComp.FlowTranslationEnd.../Classes/Http/TranslationRequestMiddleware.php

189 lines
6 KiB
PHP
Raw Normal View History

2023-08-05 15:49:46 +02:00
<?php
declare(strict_types=1);
namespace DigiComp\FlowTranslationEndpoint\Http;
use GuzzleHttp\Psr7\Response;
use Neos\Cache\Exception as CacheException;
use Neos\Cache\Exception\InvalidDataException;
use Neos\Cache\Frontend\StringFrontend;
use Neos\Flow\I18n\Detector;
use Neos\Flow\I18n\Service;
use Neos\Flow\I18n\Xliff\Service\XliffFileProvider;
use Neos\Utility\Arrays;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class TranslationRequestMiddleware implements MiddlewareInterface
2023-08-05 15:49:46 +02:00
{
/**
* @var string
*/
protected string $reactOnPath;
/**
* @var string
*/
protected string $getParameterName;
/**
* @var int
*/
protected int $browserCacheMaxAge;
2023-08-05 15:49:46 +02:00
/**
* @var Service
*/
protected Service $i18nService;
/**
* @var XliffFileProvider
*/
protected XliffFileProvider $fileProvider;
/**
* @var Detector
*/
protected Detector $detector;
/**
* @var StringFrontend
*/
protected StringFrontend $responseCache;
public function __construct(
string $reactOnPath,
string $getParameterName,
int $browserCacheMaxAge,
2023-08-05 15:49:46 +02:00
Service $i18nService,
XliffFileProvider $fileProvider,
Detector $detector,
StringFrontend $responseCache
) {
$this->reactOnPath = $reactOnPath;
$this->getParameterName = $getParameterName;
$this->browserCacheMaxAge = $browserCacheMaxAge;
2023-08-05 15:49:46 +02:00
$this->i18nService = $i18nService;
$this->fileProvider = $fileProvider;
$this->detector = $detector;
$this->responseCache = $responseCache;
}
/**
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
* @throws CacheException
* @throws InvalidDataException
* @throws \JsonException
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ($request->getUri()->getPath() === $this->reactOnPath) {
2023-08-05 15:49:46 +02:00
return $this->createResponse($request);
}
return $handler->handle($request);
}
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
* @throws \JsonException
* @throws CacheException
* @throws InvalidDataException
*/
protected function createResponse(ServerRequestInterface $request): ResponseInterface
{
if ($request->hasHeader('Accept-Language')) {
$wishedLocale = $this->detector->detectLocaleFromHttpHeader($request->getHeader('Accept-Language')[0]);
$bestMatching = $this->i18nService->findBestMatchingLocale($wishedLocale);
$this->i18nService->getConfiguration()->setCurrentLocale($bestMatching);
}
$idPatternList = $request->getQueryParams()[$this->getParameterName] ?? '';
2023-08-05 15:49:46 +02:00
$cacheId = $this->i18nService->getConfiguration()->getCurrentLocale() . '_' . \sha1(\serialize($idPatternList));
if ($this->responseCache->has($cacheId)) {
$response = $this->responseCache->get($cacheId);
} else {
$result = $this->getTranslations(Arrays::trimExplode(',', $idPatternList));
$response = \json_encode($result, \JSON_THROW_ON_ERROR);
$this->responseCache->set($cacheId, $response);
}
return new Response(
200,
[
'Content-Type' => 'application/json',
'Cache-Control' => 'max-age=' . $this->browserCacheMaxAge . ', must-revalidate',
'Last-Modified' => $this->getCacheDate(),
'Vary' => 'Accept, Accept-Language'
],
$response
);
2023-08-05 15:49:46 +02:00
}
/**
* @param array $idPatternList
*
* @return array
*/
protected function getTranslations(array $idPatternList): array
{
$result = [];
foreach ($idPatternList as $idPattern) {
$package = 'Neos.Flow:Main';
$parts = \explode('|', $idPattern);
switch (\count($parts)) {
case 2:
[$package, $pattern] = $parts;
break;
case 1:
[$pattern] = $parts;
break;
default:
throw new \InvalidArgumentException('Could not parse idPattern: ' . $idPattern);
}
$translationUnits = $this->fileProvider->getFile(
$package,
$this->i18nService->getConfiguration()->getCurrentLocale()
)->getTranslationUnits();
if (!isset($result[$package])) {
$result[$package] = [];
}
$matchingUnits = \array_filter(
$translationUnits,
static fn($unitId) => \preg_match('~^' . $pattern . '$~', $unitId),
\ARRAY_FILTER_USE_KEY
);
$result[$package] += \array_map(
static function ($value) {
if (\count($value) === 1) {
return $value[0]['target'];
}
return \array_map(
static fn ($value) => $value['target'],
$value
);
},
2023-08-05 15:49:46 +02:00
$matchingUnits
);
}
return $result;
}
/**
* @return string
*/
protected function getCacheDate(): string
{
// the translation file monitor will flush our complete cache, each time a file is modified. That way it resets
// the last modified date here, too.
if (false === $lastModified = $this->responseCache->get('lastModified')) {
$lastModified = (new \DateTimeImmutable())->format(\DATE_RFC7231);
$this->responseCache->set('lastModified', $lastModified);
}
return $lastModified;
}
2023-08-05 15:49:46 +02:00
}