• Модуль: translate
  • Путь к файлу: ~/bitrix/modules/translate/lib/controller/import/importcsv.php
  • Класс: BitrixTranslateControllerImportImportCsv
  • Вызов: ImportCsv::runImporting
private function runImporting(): array
{
	$fileIndex = $this->columnList['file'];
	$keyIndex = $this->columnList['key'];

	$currentLine = 0;
	$maxLinePortion = 500;
	$hasFinishedReading = false;

	while (true)
	{
		$linePortion = 0;
		$phraseList = [];

		while ($csvRow = $this->csvFile->fetch())
		{
			$currentLine ++;

			if ($this->seekLine > 0)
			{
				if ($currentLine <= $this->seekLine)
				{
					continue;
				}
			}

			if (
				!is_array($csvRow) ||
				empty($csvRow) ||
				(count($csvRow) == 1 && ($csvRow[0] === null || $csvRow[0] === ''))
			)
			{
				continue;
			}

			$rowErrors = [];

			$filePath = (isset($csvRow[$fileIndex]) ? $csvRow[$fileIndex] : '');
			$key = (isset($csvRow[$keyIndex]) ? $csvRow[$keyIndex] : '');
			if ($filePath == '' || $key == '')
			{
				if ($filePath == '')
				{
					$rowErrors[] = Loc::getMessage('TR_IMPORT_ERROR_DESTINATION_FILEPATH_ABSENT');
				}
				if ($key == '')
				{
					$rowErrors[] = Loc::getMessage('TR_IMPORT_ERROR_PHRASE_CODE_ABSENT');
				}
				$this->addError(new MainError(Loc::getMessage(
					'TR_IMPORT_ERROR_LINE_FILE_EXT',
					[
						'#LINE#' => ($currentLine + 1),
						'#ERROR#' => implode('; ', $rowErrors)
					]
				)));

				continue;
			}

			$linePortion ++;

			if (!isset($phraseList[$filePath]))
			{
				$phraseList[$filePath] = [];
			}
			foreach ($this->languageList as $languageId)
			{
				if (!isset($phraseList[$filePath][$languageId]))
				{
					$phraseList[$filePath][$languageId] = [];
				}

				$langIndex = $this->columnList[$languageId];
				if (!isset($csvRow[$langIndex]) || (empty($csvRow[$langIndex]) && $csvRow[$langIndex] !== '0'))
				{
					continue;
				}

				//$phrase = str_replace("\\", "\", $csvRow[$langIndex]);
				$phrase = $csvRow[$langIndex];

				$encodingOut = self::$sourceEncoding[$languageId];

				if (!empty($this->encodingIn) && $this->encodingIn !== $encodingOut)
				{
					$phrase = MainTextEncoding::convertEncoding($phrase, $this->encodingIn, $encodingOut);
				}

				$checked = true;
				if ($encodingOut === 'utf-8')
				{
					$validPhrase = preg_replace("/[^x01-x7F]/", '', $phrase);// remove ASCII characters
					if ($validPhrase !== $phrase)
					{
						$checked = TranslateTextStringHelper::validateUtf8OctetSequences($phrase);
					}
					unset($validPhrase);
				}

				if ($checked)
				{
					$phraseList[$filePath][$languageId][$key] = $phrase;
				}
				else
				{
					$rowErrors[] = Loc::getMessage('TR_IMPORT_ERROR_NO_VALID_UTF8_PHRASE', ['#LANG#' => $languageId]);
				}

				unset($checked, $phrase);
			}

			if (!empty($rowErrors))
			{
				$this->addError(new MainError(Loc::getMessage(
					'TR_IMPORT_ERROR_LINE_FILE_BIG',
					[
						'#LINE#' => ($currentLine + 1),
						'#FILENAME#' => $filePath,
						'#PHRASE#' => $key,
						'#ERROR#' => implode('; ', $rowErrors),
					]
				)));
			}
			unset($rowErrors);


			if ($linePortion >= $maxLinePortion)
			{
				break;
			}
		}

		if ($csvRow === null)
		{
			$hasFinishedReading = true;
		}
		unset($csvRow);

		$this->processedItems += $linePortion;

		foreach ($phraseList as $filePath => $translationList)
		{
			if (TranslateIOPath::isLangDir($filePath, true) !== true)
			{
				$this->addError(new MainError(Loc::getMessage('TR_IMPORT_ERROR_FILE_NOT_LANG', ['#FILE#' => $filePath])));
				continue;
			}

			$filePath = TranslateIOPath::normalize('/'.$filePath);

			foreach ($translationList as $languageId => $fileMessages)
			{
				if (empty($fileMessages))
				{
					continue;
				}

				$langFilePath = TranslateIOPath::replaceLangId($filePath, $languageId);

				if (Rel2Abs('/', $langFilePath) !== $langFilePath)
				{
					$this->addError(new MainError(Loc::getMessage('TR_IMPORT_ERROR_BAD_FILEPATH', ['#FILE#' => $filePath])));
					break;
				}

				$fullPath = self::$documentRoot. $langFilePath;
				$fullPath = MainLocalizationTranslation::convertLangPath($fullPath, $languageId);

				$langFile = new TranslateFile($fullPath);
				$langFile->setLangId($languageId);
				$langFile->setOperatingEncoding(self::$sourceEncoding[$languageId]);

				if (!$langFile->loadTokens())
				{
					if (!$langFile->load() && $langFile->hasErrors())
					{
						foreach ($langFile->getErrors() as $error)
						{
							if ($error->getCode() !== 'EMPTY_CONTENT')
							{
								$this->addError($error);
							}
						}
					}
				}
				if (count($this->getErrors()) > 0)
				{
					continue;
				}

				$hasDataToUpdate = false;

				/** @var ArrayAccess $langFile */
				foreach ($fileMessages as $key => $phrase)
				{
					switch ($this->updateMethod)
					{
						// import only new messages
						case TranslateControllerImportCsv::METHOD_ADD_ONLY:
							if (!isset($langFile[$key]) || (empty($langFile[$key]) && $langFile[$key] !== '0'))
							{
								$langFile[$key] = $phrase;
								$hasDataToUpdate = true;
								$this->importedPhraseCount ++;
							}
							break;

						// update only existing messages
						case TranslateControllerImportCsv::METHOD_UPDATE_ONLY:
							if (isset($langFile[$key]) && $langFile[$key] !== $phrase)
							{
								$langFile[$key] = $phrase;
								$hasDataToUpdate = true;
								$this->importedPhraseCount ++;
							}
							break;


						// import new messages and replace all existing with new ones
						case TranslateControllerImportCsv::METHOD_ADD_UPDATE:
							if ($langFile[$key] !== $phrase)
							{
								$langFile[$key] = $phrase;
								$hasDataToUpdate = true;
								$this->importedPhraseCount ++;
							}
							break;
					}
				}

				if ($hasDataToUpdate)
				{
					// backup
					if ($langFile->isExists() && TranslateConfig::needToBackUpFiles())
					{
						if (!$langFile->backup())
						{
							$this->addError(new MainError(
								Loc::getMessage('TR_IMPORT_ERROR_CREATE_BACKUP', ['#FILE#' => $langFilePath])
							));
						}
					}

					// sort phrases by key
					if (TranslateConfig::needToSortPhrases())
					{
						if (in_array($languageId, TranslateConfig::getNonSortPhraseLanguages()) === false)
						{
							$langFile->sortPhrases();
						}
					}

					try
					{
						if (!$langFile->save())
						{
							if ($langFile->hasErrors())
							{
								$this->addErrors($langFile->getErrors());
							}
						}
					}
					catch (MainIOIoException $exception)
					{
						if (!$langFile->isExists())
						{
							$this->addError(new MainError(
								Loc::getMessage('TR_IMPORT_ERROR_WRITE_CREATE', ['#FILE#' => $langFilePath])
							));
						}
						else
						{
							$this->addError(new MainError(
								Loc::getMessage('TR_IMPORT_ERROR_WRITE_UPDATE', ['#FILE#' => $langFilePath])
							));
						}
					}
				}
			}
		}

		if ($this->instanceTimer()->hasTimeLimitReached())
		{
			$this->seekLine = $currentLine;
			break;
		}

		if ($hasFinishedReading)
		{
			$this->declareAccomplishment();
			$this->clearProgressParameters();
			break;
		}
	}

	$this->csvFile->close();

	if ($this->instanceTimer()->hasTimeLimitReached() !== true)
	{
		$this->declareAccomplishment();
		$this->clearProgressParameters();
	}

	return [
		'PROCESSED_ITEMS' => $this->processedItems,
		'TOTAL_ITEMS' => $this->totalItems,
		'TOTAL_PHRASES' => $this->importedPhraseCount,
	];
}