'language', 'addQueryString' => 1, ]; protected array $menuDefaults = [ 'as' => 'languagemenu', ]; public function __construct( protected readonly MenuContentObjectFactory $menuContentObjectFactory, protected readonly PageRepository $pageRepository, ) {} /** * Get configuration value from processorConfiguration */ protected function getConfigurationValue(string $key): string { return $this->cObj->stdWrapValue($key, $this->processorConfiguration, $this->menuDefaults[$key] ?? ''); } protected function getRequest(): ServerRequestInterface { return $this->cObj->getRequest(); } /** * Returns the currently configured "site" if a site is configured (= resolved) in the current request. */ protected function getCurrentSite(): Site { return $this->getRequest()->getAttribute('site'); } /** * @throws \InvalidArgumentException */ protected function validateConfiguration(): void { $invalidArguments = []; foreach ($this->processorConfiguration as $key => $value) { if (!in_array($key, $this->allowedConfigurationKeys)) { $invalidArguments[str_replace('.', '', $key)] = $key; } } if (!empty($invalidArguments)) { throw new \InvalidArgumentException('LanguageMenuProcessor configuration contains invalid arguments: ' . implode(', ', $invalidArguments), 1522959188); } } /** * Process languages and filter the configuration */ protected function prepareConfiguration(): void { $this->menuConfig = array_merge($this->menuConfig, $this->processorConfiguration); // Process languages $this->menuConfig['special.']['value'] = $this->cObj->stdWrapValue('languages', $this->menuConfig, 'auto'); // Filter configuration foreach ($this->menuConfig as $key => $value) { if (in_array($key, $this->removeConfigurationKeysForHmenu, true)) { unset($this->menuConfig[$key]); } } $paramsToExclude = CanonicalizationUtility::getParamsToExcludeForCanonicalizedUrl( $this->getRequest()->getAttribute('frontend.page.information')->getId(), (array)$GLOBALS['TYPO3_CONF_VARS']['FE']['additionalCanonicalizedUrlParameters'], $this->cObj->getRequest() ); $this->menuConfig['addQueryString.']['exclude'] = implode( ',', array_merge( GeneralUtility::trimExplode(',', $this->menuConfig['addQueryString.']['exclude'] ?? '', true), $paramsToExclude ) ); } /** * Build the menu configuration so it can be treated by TMENU */ protected function buildConfiguration(): void { $this->menuConfig['1'] = 'TMENU'; $this->menuConfig['1.']['NO'] = '1'; } /** * @param ContentObjectRenderer $cObj The data of the content element or page * @param array $contentObjectConfiguration The configuration of Content Object * @param array $processorConfiguration The configuration of this processor * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View) * @return array the processed data as key/value store */ public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData): array { if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) { return $processedData; } $this->cObj = $cObj; $this->processorConfiguration = $processorConfiguration; // Validate Configuration $this->validateConfiguration(); // Build Configuration $this->prepareConfiguration(); $this->buildConfiguration(); // Create menu object and get menu items directly $request = $cObj->getRequest(); $site = $this->getCurrentSite(); $menu = $this->menuContentObjectFactory->getMenuObjectByType('TMENU'); $menu->parent_cObj = $cObj; if (!$menu->start(null, $this->pageRepository, '', $this->menuConfig, 1, '', $request)) { return $processedData; } $menu->makeMenu(); $menuItems = $menu->getMenuItems(); if ($menuItems === []) { return $processedData; } // Enrich with language-specific fields $processedMenu = []; foreach ($menuItems as $key => $item) { $languageId = (int)($item['data']['_REQUESTED_OVERLAY_LANGUAGE'] ?? 0); try { $languageObject = $site->getLanguageById($languageId); } catch (\InvalidArgumentException) { // Language not found in site config continue; } $item['languageId'] = $languageId; $item['locale'] = $languageObject->getLocale()->getName(); // Override title with language title (not page title) $item['title'] = $languageObject->getTitle(); $item['navigationTitle'] = $languageObject->getNavigationTitle(); $item['twoLetterIsoCode'] = $languageObject->getLocale()->getLanguageCode(); $item['hreflang'] = $languageObject->getHreflang(); $item['direction'] = $languageObject->getLocale()->isRightToLeftLanguageDirection() ? 'rtl' : 'ltr'; $item['flag'] = $languageObject->getFlagIdentifier(); // Determine state from ITEM_STATE set by the menu system $itemState = $item['data']['ITEM_STATE'] ?? ''; // active = 1 if state is ACT, ACTIFSUB, USERDEF2 (active states) $item['active'] = in_array($itemState, ['ACT', 'ACTIFSUB', 'USERDEF2'], true) ? 1 : 0; // current = 1 if state is CUR, CURIFSUB (current language) $item['current'] = in_array($itemState, ['CUR', 'CURIFSUB'], true) ? 1 : 0; // available = 1 unless USERDEF1/USERDEF2 state (language not available) $item['available'] = !in_array($itemState, ['USERDEF1', 'USERDEF2'], true) ? 1 : 0; $processedMenu[$key] = $item; } // Return processed data $processedData[$this->getConfigurationValue('as')] = $processedMenu; return $processedData; } }