Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
exportaction.php
1<?php
3
8
16{
17 protected static string $documentRoot = '';
18
19 protected static bool $useTranslationRepository = false;
21 protected static array $translationRepositoryLanguages = [];
22
23 protected int $tabId = 0;
24
25 protected string $exportFilePath = '';
26 protected string $exportFileName = '';
27 protected string $exportFileType = 'application/csv';
28 protected int $exportFileSize = 0;
29
30 protected bool $convertEncoding;
31
32 protected string $encodingOut;
33
34 protected bool $collectUntranslated;
35
36 /* Look for translation samples */
37 protected bool $appendSamples = false;
38 protected int $samplesCount = 10;
39 protected array $samplesRestriction = [];
40 protected string $samplesFilePath = '';
41 protected string $samplesFileName = '';
42 protected int $samplesFileSize = 0;
43
44 /* Don't look for samples for a long text */
45 protected int $maxSampleSourceLength = 500;
46
48 protected array $languages = [];
49
50 protected int $exportedPhraseCount = 0;
51
52 protected int $exportedSamplesCount = 0;
53
55
56 protected array $fullPathCache = [];
57
58
66 public function __construct($name, Main\Engine\Controller $controller, array $config = [])
67 {
68 $this->filter = new Translate\Filter();
69
70 Loc::loadLanguageFile(__FILE__);
71
72 if ($this instanceof Translate\Controller\IProcessParameters)
73 {
74 $this->keepField([
75 'tabId',
76 'exportFileName',
77 'exportFilePath',
78 'exportFileSize',
79 'exportedPhraseCount',
80 'collectUntranslated',
81 'appendSamples',
82 'samplesCount',
83 'samplesRestriction',
84 'samplesFileName',
85 'samplesFilePath',
86 'samplesFileSize',
87 'exportedSamplesCount',
88 'convertEncoding',
89 'encodingOut',
90 'languages'
91 ]);
92 }
93
94 $fields = [
95 'collectUntranslated',
96 'appendSamples',
97 'samplesCount',
98 'samplesRestriction',
99 'convertEncoding',
100 'encodingOut',
101 'languages',
102 'filter'
103 ];
104 foreach ($fields as $key)
105 {
106 if (isset($config[$key]))
107 {
108 if ($key == 'filter')
109 {
110 if (!$config[$key] instanceof Translate\Filter)
111 {
112 continue;
113 }
114 }
115 $this->{$key} = $config[$key];
116 }
117 }
118
119 self::$documentRoot = \rtrim(Translate\IO\Path::tidy(Main\Application::getDocumentRoot()), '/');
120
121 self::$useTranslationRepository = Main\Localization\Translation::useTranslationRepository();
122 if (self::$useTranslationRepository)
123 {
124 self::$translationRepositoryLanguages = Translate\Config::getTranslationRepositoryLanguages();
125 }
126
127 if (in_array('all', $this->languages) || empty($this->languages))
128 {
129 $this->languages = Translate\Config::getEnabledLanguages();
130 }
131
132 parent::__construct($name, $controller, $config);
133 }
134
135
142 protected function createExportTempFile(string $exportFileName): Translate\IO\CsvFile
143 {
145 $exportFolder = Translate\Config::getExportFolder();
146 if (!empty($exportFolder))
147 {
148 $tempDir = new Translate\IO\Directory($exportFolder);
149 if ($tempDir->isExists())
150 {
151 $tempDir->wipe(function(Main\IO\FileSystemEntry $entry){
152 // clear .csv files older than 3 hours
153 return (
154 $entry->isFile() &&
155 \preg_match("#.+_([0-9]+)\.csv$#", $entry->getName(), $matches) &&
156 (\time() - (int)$matches[1] > 3 * 3600)
157 );
158 });
159 }
160 else
161 {
162 $tempDir->create();
163 }
164
165 $fileName = \preg_replace("#(.+)\.csv$#", "$1_".\time().'.csv', $exportFileName);
166
167 $csvFile = new Translate\IO\CsvFile($tempDir->getPhysicalPath() .'/'. $fileName);
168 }
169 else
170 {
171 $csvFile = Translate\IO\CsvFile::generateTemporalFile('translate', '.csv', 3);
172 }
173
174 $this->configureExportCsvFile($csvFile);
175
176 $csvFile->openWrite();
177
178 $row = ['file', 'key'];
179 foreach ($this->languages as $langId)
180 {
181 $row[] = $langId;
182 }
183 $csvFile->put($row);
184 $csvFile->close();
185
186 return $csvFile;
187 }
188
196 protected function configureExportCsvFile(Translate\IO\CsvFile $csvFile): void
197 {
198 $csvFile
199 ->setRowDelimiter(Translate\IO\CsvFile::LINE_DELIMITER_WIN)
200 ->prefaceWithUtf8Bom($this->encodingOut === 'utf-8')
201 ;
202
203 switch (Translate\Config::getOption(Translate\Config::OPTION_EXPORT_CSV_DELIMITER))
204 {
205 case 'TAB':
206 $csvFile->setFieldDelimiter(Translate\IO\CsvFile::DELIMITER_TAB);
207 break;
208 case 'ZPT':
209 $csvFile->setFieldDelimiter(Translate\IO\CsvFile::DELIMITER_ZPT);
210 break;
211 case 'TZP':
212 default:
213 $csvFile->setFieldDelimiter(Translate\IO\CsvFile::DELIMITER_TZP);
214 }
215 }
216
225 protected function generateExportFileName(string $path, array $languages): string
226 {
227 return \trim(\str_replace(['.php', '/'], ['', '_'], $path), '_').'_'.\implode('_', $languages).'.csv';
228 }
229
235 public function getDownloadingParameters(): array
236 {
237 return [
238 'fileName' => $this->exportFileName,
239 'filePath' => $this->exportFilePath,
240 'fileType' => $this->exportFileType,
241 'fileSize' => $this->exportFileSize,
242 ];
243 }
244
250 public function getDownloadingSamplesParameters(): array
251 {
252 return [
253 'fileName' => $this->samplesFileName,
254 'filePath' => $this->samplesFilePath,
255 'fileType' => $this->exportFileType,
256 'fileSize' => $this->samplesFileSize,
257 ];
258 }
259
270 public function mergeLangFiles(
271 string $langFilePath,
272 array $fullLangFilePaths,
273 bool $collectUntranslated = false,
274 array $filterByCodeList = []
275 ): array
276 {
277 $mergedContent = [];
278
279 $rowLang0 = [];
280 foreach ($this->languages as $langId)
281 {
282 $rowLang0[$langId] = '';
283 }
284
285 $filterByCode = !empty($filterByCodeList);
286
287 foreach ($this->languages as $langId)
288 {
289 if (empty($fullLangFilePaths[$langId]))
290 {
291 continue;
292 }
293
294 $fullPath = $fullLangFilePaths[$langId];
295 $file = new Translate\File($fullPath);
296 $file->setLangId($langId);
297
298 if ($this->convertEncoding)
299 {
300 $file->setOperatingEncoding($this->encodingOut);
301 }
302 else
303 {
304 $file->setOperatingEncoding(Main\Localization\Translation::getSourceEncoding($langId));
305 }
306
307 if (!$file->loadTokens())
308 {
309 if (!$file->load())
310 {
311 continue;
312 }
313 }
314
315 foreach ($file as $code => $phrase)
316 {
317 if ($filterByCode)
318 {
319 if (!\in_array($code, $filterByCodeList))
320 {
321 continue;
322 }
323 }
324 if (!isset($mergedContent[$code]))
325 {
326 $mergedContent[$code] = \array_merge(['file' => $langFilePath, 'key' => $code], $rowLang0);
327 }
328 $mergedContent[$code][$langId] = $phrase;
329 }
330 }
331
333 {
334 // settings
335 $hasObligatorySetting = false;
336 if ($settingsFile = Translate\Settings::instantiateByPath(self::$documentRoot. '/'. $langFilePath))
337 {
338 if ($settingsFile->load())
339 {
340 $langSettings = $settingsFile->getOptions($langFilePath);
341 $hasObligatorySetting = !empty($langSettings[Translate\Settings::OPTION_LANGUAGES]);
342 }
343 }
344
345 foreach ($mergedContent as $code => $row)
346 {
347 foreach ($row as $langId => $phr)
348 {
349 if ($langId == 'file' || $langId == 'key')
350 {
351 continue;
352 }
353 $isObligatory = true;
354 if ($hasObligatorySetting)
355 {
356 $isObligatory = \in_array($langId, $langSettings[Translate\Settings::OPTION_LANGUAGES]);
357 }
358 if (empty($phr) && ($phr !== '0') && $isObligatory)
359 {
360 continue 2;
361 }
362 }
363 unset($mergedContent[$code]);
364 }
365 }
366
367 return $mergedContent;
368 }
369
370
378 public function lookThroughLangFolder(string $langPath): iterable
379 {
380 $files = [];
381 $folders = [];
382
383 foreach ($this->languages as $langId)
384 {
385 $langFolderRelPath = Translate\IO\Path::replaceLangId($langPath, $langId);
386 $langFolderFullPath = Translate\IO\Path::tidy(self::$documentRoot.'/'.$langFolderRelPath);
387
388 if (self::$useTranslationRepository && \in_array($langId, self::$translationRepositoryLanguages))
389 {
390 $langFolderFullPath = Main\Localization\Translation::convertLangPath($langFolderFullPath, $langId);
391 }
392
393 $childrenList = Translate\IO\FileSystemHelper::getFileList($langFolderFullPath);
394 if (!empty($childrenList))
395 {
396 foreach ($childrenList as $fullPath)
397 {
398 $name = \basename($fullPath);
399 if (\in_array($name, Translate\IGNORE_FS_NAMES))
400 {
401 continue;
402 }
403
404 if (Translate\IO\Path::isPhpFile($fullPath, true))
405 {
406 $files[$langPath.'/'.$name][$langId] = $fullPath;
407 }
408 }
409 }
410
411 // dir only
412 $childrenList = Translate\IO\FileSystemHelper::getFolderList($langFolderFullPath);
413 if (!empty($childrenList))
414 {
415 $ignoreDev = \implode('|', Translate\IGNORE_MODULE_NAMES);
416 foreach ($childrenList as $fullPath)
417 {
418 $name = \basename($fullPath);
419 if (\in_array($name, Translate\IGNORE_FS_NAMES))
420 {
421 continue;
422 }
423
424 $relPath = $langFolderRelPath.'/'.$name;
425
426 if (!\is_dir($fullPath))
427 {
428 continue;
429 }
430
431 if (\in_array($relPath, Translate\IGNORE_BX_NAMES))
432 {
433 continue;
434 }
435
436 // /bitrix/modules/[smth]/dev/
437 if (\preg_match("#^bitrix/modules/[^/]+/({$ignoreDev})$#", \trim($relPath, '/')))
438 {
439 continue;
440 }
441
442 if (\in_array($name, Translate\IGNORE_LANG_NAMES))
443 {
444 continue;
445 }
446
447 $folders[$langPath.'/'.$name] = $langPath.'/'.$name;
448 }
449 }
450 }
451
452 if (\count($files) > 0)
453 {
454 yield $files;
455 }
456
457 if (\count($folders) > 0)
458 {
459 foreach ($folders as $subFolderPath)
460 {
461 foreach ($this->lookThroughLangFolder($subFolderPath) as $subFiles)// go deeper
462 {
463 yield $subFiles;
464 }
465 }
466 }
467 }
468
479 public function findSamples(string $searchPhrase, string $searchLangId, $stripPath, int $limit = 50, array $restrictByPathId = []): array
480 {
481 $select = [
482 'PATH_ID' => 'PATH_ID',
483 'PHRASE_CODE' => 'CODE',
484 'FILE_PATH' => 'PATH.PATH',
485 ];
486
487 $phraseFilter = [];
488
489 if (!Index\PhraseIndexSearch::disallowFtsIndex($searchLangId))
490 {
491 $minLengthFulltextWorld = Index\PhraseIndexSearch::getFullTextMinLength();
492 $fulltextIndexSearchStr = Index\PhraseIndexSearch::prepareTextForFulltextSearch($searchPhrase);
493 if (\mb_strlen($fulltextIndexSearchStr) > $minLengthFulltextWorld)
494 {
495 $phraseFilter['*=PHRASE'] = $fulltextIndexSearchStr;
496 }
497 }
498
499 $phraseFilter['=PHRASE'] = $searchPhrase;
500 if (is_numeric($stripPath))
501 {
502 $phraseFilter['!=PATH_ID'] = $stripPath;
503 }
504 else
505 {
506 $phraseFilter['!=PATH.PATH'] = $stripPath;
507 }
508
509 if (!empty($restrictByPathId))
510 {
511 $phraseFilter['=PATH.DESCENDANTS.PARENT_ID'] = $restrictByPathId; //ancestor
512 }
513
514 $ftsClass = Index\Internals\PhraseFts::getFtsEntityClass($searchLangId);
515
517 $phraseInxRes = $ftsClass::getList([
518 'filter' => $phraseFilter,
519 'select' => $select,
520 'limit' => $limit,
521 ]);
522
523 $samples = [];
524 $fileInxCache = [];
525 while ($phraseInx = $phraseInxRes->fetch())
526 {
527 $pathId = (int)$phraseInx['PATH_ID'];
528 $phraseCode = $phraseInx['PHRASE_CODE'];
529
530 if (!isset($fileInxCache[$pathId]))
531 {
532 $fullPaths = $this->getFullPath($pathId);
533 $fileInxCache[$pathId] = $this->mergeLangFiles($phraseInx['FILE_PATH'], $fullPaths);
534 }
535
536 if (
537 isset($fileInxCache[$pathId][$phraseCode])
538 && $fileInxCache[$pathId][$phraseCode][$searchLangId] == $searchPhrase
539 )
540 {
541 $samples[] = $fileInxCache[$pathId][$phraseCode];
542 }
543 }
544
545 return $samples;
546 }
547
553 protected function getFullPath(int $pathId): array
554 {
555 if (!isset($this->fullPathCache[$pathId]))
556 {
557 $this->fullPathCache[$pathId] = [];
558 $fileInxRes = Translate\Index\Internals\FileIndexTable::getList([
559 'filter' => ['=PATH_ID' => $pathId],
560 'order' => ['ID' => 'ASC'],
561 'select' => ['LANG_ID', 'FULL_PATH'],
562 ]);
563 while ($fileInx = $fileInxRes->fetch())
564 {
565 $this->fullPathCache[$pathId][$fileInx['LANG_ID']] = $fileInx['FULL_PATH'];
566 }
567 }
568
569 return $this->fullPathCache[$pathId];
570 }
571}
static loadLanguageFile($file, $language=null, $normalize=true)
Definition loc.php:224
configureExportCsvFile(Translate\IO\CsvFile $csvFile)
generateExportFileName(string $path, array $languages)
mergeLangFiles(string $langFilePath, array $fullLangFilePaths, bool $collectUntranslated=false, array $filterByCodeList=[])
__construct($name, Main\Engine\Controller $controller, array $config=[])
static instantiateByPath(string $fullPath)
Definition settings.php:38