Skip to content

Commit 1b94e6c

Browse files
committed
refactor: migrate lang:* commands as modern commands
1 parent 5ba38c3 commit 1b94e6c

6 files changed

Lines changed: 213 additions & 170 deletions

File tree

system/Commands/Translation/LocalizationFinder.php

Lines changed: 117 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313

1414
namespace CodeIgniter\Commands\Translation;
1515

16-
use CodeIgniter\CLI\BaseCommand;
16+
use CodeIgniter\CLI\AbstractCommand;
17+
use CodeIgniter\CLI\Attributes\Command;
1718
use CodeIgniter\CLI\CLI;
19+
use CodeIgniter\CLI\Input\Option;
1820
use CodeIgniter\Helpers\Array\ArrayHelper;
1921
use Config\App;
2022
use Locale;
@@ -23,73 +25,85 @@
2325
use SplFileInfo;
2426

2527
/**
26-
* @see \CodeIgniter\Commands\Translation\LocalizationFinderTest
28+
* Finds and saves available phrases to translate.
2729
*/
28-
class LocalizationFinder extends BaseCommand
30+
#[Command(
31+
name: 'lang:find',
32+
description: 'Find and save available phrases to translate.',
33+
group: 'Translation',
34+
)]
35+
class LocalizationFinder extends AbstractCommand
2936
{
30-
protected $group = 'Translation';
31-
protected $name = 'lang:find';
32-
protected $description = 'Find and save available phrases to translate.';
33-
protected $usage = 'lang:find [options]';
34-
protected $arguments = [];
35-
protected $options = [
36-
'--locale' => 'Specify locale (en, ru, etc.) to save files.',
37-
'--dir' => 'Directory to search for translations relative to APPPATH.',
38-
'--show-new' => 'Show only new translations in table. Does not write to files.',
39-
'--verbose' => 'Output detailed information.',
40-
];
37+
private string $languagePath;
4138

42-
/**
43-
* Flag for output detailed information
44-
*/
45-
private bool $verbose = false;
39+
protected function configure(): void
40+
{
41+
$this
42+
->addOption(new Option(
43+
name: 'locale',
44+
description: 'Specify locale (en, ru, etc.) to save files.',
45+
requiresValue: true,
46+
default: '',
47+
))
48+
->addOption(new Option(
49+
name: 'dir',
50+
description: 'Directory to search for translations relative to APPPATH.',
51+
requiresValue: true,
52+
default: '',
53+
))
54+
->addOption(new Option(
55+
name: 'show-new',
56+
description: 'Show only new translations in table. Does not write to files.',
57+
))
58+
->addOption(new Option(
59+
name: 'verbose',
60+
description: 'Output detailed information.',
61+
));
62+
}
4663

47-
/**
48-
* Flag for showing only translations, without saving
49-
*/
50-
private bool $showNew = false;
64+
protected function execute(array $arguments, array $options): int
65+
{
66+
$locale = $options['locale'];
67+
assert(is_string($locale));
5168

52-
private string $languagePath;
69+
$dir = $options['dir'];
70+
assert(is_string($dir));
5371

54-
public function run(array $params)
55-
{
56-
$this->verbose = array_key_exists('verbose', $params);
57-
$this->showNew = array_key_exists('show-new', $params);
58-
$optionLocale = $params['locale'] ?? null;
59-
$optionDir = $params['dir'] ?? null;
60-
$currentLocale = Locale::getDefault();
61-
$currentDir = APPPATH;
62-
$this->languagePath = $currentDir . 'Language';
72+
$currentLocale = Locale::getDefault();
6373

64-
if (service('environment')->isTesting()) {
65-
$currentDir = SUPPORTPATH . 'Services' . DIRECTORY_SEPARATOR;
66-
$this->languagePath = SUPPORTPATH . 'Language';
67-
}
74+
['currentDir' => $currentDir, 'languagePath' => $this->languagePath] = $this->resolvePaths();
75+
76+
if ($locale !== '') {
77+
$supportedLocales = config(App::class)->supportedLocales;
6878

69-
if (is_string($optionLocale)) {
70-
if (! in_array($optionLocale, config(App::class)->supportedLocales, true)) {
79+
if (! in_array($locale, $supportedLocales, true)) {
7180
CLI::error(
72-
'Error: "' . $optionLocale . '" is not supported. Supported locales: '
73-
. implode(', ', config(App::class)->supportedLocales),
81+
sprintf(
82+
'Error: "%s" is not supported. Supported locales: %s',
83+
$locale,
84+
implode(', ', $supportedLocales),
85+
),
86+
'light_gray',
87+
'red',
7488
);
7589

7690
return EXIT_USER_INPUT;
7791
}
7892

79-
$currentLocale = $optionLocale;
93+
$currentLocale = $locale;
8094
}
8195

82-
if (is_string($optionDir)) {
83-
$tempCurrentDir = realpath($currentDir . $optionDir);
96+
if ($dir !== '') {
97+
$tempCurrentDir = realpath($currentDir . $dir);
8498

8599
if ($tempCurrentDir === false) {
86-
CLI::error('Error: Directory must be located in "' . $currentDir . '"');
100+
CLI::error(sprintf('Error: Directory must be located in "%s"', $currentDir), 'light_gray', 'red');
87101

88102
return EXIT_USER_INPUT;
89103
}
90104

91-
if ($this->isSubDirectory($tempCurrentDir, $this->languagePath)) {
92-
CLI::error('Error: Directory "' . $this->languagePath . '" restricted to scan.');
105+
if ($this->isSubdirectory($tempCurrentDir, $this->languagePath)) {
106+
CLI::error(sprintf('Error: Directory "%s" restricted to scan.', $this->languagePath), 'light_gray', 'red');
93107

94108
return EXIT_USER_INPUT;
95109
}
@@ -99,18 +113,44 @@ public function run(array $params)
99113

100114
$this->process($currentDir, $currentLocale);
101115

102-
CLI::write('All operations done!');
116+
CLI::write('All operations done!', 'green');
103117

104118
return EXIT_SUCCESS;
105119
}
106120

121+
/**
122+
* Resolves the directory to scan and the directory that holds the language
123+
* files, swapping in the test fixtures under the testing environment.
124+
*
125+
* @return array{currentDir: string, languagePath: string}
126+
*/
127+
private function resolvePaths(): array
128+
{
129+
$paths = [
130+
'currentDir' => APPPATH,
131+
'languagePath' => APPPATH . 'Language',
132+
];
133+
134+
if (service('environment')->isTesting()) {
135+
$paths = [
136+
'currentDir' => SUPPORTPATH . 'Services' . DIRECTORY_SEPARATOR,
137+
'languagePath' => SUPPORTPATH . 'Language',
138+
];
139+
}
140+
141+
return $paths;
142+
}
143+
107144
private function process(string $currentDir, string $currentLocale): void
108145
{
146+
$showNew = $this->getValidatedOption('show-new') === true;
147+
$verbose = $this->getValidatedOption('verbose') === true;
148+
109149
$tableRows = [];
110150
$countNewKeys = 0;
111151

112152
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($currentDir));
113-
$files = iterator_to_array($iterator, true);
153+
$files = iterator_to_array($iterator);
114154
ksort($files);
115155

116156
[
@@ -121,10 +161,7 @@ private function process(string $currentDir, string $currentLocale): void
121161

122162
ksort($foundLanguageKeys);
123163

124-
$languageDiff = [];
125-
$languageFoundGroups = array_unique(array_keys($foundLanguageKeys));
126-
127-
foreach ($languageFoundGroups as $langFileName) {
164+
foreach ($foundLanguageKeys as $langFileName => $foundKeys) {
128165
$languageStoredKeys = [];
129166
$languageFilePath = $this->languagePath . DIRECTORY_SEPARATOR . $currentLocale . DIRECTORY_SEPARATOR . $langFileName . '.php';
130167

@@ -133,38 +170,38 @@ private function process(string $currentDir, string $currentLocale): void
133170
$languageStoredKeys = require $languageFilePath;
134171
}
135172

136-
$languageDiff = ArrayHelper::recursiveDiff($foundLanguageKeys[$langFileName], $languageStoredKeys);
173+
$languageDiff = ArrayHelper::recursiveDiff($foundKeys, $languageStoredKeys);
137174
$countNewKeys += ArrayHelper::recursiveCount($languageDiff);
138175

139-
if ($this->showNew) {
176+
if ($showNew) {
140177
$tableRows = array_merge($this->arrayToTableRows($langFileName, $languageDiff), $tableRows);
141178
} else {
142-
$newLanguageKeys = array_replace_recursive($foundLanguageKeys[$langFileName], $languageStoredKeys);
179+
$newLanguageKeys = array_replace_recursive($foundKeys, $languageStoredKeys);
143180

144181
if ($languageDiff !== []) {
145182
if (file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys)) === false) {
146-
$this->writeIsVerbose('Lang file ' . $langFileName . ' (error write).', 'red');
183+
$this->writeIsVerbose(sprintf('Lang file %s (error write).', $langFileName), 'red');
147184
} else {
148-
$this->writeIsVerbose('Lang file "' . $langFileName . '" successful updated!', 'green');
185+
$this->writeIsVerbose(sprintf('Lang file "%s" successful updated!', $langFileName), 'green');
149186
}
150187
}
151188
}
152189
}
153190

154-
if ($this->showNew && $tableRows !== []) {
191+
if ($showNew && $tableRows !== []) {
155192
sort($tableRows);
156193
CLI::table($tableRows, ['File', 'Key']);
157194
}
158195

159-
if (! $this->showNew && $countNewKeys > 0) {
196+
if (! $showNew && $countNewKeys > 0) {
160197
CLI::write('Note: You need to run your linting tool to fix coding standards issues.', 'white', 'red');
161198
}
162199

163-
$this->writeIsVerbose('Files found: ' . $countFiles);
164-
$this->writeIsVerbose('New translates found: ' . $countNewKeys);
165-
$this->writeIsVerbose('Bad translates found: ' . count($badLanguageKeys));
200+
$this->writeIsVerbose(sprintf('Files found: %d', $countFiles));
201+
$this->writeIsVerbose(sprintf('New translates found: %d', $countNewKeys));
202+
$this->writeIsVerbose(sprintf('Bad translates found: %d', count($badLanguageKeys)));
166203

167-
if ($this->verbose && $badLanguageKeys !== []) {
204+
if ($verbose && $badLanguageKeys !== []) {
168205
$tableBadRows = [];
169206

170207
foreach ($badLanguageKeys as $value) {
@@ -178,19 +215,13 @@ private function process(string $currentDir, string $currentLocale): void
178215
}
179216

180217
/**
181-
* @param SplFileInfo|string $file
182-
*
183-
* @return array<string, array>
218+
* @return array{foundLanguageKeys: array<string, mixed>, badLanguageKeys: list<array{string, string}>}
184219
*/
185-
private function findTranslationsInFile($file): array
220+
private function findTranslationsInFile(SplFileInfo $file): array
186221
{
187222
$foundLanguageKeys = [];
188223
$badLanguageKeys = [];
189224

190-
if (is_string($file) && is_file($file)) {
191-
$file = new SplFileInfo($file);
192-
}
193-
194225
$fileContent = file_get_contents($file->getRealPath());
195226
preg_match_all('/lang\(\'([._a-z0-9\-]+)\'\)/ui', $fileContent, $matches);
196227

@@ -233,13 +264,16 @@ private function findTranslationsInFile($file): array
233264

234265
private function isIgnoredFile(SplFileInfo $file): bool
235266
{
236-
if ($file->isDir() || $this->isSubDirectory($file->getRealPath(), $this->languagePath)) {
267+
if ($file->isDir() || $this->isSubdirectory($file->getRealPath(), $this->languagePath)) {
237268
return true;
238269
}
239270

240271
return $file->getExtension() !== 'php';
241272
}
242273

274+
/**
275+
* @param array<array-key, mixed> $language
276+
*/
243277
private function templateFile(array $language = []): string
244278
{
245279
if ($language !== []) {
@@ -304,6 +338,10 @@ private function replaceArraySyntax(string $code): string
304338

305339
/**
306340
* Create multidimensional array from another keys
341+
*
342+
* @param list<string> $fromKeys
343+
*
344+
* @return array<string, mixed>
307345
*/
308346
private function buildMultiArray(array $fromKeys, string $lastArrayValue = ''): array
309347
{
@@ -323,6 +361,10 @@ private function buildMultiArray(array $fromKeys, string $lastArrayValue = ''):
323361

324362
/**
325363
* Convert multi arrays to specific CLI table rows (flat array)
364+
*
365+
* @param array<array-key, mixed> $array
366+
*
367+
* @return list<array{string, string}>
326368
*/
327369
private function arrayToTableRows(string $langFileName, array $array): array
328370
{
@@ -348,12 +390,12 @@ private function arrayToTableRows(string $langFileName, array $array): array
348390
*/
349391
private function writeIsVerbose(string $text = '', ?string $foreground = null, ?string $background = null): void
350392
{
351-
if ($this->verbose) {
393+
if ($this->getValidatedOption('verbose') === true) {
352394
CLI::write($text, $foreground, $background);
353395
}
354396
}
355397

356-
private function isSubDirectory(string $directory, string $rootDirectory): bool
398+
private function isSubdirectory(string $directory, string $rootDirectory): bool
357399
{
358400
return 0 === strncmp($directory, $rootDirectory, strlen($directory));
359401
}
@@ -374,7 +416,7 @@ private function findLanguageKeysInFiles(array $files): array
374416
continue;
375417
}
376418

377-
$this->writeIsVerbose('File found: ' . mb_substr($file->getRealPath(), mb_strlen(APPPATH)));
419+
$this->writeIsVerbose(sprintf('File found: %s', mb_substr($file->getRealPath(), mb_strlen(APPPATH))));
378420
$countFiles++;
379421

380422
$findInFile = $this->findTranslationsInFile($file);

0 commit comments

Comments
 (0)