reactOnPath = $reactOnPath; $this->getParameterName = $getParameterName; $this->browserCacheMaxAge = $browserCacheMaxAge; $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) { 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] ?? ''; $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 ); } /** * @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 ); }, $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; } }