273 lines
11 KiB
PHP
273 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KupShop\I18nBundle\Resources\script;
|
|
|
|
use KupShop\AdminBundle\Util\Script\Script;
|
|
use KupShop\I18nBundle\Translations\BlocksTranslation;
|
|
use KupShop\I18nBundle\Translations\ITranslation;
|
|
use KupShop\I18nBundle\Util\TranslationEngine;
|
|
use KupShop\I18nBundle\Util\TranslationLocator;
|
|
use KupShop\KupShopBundle\Context\LanguageContext;
|
|
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use Query\Operator;
|
|
use Query\QueryBuilder;
|
|
use Query\Translation;
|
|
|
|
class AutoTranslateScript extends Script
|
|
{
|
|
protected static $name = 'Auto-translate translations using translator';
|
|
protected static $defaultParameters = [
|
|
// realRun: true = translate and save translations into DB
|
|
'realRun' => false,
|
|
'run_auto_translates' => false,
|
|
// translate from default language to specific languages
|
|
'languages' => ['sk'],
|
|
'po_files' => false,
|
|
];
|
|
|
|
private static array $translateDefinition = [];
|
|
|
|
public static function getDefaultParameters(): array
|
|
{
|
|
$translationLocator = ServiceContainer::getService(TranslationLocator::class);
|
|
$translations = $translationLocator->getTranslations();
|
|
|
|
$defaultParameters = self::$defaultParameters;
|
|
foreach ($translations as $translationClass) {
|
|
/** @var ITranslation $translation */
|
|
$translation = ServiceContainer::getService($translationClass);
|
|
$defaultParameters['autoTranslateClasses'][$translationClass] = array_keys($translation->getColumns());
|
|
}
|
|
self::$translateDefinition = $defaultParameters['autoTranslateClasses'] ?? [];
|
|
|
|
return $defaultParameters;
|
|
}
|
|
|
|
protected function run(array $arguments)
|
|
{
|
|
$languages = $arguments['languages'];
|
|
if (empty($languages)) {
|
|
$this->log('Argument "languages" cannot be empty!');
|
|
|
|
return;
|
|
}
|
|
|
|
$missingTranslations = [];
|
|
if (!empty($arguments['run_auto_translates'])) {
|
|
foreach ($arguments['autoTranslateClasses'] ?? [] as $className => $class) {
|
|
$missingTranslations[$className] = $this->getMissingTranslations($className, $languages);
|
|
}
|
|
}
|
|
|
|
$missingPoTranslations = [];
|
|
if (!empty($arguments['po_files'])) {
|
|
foreach ($languages as $language) {
|
|
$missingPoTranslations[$language] = $this->getMissingPoTranslations($language);
|
|
}
|
|
}
|
|
|
|
if (!($arguments['realRun'] ?? false)) {
|
|
foreach ($missingTranslations as $class => $translations) {
|
|
foreach ($translations as $lang => $items) {
|
|
$this->log("[{$lang}] {$class}: ".count($items));
|
|
}
|
|
}
|
|
|
|
$this->log('Statické texty: '.count($missingPoTranslations));
|
|
|
|
return;
|
|
}
|
|
ini_set('max_execution_time', 60 * 60 * 2); // 2 hours
|
|
|
|
$translator = ServiceContainer::getService(TranslationEngine::class);
|
|
$languageContext = Contexts::get(LanguageContext::class);
|
|
|
|
foreach ($missingTranslations as $class => $translations) {
|
|
$this->log("Translating {$class}...");
|
|
|
|
/** @var ITranslation $translation */
|
|
$translation = ServiceContainer::getService($class);
|
|
|
|
foreach ($translations as $lang => $items) {
|
|
$totalItems = count($items);
|
|
$processedItems = 0;
|
|
foreach ($items as $id => $item) {
|
|
$translatedValues = [];
|
|
foreach ($item as $column => $text) {
|
|
$translated = null;
|
|
if ($column == 'json_content' && $translation instanceof BlocksTranslation) {
|
|
$blocks = json_decode($text, true);
|
|
$text_blocks = [];
|
|
BlocksTranslation::getTextBlocks($blocks, $text_blocks);
|
|
foreach ($text_blocks as $key => $blockText) {
|
|
if (empty($blockText['settings']['html'])) {
|
|
continue;
|
|
}
|
|
try {
|
|
$translated = $translator->getTranslation(
|
|
$blockText['settings']['html'],
|
|
$languageContext->getDefaultId(),
|
|
$lang,
|
|
(bool) ($translation->getColumns()[$column]['richtext'] ?? false)
|
|
)['translatedText'] ?? null;
|
|
$blockText['settings']['html'] = $translated;
|
|
$text_blocks[$key] = $blockText;
|
|
} catch (\Throwable $e) {
|
|
}
|
|
}
|
|
BlocksTranslation::replaceRecursive($blocks, $text_blocks);
|
|
$translated = $blocks;
|
|
} else {
|
|
if (isLocalDevelopment()) {
|
|
$this->log('Překlad není povolen pro localDevelopment');
|
|
continue;
|
|
}
|
|
try {
|
|
$translated = $translator->getTranslation(
|
|
$text,
|
|
$languageContext->getDefaultId(),
|
|
$lang,
|
|
(bool) ($translation->getColumns()[$column]['richtext'] ?? false)
|
|
);
|
|
if (!empty($translated['error'])) {
|
|
$this->log($translated['error']);
|
|
continue;
|
|
}
|
|
$translated = $translated['translatedText'] ?? null;
|
|
} catch (\Throwable $e) {
|
|
}
|
|
}
|
|
|
|
// pokud mam preklad
|
|
if (!empty($translated)) {
|
|
$translatedValues[$column] = $translated;
|
|
}
|
|
|
|
if (!empty($translatedValues)) {
|
|
if ($column == 'json_content' && $translation instanceof BlocksTranslation) {
|
|
$translation->saveSingleObjectForce($lang, $id, $translatedValues, false);
|
|
} else {
|
|
$translation->saveSingleObject($lang, $id, $translatedValues);
|
|
}
|
|
}
|
|
}
|
|
|
|
$processedItems++;
|
|
if ($processedItems % 10 === 0) {
|
|
$this->log("{$processedItems}/{$totalItems}...");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($missingPoTranslations as $language => $translations) {
|
|
$totalItems = count($translations);
|
|
$processedItems = 0;
|
|
foreach ($translations as $translation) {
|
|
try {
|
|
$translated = $translator->getTranslation(
|
|
$translation['original'],
|
|
$languageContext->getDefaultId(),
|
|
$language,
|
|
false
|
|
);
|
|
if (!empty($translated['error'])) {
|
|
$this->log($translated['error']);
|
|
continue;
|
|
}
|
|
$translated = $translated['translatedText'] ?? null;
|
|
} catch (\Throwable $e) {
|
|
$this->log($e->getMessage());
|
|
}
|
|
|
|
if (!empty($translated)) {
|
|
sqlQueryBuilder()->update('translations_frontend')->directValues([
|
|
'translation' => $translated,
|
|
])->where(Operator::equals(['id' => $translation['id']]))->execute();
|
|
}
|
|
$processedItems++;
|
|
if ($processedItems % 10 === 0) {
|
|
$this->log("{$processedItems}/{$totalItems}...");
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->log('Translations done');
|
|
}
|
|
|
|
private function getMissingTranslations(string $translationClass, array $languages): array
|
|
{
|
|
/** @var ITranslation $translation */
|
|
$translation = ServiceContainer::getService($translationClass);
|
|
|
|
if (!($columns = (static::$translateDefinition[$translationClass] ?? false))) {
|
|
$this->log('Žádné sloupce pro tabulku: '.$translationClass);
|
|
|
|
return [];
|
|
}
|
|
$missingTranslations = [];
|
|
$SQL = sqlQuery("SHOW TABLES LIKE '".$translation->getTableName()."'");
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$qb = sqlQueryBuilder()
|
|
->select("{$translation->getTableAlias()}.id")
|
|
->from($translation->getTableName(), $translation->getTableAlias());
|
|
|
|
foreach ($columns as $column) {
|
|
$qb->addSelect("{$translation->getTableAlias()}.{$column}");
|
|
}
|
|
|
|
$qb->andWhere(
|
|
Translation::joinTranslations(
|
|
$languages,
|
|
$translation,
|
|
function (QueryBuilder $qb, $columnName, $translatedColumn, $langID) {
|
|
$qb->addSelect("{$translatedColumn} as translated_{$columnName}_{$langID}");
|
|
|
|
return false;
|
|
},
|
|
$columns
|
|
)
|
|
);
|
|
|
|
foreach ($qb->execute() as $item) {
|
|
foreach ($columns as $column) {
|
|
if (empty($item[$column] ?? '')) {
|
|
continue;
|
|
}
|
|
$originalText = trim($item[$column]);
|
|
// preskocim polozky, ktere nemaji text v default jazyce
|
|
if (empty($originalText)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($languages as $language) {
|
|
$translatedColumn = "translated_{$column}_{$language}";
|
|
|
|
if (empty($item[$translatedColumn])) {
|
|
$missingTranslations[$language][$item['id']][$column] = $originalText;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $missingTranslations;
|
|
}
|
|
|
|
protected function getMissingPoTranslations($language)
|
|
{
|
|
return sqlQueryBuilder()->select('id, original, id_language')->from('translations_frontend')
|
|
->where(Operator::equals(['id_language' => $language]))
|
|
->andWhere(Operator::orX(
|
|
Operator::isNull('translation'),
|
|
Operator::equals(['translation' => ''])
|
|
))
|
|
->execute()->fetchAllAssociative();
|
|
}
|
|
}
|
|
|
|
return AutoTranslateScript::class;
|