WinterCMS research

This commit is contained in:
Jakub Zych
2026-02-18 01:31:41 +01:00
parent bec00a8bd5
commit 29766aee93
40 changed files with 8529 additions and 0 deletions

View 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>