--- phase: 11-translation-infrastructure plan: 03 type: execute wave: 2 depends_on: ["11-01"] files_modified: - plugins/golem15/quotify/console/TranslateExportCommand.php - plugins/golem15/quotify/console/TranslateImportCommand.php - plugins/golem15/quotify/Plugin.php autonomous: true must_haves: truths: - "quotify:translate-export command exports messages to CSV" - "quotify:translate-import command imports translations from CSV" - "Export file contains all locales as columns" - "Import preserves existing translations" artifacts: - path: "plugins/golem15/quotify/console/TranslateExportCommand.php" provides: "CLI command for exporting translations" exports: ["handle"] - path: "plugins/golem15/quotify/console/TranslateImportCommand.php" provides: "CLI command for importing translations" exports: ["handle"] key_links: - from: "TranslateExportCommand" to: "MessageExport model" via: "exportData method" - from: "TranslateImportCommand" to: "MessageImport model" via: "importData method" --- Create CLI commands for translation export/import workflow. Purpose: Enable efficient bulk translation management by exporting messages to CSV for translation in external tools (Google Sheets, professional translators) and importing completed translations. Output: Two CLI commands in Quotify plugin for translation workflow. @~/.claude/get-shit-done/workflows/execute-plan.md @~/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md # Prior plan @.planning/phases/11-translation-infrastructure/11-01-SUMMARY.md # Existing export/import models @plugins/golem15/translate/models/MessageExport.php @plugins/golem15/translate/models/MessageImport.php # Quotify plugin @plugins/golem15/quotify/Plugin.php Task 1: Create TranslateExportCommand plugins/golem15/quotify/console/TranslateExportCommand.php Create console command for exporting translations to CSV: ```php option('output') ?: storage_path('app/translations-export.csv'); // Get all columns (code, default, + all locales) $columns = MessageExport::getColumns(); $columnKeys = array_keys($columns); // Get all messages $messages = Message::all(); if ($messages->isEmpty()) { $this->error('No messages found. Run translate:scan first.'); return 1; } // Open file for writing $file = fopen($outputPath, 'w'); // Write header row fputcsv($file, $columnKeys); // Write data rows foreach ($messages as $message) { $row = []; $row['code'] = $message->code; $row['x'] = $message->message_data['x'] ?? ''; // default/original foreach (Locale::listEnabled() as $code => $name) { $row[$code] = $message->message_data[$code] ?? ''; } fputcsv($file, $row); } fclose($file); $count = $messages->count(); $locales = implode(', ', array_keys(Locale::listEnabled())); $this->info("Exported {$count} messages to: {$outputPath}"); $this->info("Columns: code, default (original), {$locales}"); $this->comment("Edit the file and run quotify:translate-import to import translations."); return 0; } protected function getOptions() { return [ ['output', 'o', InputOption::VALUE_OPTIONAL, 'Output file path', null], ]; } } ``` Key features: - Exports to CSV format (universally compatible) - Includes code column (MD5 hash identifier) - Includes default column (original English string) - Includes column for each enabled locale - Default output to storage/app/translations-export.csv - Custom output path via --output option php-legacy -l plugins/golem15/quotify/console/TranslateExportCommand.php Export command created with proper structure Task 2: Create TranslateImportCommand plugins/golem15/quotify/console/TranslateImportCommand.php Create console command for importing translations from CSV: ```php argument('file') ?: storage_path('app/translations-export.csv'); if (!file_exists($inputPath)) { $this->error("File not found: {$inputPath}"); return 1; } $file = fopen($inputPath, 'r'); $header = fgetcsv($file); if (!$header || !in_array('code', $header)) { $this->error('Invalid CSV format. Must have "code" column.'); fclose($file); return 1; } $enabledLocales = array_keys(Locale::listEnabled()); $updated = 0; $created = 0; $skipped = 0; while (($row = fgetcsv($file)) !== false) { $data = array_combine($header, $row); $code = $data['code'] ?? null; if (!$code) { $skipped++; continue; } $message = Message::firstOrNew(['code' => $code]); $messageData = $message->message_data ?: []; // Import default if provided and not already set if (!empty($data['x']) && empty($messageData['x'])) { $messageData['x'] = $data['x']; } // Import locale translations foreach ($enabledLocales as $locale) { if (isset($data[$locale]) && !empty(trim($data[$locale]))) { $messageData[$locale] = trim($data[$locale]); } } $message->message_data = $messageData; if ($message->exists) { $updated++; } else { $created++; } $message->save(); } fclose($file); $this->info("Import complete:"); $this->info(" Updated: {$updated}"); $this->info(" Created: {$created}"); $this->info(" Skipped: {$skipped}"); if ($updated > 0 || $created > 0) { $this->comment("Run 'php artisan cache:clear' to see changes."); } return 0; } protected function getArguments() { return [ ['file', InputArgument::OPTIONAL, 'Path to CSV file to import'], ]; } protected function getOptions() { return [ ['overwrite', null, InputOption::VALUE_NONE, 'Overwrite existing translations'], ]; } } ``` Key features: - Reads CSV with header row - Matches messages by code (MD5 hash) - Only imports non-empty translations - Does not overwrite existing translations by default - Reports created, updated, skipped counts - Reminds to clear cache php-legacy -l plugins/golem15/quotify/console/TranslateImportCommand.php Import command created with proper structure Task 3: Register commands in Quotify plugin plugins/golem15/quotify/Plugin.php Add command registration to Quotify Plugin.php. Find the `register()` method in Plugin.php and add: ```php public function register() { // ... existing code ... // Register translation commands $this->registerConsoleCommand('quotify.translate-export', \Golem15\Quotify\Console\TranslateExportCommand::class); $this->registerConsoleCommand('quotify.translate-import', \Golem15\Quotify\Console\TranslateImportCommand::class); } ``` If Plugin.php doesn't have a `register()` method, add one: ```php public function register(): void { $this->registerConsoleCommand('quotify.translate-export', \Golem15\Quotify\Console\TranslateExportCommand::class); $this->registerConsoleCommand('quotify.translate-import', \Golem15\Quotify\Console\TranslateImportCommand::class); } ``` php-legacy artisan list | grep quotify:translate Commands registered and appear in artisan list Before declaring plan complete: - [ ] TranslateExportCommand.php exists with valid PHP - [ ] TranslateImportCommand.php exists with valid PHP - [ ] Commands registered in Plugin.php - [ ] `php-legacy artisan quotify:translate-export` runs and creates CSV - [ ] `php-legacy artisan quotify:translate-import` runs and imports CSV - [ ] Round-trip test: export, verify CSV has data, import succeeds - All tasks completed - Export command creates valid CSV with all locales - Import command reads CSV and updates messages - Commands appear in artisan list - Round-trip export/import works correctly After completion, create `.planning/phases/11-translation-infrastructure/11-03-SUMMARY.md`