> */ private const SEEDERS = [ 'storefront' => StorefrontTranslationsSeeder::class, 'checkout' => CheckoutTranslationsSeeder::class, 'validation' => ValidationTranslationsSeeder::class, ]; public function handle(): int { $dryRun = (bool) $this->option('dry-run'); $total = 0; foreach (self::SEEDERS as $group => $seederClass) { $file = realpath((new ReflectionClass($seederClass))->getFileName()); if (! $dryRun && str_contains($file, '/vendor/')) { $this->error("{$file} is an installed copy, not your ../boboko-core checkout."); $this->line('Switch to local mode first (bin/core-mode local), or use --dry-run.'); return self::FAILURE; } $known = (new ReflectionMethod($seederClass, 'lines'))->invoke(new $seederClass); $missing = LanguageLine::query() ->where('group', $group) ->whereNotIn('key', array_keys($known)) ->orderBy('key') ->get(); if ($missing->isEmpty()) { $this->line("{$group}: nothing missing"); continue; } $entries = ''; foreach ($missing as $line) { $en = $line->text['en'] ?? ''; $el = $line->text['el'] ?? ''; if ($en === '' || $el === '') { $this->warn(" {$group}.{$line->key} has no ".($en === '' ? 'English' : 'Greek').' text — added empty, fill it in'); } $entries .= $this->entry($line->key, $en, $el); $this->info(" + {$group}.{$line->key}"); } $total += $missing->count(); if (! $dryRun && ! $this->append($file, $entries)) { return self::FAILURE; } } $this->newLine(); $this->line($dryRun ? "{$total} line(s) would be added." : "{$total} line(s) added — move them into the right section and commit core."); return self::SUCCESS; } /** * One lines() entry in the seeders' own style: single line when short, * split over several lines when long. */ private function entry(string $key, string $en, string $el): string { [$key, $en, $el] = array_map(fn (string $value) => var_export($value, true), [$key, $en, $el]); $single = " {$key} => [{$en}, {$el}],\n"; if (mb_strlen($single) <= 120) { return $single; } return " {$key} => [\n {$en},\n {$el},\n ],\n"; } /** * Inserts the entries right before the closing `];` of lines(), then * lints the file and restores the original if the result doesn't parse. */ private function append(string $file, string $entries): bool { $original = file_get_contents($file); $method = strpos($original, 'function lines(): array'); $close = $method === false ? false : strpos($original, "\n ];\n }", $method); if ($close === false) { $this->error("Couldn't find the end of lines() in {$file} — add these by hand."); return false; } $before = rtrim(substr($original, 0, $close)); // The last existing entry doesn't always have a trailing comma. if (! str_ends_with($before, ',') && ! str_ends_with($before, '[')) { $before .= ','; } $updated = $before ."\n\n // ── Pulled from the database (boboko:translations:pull) — move into the right section ──\n" .$entries .substr($original, $close + 1); file_put_contents($file, $updated); exec(PHP_BINARY.' -l '.escapeshellarg($file).' 2>&1', $output, $exitCode); if ($exitCode !== 0) { file_put_contents($file, $original); $this->error("Writing {$file} produced invalid PHP — restored the original:"); $this->line(implode("\n", $output)); return false; } return true; } }