diff --git a/ProcessMaker/Console/Commands/SyncTranslations.php b/ProcessMaker/Console/Commands/SyncTranslations.php index ff185d5d44..2a02052218 100644 --- a/ProcessMaker/Console/Commands/SyncTranslations.php +++ b/ProcessMaker/Console/Commands/SyncTranslations.php @@ -40,9 +40,9 @@ public function handle() //Search files $this->listFiles($translationsCore . '/lang'); - //updating languages by default + // updating languages by default foreach ($this->files as $pathFile) { - if (!(str_contains($pathFile, '.json') || str_contains($pathFile, '.php'))) { + if (!(str_contains($pathFile, '.json') || str_contains($pathFile, '.php')) || str_contains($pathFile, '.bak.')) { continue; } // updating resources/lang @@ -57,13 +57,9 @@ public function handle() $this->files = []; $translationsCore = app()->basePath() . '/resources/lang'; $this->listFiles($translationsCore); - $filesIgnore = ['/fr/', '/de/', '/en/', '/es/', '.gitignore', '/en.json', '/es.json', '/de.json', '/fr.json']; foreach ($this->files as $pathFile) { - // ignore languages by default - foreach ($filesIgnore as $value) { - if (str_contains($pathFile, $value)) { - continue 2; - } + if (!(str_contains($pathFile, '.json') || str_contains($pathFile, '.php')) || str_contains($pathFile, '.bak.')) { + continue; } // updating resources/lang $backup = str_replace('/resources/', '/resources-core/', preg_replace('/(?<=lang).+?(?=json)/', '/en.', $pathFile)); @@ -98,14 +94,14 @@ public function handle() $this->listFiles($translationsPackage . '/lang-' . last($package)); $existsLangOrig = $this->fileExists($translationsPackage . '/lang.orig-' . last($package)); foreach ($this->files as $pathFile) { - if (!str_contains($pathFile, '.json')) { + if (!str_contains($pathFile, '.json') || str_contains($pathFile, '.bak.')) { continue; } // updating resources/lang - $this->syncFile($pathFile, $pathPackage . '/en.json', true); + $this->syncFile($pathFile, $pathPackage . '/en.json'); if ($existsLangOrig) { // updating resources/lang.orig - $this->syncFile(str_replace('/lang-', '/lang.orig-', $pathFile), $pathPackage . '/en.json', true); + $this->syncFile(str_replace('/lang-', '/lang.orig-', $pathFile), $pathPackage . '/en.json'); } } @@ -163,46 +159,112 @@ private function parseFile($path) return collect($lines); } - private function syncFile($target, $backup, $package = false) + /** + * Synchronize translations between target and backup files + * + * @param string $target Path to target file + * @param string $backup Path to backup file + * @return bool + * @throws \Exception + */ + private function syncFile($target, $backup) { - $pathInfo = pathinfo($target); - $targetTranslations = $this->parseFile($target); - $origin = $this->parseFile($backup); + if (str_contains($target, '.bak.')) { + // Clean up old backup if everything succeeded + if (file_exists($target)) { + unlink($target); + $this->info('Removed bak: ' . $target); + } + $this->info("Skipping backup file: {$target}"); - if ($package) { - $filesIgnore = ['/en.json']; - } else { - $filesIgnore = ['/fr/', '/de/', '/en/', '/es/', '/en.json', '/es.json', '/de.json', '/fr.json']; + return true; } - $clear = true; - foreach ($filesIgnore as $value) { - if (str_contains($target, $value)) { - $clear = false; - continue; + // Create backup before modifications + $backupPath = $target . '.bak.' . date('Y-m-d-His'); + try { + if (!copy($target, $backupPath)) { + $this->error("Failed to create backup file: {$backupPath}"); + + return false; } + $this->info("Backup created: {$backupPath}"); + } catch (\Exception $e) { + $this->error('Error creating backup: ' . $e->getMessage()); + + return false; } - if ($clear) { - $diff = $origin->diffKeys($targetTranslations); + $pathInfo = pathinfo($target); - $targetTranslations = $diff->map(function () { - return ''; - }); + try { + $targetTranslations = $this->parseFile($target); + $origin = $this->parseFile($backup); + } catch (\Exception $e) { + $this->error('Error parsing files: ' . $e->getMessage()); + + return false; } - $merged = $origin->merge($targetTranslations); - // search empty values - // send values to openAI. - $contents = $this->generateFile($merged, $pathInfo['extension']); + // Solo obtener las claves que están en origin pero no en target + $diff = $origin->diffKeys($targetTranslations); - file_put_contents($target, $contents); - $this->info($target . ' Saved'); + if ($diff->isNotEmpty()) { + $this->info('Found ' . $diff->count() . " new translations to add in {$target}"); + + // only files en.json to en.json have translations others are empty + $clear = true; + if (str_contains($target, 'en.json') && str_contains($backup, 'en.json')) { + $clear = false; + } - if ($pathInfo['extension'] == 'php') { - $this->clearCache(); + // Agregar solo las nuevas claves al targetTranslations + foreach ($diff as $key => $value) { + $targetTranslations[$key] = $clear ? '' : $value; + } } - return true; + try { + $contents = $this->generateFile($targetTranslations, $pathInfo['extension']); + + // Validate content before saving + if (empty($contents)) { + throw new \Exception('Generated content is empty'); + } + + // Use atomic file writing + $tempFile = $target . '.tmp'; + if (file_put_contents($tempFile, $contents) === false) { + throw new \Exception('Failed to write temporary file'); + } + + if (!rename($tempFile, $target)) { + unlink($tempFile); + throw new \Exception('Failed to move temporary file'); + } + + $this->info("Successfully updated: {$target}"); + + // Clean up old backup if everything succeeded + if (file_exists($backupPath)) { + unlink($backupPath); + $this->info('Removed backup file after successful update'); + } + + if ($pathInfo['extension'] == 'php') { + $this->clearCache(); + } + + return true; + } catch (\Exception $e) { + // Restore from backup if something went wrong + if (file_exists($backupPath)) { + copy($backupPath, $target); + $this->info('Restored from backup due to error'); + } + $this->error('Error saving file: ' . $e->getMessage()); + + return false; + } } private function generateFile($lines, $type)