WinterCMS research
This commit is contained in:
335
docs/research/wintercms/quotifypro-11-i18n/11-03-PLAN.md
Normal file
335
docs/research/wintercms/quotifypro-11-i18n/11-03-PLAN.md
Normal file
@@ -0,0 +1,335 @@
|
||||
---
|
||||
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>
|
||||
Reference in New Issue
Block a user