Skip to content

Commit 778f18e

Browse files
authored
Merge pull request #8285 from neznaika0/lang-find-show-bad-keys
feat: [Commands] `lang:find` show bad keys when scanning (v2)
2 parents a922030 + 9747d12 commit 778f18e

File tree

6 files changed

+236
-9
lines changed

6 files changed

+236
-9
lines changed

system/Commands/Translation/LocalizationFinder.php

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public function run(array $params)
8282
if (is_string($optionDir)) {
8383
$tempCurrentDir = realpath($currentDir . $optionDir);
8484

85-
if (false === $tempCurrentDir) {
85+
if ($tempCurrentDir === false) {
8686
CLI::error('Error: Directory must be located in "' . $currentDir . '"');
8787

8888
return EXIT_USER_INPUT;
@@ -113,7 +113,12 @@ private function process(string $currentDir, string $currentLocale): void
113113
$files = iterator_to_array($iterator, true);
114114
ksort($files);
115115

116-
[$foundLanguageKeys, $countFiles] = $this->findLanguageKeysInFiles($files);
116+
[
117+
'foundLanguageKeys' => $foundLanguageKeys,
118+
'badLanguageKeys' => $badLanguageKeys,
119+
'countFiles' => $countFiles
120+
] = $this->findLanguageKeysInFiles($files);
121+
117122
ksort($foundLanguageKeys);
118123

119124
$languageDiff = [];
@@ -137,7 +142,7 @@ private function process(string $currentDir, string $currentLocale): void
137142
$newLanguageKeys = array_replace_recursive($foundLanguageKeys[$langFileName], $languageStoredKeys);
138143

139144
if ($languageDiff !== []) {
140-
if (false === file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys))) {
145+
if (file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys)) === false) {
141146
$this->writeIsVerbose('Lang file ' . $langFileName . ' (error write).', 'red');
142147
} else {
143148
$this->writeIsVerbose('Lang file "' . $langFileName . '" successful updated!', 'green');
@@ -157,14 +162,30 @@ private function process(string $currentDir, string $currentLocale): void
157162

158163
$this->writeIsVerbose('Files found: ' . $countFiles);
159164
$this->writeIsVerbose('New translates found: ' . $countNewKeys);
165+
$this->writeIsVerbose('Bad translates found: ' . count($badLanguageKeys));
166+
167+
if ($this->verbose && $badLanguageKeys !== []) {
168+
$tableBadRows = [];
169+
170+
foreach ($badLanguageKeys as $value) {
171+
$tableBadRows[] = [$value[1], $value[0]];
172+
}
173+
174+
ArrayHelper::sortValuesByNatural($tableBadRows, 0);
175+
176+
CLI::table($tableBadRows, ['Bad Key', 'Filepath']);
177+
}
160178
}
161179

162180
/**
163181
* @param SplFileInfo|string $file
182+
*
183+
* @return array<string, array>
164184
*/
165185
private function findTranslationsInFile($file): array
166186
{
167187
$foundLanguageKeys = [];
188+
$badLanguageKeys = [];
168189

169190
if (is_string($file) && is_file($file)) {
170191
$file = new SplFileInfo($file);
@@ -174,14 +195,16 @@ private function findTranslationsInFile($file): array
174195
preg_match_all('/lang\(\'([._a-z0-9\-]+)\'\)/ui', $fileContent, $matches);
175196

176197
if ($matches[1] === []) {
177-
return [];
198+
return compact('foundLanguageKeys', 'badLanguageKeys');
178199
}
179200

180201
foreach ($matches[1] as $phraseKey) {
181202
$phraseKeys = explode('.', $phraseKey);
182203

183204
// Language key not have Filename or Lang key
184205
if (count($phraseKeys) < 2) {
206+
$badLanguageKeys[] = [mb_substr($file->getRealPath(), mb_strlen(ROOTPATH)), $phraseKey];
207+
185208
continue;
186209
}
187210

@@ -191,6 +214,8 @@ private function findTranslationsInFile($file): array
191214
|| ($languageFileName === '' && $phraseKeys[0] === '');
192215

193216
if ($isEmptyNestedArray) {
217+
$badLanguageKeys[] = [mb_substr($file->getRealPath(), mb_strlen(ROOTPATH)), $phraseKey];
218+
194219
continue;
195220
}
196221

@@ -203,7 +228,7 @@ private function findTranslationsInFile($file): array
203228
}
204229
}
205230

206-
return $foundLanguageKeys;
231+
return compact('foundLanguageKeys', 'badLanguageKeys');
207232
}
208233

209234
private function isIgnoredFile(SplFileInfo $file): bool
@@ -336,12 +361,13 @@ private function isSubDirectory(string $directory, string $rootDirectory): bool
336361
/**
337362
* @param SplFileInfo[] $files
338363
*
339-
* @return array<int, array|int>
340-
* @phpstan-return list{0: array<string, array<string, string>>, 1: int}
364+
* @return array<string, array|int>
365+
* @phpstan-return array{'foundLanguageKeys': array<string, array<string, string>>, 'badLanguageKeys': array<int, array<int, string>>, 'countFiles': int}
341366
*/
342367
private function findLanguageKeysInFiles(array $files): array
343368
{
344369
$foundLanguageKeys = [];
370+
$badLanguageKeys = [];
345371
$countFiles = 0;
346372

347373
foreach ($files as $file) {
@@ -351,9 +377,13 @@ private function findLanguageKeysInFiles(array $files): array
351377

352378
$this->writeIsVerbose('File found: ' . mb_substr($file->getRealPath(), mb_strlen(APPPATH)));
353379
$countFiles++;
354-
$foundLanguageKeys = array_replace_recursive($this->findTranslationsInFile($file), $foundLanguageKeys);
380+
381+
$findInFile = $this->findTranslationsInFile($file);
382+
383+
$foundLanguageKeys = array_replace_recursive($findInFile['foundLanguageKeys'], $foundLanguageKeys);
384+
$badLanguageKeys = array_merge($findInFile['badLanguageKeys'], $badLanguageKeys);
355385
}
356386

357-
return [$foundLanguageKeys, $countFiles];
387+
return compact('foundLanguageKeys', 'badLanguageKeys', 'countFiles');
358388
}
359389
}

