336 lines
10 KiB
Markdown
336 lines
10 KiB
Markdown
---
|
|
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"
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@~/.claude/get-shit-done/workflows/execute-plan.md
|
|
@~/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<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
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Create TranslateExportCommand</name>
|
|
<files>plugins/golem15/quotify/console/TranslateExportCommand.php</files>
|
|
<action>
|
|
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
|
|
</action>
|
|
<verify>php-legacy -l plugins/golem15/quotify/console/TranslateExportCommand.php</verify>
|
|
<done>Export command created with proper structure</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Create TranslateImportCommand</name>
|
|
<files>plugins/golem15/quotify/console/TranslateImportCommand.php</files>
|
|
<action>
|
|
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
|
|
</action>
|
|
<verify>php-legacy -l plugins/golem15/quotify/console/TranslateImportCommand.php</verify>
|
|
<done>Import command created with proper structure</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 3: Register commands in Quotify plugin</name>
|
|
<files>plugins/golem15/quotify/Plugin.php</files>
|
|
<action>
|
|
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);
|
|
}
|
|
```
|
|
</action>
|
|
<verify>php-legacy artisan list | grep quotify:translate</verify>
|
|
<done>Commands registered and appear in artisan list</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
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
|
|
</verification>
|
|
|
|
<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>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/11-translation-infrastructure/11-03-SUMMARY.md`
|
|
</output>
|