From 33357b6a0eddbb5df2bcca35e997c7b2d2e98703 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sat, 4 Nov 2023 11:45:50 +0300 Subject: [PATCH 1/8] feat: Show bad lang keys when scanning --- .../Translation/LocalizationFinder.php | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index d8fbbf1aad50..acd595d4bab7 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -111,7 +111,7 @@ private function process(string $currentDir, string $currentLocale): void $files = iterator_to_array($iterator, true); ksort($files); - [$foundLanguageKeys, $countFiles] = $this->findLanguageKeysInFiles($files); + [$foundLanguageKeys, $badLanguageKeys, $countFiles] = $this->findLanguageKeysInFiles($files); ksort($foundLanguageKeys); $languageDiff = []; @@ -155,14 +155,28 @@ private function process(string $currentDir, string $currentLocale): void $this->writeIsVerbose('Files found: ' . $countFiles); $this->writeIsVerbose('New translates found: ' . $countNewKeys); + $this->writeIsVerbose('Bad translates found: ' . count($badLanguageKeys)); + + if ($this->verbose && $badLanguageKeys !== []) { + $tableBadRows = []; + + foreach ($badLanguageKeys as $key => $value) { + $tableBadRows[] = [$key, $value]; + } + + CLI::table($tableBadRows, ['№', 'Bad key']); + } } /** * @param SplFileInfo|string $file + * + * @return array */ private function findTranslationsInFile($file): array { $foundLanguageKeys = []; + $badLanguageKeys = []; if (is_string($file) && is_file($file)) { $file = new SplFileInfo($file); @@ -172,7 +186,7 @@ private function findTranslationsInFile($file): array preg_match_all('/lang\(\'([._a-z0-9\-]+)\'\)/ui', $fileContent, $matches); if ($matches[1] === []) { - return []; + return [[], []]; } foreach ($matches[1] as $phraseKey) { @@ -180,6 +194,8 @@ private function findTranslationsInFile($file): array // Language key not have Filename or Lang key if (count($phraseKeys) < 2) { + $badLanguageKeys[] = $phraseKey; + continue; } @@ -189,6 +205,8 @@ private function findTranslationsInFile($file): array || ($languageFileName === '' && $phraseKeys[0] === ''); if ($isEmptyNestedArray) { + $badLanguageKeys[] = $phraseKey; + continue; } @@ -201,7 +219,7 @@ private function findTranslationsInFile($file): array } } - return $foundLanguageKeys; + return [$foundLanguageKeys, $badLanguageKeys]; } private function isIgnoredFile(SplFileInfo $file): bool @@ -335,11 +353,11 @@ private function isSubDirectory(string $directory, string $rootDirectory): bool * @param SplFileInfo[] $files * * @return array - * @phpstan-return list{0: array>, 1: int} + * @phpstan-return list{0: array>, 1: array>, 2: int} */ private function findLanguageKeysInFiles(array $files): array { - $foundLanguageKeys = []; + $foundLanguageKeys = [[], []]; $countFiles = 0; foreach ($files as $file) { @@ -349,9 +367,13 @@ private function findLanguageKeysInFiles(array $files): array $this->writeIsVerbose('File found: ' . mb_substr($file->getRealPath(), mb_strlen(APPPATH))); $countFiles++; - $foundLanguageKeys = array_replace_recursive($this->findTranslationsInFile($file), $foundLanguageKeys); + + $findInFile = $this->findTranslationsInFile($file); + + $foundLanguageKeys[0] = array_replace_recursive($findInFile[0], $foundLanguageKeys[0]); + $foundLanguageKeys[1] = array_merge($findInFile[1], $foundLanguageKeys[1]); } - return [$foundLanguageKeys, $countFiles]; + return [$foundLanguageKeys[0], array_unique($foundLanguageKeys[1]), $countFiles]; } } From 6610e321393b8a2a89b58933a4d70b303928ff63 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sat, 4 Nov 2023 11:46:41 +0300 Subject: [PATCH 2/8] test: Test bad lang keys when scanning --- .../Translation/LocalizationFinderTest.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/system/Commands/Translation/LocalizationFinderTest.php b/tests/system/Commands/Translation/LocalizationFinderTest.php index f5cc38ec7b2f..28555b0cd1fd 100644 --- a/tests/system/Commands/Translation/LocalizationFinderTest.php +++ b/tests/system/Commands/Translation/LocalizationFinderTest.php @@ -103,6 +103,15 @@ public function testShowNewTranslation(): void $this->assertStringContainsString($this->getActualTableWithNewKeys(), $this->getStreamFilterBuffer()); } + public function testShowBadTranslation(): void + { + $this->makeLocaleDirectory(); + + command('lang:find --dir Translation --verbose'); + + $this->assertStringContainsString($this->getActualTableWithBadKeys(), $this->getStreamFilterBuffer()); + } + private function getActualTranslationOneKeys(): array { return [ @@ -193,6 +202,21 @@ private function getActualTableWithNewKeys(): string TEXT_WRAP; } + private function getActualTableWithBadKeys(): string + { + return <<<'TEXT_WRAP' + +---+------------------------+ + | № | Bad key | + +---+------------------------+ + | 0 | TranslationTwo | + | 1 | .invalid_key | + | 2 | TranslationTwo. | + | 3 | TranslationTwo... | + | 4 | ..invalid_nested_key.. | + +---+------------------------+ + TEXT_WRAP; + } + private function assertTranslationsExistAndHaveTranslatedKeys(): void { $this->assertFileExists(self::$languageTestPath . self::$locale . '/TranslationOne.php'); From 25a157325a4c0cc6459837b78f6b6a5b34b6f0f0 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sun, 3 Dec 2023 13:17:49 +0300 Subject: [PATCH 3/8] fix: Show the keys and files where they are used --- .../Translation/LocalizationFinder.php | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index acd595d4bab7..7abc3209c07d 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -80,7 +80,7 @@ public function run(array $params) if (is_string($optionDir)) { $tempCurrentDir = realpath($currentDir . $optionDir); - if (false === $tempCurrentDir) { + if ($tempCurrentDir === false) { CLI::error('Error: Directory must be located in "' . $currentDir . '"'); return EXIT_USER_INPUT; @@ -111,7 +111,12 @@ private function process(string $currentDir, string $currentLocale): void $files = iterator_to_array($iterator, true); ksort($files); - [$foundLanguageKeys, $badLanguageKeys, $countFiles] = $this->findLanguageKeysInFiles($files); + [ + 'foundLanguageKeys' => $foundLanguageKeys, + 'badLanguageKeys' => $badLanguageKeys, + 'countFiles' => $countFiles + ] = $this->findLanguageKeysInFiles($files); + ksort($foundLanguageKeys); $languageDiff = []; @@ -135,7 +140,7 @@ private function process(string $currentDir, string $currentLocale): void $newLanguageKeys = array_replace_recursive($foundLanguageKeys[$langFileName], $languageStoredKeys); if ($languageDiff !== []) { - if (false === file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys))) { + if (file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys)) === false) { $this->writeIsVerbose('Lang file ' . $langFileName . ' (error write).', 'red'); } else { $this->writeIsVerbose('Lang file "' . $langFileName . '" successful updated!', 'green'); @@ -160,18 +165,20 @@ private function process(string $currentDir, string $currentLocale): void if ($this->verbose && $badLanguageKeys !== []) { $tableBadRows = []; - foreach ($badLanguageKeys as $key => $value) { - $tableBadRows[] = [$key, $value]; + foreach ($badLanguageKeys as $value) { + $tableBadRows[] = [$value[1], $value[0]]; } - CLI::table($tableBadRows, ['№', 'Bad key']); + ArrayHelper::sortValuesByNatural($tableBadRows, 0); + + CLI::table($tableBadRows, ['Bad Key', 'Filepath']); } } /** * @param SplFileInfo|string $file * - * @return array + * @return array */ private function findTranslationsInFile($file): array { @@ -186,7 +193,7 @@ private function findTranslationsInFile($file): array preg_match_all('/lang\(\'([._a-z0-9\-]+)\'\)/ui', $fileContent, $matches); if ($matches[1] === []) { - return [[], []]; + return compact('foundLanguageKeys', 'badLanguageKeys'); } foreach ($matches[1] as $phraseKey) { @@ -194,7 +201,7 @@ private function findTranslationsInFile($file): array // Language key not have Filename or Lang key if (count($phraseKeys) < 2) { - $badLanguageKeys[] = $phraseKey; + $badLanguageKeys[] = [mb_substr($file->getRealPath(), mb_strlen(ROOTPATH)), $phraseKey]; continue; } @@ -205,7 +212,7 @@ private function findTranslationsInFile($file): array || ($languageFileName === '' && $phraseKeys[0] === ''); if ($isEmptyNestedArray) { - $badLanguageKeys[] = $phraseKey; + $badLanguageKeys[] = [mb_substr($file->getRealPath(), mb_strlen(ROOTPATH)), $phraseKey]; continue; } @@ -219,7 +226,7 @@ private function findTranslationsInFile($file): array } } - return [$foundLanguageKeys, $badLanguageKeys]; + return compact('foundLanguageKeys', 'badLanguageKeys'); } private function isIgnoredFile(SplFileInfo $file): bool @@ -352,12 +359,13 @@ private function isSubDirectory(string $directory, string $rootDirectory): bool /** * @param SplFileInfo[] $files * - * @return array - * @phpstan-return list{0: array>, 1: array>, 2: int} + * @return array + * @phpstan-return array{'foundLanguageKeys': array>, 'badLanguageKeys': array>, 'countFiles': int} */ private function findLanguageKeysInFiles(array $files): array { - $foundLanguageKeys = [[], []]; + $foundLanguageKeys = []; + $badLanguageKeys = []; $countFiles = 0; foreach ($files as $file) { @@ -370,10 +378,10 @@ private function findLanguageKeysInFiles(array $files): array $findInFile = $this->findTranslationsInFile($file); - $foundLanguageKeys[0] = array_replace_recursive($findInFile[0], $foundLanguageKeys[0]); - $foundLanguageKeys[1] = array_merge($findInFile[1], $foundLanguageKeys[1]); + $foundLanguageKeys = array_replace_recursive($findInFile['foundLanguageKeys'], $foundLanguageKeys); + $badLanguageKeys = array_merge($findInFile['badLanguageKeys'], $badLanguageKeys); } - return [$foundLanguageKeys[0], array_unique($foundLanguageKeys[1]), $countFiles]; + return compact('foundLanguageKeys', 'badLanguageKeys', 'countFiles'); } } From a166470040abe6581c197cd21ff6e3da44a27231 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sun, 3 Dec 2023 13:19:18 +0300 Subject: [PATCH 4/8] feat: Sort array values by natural order --- system/Helpers/Array/ArrayHelper.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index 5b359a761384..04f4bd335623 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -19,7 +19,9 @@ * If there are any methods that should be provided, make them * public APIs via helper functions. * + * @see \CodeIgniter\Helpers\Array\ArrayHelperDotKeyExistsTest * @see \CodeIgniter\Helpers\Array\ArrayHelperRecursiveDiffTest + * @see \CodeIgniter\Helpers\Array\ArrayHelperSortValuesByNaturalTest */ final class ArrayHelper { @@ -297,4 +299,21 @@ public static function recursiveCount(array $array, int $counter = 0): int return $counter; } + + /** + * Sorts array values in natural order + * If the value is an array, you need to specify the $sortByIndex of the key to sort + * + * @param int|string|null $sortByIndex + */ + public static function sortValuesByNatural(array &$array, $sortByIndex = null): bool + { + return usort($array, static function ($currentValue, $nextValue) use ($sortByIndex) { + if ($sortByIndex !== null) { + return strnatcmp($currentValue[$sortByIndex], $nextValue[$sortByIndex]); + } + + return strnatcmp($currentValue, $nextValue); + }); + } } From e23c44cf0ae45882c6120c1c64d1ca17ca8a8c7d Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sun, 3 Dec 2023 13:20:15 +0300 Subject: [PATCH 5/8] test: Add duplicate data for example --- tests/_support/Services/Translation/TranslationTwo.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/_support/Services/Translation/TranslationTwo.php b/tests/_support/Services/Translation/TranslationTwo.php index 71e5e663b27e..aa6d1e0a9ee2 100644 --- a/tests/_support/Services/Translation/TranslationTwo.php +++ b/tests/_support/Services/Translation/TranslationTwo.php @@ -26,6 +26,13 @@ public function list() $translationError6 = lang('TranslationTwo...'); $translationError7 = lang('..invalid_nested_key..'); + $copyTranslationError1 = lang('TranslationTwo'); + $copyTranslationError2 = lang(' '); + $copyTranslationError3 = lang(''); + $copyTranslationError4 = lang('.invalid_key'); + $copyTranslationError5 = lang('TranslationTwo.'); + $copyTranslationError6 = lang('TranslationTwo...'); + $copyTranslationError7 = lang('..invalid_nested_key..'); // Empty in comments lang('') lang(' ') } } From fe3b43047fedae9e7c04db9329295d80822c5d67 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sun, 3 Dec 2023 13:21:37 +0300 Subject: [PATCH 6/8] test: Fix test for bad language keys --- .../Translation/LocalizationFinderTest.php | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tests/system/Commands/Translation/LocalizationFinderTest.php b/tests/system/Commands/Translation/LocalizationFinderTest.php index 28555b0cd1fd..fa976c752c24 100644 --- a/tests/system/Commands/Translation/LocalizationFinderTest.php +++ b/tests/system/Commands/Translation/LocalizationFinderTest.php @@ -205,15 +205,20 @@ private function getActualTableWithNewKeys(): string private function getActualTableWithBadKeys(): string { return <<<'TEXT_WRAP' - +---+------------------------+ - | № | Bad key | - +---+------------------------+ - | 0 | TranslationTwo | - | 1 | .invalid_key | - | 2 | TranslationTwo. | - | 3 | TranslationTwo... | - | 4 | ..invalid_nested_key.. | - +---+------------------------+ + +------------------------+--------------------------------------------------------+ + | Bad Key | Filepath | + +------------------------+--------------------------------------------------------+ + | ..invalid_nested_key.. | tests/_support/Services/Translation/TranslationTwo.php | + | ..invalid_nested_key.. | tests/_support/Services/Translation/TranslationTwo.php | + | .invalid_key | tests/_support/Services/Translation/TranslationTwo.php | + | .invalid_key | tests/_support/Services/Translation/TranslationTwo.php | + | TranslationTwo | tests/_support/Services/Translation/TranslationTwo.php | + | TranslationTwo | tests/_support/Services/Translation/TranslationTwo.php | + | TranslationTwo. | tests/_support/Services/Translation/TranslationTwo.php | + | TranslationTwo. | tests/_support/Services/Translation/TranslationTwo.php | + | TranslationTwo... | tests/_support/Services/Translation/TranslationTwo.php | + | TranslationTwo... | tests/_support/Services/Translation/TranslationTwo.php | + +------------------------+--------------------------------------------------------+ TEXT_WRAP; } From 66d4f0e91077624be17d29750adf7c4e846c14f2 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sun, 3 Dec 2023 13:22:42 +0300 Subject: [PATCH 7/8] test: Add for ArrayHelper::sortValuesByNatural --- .../ArrayHelperSortValuesByNaturalTest.php | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 tests/system/Helpers/Array/ArrayHelperSortValuesByNaturalTest.php diff --git a/tests/system/Helpers/Array/ArrayHelperSortValuesByNaturalTest.php b/tests/system/Helpers/Array/ArrayHelperSortValuesByNaturalTest.php new file mode 100644 index 000000000000..312eb0853c9d --- /dev/null +++ b/tests/system/Helpers/Array/ArrayHelperSortValuesByNaturalTest.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Helpers\Array; + +use CodeIgniter\Test\CIUnitTestCase; + +/** + * @group Others + * + * @internal + */ +final class ArrayHelperSortValuesByNaturalTest extends CIUnitTestCase +{ + private array $arrayWithStringValues = [ + 'apple10', + 'banana', + 'apple1', + 'банан', + 'Banana', + 'Apple', + 100000, + 'яблоко', + 1200, + 13000, + 'Банан', + 'Яблоко', + 'apple', + ]; + private array $arrayWithArrayValues = [ + ['apple', 'Banana'], + ['apple10', 'Apple'], + ['Яблоко', 1200], + [13000, 'Банан'], + ['Apple', 'apple1'], + ['banana', 'банан'], + [100000, 13000], + ['Banana', 'Яблоко'], + ['Банан', 'banana'], + [1200, 'apple'], + ['apple1', 'apple10'], + ['яблоко', 100000], + ['банан', 'яблоко'], + ]; + + public function testSortWithStringValues(): void + { + shuffle($this->arrayWithStringValues); + + ArrayHelper::sortValuesByNatural($this->arrayWithStringValues); + + $this->assertSame([ + 1200, + 13000, + 100000, + 'Apple', + 'Banana', + 'apple', + 'apple1', + 'apple10', + 'banana', + 'Банан', + 'Яблоко', + 'банан', + 'яблоко', + ], $this->arrayWithStringValues); + } + + public function testSortWithArrayValues(): void + { + shuffle($this->arrayWithArrayValues); + + // For first index + ArrayHelper::sortValuesByNatural($this->arrayWithArrayValues, 0); + + $this->assertSame([ + [1200, 'apple'], + [13000, 'Банан'], + [100000, 13000], + ['Apple', 'apple1'], + ['Banana', 'Яблоко'], + ['apple', 'Banana'], + ['apple1', 'apple10'], + ['apple10', 'Apple'], + ['banana', 'банан'], + ['Банан', 'banana'], + ['Яблоко', 1200], + ['банан', 'яблоко'], + ['яблоко', 100000], + ], $this->arrayWithArrayValues); + + shuffle($this->arrayWithArrayValues); + + // For other index + ArrayHelper::sortValuesByNatural($this->arrayWithArrayValues, 1); + + $this->assertSame([ + ['Яблоко', 1200], + [100000, 13000], + ['яблоко', 100000], + ['apple10', 'Apple'], + ['apple', 'Banana'], + [1200, 'apple'], + ['Apple', 'apple1'], + ['apple1', 'apple10'], + ['Банан', 'banana'], + [13000, 'Банан'], + ['Banana', 'Яблоко'], + ['banana', 'банан'], + ['банан', 'яблоко'], + ], $this->arrayWithArrayValues); + } +} From 9747d1293427b1b2c27b1b38ca25710cbad550ad Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sun, 3 Dec 2023 13:23:24 +0300 Subject: [PATCH 8/8] docs: Add example output bad lang keys --- .../source/outgoing/localization.rst | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/user_guide_src/source/outgoing/localization.rst b/user_guide_src/source/outgoing/localization.rst index 837807989b0d..0aa680371255 100644 --- a/user_guide_src/source/outgoing/localization.rst +++ b/user_guide_src/source/outgoing/localization.rst @@ -284,6 +284,27 @@ Before updating, it is possible to preview the translations found by the command php spark lang:find --verbose --show-new +The detailed output of ``--verbose`` also shows a list of invalid keys. For example: + +.. code-block:: console + + ... + + Files found: 10 + New translates found: 30 + Bad translates found: 5 + +------------------------+---------------------------------+ + | Bad Key | Filepath | + +------------------------+---------------------------------+ + | ..invalid_nested_key.. | app/Controllers/Translation.php | + | .invalid_key | app/Controllers/Translation.php | + | TranslationBad | app/Controllers/Translation.php | + | TranslationBad. | app/Controllers/Translation.php | + | TranslationBad... | app/Controllers/Translation.php | + +------------------------+---------------------------------+ + + All operations done! + For a more accurate search, specify the desired locale or directory to scan. .. code-block:: console