system/Helpers/Array/ArrayHelper.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
* If there are any methods that should be provided, make them
2222
* public APIs via helper functions.
2323
*
24+
* @see \CodeIgniter\Helpers\Array\ArrayHelperDotKeyExistsTest
2425
* @see \CodeIgniter\Helpers\Array\ArrayHelperRecursiveDiffTest
26+
* @see \CodeIgniter\Helpers\Array\ArrayHelperSortValuesByNaturalTest
2527
*/
2628
final class ArrayHelper
2729
{
@@ -299,4 +301,21 @@ public static function recursiveCount(array $array, int $counter = 0): int
299301

300302
return $counter;
301303
}
304+
305+
/**
306+
* Sorts array values in natural order
307+
* If the value is an array, you need to specify the $sortByIndex of the key to sort
308+
*
309+
* @param int|string|null $sortByIndex
310+
*/
311+
public static function sortValuesByNatural(array &$array, $sortByIndex = null): bool
312+
{
313+
return usort($array, static function ($currentValue, $nextValue) use ($sortByIndex) {
314+
if ($sortByIndex !== null) {
315+
return strnatcmp($currentValue[$sortByIndex], $nextValue[$sortByIndex]);
316+
}
317+
318+
return strnatcmp($currentValue, $nextValue);
319+
});
320+
}
302321
}

tests/_support/Services/Translation/TranslationTwo.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ public function list()
2828
$translationError6 = lang('TranslationTwo...');
2929
$translationError7 = lang('..invalid_nested_key..');
3030

31+
$copyTranslationError1 = lang('TranslationTwo');
32+
$copyTranslationError2 = lang(' ');
33+
$copyTranslationError3 = lang('');
34+
$copyTranslationError4 = lang('.invalid_key');
35+
$copyTranslationError5 = lang('TranslationTwo.');
36+
$copyTranslationError6 = lang('TranslationTwo...');
37+
$copyTranslationError7 = lang('..invalid_nested_key..');
3138
// Empty in comments lang('') lang(' ')
3239
}
3340
}

tests/system/Commands/Translation/LocalizationFinderTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ public function testShowNewTranslation(): void
105105
$this->assertStringContainsString($this->getActualTableWithNewKeys(), $this->getStreamFilterBuffer());
106106
}
107107

