Files
summer-phrasebook/docs/research/wintercms/quotifypro-11-i18n/11-03-PLAN.md
2026-02-18 01:31:41 +01:00

10 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
phase plan type wave depends_on files_modified autonomous must_haves
11-translation-infrastructure 03 execute 2
11-01
plugins/golem15/quotify/console/TranslateExportCommand.php
plugins/golem15/quotify/console/TranslateImportCommand.php
plugins/golem15/quotify/Plugin.php
true
truths artifacts key_links
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
path provides exports
plugins/golem15/quotify/console/TranslateExportCommand.php CLI command for exporting translations
handle
path provides exports
plugins/golem15/quotify/console/TranslateImportCommand.php CLI command for importing translations
handle
from to via
TranslateExportCommand MessageExport model exportData method
from to via
TranslateImportCommand MessageImport model 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.

<execution_context> @/.claude/get-shit-done/workflows/execute-plan.md @/.claude/get-shit-done/templates/summary.md </execution_context>

@.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
<?php

namespace Golem15\Quotify\Console;

use Illuminate\Console\Command;
use Golem15\Translate\Models\Message;
use Golem15\Translate\Models\Locale;
use Golem15\Translate\Models\MessageExport;
use Symfony\Component\Console\Input\InputOption;

class TranslateExportCommand extends Command
{
    protected $name = 'quotify:translate-export';

    protected $description = 'Export translation messages to CSV for bulk translation.';

    public function handle()
    {
        $outputPath = $this->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
<?php

namespace Golem15\Quotify\Console;

use Illuminate\Console\Command;
use Golem15\Translate\Models\Message;
use Golem15\Translate\Models\Locale;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

class TranslateImportCommand extends Command
{
    protected $name = 'quotify:translate-import';

    protected $description = 'Import translations from CSV file.';

    public function handle()
    {
        $inputPath = $this->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

<success_criteria>

  • 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 </success_criteria>
After completion, create `.planning/phases/11-translation-infrastructure/11-03-SUMMARY.md`