session->getDataByKey('languageFileSelection'); $extensionKey = $this->session->getDataByKey('extkey'); if ($file !== '' && $extensionKey !== '') { $typo3ExtRelativeFilePath = 'EXT:' . $this->cleanSessionExtKey($extensionKey) . '/' . $file; } if ($typo3ExtRelativeFilePath === '') { try { $typo3ExtRelativeFilePath = Typo3Lib::transTypo3File($originalFileObject->getAbsFile(), FALSE); } catch (\Exception $e) { $typo3ExtRelativeFilePath = ''; } } $pathSite = Environment::getPublicPath() . '/'; $overrideFileAbsolutePath = Typo3Lib::fixFilePath( $pathSite . '/' . ($GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$typo3ExtRelativeFilePath][0] ?? '') ); if (!is_file($overrideFileAbsolutePath)) { $extRelativeFilePath = SgLib::trimPath('EXT:', $typo3ExtRelativeFilePath); $extRelativeFilePath = substr($extRelativeFilePath, 0, strrpos($extRelativeFilePath, '.')) . '.xlf'; /** @var ConfigurationService $configurationService */ $configurationService = GeneralUtility::makeInstance(ConfigurationService::class); $extConfig = $configurationService->getExtConfig(); $overrideFileAbsolutePath = $extConfig['pathOverrideFiles'] . $extRelativeFilePath; } $this->originalFileObject = $originalFileObject; parent::init(basename($overrideFileAbsolutePath), dirname($overrideFileAbsolutePath), $metaFile); } /** * Calls the parent function and convert all values from utf-8 to the original charset * * @return void * @throws LFException raised if the parent read file method fails */ public function readFile() { if (is_file($this->absFile)) { parent::readFile(); } $oldXMLFile = $this->getOlderPath($this->absFile); if (is_file($oldXMLFile)) { $xmlFile = GeneralUtility::makeInstance(FileBaseXMLService::class); // i do think we are fine omitting metaFile $xmlFile->init(basename($oldXMLFile), dirname($oldXMLFile), ''); $xmlFile->readFile(); $this->mergeFileObjectWidthOverrideLangData($xmlFile); } $this->originalFileObject->readFile(); $this->mergeOriginalWidthOverrideLangData(); } /** * when setting session key, we are using the folder, which has * a) "-" as delimiter * b) "cms-" as prefix for core extension * * @param string $extkey * @return string */ private function cleanSessionExtKey(string $extkey): string { // cant be merged, because cms_ only is in string after we did first replacement return str_replace('cms_', '', str_replace('-', '_', $extkey)); } /** * check for possible old files: * 1. we do now have cms_ prefix for some reason * 2. we only write localLang.xlf * * @param string $absolutePath * @return string */ public function getOlderPath(string $absolutePath): string { return str_replace(['.xlf', 'cms_'], ['.xml', ''], $absolutePath); } /** * Merges Language data from original and override files, so all data can e presented to user. * * @return void */ protected function mergeOriginalWidthOverrideLangData() { foreach ($this->originalFileObject->getLocalLang() as $lang => $langData) { if (!is_array($langData)) { continue; } foreach ($langData as $costKey => $constValue) { if (isset($this->localLang[$lang][$costKey]) && $this->localLang[$lang][$costKey] !== $constValue) { continue; } if (!array_key_exists($lang, $this->localLang) || !is_array($this->localLang[$lang])) { $this->localLang[$lang] = []; } $this->localLang[$lang][$costKey] = $constValue; } } foreach ($this->originalFileObject->getMeta() as $metaTag => $metaTagValue) { if ($metaTag === '@attributes') { foreach ($metaTagValue as $attributeKey => $attributeValue) { if (isset($this->meta[$metaTag][$attributeKey]) && $this->meta[$metaTag][$attributeKey] !== $attributeValue ) { continue; } $this->meta[$metaTag][$attributeKey] = $attributeValue; } } else { if (isset($this->meta[$metaTag]) && $this->meta[$metaTag] !== $metaTagValue) { continue; } $this->meta[$metaTag] = $metaTagValue; } } } /** * Merges Language data from original and override files, so all data can e presented to user. * * @param FileService $fileObject * @return void */ protected function mergeFileObjectWidthOverrideLangData(FileService $fileObject) { foreach ($fileObject->getLocalLang() as $lang => $langData) { if (!is_array($langData)) { continue; } foreach ($langData as $costKey => $constValue) { if (isset($this->localLang[$lang][$costKey]) && $this->localLang[$lang][$costKey] !== $constValue) { continue; } if (!array_key_exists($lang, $this->localLang) || !is_array($this->localLang[$lang])) { $this->localLang[$lang] = []; } $this->localLang[$lang][$costKey] = $constValue; } } foreach ($fileObject->getMeta() as $metaTag => $metaTagValue) { if ($metaTag === '@attributes') { foreach ($metaTagValue as $attributeKey => $attributeValue) { if (isset($this->meta[$metaTag][$attributeKey]) && $this->meta[$metaTag][$attributeKey] !== $attributeValue ) { continue; } $this->meta[$metaTag][$attributeKey] = $attributeValue; } } else { if (isset($this->meta[$metaTag]) && $this->meta[$metaTag] !== $metaTagValue) { continue; } $this->meta[$metaTag] = $metaTagValue; } } } /** * Writes language override files. * * @param array|null $editedLanguages * @return void * @throws \Exception * @throws LFException raised if a file cant be written */ public function writeFile($editedLanguages = NULL) { $this->deleteDuplicates(); if (!$this->langDataExists() && !is_file($this->absFile)) { return; } $pathSite = Environment::getPublicPath() . '/'; try { SgLib::createDir($this->absPath, $pathSite); } catch (\Exception $exception) { throw new LFException('failure.failure', 0, '(' . $exception->getMessage() . ')'); } $translationFile = basename($this->absFile); $translationDir = dirname($this->absFile); foreach ($this->localLang as $langKey => $val) { if ($langKey !== 'default' && $langKey !== 'trans-unit') { $this->originLang[$langKey] = $translationDir . '/' . $langKey . '.' . $translationFile; } } parent::writeFile(); // Set only new values in GlobalConfiguration if something changed $originalAbsPath = $this->originalFileObject->getAbsFile(); $pathSpecsOriginialFile = pathinfo($originalAbsPath); $locallangXMLOverride = &$GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride']; $file = $this->session->getDataByKey('languageFileSelection'); $extensionKey = $this->session->getDataByKey('extkey'); $oldExtensionName = $this->cleanSessionExtKey($extensionKey); $typo3ExtRelativeFilePath = 'EXT:' . $oldExtensionName . '/' . $file; $relativeOverridePath = str_replace( 'EXT:' . $oldExtensionName, 'typo3conf/LFEditor/OverrideFiles/' . $oldExtensionName, $typo3ExtRelativeFilePath ); if (array_key_exists($typo3ExtRelativeFilePath, $locallangXMLOverride) && $locallangXMLOverride[$typo3ExtRelativeFilePath][0] === $relativeOverridePath) { return; } /** @var ConfigurationService $configurationService */ $configurationService = GeneralUtility::makeInstance(ConfigurationService::class); $additionalConfigurationFilePath = $configurationService->getAdditionalConfigurationFilePath(); try { // what we need: $addLine = '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'locallangXMLOverride\'][\'' . $typo3ExtRelativeFilePath . '\'][0] = \'' . $relativeOverridePath . '\';'; Typo3Lib::writeLineToAdditionalConfiguration($addLine, $additionalConfigurationFilePath); $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$typo3ExtRelativeFilePath][0] = $relativeOverridePath; } catch (\Exception $exception) { throw new LFException('failure.failure', 0, '(' . $exception->getMessage() . ')'); } } /** * Deletes duplicated language data (data that exist in both files - original file and override file), * so that only changed data will be saved in override file. * * @return void */ public function deleteDuplicates() { $defaultLangData = $this->originalFileObject->getLocalLangData('default'); foreach ($this->localLang as $language => $languageData) { if (!is_array($languageData)) { continue; } foreach ($languageData as $constKey => $constValue) { $localLangData = $this->originalFileObject->getLocalLangData($language); if (array_key_exists($constKey, $localLangData) && SgLib::strCmpIgnoreCR( $constValue, $localLangData[$constKey] )) { unset($this->localLang[$language][$constKey]); } elseif ($language !== 'default' && $language !== 'trans-unit') { // if we are not in default, but we deleted the default key, we have to restore it, else we would // not write the correct language content in xlf if (!array_key_exists($constKey, $this->localLang['default'])) { $this->localLang['default'][$constKey] = $defaultLangData[$constKey] ?? ''; } } } } foreach ($this->meta as $metaTag => $metaValue) { if (SgLib::strCmpIgnoreCR($metaValue, $this->originalFileObject->getMetaData($metaTag))) { unset($this->meta[$metaTag]); } } } /** * Checks is there any lang data (constants in $localLang and meta data). * * @return bool Returns true if any constant or meta data exist. */ protected function langDataExists() { foreach ($this->meta as $metaTag => $metaValue) { if (isset($this->meta[$metaTag]) && $metaTag !== '@attributes') { return TRUE; } } foreach ($this->localLang as $language => $languageData) { if (!is_array($languageData)) { continue; } foreach ($languageData as $constKey => $constValue) { if (isset($languageData[$constKey])) { return TRUE; } } } return FALSE; } /** * Writes generator meta tag. * * @return void */ protected function addGeneratorString() { if ($this->originalFileObject->getMetaData('generator') !== 'LFEditor') { $this->meta['generator'] = 'LFEditor'; } } }