1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
exportaction.php
См. документацию.
1<?php
2namespace Bitrix\Translate\Controller\Export;
3
4use Bitrix\Main;
5use Bitrix\Translate;
6use Bitrix\Main\Localization\Loc;
7use Bitrix\Translate\Index;
8
16{
17 protected static string $documentRoot = '';
18
19 protected static bool $useTranslationRepository = false;
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
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
236 {
237 return [
238 'fileName' => $this->exportFileName,
239 'filePath' => $this->exportFilePath,
240 'fileType' => $this->exportFileType,
241 'fileSize' => $this->exportFileSize,
242 ];
243 }
244
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] = [];
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}
$path
Определения access_edit.php:21
$controller
Определения action.php:23
static getSourceEncoding($lang)
Определения translation.php:59
static useTranslationRepository()
Определения translation.php:173
static convertLangPath($langFile, $language)
Определения translation.php:267
static getList(array $parameters=array())
Определения datamanager.php:431
static getEnabledLanguages()
Определения config.php:211
static getExportFolder()
Определения config.php:490
static getTranslationRepositoryLanguages()
Определения config.php:316
lookThroughLangFolder(string $langPath)
Определения exportaction.php:378
configureExportCsvFile(Translate\IO\CsvFile $csvFile)
Определения exportaction.php:196
static array $translationRepositoryLanguages
Определения exportaction.php:21
generateExportFileName(string $path, array $languages)
Определения exportaction.php:225
mergeLangFiles(string $langFilePath, array $fullLangFilePaths, bool $collectUntranslated=false, array $filterByCodeList=[])
Определения exportaction.php:270
__construct($name, Main\Engine\Controller $controller, array $config=[])
Определения exportaction.php:66
static getFileList(string $path)
Определения filesystemhelper.php:35
static getFolderList(string $path)
Определения filesystemhelper.php:15
static replaceLangId(string $path, string $langId)
Определения path.php:98
static tidy(string $path)
Определения path.php:16
static prepareTextForFulltextSearch(string $text)
Определения phraseindexsearch.php:811
static disallowFtsIndex(string $langId)
Определения phraseindexsearch.php:763
static instantiateByPath(string $fullPath)
Определения settings.php:38
const OPTION_LANGUAGES
Определения settings.php:15
$relPath
Определения component_props2.php:52
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$select
Определения iblock_catalog_list.php:194
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
Определения action.php:3
Определения Image.php:9
Определения directory.php:3
$files
Определения mysql_to_pgsql.php:30
keepField($fieldName)
Определения processparams.php:53
Определения autoload.php:3
const IGNORE_BX_NAMES
Определения autoload.php:56
const IGNORE_MODULE_NAMES
Определения autoload.php:81
const IGNORE_FS_NAMES
Определения autoload.php:41
const IGNORE_LANG_NAMES
Определения autoload.php:77
$fileName
Определения quickway.php:305
if(empty($signedUserToken)) $key
Определения quickway.php:257
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$matches
Определения index.php:22
$fields
Определения yandex_run.php:501