108+
public function testShowBadTranslation(): void
109+
{
110+
$this->makeLocaleDirectory();
111+
112+
command('lang:find --dir Translation --verbose');
113+
114+
$this->assertStringContainsString($this->getActualTableWithBadKeys(), $this->getStreamFilterBuffer());
115+
}
116+
108117
private function getActualTranslationOneKeys(): array
109118
{
110119
return [
@@ -195,6 +204,26 @@ private function getActualTableWithNewKeys(): string
195204
TEXT_WRAP;
196205
}
197206

207+
private function getActualTableWithBadKeys(): string
208+
{
209+
return <<<'TEXT_WRAP'
210+
+------------------------+--------------------------------------------------------+
211+
| Bad Key | Filepath |
212+
+------------------------+--------------------------------------------------------+
213+
| ..invalid_nested_key.. | tests/_support/Services/Translation/TranslationTwo.php |
214+
| ..invalid_nested_key.. | tests/_support/Services/Translation/TranslationTwo.php |
215+
| .invalid_key | tests/_support/Services/Translation/TranslationTwo.php |
216+
| .invalid_key | tests/_support/Services/Translation/TranslationTwo.php |
217+
| TranslationTwo | tests/_support/Services/Translation/TranslationTwo.php |
218+
| TranslationTwo | tests/_support/Services/Translation/TranslationTwo.php |
219+
| TranslationTwo. | tests/_support/Services/Translation/TranslationTwo.php |
220+
| TranslationTwo. | tests/_support/Services/Translation/TranslationTwo.php |
221+
| TranslationTwo... | tests/_support/Services/Translation/TranslationTwo.php |
222+
| TranslationTwo... | tests/_support/Services/Translation/TranslationTwo.php |
223+
+------------------------+--------------------------------------------------------+
224+
TEXT_WRAP;
225+
}
226+
198227
private function assertTranslationsExistAndHaveTranslatedKeys(): void
199228
{
200229
$this->assertFileExists(self::$languageTestPath . self::$locale . '/TranslationOne.php');
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
/**
4+
* This file is part of CodeIgniter 4 framework.
5+
*
6+
* (c) CodeIgniter Foundation <admin@codeigniter.com>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
namespace CodeIgniter\Helpers\Array;
13+
14+
use CodeIgniter\Test\CIUnitTestCase;
15+
16+
/**
17+
* @group Others
18+
*
19+
* @internal
20+
*/
21+
final class ArrayHelperSortValuesByNaturalTest extends CIUnitTestCase
22+
{
23+
private array $arrayWithStringValues = [
24+
'apple10',
25+
'banana',
26+
'apple1',
27+
'банан',
28+
'Banana',
29+
'Apple',
30+
100000,
31+
'яблоко',
32+
1200,
33+
13000,
34+
'Банан',
35+
'Яблоко',
36+
'apple',
37+
];
38+
private array $arrayWithArrayValues = [
39+
['apple', 'Banana'],
40+
['apple10', 'Apple'],
41+
['Яблоко', 1200],
42+
[13000, 'Банан'],
43+
['Apple', 'apple1'],
44+
['banana', 'банан'],
45+
[100000, 13000],
46+
['Banana', 'Яблоко'],
47+
['Банан', 'banana'],
48+
[1200, 'apple'],
49+
['apple1', 'apple10'],
50+
['яблоко', 100000],
51+
['банан', 'яблоко'],
52+
];
53+
54+
public function testSortWithStringValues(): void
55+
{
56+
shuffle($this->arrayWithStringValues);
57+
58+
ArrayHelper::sortValuesByNatural($this->arrayWithStringValues);
59+
60+
$this->assertSame([
61+
1200,
62+
13000,
63+
100000,
64+
'Apple',
65+
'Banana',
66+
'apple',
67+
'apple1',
68+
'apple10',
69+
'banana',
70+
'Банан',
71+
'Яблоко',
72+
'банан',
73+
'яблоко',
74+
], $this->arrayWithStringValues);
75+
}
76+
77+
public function testSortWithArrayValues(): void
78+
{
79+
shuffle($this->arrayWithArrayValues);
80+
81+
// For first index
82+
ArrayHelper::sortValuesByNatural($this->arrayWithArrayValues, 0);
83+
84+
$this->assertSame([
85+
[1200, 'apple'],
86+
[13000, 'Банан'],
87+
[100000, 13000],
88+
['Apple', 'apple1'],
89+
['Banana', 'Яблоко'],
90+
['apple', 'Banana'],
91+
['apple1', 'apple10'],
92+
['apple10', 'Apple'],
93+
['banana', 'банан'],
94+
['Банан', 'banana'],
95+
['Яблоко', 1200],
96+
['банан', 'яблоко'],
97+
['яблоко', 100000],
98+
], $this->arrayWithArrayValues);
99+
100+
shuffle($this->arrayWithArrayValues);
101+
102+
// For other index
103+
ArrayHelper::sortValuesByNatural($this->arrayWithArrayValues, 1);
104+
105+
$this->assertSame([
106+
['Яблоко', 1200],
107+
[100000, 13000],
108+
['яблоко', 100000],
109+
['apple10', 'Apple'],
110+
['apple', 'Banana'],
111+
[1200, 'apple'],
112+
['Apple', 'apple1'],
113+
['apple1', 'apple10'],
114+
['Банан', 'banana'],
115+
[13000, 'Банан'],
116+
['Banana', 'Яблоко'],
117+
['banana', 'банан'],
118+
['банан', 'яблоко'],
119+
], $this->arrayWithArrayValues);
120+
}
121+
}

user_guide_src/source/outgoing/localization.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,27 @@ Before updating, it is possible to preview the translations found by the command
284284
285285
php spark lang:find --verbose --show-new
286286
287+
The detailed output of ``--verbose`` also shows a list of invalid keys. For example:
288+
289+
.. code-block:: console
290+
291+
...
292+
293+
Files found: 10
294+
New translates found: 30
295+
Bad translates found: 5
296+
+------------------------+---------------------------------+
297+
| Bad Key | Filepath |
298+
+------------------------+---------------------------------+
299+
| ..invalid_nested_key.. | app/Controllers/Translation.php |
300+
| .invalid_key | app/Controllers/Translation.php |
301+
| TranslationBad | app/Controllers/Translation.php |
302+
| TranslationBad. | app/Controllers/Translation.php |
303+
| TranslationBad... | app/Controllers/Translation.php |
304+
+------------------------+---------------------------------+
305+
306+
All operations done!
307+
287308
For a more accurate search, specify the desired locale or directory to scan.
288309

289310
.. code-block:: console

0 commit comments

Comments
 (0)