From 32c5307f36379c809c7829333ec5d4877b30e89e Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Mon, 4 Sep 2023 22:48:52 +0300 Subject: [PATCH 01/46] feat: Add lang:find command --- .../Translation/LocalizationFinder.php | 339 ++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 system/Commands/Translation/LocalizationFinder.php diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php new file mode 100644 index 000000000000..37f6d0be7d2c --- /dev/null +++ b/system/Commands/Translation/LocalizationFinder.php @@ -0,0 +1,339 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Commands\Translation; + +use CodeIgniter\CLI\BaseCommand; +use CodeIgniter\CLI\CLI; +use Config\App; +use Locale; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use SplFileInfo; + +class LocalizationFinder extends BaseCommand +{ + protected $group = 'Translation'; + protected $name = 'lang:find'; + protected $description = 'Find and save available phrases to translate'; + protected $usage = 'lang:find [options]'; + protected $arguments = []; + protected $options = [ + '--locale' => 'Apply changes to the selected locale (en, ru, etc...).', + '--dir' => 'Directory for searching for translations (in relation to the APPPATH).', + '--show-new' => 'See only new translations as table.', + '--verbose' => 'Output detailed information.', + ]; + + /** + * Flag for output detailed information + */ + private bool $verbose = false; + + private string $languagePath; + + public function run(array $params) + { + $this->verbose = array_key_exists('verbose', $params); + $cliOptionShowNew = array_key_exists('show-new', $params); + $cliOptionLocale = ! empty($params['locale']) ? $params['locale'] : null; + $cliOptionDir = ! empty($params['dir']) ? $params['dir'] : null; + $currentLocale = Locale::getDefault(); + $currentDir = APPPATH; + $this->languagePath = $currentDir . 'Language'; + $tableRows = []; + $languageFoundKeys = []; + $countFiles = 0; + $countNewKeys = 0; + + if (ENVIRONMENT === 'testing') { + $currentDir = SUPPORTPATH; + $this->languagePath = $currentDir . 'Language/'; + } + + if (is_string($cliOptionLocale)) { + if (! in_array($cliOptionLocale, config(App::class)->supportedLocales, true)) { + CLI::error('Error: Supported locales ' . implode(', ', config(App::class)->supportedLocales)); + + return -1; + } + + $currentLocale = $cliOptionLocale; + } + + if (is_string($cliOptionDir)) { + $tempCurrentDir = realpath($currentDir . $cliOptionDir); + + if (false === $tempCurrentDir) { + CLI::error('Error: Dir must be located in ' . $currentDir); + + return -1; + } + + if (str_starts_with($tempCurrentDir, $this->languagePath)) { + CLI::error('Error: Dir ' . $this->languagePath . ' restricted to scan.'); + + return -1; + } + + $currentDir = $tempCurrentDir; + } + + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($currentDir)); + $this->writeIsVerbose('Current locale: ' . $currentLocale); + $this->writeIsVerbose('Find phrases in ' . $currentDir . ' folder...'); + + /** + * @var SplFileInfo $file + */ + foreach ($iterator as $file) { + if ($this->isIgnoredFile($file)) { + continue; + } + + $this->writeIsVerbose('File found: ' . mb_substr($file->getRealPath(), mb_strlen(APPPATH))); + $countFiles++; + $languageFoundKeys = array_replace_recursive($this->findTranslationsInFile($file), $languageFoundKeys); + } + + ksort($languageFoundKeys); + $languageDiff = []; + $languageFoundGroups = array_unique(array_keys($languageFoundKeys)); + + foreach ($languageFoundGroups as $langFileName) { + $languageStoredKeys = []; + $languageFilePath = $this->languagePath . DIRECTORY_SEPARATOR . $currentLocale . DIRECTORY_SEPARATOR . $langFileName . '.php'; + + if (is_file($languageFilePath)) { + /** + * Load old localization + */ + $languageStoredKeys = require $languageFilePath; + } + + $languageDiff = $this->arrayDiffRecursive($languageFoundKeys[$langFileName], $languageStoredKeys); + $countNewKeys += $this->arrayCountRecursive($languageDiff); + + if ($cliOptionShowNew) { + $tableRows = array_merge($this->arrayToTableRows($langFileName, $languageDiff), $tableRows); + } else { + $newLanguageKeys = array_replace_recursive($languageFoundKeys[$langFileName], $languageStoredKeys); + + /** + * New translates exists + */ + if ($languageDiff !== []) { + if (false === file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys))) { + $this->writeIsVerbose('Lang file ' . $langFileName . ' (error write).', 'red'); + } else { + /** + * FIXME: Need help command (slow run) + */ + exec('composer cs-fix --quiet ' . $this->languagePath); + $this->writeIsVerbose('Lang file "' . $langFileName . '" successful updated!', 'green'); + } + } + } + } + + if ($cliOptionShowNew && ! empty($tableRows)) { + sort($tableRows); + CLI::table($tableRows, ['File', 'Key']); + } + + $this->writeIsVerbose('Files found: ' . $countFiles); + $this->writeIsVerbose('New translates found: ' . $countNewKeys); + CLI::write('All operations done!'); + } + + /** + * @param SplFileInfo|string $file + */ + private function findTranslationsInFile($file): array + { + $languageFoundKeys = []; + + if (is_string($file) && is_file($file)) { + $file = new SplFileInfo($file); + } + + $fileContent = file_get_contents($file->getRealPath()); + preg_match_all('/lang\(\'([._a-z0-9\-]+)\'\)/ui', $fileContent, $matches); + + if (empty($matches[1])) { + return []; + } + + foreach ($matches[1] as $phraseKey) { + $phraseKeys = explode('.', $phraseKey); + + /** + * Language key not have Filename or Lang key + */ + if (count($phraseKeys) < 2) { + continue; + } + + $languageFileName = array_shift($phraseKeys); + $isEmptyNestedArray = (! empty($languageFileName) && empty($phraseKeys[0])) + || (empty($languageFileName) && ! empty($phraseKeys[0])) + || (empty($languageFileName) && empty($phraseKeys[0])); + + if ($isEmptyNestedArray) { + continue; + } + + /** + * Language key as string + */ + if (count($phraseKeys) === 1) { + /** + * Add found language keys to temporary array + */ + $languageFoundKeys[$languageFileName][$phraseKeys[0]] = $phraseKey; + } else { + $childKeys = $this->buildMultiArray($phraseKeys, $phraseKey); + $languageFoundKeys[$languageFileName] = array_replace_recursive($languageFoundKeys[$languageFileName] ?? [], $childKeys); + } + } + + return $languageFoundKeys; + } + + private function isIgnoredFile(SplFileInfo $file): bool + { + if ($file->isDir() || str_contains($file->getRealPath(), $this->languagePath)) { + return true; + } + + return 'php' !== $file->getExtension(); + } + + private function templateFile(array $language = []): string + { + if (! empty($language)) { + $languageArrayString = $this->languageKeysDump($language); + + return << $originalValue) { + if (is_array($originalValue)) { + $diffArrays = null; + + if (isset($compareArray[$originalKey])) { + $diffArrays = $this->arrayDiffRecursive($originalValue, $compareArray[$originalKey]); + } else { + $difference[$originalKey] = $originalValue; + } + + if (! empty($diffArrays)) { + $difference[$originalKey] = $diffArrays; + } + } elseif (is_string($originalValue) && ! array_key_exists($originalKey, $compareArray)) { + $difference[$originalKey] = $originalValue; + } + } + + return $difference; + } + + /** + * Convert multi arrays to specific CLI table rows (flat array) + */ + private function arrayToTableRows(string $langFileName, array $array): array + { + $rows = []; + + foreach ($array as $value) { + if (is_array($value)) { + $rows = array_merge($rows, $this->arrayToTableRows($langFileName, $value)); + + continue; + } + + if (is_string($value)) { + $rows[] = [$langFileName, $value]; + } + } + + return $rows; + } + + private function arrayCountRecursive(array $array, int $counter = 0): int + { + foreach ($array as $value) { + if (! is_array($value)) { + $counter++; + } else { + $counter = $this->arrayCountRecursive($value, $counter); + } + } + + return $counter; + } + + private function languageKeysDump(array $inputArray): string + { + return var_export($inputArray, true); + } + + /** + * Show details in the console if the flag is set + */ + private function writeIsVerbose(string $text = '', ?string $foreground = null, ?string $background = null): void + { + if ($this->verbose) { + CLI::write($text, $foreground, $background); + } + } +} From 03a81b6926cce37bb7549d91a034e1460aa50de7 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Mon, 4 Sep 2023 22:49:22 +0300 Subject: [PATCH 02/46] test: Add test for lang:find command --- .../Services/Translation/TranslationFour.php | 30 +++ .../Services/Translation/TranslationOne.php | 36 +++ .../Services/Translation/TranslationThree.php | 37 +++ .../Services/Translation/TranslationTwo.php | 35 +++ .../Translation/LocalizationFinderTest.php | 221 ++++++++++++++++++ 5 files changed, 359 insertions(+) create mode 100644 tests/_support/Services/Translation/TranslationFour.php create mode 100644 tests/_support/Services/Translation/TranslationOne.php create mode 100644 tests/_support/Services/Translation/TranslationThree.php create mode 100644 tests/_support/Services/Translation/TranslationTwo.php create mode 100644 tests/system/Commands/Translation/LocalizationFinderTest.php diff --git a/tests/_support/Services/Translation/TranslationFour.php b/tests/_support/Services/Translation/TranslationFour.php new file mode 100644 index 000000000000..10f5a7714d51 --- /dev/null +++ b/tests/_support/Services/Translation/TranslationFour.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Tests\Support\Services\Translation; + +class TranslationFour +{ + public function list() + { + $translationOne1 = lang('TranslationOne.title'); + $translationOne5 = lang('TranslationOne.last_operation_success'); + + $translationThree1 = lang('TranslationThree.alerts.created'); + $translationThree2 = lang('TranslationThree.alerts.failed_insert'); + + $translationThree5 = lang('TranslationThree.formFields.new.name'); + $translationThree7 = lang('TranslationThree.formFields.new.short_tag'); + + $translationFour1 = lang('Translation-Four.dashed.key-with-dash'); + $translationFour2 = lang('Translation-Four.dashed.key-with-dash-two'); + } +} diff --git a/tests/_support/Services/Translation/TranslationOne.php b/tests/_support/Services/Translation/TranslationOne.php new file mode 100644 index 000000000000..bf6b0e1e31c1 --- /dev/null +++ b/tests/_support/Services/Translation/TranslationOne.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Tests\Support\Services\Translation; + +class TranslationOne +{ + public function list() + { + $translationOne1 = lang('TranslationOne.title'); + $translationOne2 = lang('TranslationOne.DESCRIPTION'); + $translationOne3 = lang('TranslationOne.metaTags'); + $translationOne4 = lang('TranslationOne.Copyright'); + $translationOne5 = lang('TranslationOne.last_operation_success'); + + $translationThree1 = lang('TranslationThree.alerts.created'); + $translationThree2 = lang('TranslationThree.alerts.failed_insert'); + $translationThree3 = lang('TranslationThree.alerts.Updated'); + $translationThree4 = lang('TranslationThree.alerts.DELETED'); + + $translationThree5 = lang('TranslationThree.formFields.new.name'); + $translationThree6 = lang('TranslationThree.formFields.new.TEXT'); + $translationThree7 = lang('TranslationThree.formFields.new.short_tag'); + $translationThree8 = lang('TranslationThree.formFields.edit.name'); + $translationThree9 = lang('TranslationThree.formFields.edit.TEXT'); + $translationThree10 = lang('TranslationThree.formFields.edit.short_tag'); + } +} diff --git a/tests/_support/Services/Translation/TranslationThree.php b/tests/_support/Services/Translation/TranslationThree.php new file mode 100644 index 000000000000..d9da9ba67c75 --- /dev/null +++ b/tests/_support/Services/Translation/TranslationThree.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Tests\Support\Services\Translation; + +class TranslationThree +{ + public function list() + { + $translationOne1 = lang('TranslationOne.title'); + $translationOne2 = lang('TranslationOne.DESCRIPTION'); + $translationOne6 = lang('TranslationOne.subTitle'); + $translationOne7 = lang('TranslationOne.overflow_style'); + + $translationThree1 = lang('TranslationThree.alerts.created'); + $translationThree2 = lang('TranslationThree.alerts.failed_insert'); + + $translationThree5 = lang('TranslationThree.formFields.new.name'); + $translationThree6 = lang('TranslationThree.formFields.new.TEXT'); + $translationThree7 = lang('TranslationThree.formFields.new.short_tag'); + + $translationThree11 = lang('TranslationThree.alerts.CANCELED'); + $translationThree12 = lang('TranslationThree.alerts.missing_keys'); + + $translationThree13 = lang('TranslationThree.formErrors.edit.empty_name'); + $translationThree14 = lang('TranslationThree.formErrors.edit.INVALID_TEXT'); + $translationThree15 = lang('TranslationThree.formErrors.edit.missing_short_tag'); + } +} diff --git a/tests/_support/Services/Translation/TranslationTwo.php b/tests/_support/Services/Translation/TranslationTwo.php new file mode 100644 index 000000000000..be40786b5ee2 --- /dev/null +++ b/tests/_support/Services/Translation/TranslationTwo.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Tests\Support\Services\Translation; + +class TranslationTwo +{ + public function list() + { + $langKey = 'TranslationTwo.error_key'; + + /** + * Error language keys + */ + $translationError1 = lang('TranslationTwo'); + $translationError2 = lang(' '); + $translationError3 = lang(''); + $translationError4 = lang('.invalid_key'); + $translationError5 = lang('TranslationTwo.'); + $translationError6 = lang('TranslationTwo...'); + $translationError7 = lang('..invalid_nested_key..'); + + /** + * Empty in comments lang('') lang(' ') + */ + } +} diff --git a/tests/system/Commands/Translation/LocalizationFinderTest.php b/tests/system/Commands/Translation/LocalizationFinderTest.php new file mode 100644 index 000000000000..a3f15a4ba4cf --- /dev/null +++ b/tests/system/Commands/Translation/LocalizationFinderTest.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Commands\Translation; + +use CodeIgniter\Test\CIUnitTestCase; +use CodeIgniter\Test\StreamFilterTrait; +use Config\App; +use Config\Services; +use Locale; + +/** + * @group Others + * + * @internal + */ +final class LocalizationFinderTest extends CIUnitTestCase +{ + use StreamFilterTrait; + + private static string $locale; + private static string $languageTestPath; + + protected function setUp(): void + { + parent::setUp(); + self::$locale = Locale::getDefault(); + self::$languageTestPath = SUPPORTPATH . 'Language/'; + $this->clearGeneratedFiles(); + } + + public function testUpdateDefaultLocale(): void + { + @mkdir(self::$languageTestPath . self::$locale, 0777, true); + Services::commands()->run('lang:find', [ + 'dir' => 'Services/Translation', + ]); + $this->realizeAssertion(); + $this->clearGeneratedFiles(); + } + + public function testUpdateWithLocaleOption(): void + { + self::$locale = config(App::class)->supportedLocales[0]; + @mkdir(self::$languageTestPath . self::$locale, 0777, true); + Services::commands()->run('lang:find', [ + 'dir' => 'Services/Translation', + 'locale' => self::$locale, + ]); + $this->realizeAssertion(); + $this->clearGeneratedFiles(); + } + + public function testUpdateWithIncorrectLocaleOption(): void + { + self::$locale = 'test_locale_incorrect'; + @mkdir(self::$languageTestPath . self::$locale, 0777, true); + $status = Services::commands()->run('lang:find', [ + 'dir' => 'Services/Translation', + 'locale' => self::$locale, + ]); + $this->assertSame($status, -1); + $this->clearGeneratedFiles(); + } + + public function testUpdateWithEmptyDirOption(): void + { + @mkdir(self::$languageTestPath . self::$locale, 0777, true); + Services::commands()->run('lang:find', []); + $this->realizeAssertion(); + $this->clearGeneratedFiles(); + } + + public function testUpdateWithIncorrectDirOption(): void + { + @mkdir(self::$languageTestPath . self::$locale, 0777, true); + $status = Services::commands()->run('lang:find', [ + 'dir' => 'Services/Translation/NotExistFolder', + ]); + $this->assertSame($status, -1); + $this->clearGeneratedFiles(); + } + + public function testShowNewTranslation(): void + { + @mkdir(self::$languageTestPath . self::$locale, 0777, true); + Services::commands()->run('lang:find', [ + 'dir' => 'Services/Translation', + 'show-new' => null, + ]); + $this->assertStringContainsString($this->getActualTableWithNewKeys(), $this->getStreamFilterBuffer()); + $this->clearGeneratedFiles(); + } + + private function getActualTranslationOneKeys(): array + { + return [ + 'title' => 'TranslationOne.title', + 'DESCRIPTION' => 'TranslationOne.DESCRIPTION', + 'subTitle' => 'TranslationOne.subTitle', + 'overflow_style' => 'TranslationOne.overflow_style', + 'metaTags' => 'TranslationOne.metaTags', + 'Copyright' => 'TranslationOne.Copyright', + 'last_operation_success' => 'TranslationOne.last_operation_success', + ]; + } + + private function getActualTranslationThreeKeys(): array + { + return [ + 'alerts' => [ + 'created' => 'TranslationThree.alerts.created', + 'failed_insert' => 'TranslationThree.alerts.failed_insert', + 'CANCELED' => 'TranslationThree.alerts.CANCELED', + 'missing_keys' => 'TranslationThree.alerts.missing_keys', + 'Updated' => 'TranslationThree.alerts.Updated', + 'DELETED' => 'TranslationThree.alerts.DELETED', + ], + 'formFields' => [ + 'new' => [ + 'name' => 'TranslationThree.formFields.new.name', + 'TEXT' => 'TranslationThree.formFields.new.TEXT', + 'short_tag' => 'TranslationThree.formFields.new.short_tag', + ], + 'edit' => [ + 'name' => 'TranslationThree.formFields.edit.name', + 'TEXT' => 'TranslationThree.formFields.edit.TEXT', + 'short_tag' => 'TranslationThree.formFields.edit.short_tag', + ], + ], + 'formErrors' => [ + 'edit' => [ + 'empty_name' => 'TranslationThree.formErrors.edit.empty_name', + 'INVALID_TEXT' => 'TranslationThree.formErrors.edit.INVALID_TEXT', + 'missing_short_tag' => 'TranslationThree.formErrors.edit.missing_short_tag', + ], + ], + ]; + } + + private function getActualTranslationFourKeys(): array + { + return [ + 'dashed' => [ + 'key-with-dash' => 'Translation-Four.dashed.key-with-dash', + 'key-with-dash-two' => 'Translation-Four.dashed.key-with-dash-two', + ], + ]; + } + + private function getActualTableWithNewKeys(): string + { + return <<<'TEXT_WRAP' + +------------------+----------------------------------------------------+ + | File | Key | + +------------------+----------------------------------------------------+ + | Translation-Four | Translation-Four.dashed.key-with-dash | + | Translation-Four | Translation-Four.dashed.key-with-dash-two | + | TranslationOne | TranslationOne.Copyright | + | TranslationOne | TranslationOne.DESCRIPTION | + | TranslationOne | TranslationOne.last_operation_success | + | TranslationOne | TranslationOne.metaTags | + | TranslationOne | TranslationOne.overflow_style | + | TranslationOne | TranslationOne.subTitle | + | TranslationOne | TranslationOne.title | + | TranslationThree | TranslationThree.alerts.CANCELED | + | TranslationThree | TranslationThree.alerts.DELETED | + | TranslationThree | TranslationThree.alerts.Updated | + | TranslationThree | TranslationThree.alerts.created | + | TranslationThree | TranslationThree.alerts.failed_insert | + | TranslationThree | TranslationThree.alerts.missing_keys | + | TranslationThree | TranslationThree.formErrors.edit.INVALID_TEXT | + | TranslationThree | TranslationThree.formErrors.edit.empty_name | + | TranslationThree | TranslationThree.formErrors.edit.missing_short_tag | + | TranslationThree | TranslationThree.formFields.edit.TEXT | + | TranslationThree | TranslationThree.formFields.edit.name | + | TranslationThree | TranslationThree.formFields.edit.short_tag | + | TranslationThree | TranslationThree.formFields.new.TEXT | + | TranslationThree | TranslationThree.formFields.new.name | + | TranslationThree | TranslationThree.formFields.new.short_tag | + +------------------+----------------------------------------------------+ + TEXT_WRAP; + } + + private function realizeAssertion(): void + { + $this->assertFileExists(self::$languageTestPath . self::$locale . '/TranslationOne.php'); + $this->assertFileExists(self::$languageTestPath . self::$locale . '/TranslationThree.php'); + + $translationOneKeys = require self::$languageTestPath . self::$locale . '/TranslationOne.php'; + $translationThreeKeys = require self::$languageTestPath . self::$locale . '/TranslationThree.php'; + $translationFourKeys = require self::$languageTestPath . self::$locale . '/Translation-Four.php'; + + $this->assertSame($translationOneKeys, $this->getActualTranslationOneKeys()); + $this->assertSame($translationThreeKeys, $this->getActualTranslationThreeKeys()); + $this->assertSame($translationFourKeys, $this->getActualTranslationFourKeys()); + } + + private function clearGeneratedFiles(): void + { + if (is_file(self::$languageTestPath . self::$locale . '/TranslationOne.php')) { + unlink(self::$languageTestPath . self::$locale . '/TranslationOne.php'); + } + + if (is_file(self::$languageTestPath . self::$locale . '/TranslationThree.php')) { + unlink(self::$languageTestPath . self::$locale . '/TranslationThree.php'); + } + + if (is_file(self::$languageTestPath . self::$locale . '/Translation-Four.php')) { + unlink(self::$languageTestPath . self::$locale . '/Translation-Four.php'); + } + } +} From 624ba0187a550e9aa87b84d821b68e66c9700be6 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Mon, 4 Sep 2023 22:55:01 +0300 Subject: [PATCH 03/46] docs: Description using command --- .../source/outgoing/localization.rst | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/user_guide_src/source/outgoing/localization.rst b/user_guide_src/source/outgoing/localization.rst index f84e9b029004..3f5aa7bb77c3 100644 --- a/user_guide_src/source/outgoing/localization.rst +++ b/user_guide_src/source/outgoing/localization.rst @@ -254,3 +254,42 @@ project: The translated messages will be automatically picked up because the translations folders get mapped appropriately. + + +Automatic Translation Search via Command +======================================== + +.. versionadded:: 4.5.0 + +You can automatically find and update translation files in your **app/** folder. The command will search for the use of the ``lang()`` function, combine the current translation keys in **app/Language** by defining the locale ``defaultLocale`` from **Config/App**. +After the operation, you need to translate the language keys yourself. +The command is able to recognize nested keys normally ``File.array.nested.text``. +Previously saved keys do not change. + +.. code-block:: console + + php spark lang:find + +.. literalinclude:: localization/019.php + + +.. note:: Command ignoring **app/Language/** path. + + +Before updating, it is possible to preview the translations found by the command: + +.. code-block:: console + + php spark lang:find --verbose --show-new + +For a more accurate search, specify the desired locale or directory to scan. + +.. code-block:: console + + php spark lang:find --dir Controllers/Translation --locale en --show-new + +Detailed information can be found by running the command: + +.. code-block:: console + + php spark help lang:find From 034f9ddf6efc20ea9b290db50c5908283d687164 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Mon, 4 Sep 2023 22:55:24 +0300 Subject: [PATCH 04/46] docs: Example code with command --- user_guide_src/source/outgoing/localization/019.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 user_guide_src/source/outgoing/localization/019.php diff --git a/user_guide_src/source/outgoing/localization/019.php b/user_guide_src/source/outgoing/localization/019.php new file mode 100644 index 000000000000..ed16f1a056db --- /dev/null +++ b/user_guide_src/source/outgoing/localization/019.php @@ -0,0 +1,13 @@ + [ + 'success' => 'Text.info.success', + ], + 'paragraph' => 'Text.paragraph', +]; From 1fc285298f64493f472ab8169551c75a89a73271 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Tue, 5 Sep 2023 14:10:20 +0300 Subject: [PATCH 05/46] fix: Sorting of found files --- system/Commands/Translation/LocalizationFinder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 37f6d0be7d2c..e8ac280fcd0c 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -88,6 +88,8 @@ public function run(array $params) } $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($currentDir)); + $files = iterator_to_array($iterator, true); + ksort($files); $this->writeIsVerbose('Current locale: ' . $currentLocale); $this->writeIsVerbose('Find phrases in ' . $currentDir . ' folder...'); From 119f386048be910813790bb7211b0a2c8d41e16e Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Tue, 5 Sep 2023 14:13:47 +0300 Subject: [PATCH 06/46] test: Reduce the search depth --- system/Commands/Translation/LocalizationFinder.php | 4 ++-- .../Commands/Translation/LocalizationFinderTest.php | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index e8ac280fcd0c..4e60b908fe18 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -55,8 +55,8 @@ public function run(array $params) $countNewKeys = 0; if (ENVIRONMENT === 'testing') { - $currentDir = SUPPORTPATH; - $this->languagePath = $currentDir . 'Language/'; + $currentDir = SUPPORTPATH . 'Services/'; + $this->languagePath = SUPPORTPATH . 'Language/'; } if (is_string($cliOptionLocale)) { diff --git a/tests/system/Commands/Translation/LocalizationFinderTest.php b/tests/system/Commands/Translation/LocalizationFinderTest.php index a3f15a4ba4cf..2d0aca08c90c 100644 --- a/tests/system/Commands/Translation/LocalizationFinderTest.php +++ b/tests/system/Commands/Translation/LocalizationFinderTest.php @@ -41,7 +41,7 @@ public function testUpdateDefaultLocale(): void { @mkdir(self::$languageTestPath . self::$locale, 0777, true); Services::commands()->run('lang:find', [ - 'dir' => 'Services/Translation', + 'dir' => 'Translation', ]); $this->realizeAssertion(); $this->clearGeneratedFiles(); @@ -52,7 +52,7 @@ public function testUpdateWithLocaleOption(): void self::$locale = config(App::class)->supportedLocales[0]; @mkdir(self::$languageTestPath . self::$locale, 0777, true); Services::commands()->run('lang:find', [ - 'dir' => 'Services/Translation', + 'dir' => 'Translation', 'locale' => self::$locale, ]); $this->realizeAssertion(); @@ -64,7 +64,7 @@ public function testUpdateWithIncorrectLocaleOption(): void self::$locale = 'test_locale_incorrect'; @mkdir(self::$languageTestPath . self::$locale, 0777, true); $status = Services::commands()->run('lang:find', [ - 'dir' => 'Services/Translation', + 'dir' => 'Translation', 'locale' => self::$locale, ]); $this->assertSame($status, -1); @@ -83,7 +83,7 @@ public function testUpdateWithIncorrectDirOption(): void { @mkdir(self::$languageTestPath . self::$locale, 0777, true); $status = Services::commands()->run('lang:find', [ - 'dir' => 'Services/Translation/NotExistFolder', + 'dir' => 'Translation/NotExistFolder', ]); $this->assertSame($status, -1); $this->clearGeneratedFiles(); @@ -93,7 +93,7 @@ public function testShowNewTranslation(): void { @mkdir(self::$languageTestPath . self::$locale, 0777, true); Services::commands()->run('lang:find', [ - 'dir' => 'Services/Translation', + 'dir' => 'Translation', 'show-new' => null, ]); $this->assertStringContainsString($this->getActualTableWithNewKeys(), $this->getStreamFilterBuffer()); From e6707619edde63ba23c5345730105c936ca5dc11 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Tue, 5 Sep 2023 14:16:24 +0300 Subject: [PATCH 07/46] test: Added missed actions --- tests/system/Commands/Translation/LocalizationFinderTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/system/Commands/Translation/LocalizationFinderTest.php b/tests/system/Commands/Translation/LocalizationFinderTest.php index 2d0aca08c90c..ceeb085ea432 100644 --- a/tests/system/Commands/Translation/LocalizationFinderTest.php +++ b/tests/system/Commands/Translation/LocalizationFinderTest.php @@ -194,6 +194,7 @@ private function realizeAssertion(): void { $this->assertFileExists(self::$languageTestPath . self::$locale . '/TranslationOne.php'); $this->assertFileExists(self::$languageTestPath . self::$locale . '/TranslationThree.php'); + $this->assertFileExists(self::$languageTestPath . self::$locale . '/Translation-Four.php'); $translationOneKeys = require self::$languageTestPath . self::$locale . '/TranslationOne.php'; $translationThreeKeys = require self::$languageTestPath . self::$locale . '/TranslationThree.php'; @@ -217,5 +218,9 @@ private function clearGeneratedFiles(): void if (is_file(self::$languageTestPath . self::$locale . '/Translation-Four.php')) { unlink(self::$languageTestPath . self::$locale . '/Translation-Four.php'); } + + if (is_dir(self::$languageTestPath . '/test_locale_incorrect')) { + rmdir(self::$languageTestPath . '/test_locale_incorrect'); + } } } From 9602e7909ce382e39b5c30580b1ee04dbb23c865 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Tue, 5 Sep 2023 14:18:53 +0300 Subject: [PATCH 08/46] test: Move test file to subfolder --- .../Translation/{ => TranslationNested}/TranslationFour.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/_support/Services/Translation/{ => TranslationNested}/TranslationFour.php (94%) diff --git a/tests/_support/Services/Translation/TranslationFour.php b/tests/_support/Services/Translation/TranslationNested/TranslationFour.php similarity index 94% rename from tests/_support/Services/Translation/TranslationFour.php rename to tests/_support/Services/Translation/TranslationNested/TranslationFour.php index 10f5a7714d51..545e821bb049 100644 --- a/tests/_support/Services/Translation/TranslationFour.php +++ b/tests/_support/Services/Translation/TranslationNested/TranslationFour.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace Tests\Support\Services\Translation; +namespace Tests\Support\Services\Translation\Nested; class TranslationFour { From 49d2c5745bb58dccf87d9a5ac6b4e97790f1e5dc Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 06:26:40 +0300 Subject: [PATCH 09/46] fix: Missing sorted files in a loop --- system/Commands/Translation/LocalizationFinder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 4e60b908fe18..f3ed2d6c66b8 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -96,7 +96,7 @@ public function run(array $params) /** * @var SplFileInfo $file */ - foreach ($iterator as $file) { + foreach ($files as $file) { if ($this->isIgnoredFile($file)) { continue; } From 13d93ea2edccba598768e05b948cc4ada9266a7c Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 06:35:46 +0300 Subject: [PATCH 10/46] fix: Remove prefix variables --- .../Translation/LocalizationFinder.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index f3ed2d6c66b8..cf1a6f001752 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -43,9 +43,9 @@ class LocalizationFinder extends BaseCommand public function run(array $params) { $this->verbose = array_key_exists('verbose', $params); - $cliOptionShowNew = array_key_exists('show-new', $params); - $cliOptionLocale = ! empty($params['locale']) ? $params['locale'] : null; - $cliOptionDir = ! empty($params['dir']) ? $params['dir'] : null; + $optionShowNew = array_key_exists('show-new', $params); + $optionLocale = ! empty($params['locale']) ? $params['locale'] : null; + $optionDir = ! empty($params['dir']) ? $params['dir'] : null; $currentLocale = Locale::getDefault(); $currentDir = APPPATH; $this->languagePath = $currentDir . 'Language'; @@ -59,18 +59,18 @@ public function run(array $params) $this->languagePath = SUPPORTPATH . 'Language/'; } - if (is_string($cliOptionLocale)) { - if (! in_array($cliOptionLocale, config(App::class)->supportedLocales, true)) { + if (is_string($optionLocale)) { + if (! in_array($optionLocale, config(App::class)->supportedLocales, true)) { CLI::error('Error: Supported locales ' . implode(', ', config(App::class)->supportedLocales)); return -1; } - $currentLocale = $cliOptionLocale; + $currentLocale = $optionLocale; } - if (is_string($cliOptionDir)) { - $tempCurrentDir = realpath($currentDir . $cliOptionDir); + if (is_string($optionDir)) { + $tempCurrentDir = realpath($currentDir . $optionDir); if (false === $tempCurrentDir) { CLI::error('Error: Dir must be located in ' . $currentDir); @@ -124,7 +124,7 @@ public function run(array $params) $languageDiff = $this->arrayDiffRecursive($languageFoundKeys[$langFileName], $languageStoredKeys); $countNewKeys += $this->arrayCountRecursive($languageDiff); - if ($cliOptionShowNew) { + if ($optionShowNew) { $tableRows = array_merge($this->arrayToTableRows($langFileName, $languageDiff), $tableRows); } else { $newLanguageKeys = array_replace_recursive($languageFoundKeys[$langFileName], $languageStoredKeys); @@ -146,7 +146,7 @@ public function run(array $params) } } - if ($cliOptionShowNew && ! empty($tableRows)) { + if ($optionShowNew && ! empty($tableRows)) { sort($tableRows); CLI::table($tableRows, ['File', 'Key']); } From 31457727765af2aad2d062052c191245bbce7a63 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 06:36:33 +0300 Subject: [PATCH 11/46] fix: Typo in desciption --- system/Commands/Translation/LocalizationFinder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index cf1a6f001752..58f229edc368 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -23,7 +23,7 @@ class LocalizationFinder extends BaseCommand { protected $group = 'Translation'; protected $name = 'lang:find'; - protected $description = 'Find and save available phrases to translate'; + protected $description = 'Find and save available phrases to translate.'; protected $usage = 'lang:find [options]'; protected $arguments = []; protected $options = [ From 10c81bc9b780550d42e82e2a315134273c08ce1c Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 06:43:45 +0300 Subject: [PATCH 12/46] fix: Return with a constant exit code --- system/Commands/Translation/LocalizationFinder.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 58f229edc368..4d9872260e6a 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -63,7 +63,7 @@ public function run(array $params) if (! in_array($optionLocale, config(App::class)->supportedLocales, true)) { CLI::error('Error: Supported locales ' . implode(', ', config(App::class)->supportedLocales)); - return -1; + return EXIT_USER_INPUT; } $currentLocale = $optionLocale; @@ -75,13 +75,13 @@ public function run(array $params) if (false === $tempCurrentDir) { CLI::error('Error: Dir must be located in ' . $currentDir); - return -1; + return EXIT_USER_INPUT; } if (str_starts_with($tempCurrentDir, $this->languagePath)) { CLI::error('Error: Dir ' . $this->languagePath . ' restricted to scan.'); - return -1; + return EXIT_USER_INPUT; } $currentDir = $tempCurrentDir; @@ -154,6 +154,8 @@ public function run(array $params) $this->writeIsVerbose('Files found: ' . $countFiles); $this->writeIsVerbose('New translates found: ' . $countNewKeys); CLI::write('All operations done!'); + + return EXIT_SUCCESS; } /** From 00284d1b235b9e70aad534dd6468d4725b98fb2b Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 06:49:26 +0300 Subject: [PATCH 13/46] fix: Error text is corrected --- system/Commands/Translation/LocalizationFinder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 4d9872260e6a..34923ac80648 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -79,7 +79,7 @@ public function run(array $params) } if (str_starts_with($tempCurrentDir, $this->languagePath)) { - CLI::error('Error: Dir ' . $this->languagePath . ' restricted to scan.'); + CLI::error('Error: Directory "' . $this->languagePath . '" restricted to scan.'); return EXIT_USER_INPUT; } From 3b86c1ae9870c044de072717d6200ff3a14fcfa3 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 06:51:31 +0300 Subject: [PATCH 14/46] fix: Format saved array code --- system/Commands/Translation/LocalizationFinder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 34923ac80648..9cdd7b574f7f 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -230,6 +230,7 @@ private function templateFile(array $language = []): string Date: Wed, 6 Sep 2023 07:03:01 +0300 Subject: [PATCH 15/46] fix: Add method test is subfolder --- system/Commands/Translation/LocalizationFinder.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 9cdd7b574f7f..81056bdde9fc 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -78,7 +78,7 @@ public function run(array $params) return EXIT_USER_INPUT; } - if (str_starts_with($tempCurrentDir, $this->languagePath)) { + if ($this->isSubDirectory($tempCurrentDir, $this->languagePath)) { CLI::error('Error: Directory "' . $this->languagePath . '" restricted to scan.'); return EXIT_USER_INPUT; @@ -342,4 +342,9 @@ private function writeIsVerbose(string $text = '', ?string $foreground = null, ? CLI::write($text, $foreground, $background); } } + + public function isSubDirectory(string $directory, string $rootDirectory): bool + { + return 0 === strncmp($directory, $rootDirectory, strlen($directory)); + } } From 168c5af43e7f133e210bb398f27c74344ea8f877 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 07:17:35 +0300 Subject: [PATCH 16/46] fix: Move actions to tearDown() --- .../Commands/Translation/LocalizationFinderTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/system/Commands/Translation/LocalizationFinderTest.php b/tests/system/Commands/Translation/LocalizationFinderTest.php index ceeb085ea432..774bfd45f997 100644 --- a/tests/system/Commands/Translation/LocalizationFinderTest.php +++ b/tests/system/Commands/Translation/LocalizationFinderTest.php @@ -37,6 +37,12 @@ protected function setUp(): void $this->clearGeneratedFiles(); } + protected function tearDown(): void + { + parent::tearDown(); + $this->clearGeneratedFiles(); + } + public function testUpdateDefaultLocale(): void { @mkdir(self::$languageTestPath . self::$locale, 0777, true); @@ -44,7 +50,6 @@ public function testUpdateDefaultLocale(): void 'dir' => 'Translation', ]); $this->realizeAssertion(); - $this->clearGeneratedFiles(); } public function testUpdateWithLocaleOption(): void @@ -56,7 +61,6 @@ public function testUpdateWithLocaleOption(): void 'locale' => self::$locale, ]); $this->realizeAssertion(); - $this->clearGeneratedFiles(); } public function testUpdateWithIncorrectLocaleOption(): void @@ -68,7 +72,6 @@ public function testUpdateWithIncorrectLocaleOption(): void 'locale' => self::$locale, ]); $this->assertSame($status, -1); - $this->clearGeneratedFiles(); } public function testUpdateWithEmptyDirOption(): void @@ -76,7 +79,6 @@ public function testUpdateWithEmptyDirOption(): void @mkdir(self::$languageTestPath . self::$locale, 0777, true); Services::commands()->run('lang:find', []); $this->realizeAssertion(); - $this->clearGeneratedFiles(); } public function testUpdateWithIncorrectDirOption(): void @@ -86,7 +88,6 @@ public function testUpdateWithIncorrectDirOption(): void 'dir' => 'Translation/NotExistFolder', ]); $this->assertSame($status, -1); - $this->clearGeneratedFiles(); } public function testShowNewTranslation(): void @@ -97,7 +98,6 @@ public function testShowNewTranslation(): void 'show-new' => null, ]); $this->assertStringContainsString($this->getActualTableWithNewKeys(), $this->getStreamFilterBuffer()); - $this->clearGeneratedFiles(); } private function getActualTranslationOneKeys(): array From 37ece4ecd100a0377fe2c5b53b6f0cfa3be9a7e3 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 07:20:20 +0300 Subject: [PATCH 17/46] fix: Add makeLocaleDirectory() --- .../Translation/LocalizationFinderTest.php | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/tests/system/Commands/Translation/LocalizationFinderTest.php b/tests/system/Commands/Translation/LocalizationFinderTest.php index 774bfd45f997..d5958efc07ee 100644 --- a/tests/system/Commands/Translation/LocalizationFinderTest.php +++ b/tests/system/Commands/Translation/LocalizationFinderTest.php @@ -45,58 +45,70 @@ protected function tearDown(): void public function testUpdateDefaultLocale(): void { - @mkdir(self::$languageTestPath . self::$locale, 0777, true); + $this->makeLocaleDirectory(); + Services::commands()->run('lang:find', [ 'dir' => 'Translation', ]); + $this->realizeAssertion(); } public function testUpdateWithLocaleOption(): void { self::$locale = config(App::class)->supportedLocales[0]; - @mkdir(self::$languageTestPath . self::$locale, 0777, true); + $this->makeLocaleDirectory(); + Services::commands()->run('lang:find', [ 'dir' => 'Translation', 'locale' => self::$locale, ]); + $this->realizeAssertion(); } public function testUpdateWithIncorrectLocaleOption(): void { self::$locale = 'test_locale_incorrect'; - @mkdir(self::$languageTestPath . self::$locale, 0777, true); + $this->makeLocaleDirectory(); + $status = Services::commands()->run('lang:find', [ 'dir' => 'Translation', 'locale' => self::$locale, ]); + $this->assertSame($status, -1); } public function testUpdateWithEmptyDirOption(): void { - @mkdir(self::$languageTestPath . self::$locale, 0777, true); + $this->makeLocaleDirectory(); + Services::commands()->run('lang:find', []); + $this->realizeAssertion(); } public function testUpdateWithIncorrectDirOption(): void { - @mkdir(self::$languageTestPath . self::$locale, 0777, true); + $this->makeLocaleDirectory(); + $status = Services::commands()->run('lang:find', [ 'dir' => 'Translation/NotExistFolder', ]); + $this->assertSame($status, -1); } public function testShowNewTranslation(): void { - @mkdir(self::$languageTestPath . self::$locale, 0777, true); + $this->makeLocaleDirectory(); + Services::commands()->run('lang:find', [ 'dir' => 'Translation', 'show-new' => null, ]); + $this->assertStringContainsString($this->getActualTableWithNewKeys(), $this->getStreamFilterBuffer()); } @@ -205,6 +217,11 @@ private function realizeAssertion(): void $this->assertSame($translationFourKeys, $this->getActualTranslationFourKeys()); } + private function makeLocaleDirectory(): void + { + @mkdir(self::$languageTestPath . self::$locale, 0777, true); + } + private function clearGeneratedFiles(): void { if (is_file(self::$languageTestPath . self::$locale . '/TranslationOne.php')) { From 9982db0870e8b794d7ff68e4aae5a5a239364e2f Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 07:25:36 +0300 Subject: [PATCH 18/46] docs: Correct spelling --- user_guide_src/source/outgoing/localization.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/outgoing/localization.rst b/user_guide_src/source/outgoing/localization.rst index 3f5aa7bb77c3..3cffd3ed85e7 100644 --- a/user_guide_src/source/outgoing/localization.rst +++ b/user_guide_src/source/outgoing/localization.rst @@ -256,12 +256,12 @@ The translated messages will be automatically picked up because the translations folders get mapped appropriately. -Automatic Translation Search via Command +Generating Translation Files via Command ======================================== .. versionadded:: 4.5.0 -You can automatically find and update translation files in your **app/** folder. The command will search for the use of the ``lang()`` function, combine the current translation keys in **app/Language** by defining the locale ``defaultLocale`` from **Config/App**. +You can automatically generate and update translation files in your **app** folder. The command will search for the use of the ``lang()`` function, combine the current translation keys in **app/Language** by defining the locale ``defaultLocale`` from ``Config\App``. After the operation, you need to translate the language keys yourself. The command is able to recognize nested keys normally ``File.array.nested.text``. Previously saved keys do not change. @@ -273,7 +273,7 @@ Previously saved keys do not change. .. literalinclude:: localization/019.php -.. note:: Command ignoring **app/Language/** path. +.. note:: The command ignores **app/Language/** path. Before updating, it is possible to preview the translations found by the command: @@ -292,4 +292,4 @@ Detailed information can be found by running the command: .. code-block:: console - php spark help lang:find + php spark lang:find --help From d7a724b42c953ac2f28037a1b94ce89a5f75d522 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 07:29:57 +0300 Subject: [PATCH 19/46] fix: Assert returned code --- tests/system/Commands/Translation/LocalizationFinderTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/Commands/Translation/LocalizationFinderTest.php b/tests/system/Commands/Translation/LocalizationFinderTest.php index d5958efc07ee..40d01efb820b 100644 --- a/tests/system/Commands/Translation/LocalizationFinderTest.php +++ b/tests/system/Commands/Translation/LocalizationFinderTest.php @@ -77,7 +77,7 @@ public function testUpdateWithIncorrectLocaleOption(): void 'locale' => self::$locale, ]); - $this->assertSame($status, -1); + $this->assertSame($status, EXIT_USER_INPUT); } public function testUpdateWithEmptyDirOption(): void @@ -97,7 +97,7 @@ public function testUpdateWithIncorrectDirOption(): void 'dir' => 'Translation/NotExistFolder', ]); - $this->assertSame($status, -1); + $this->assertSame($status, EXIT_USER_INPUT); } public function testShowNewTranslation(): void From 3b9766498aee6c2206b99852437d285bd8fe8c0d Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 07:37:03 +0300 Subject: [PATCH 20/46] fix: Method to private --- system/Commands/Translation/LocalizationFinder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 81056bdde9fc..162fc8184708 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -343,7 +343,7 @@ private function writeIsVerbose(string $text = '', ?string $foreground = null, ? } } - public function isSubDirectory(string $directory, string $rootDirectory): bool + private function isSubDirectory(string $directory, string $rootDirectory): bool { return 0 === strncmp($directory, $rootDirectory, strlen($directory)); } From ca3652b07502eb8758c66b3c84794464d2a1d87a Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Wed, 6 Sep 2023 08:37:46 +0300 Subject: [PATCH 21/46] fix: Move long code to process() --- .../Translation/LocalizationFinder.php | 160 ++++++++++-------- 1 file changed, 90 insertions(+), 70 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 162fc8184708..10afe46d6ab2 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -38,21 +38,22 @@ class LocalizationFinder extends BaseCommand */ private bool $verbose = false; + /** + * Flag for showing only translations, without saving + */ + private bool $showNew = false; + private string $languagePath; public function run(array $params) { $this->verbose = array_key_exists('verbose', $params); - $optionShowNew = array_key_exists('show-new', $params); + $this->showNew = array_key_exists('show-new', $params); $optionLocale = ! empty($params['locale']) ? $params['locale'] : null; $optionDir = ! empty($params['dir']) ? $params['dir'] : null; $currentLocale = Locale::getDefault(); $currentDir = APPPATH; $this->languagePath = $currentDir . 'Language'; - $tableRows = []; - $languageFoundKeys = []; - $countFiles = 0; - $countNewKeys = 0; if (ENVIRONMENT === 'testing') { $currentDir = SUPPORTPATH . 'Services/'; @@ -87,72 +88,8 @@ public function run(array $params) $currentDir = $tempCurrentDir; } - $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($currentDir)); - $files = iterator_to_array($iterator, true); - ksort($files); - $this->writeIsVerbose('Current locale: ' . $currentLocale); - $this->writeIsVerbose('Find phrases in ' . $currentDir . ' folder...'); - - /** - * @var SplFileInfo $file - */ - foreach ($files as $file) { - if ($this->isIgnoredFile($file)) { - continue; - } - - $this->writeIsVerbose('File found: ' . mb_substr($file->getRealPath(), mb_strlen(APPPATH))); - $countFiles++; - $languageFoundKeys = array_replace_recursive($this->findTranslationsInFile($file), $languageFoundKeys); - } - - ksort($languageFoundKeys); - $languageDiff = []; - $languageFoundGroups = array_unique(array_keys($languageFoundKeys)); - - foreach ($languageFoundGroups as $langFileName) { - $languageStoredKeys = []; - $languageFilePath = $this->languagePath . DIRECTORY_SEPARATOR . $currentLocale . DIRECTORY_SEPARATOR . $langFileName . '.php'; - - if (is_file($languageFilePath)) { - /** - * Load old localization - */ - $languageStoredKeys = require $languageFilePath; - } + $this->process($currentDir, $currentLocale); - $languageDiff = $this->arrayDiffRecursive($languageFoundKeys[$langFileName], $languageStoredKeys); - $countNewKeys += $this->arrayCountRecursive($languageDiff); - - if ($optionShowNew) { - $tableRows = array_merge($this->arrayToTableRows($langFileName, $languageDiff), $tableRows); - } else { - $newLanguageKeys = array_replace_recursive($languageFoundKeys[$langFileName], $languageStoredKeys); - - /** - * New translates exists - */ - if ($languageDiff !== []) { - if (false === file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys))) { - $this->writeIsVerbose('Lang file ' . $langFileName . ' (error write).', 'red'); - } else { - /** - * FIXME: Need help command (slow run) - */ - exec('composer cs-fix --quiet ' . $this->languagePath); - $this->writeIsVerbose('Lang file "' . $langFileName . '" successful updated!', 'green'); - } - } - } - } - - if ($optionShowNew && ! empty($tableRows)) { - sort($tableRows); - CLI::table($tableRows, ['File', 'Key']); - } - - $this->writeIsVerbose('Files found: ' . $countFiles); - $this->writeIsVerbose('New translates found: ' . $countNewKeys); CLI::write('All operations done!'); return EXIT_SUCCESS; @@ -347,4 +284,87 @@ private function isSubDirectory(string $directory, string $rootDirectory): bool { return 0 === strncmp($directory, $rootDirectory, strlen($directory)); } + + private function process(string $currentDir, string $currentLocale): void + { + $tableRows = []; + $countNewKeys = 0; + + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($currentDir)); + $files = iterator_to_array($iterator, true); + ksort($files); + + [$languageFoundKeys, $countFiles] = $this->findLanguageKeysInFiles($files); + ksort($languageFoundKeys); + + /** + * New translates + */ + $languageDiff = []; + $languageFoundGroups = array_unique(array_keys($languageFoundKeys)); + + foreach ($languageFoundGroups as $langFileName) { + $languageStoredKeys = []; + $languageFilePath = $this->languagePath . DIRECTORY_SEPARATOR . $currentLocale . DIRECTORY_SEPARATOR . $langFileName . '.php'; + + if (is_file($languageFilePath)) { + /** + * Load old localization + */ + $languageStoredKeys = require $languageFilePath; + } + + $languageDiff = $this->arrayDiffRecursive($languageFoundKeys[$langFileName], $languageStoredKeys); + $countNewKeys += $this->arrayCountRecursive($languageDiff); + + if ($this->showNew) { + $tableRows = array_merge($this->arrayToTableRows($langFileName, $languageDiff), $tableRows); + } else { + $newLanguageKeys = array_replace_recursive($languageFoundKeys[$langFileName], $languageStoredKeys); + + /** + * New translates exists + */ + if ($languageDiff !== []) { + if (false === file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys))) { + $this->writeIsVerbose('Lang file ' . $langFileName . ' (error write).', 'red'); + } else { + exec('composer cs-fix --quiet ' . $this->languagePath); + $this->writeIsVerbose('Lang file "' . $langFileName . '" successful updated!', 'green'); + } + } + } + } + + if ($this->showNew && ! empty($tableRows)) { + sort($tableRows); + CLI::table($tableRows, ['File', 'Key']); + } + + $this->writeIsVerbose('Files found: ' . $countFiles); + $this->writeIsVerbose('New translates found: ' . $countNewKeys); + } + + /** + * @param SplFileInfo[] $files + * + * @return array + */ + private function findLanguageKeysInFiles(array $files): array + { + $languageFoundKeys = []; + $countFiles = 0; + + foreach ($files as $file) { + if ($this->isIgnoredFile($file)) { + continue; + } + + $this->writeIsVerbose('File found: ' . mb_substr($file->getRealPath(), mb_strlen(APPPATH))); + $countFiles++; + $languageFoundKeys = array_replace_recursive($this->findTranslationsInFile($file), $languageFoundKeys); + } + + return [$languageFoundKeys, $countFiles]; + } } From 672f8f8325bb567d5b82834a7963d7970cd8a3d8 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 10:47:50 +0300 Subject: [PATCH 22/46] docs: add changelog --- user_guide_src/source/changelogs/v4.5.0.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.5.0.rst b/user_guide_src/source/changelogs/v4.5.0.rst index d18f0884e0ce..29c31dcabf8e 100644 --- a/user_guide_src/source/changelogs/v4.5.0.rst +++ b/user_guide_src/source/changelogs/v4.5.0.rst @@ -47,6 +47,8 @@ Enhancements Commands ======== +- Added ``spark lang:find`` command to update translations keys. See :ref:`generating-translation-files-via-command` for the details. + Testing ======= From 84e5ae6f2cd2da4c3265ee1a16f7cac9e02dfdba Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 10:48:56 +0300 Subject: [PATCH 23/46] docs: add link --- user_guide_src/source/outgoing/localization.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/outgoing/localization.rst b/user_guide_src/source/outgoing/localization.rst index 3cffd3ed85e7..e0c1dd4dc4d1 100644 --- a/user_guide_src/source/outgoing/localization.rst +++ b/user_guide_src/source/outgoing/localization.rst @@ -255,6 +255,7 @@ project: The translated messages will be automatically picked up because the translations folders get mapped appropriately. +.. _generating-translation-files-via-command: Generating Translation Files via Command ======================================== From 156b9474ccad90f43883ae1e7cd58b97f6e11c93 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 11:34:48 +0300 Subject: [PATCH 24/46] docs: Language correction --- user_guide_src/source/outgoing/localization/019.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/outgoing/localization/019.php b/user_guide_src/source/outgoing/localization/019.php index ed16f1a056db..5de7d9a80731 100644 --- a/user_guide_src/source/outgoing/localization/019.php +++ b/user_guide_src/source/outgoing/localization/019.php @@ -4,7 +4,7 @@ $message = lang('Text.info.success'); $message2 = lang('Text.paragraph'); -// Save in Language/en/Text.php +// The following will be saved in Language/en/Text.php return [ 'info' => [ 'success' => 'Text.info.success', From c56437424a60f21c666e4f648053adb19a89f57c Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 11:40:04 +0300 Subject: [PATCH 25/46] fix: Language correction --- system/Commands/Translation/LocalizationFinder.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 10afe46d6ab2..413afef8803c 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -27,9 +27,9 @@ class LocalizationFinder extends BaseCommand protected $usage = 'lang:find [options]'; protected $arguments = []; protected $options = [ - '--locale' => 'Apply changes to the selected locale (en, ru, etc...).', + '--locale' => 'Specify locale (en, ru, etc...) to save files.', '--dir' => 'Directory for searching for translations (in relation to the APPPATH).', - '--show-new' => 'See only new translations as table.', + '--show-new' => 'See only new translations as table. Do not write to files.', '--verbose' => 'Output detailed information.', ]; @@ -74,7 +74,7 @@ public function run(array $params) $tempCurrentDir = realpath($currentDir . $optionDir); if (false === $tempCurrentDir) { - CLI::error('Error: Dir must be located in ' . $currentDir); + CLI::error('Error: Directory must be located in "' . $currentDir . '"'); return EXIT_USER_INPUT; } @@ -199,7 +199,7 @@ private function buildMultiArray(array $fromKeys, string $lastArrayValue = ''): } /** - * Compare recursive two arrays and return new array (diference) + * Compare recursive two arrays and return new array (difference) */ private function arrayDiffRecursive(array $originalArray, array $compareArray): array { From 62bff7eb018154536ec192e02ac1d0b145f5a79b Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 11:41:55 +0300 Subject: [PATCH 26/46] fix: Shorten the syntax --- system/Commands/Translation/LocalizationFinder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 413afef8803c..db0e78028a7c 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -49,8 +49,8 @@ public function run(array $params) { $this->verbose = array_key_exists('verbose', $params); $this->showNew = array_key_exists('show-new', $params); - $optionLocale = ! empty($params['locale']) ? $params['locale'] : null; - $optionDir = ! empty($params['dir']) ? $params['dir'] : null; + $optionLocale = $params['locale'] ?? null; + $optionDir = $params['dir'] ?? null; $currentLocale = Locale::getDefault(); $currentDir = APPPATH; $this->languagePath = $currentDir . 'Language'; From cde1e0233f62e77e5c3c4bb22f8012de243ecead Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 11:44:52 +0300 Subject: [PATCH 27/46] style: Format code --- system/Commands/Translation/LocalizationFinder.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index db0e78028a7c..4189ccdd6415 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -62,7 +62,10 @@ public function run(array $params) if (is_string($optionLocale)) { if (! in_array($optionLocale, config(App::class)->supportedLocales, true)) { - CLI::error('Error: Supported locales ' . implode(', ', config(App::class)->supportedLocales)); + CLI::error( + 'Error: "' . $optionLocale . '" is not supported. Supported locales: ' + . implode(', ', config(App::class)->supportedLocales) + ); return EXIT_USER_INPUT; } From b3cb72622f5edcf6cf1b4ecf8cf5e88ee7f68b06 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 11:48:02 +0300 Subject: [PATCH 28/46] docs: add @phpstan-return --- system/Commands/Translation/LocalizationFinder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 4189ccdd6415..27bd3cf99b3c 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -352,6 +352,7 @@ private function process(string $currentDir, string $currentLocale): void * @param SplFileInfo[] $files * * @return array + * @phpstan-return list{0: array>, 1: int} */ private function findLanguageKeysInFiles(array $files): array { From 0d3ad09dabc757db675a2734125544124ab5fe7c Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 12:01:41 +0300 Subject: [PATCH 29/46] fix: Replace str_contains() --- system/Commands/Translation/LocalizationFinder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 27bd3cf99b3c..0681c2993bd5 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -154,7 +154,7 @@ private function findTranslationsInFile($file): array private function isIgnoredFile(SplFileInfo $file): bool { - if ($file->isDir() || str_contains($file->getRealPath(), $this->languagePath)) { + if ($file->isDir() || $this->isSubDirectory($file->getRealPath(), $this->languagePath)) { return true; } From b55964ea47a5ba19f238ed358af7d8987521a89a Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 12:03:00 +0300 Subject: [PATCH 30/46] fix: Shorten the syntax --- system/Commands/Translation/LocalizationFinder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 0681c2993bd5..4f8600c912d1 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -163,7 +163,7 @@ private function isIgnoredFile(SplFileInfo $file): bool private function templateFile(array $language = []): string { - if (! empty($language)) { + if ([] !== $language) { $languageArrayString = $this->languageKeysDump($language); return << Date: Fri, 8 Sep 2023 12:06:59 +0300 Subject: [PATCH 31/46] fix: Rename variable to foundLanguageKeys --- .../Translation/LocalizationFinder.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 4f8600c912d1..d601dc67fd36 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -103,7 +103,7 @@ public function run(array $params) */ private function findTranslationsInFile($file): array { - $languageFoundKeys = []; + $foundLanguageKeys = []; if (is_string($file) && is_file($file)) { $file = new SplFileInfo($file); @@ -142,14 +142,14 @@ private function findTranslationsInFile($file): array /** * Add found language keys to temporary array */ - $languageFoundKeys[$languageFileName][$phraseKeys[0]] = $phraseKey; + $foundLanguageKeys[$languageFileName][$phraseKeys[0]] = $phraseKey; } else { $childKeys = $this->buildMultiArray($phraseKeys, $phraseKey); - $languageFoundKeys[$languageFileName] = array_replace_recursive($languageFoundKeys[$languageFileName] ?? [], $childKeys); + $foundLanguageKeys[$languageFileName] = array_replace_recursive($foundLanguageKeys[$languageFileName] ?? [], $childKeys); } } - return $languageFoundKeys; + return $foundLanguageKeys; } private function isIgnoredFile(SplFileInfo $file): bool @@ -297,14 +297,14 @@ private function process(string $currentDir, string $currentLocale): void $files = iterator_to_array($iterator, true); ksort($files); - [$languageFoundKeys, $countFiles] = $this->findLanguageKeysInFiles($files); - ksort($languageFoundKeys); + [$foundLanguageKeys, $countFiles] = $this->findLanguageKeysInFiles($files); + ksort($foundLanguageKeys); /** * New translates */ $languageDiff = []; - $languageFoundGroups = array_unique(array_keys($languageFoundKeys)); + $languageFoundGroups = array_unique(array_keys($foundLanguageKeys)); foreach ($languageFoundGroups as $langFileName) { $languageStoredKeys = []; @@ -317,13 +317,13 @@ private function process(string $currentDir, string $currentLocale): void $languageStoredKeys = require $languageFilePath; } - $languageDiff = $this->arrayDiffRecursive($languageFoundKeys[$langFileName], $languageStoredKeys); + $languageDiff = $this->arrayDiffRecursive($foundLanguageKeys[$langFileName], $languageStoredKeys); $countNewKeys += $this->arrayCountRecursive($languageDiff); if ($this->showNew) { $tableRows = array_merge($this->arrayToTableRows($langFileName, $languageDiff), $tableRows); } else { - $newLanguageKeys = array_replace_recursive($languageFoundKeys[$langFileName], $languageStoredKeys); + $newLanguageKeys = array_replace_recursive($foundLanguageKeys[$langFileName], $languageStoredKeys); /** * New translates exists @@ -356,7 +356,7 @@ private function process(string $currentDir, string $currentLocale): void */ private function findLanguageKeysInFiles(array $files): array { - $languageFoundKeys = []; + $foundLanguageKeys = []; $countFiles = 0; foreach ($files as $file) { @@ -366,9 +366,9 @@ private function findLanguageKeysInFiles(array $files): array $this->writeIsVerbose('File found: ' . mb_substr($file->getRealPath(), mb_strlen(APPPATH))); $countFiles++; - $languageFoundKeys = array_replace_recursive($this->findTranslationsInFile($file), $languageFoundKeys); + $foundLanguageKeys = array_replace_recursive($this->findTranslationsInFile($file), $foundLanguageKeys); } - return [$languageFoundKeys, $countFiles]; + return [$foundLanguageKeys, $countFiles]; } } From f6d9b2b63845b59f37a7a1927df626fbb2e15fba Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 12:11:21 +0300 Subject: [PATCH 32/46] tests: Delete unnecessary cleaning --- tests/system/Commands/Translation/LocalizationFinderTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/system/Commands/Translation/LocalizationFinderTest.php b/tests/system/Commands/Translation/LocalizationFinderTest.php index 40d01efb820b..5d61c4c19bbb 100644 --- a/tests/system/Commands/Translation/LocalizationFinderTest.php +++ b/tests/system/Commands/Translation/LocalizationFinderTest.php @@ -34,7 +34,6 @@ protected function setUp(): void parent::setUp(); self::$locale = Locale::getDefault(); self::$languageTestPath = SUPPORTPATH . 'Language/'; - $this->clearGeneratedFiles(); } protected function tearDown(): void From ecad35478d978bbc41d3ece20f127624cc4d0dd2 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 21:08:25 +0300 Subject: [PATCH 33/46] fix: Add array helper as separate class --- .../LocalizationFinder/ArrayHelper.php | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 system/Commands/Translation/LocalizationFinder/ArrayHelper.php diff --git a/system/Commands/Translation/LocalizationFinder/ArrayHelper.php b/system/Commands/Translation/LocalizationFinder/ArrayHelper.php new file mode 100644 index 000000000000..b970c4ae65c4 --- /dev/null +++ b/system/Commands/Translation/LocalizationFinder/ArrayHelper.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Commands\Translation\LocalizationFinder; + +class ArrayHelper +{ + /** + * Compare recursive two associative arrays and return new array (difference) + * Returns keys that exist in $original but not in $compareWith + */ + public static function recursiveDiff(array $original, array $compareWith): array + { + $difference = []; + + if ([] === $original) { + return []; + } + + if ([] === $compareWith) { + return $original; + } + + foreach ($original as $originalKey => $originalValue) { + if ([] === $originalValue) { + continue; + } + + if (is_array($originalValue)) { + $diffArrays = []; + + if (isset($compareWith[$originalKey]) && is_array($compareWith[$originalKey])) { + $diffArrays = self::recursiveDiff($originalValue, $compareWith[$originalKey]); + } else { + $difference[$originalKey] = $originalValue; + } + + if ([] !== $diffArrays) { + $difference[$originalKey] = $diffArrays; + } + } elseif (is_string($originalValue) && ! array_key_exists($originalKey, $compareWith)) { + $difference[$originalKey] = $originalValue; + } + } + + return $difference; + } + + /** + * Count all keys (with nested keys) + */ + public static function recursiveCount(array $array, int $counter = 0): int + { + foreach ($array as $value) { + if (is_array($value)) { + $counter = self::recursiveCount($value, $counter); + } + + $counter++; + } + + return $counter; + } +} From ddf73f36d3649b631ed4c913b903848acb05da3d Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 21:08:56 +0300 Subject: [PATCH 34/46] test: Add array helper tests --- .../LocalizationFinder/ArrayHelperTest.php | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 tests/system/Commands/Translation/LocalizationFinder/ArrayHelperTest.php diff --git a/tests/system/Commands/Translation/LocalizationFinder/ArrayHelperTest.php b/tests/system/Commands/Translation/LocalizationFinder/ArrayHelperTest.php new file mode 100644 index 000000000000..499d092521fe --- /dev/null +++ b/tests/system/Commands/Translation/LocalizationFinder/ArrayHelperTest.php @@ -0,0 +1,238 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Commands\TranslationLocalizationFinder; + +use CodeIgniter\Commands\Translation\LocalizationFinder\ArrayHelper; +use CodeIgniter\Test\CIUnitTestCase; + +/** + * @group Others + * + * @internal + */ +final class ArrayHelperTest extends CIUnitTestCase +{ + private array $compareWith; + + protected function setUp(): void + { + $this->compareWith = [ + 'a' => [ + 'b' => [ + 'c' => [ + 'd' => 'value1', + ], + ], + 'e' => 'value2', + 'f' => [ + 'g' => 'value3', + 'h' => 'value4', + 'i' => [ + 'j' => 'value5', + ], + ], + 'k' => null, + 'l' => [], + 'm' => '', + ], + ]; + } + + public function testRecursiveDiffCopy(): void + { + $this->assertSame([], ArrayHelper::recursiveDiff($this->compareWith, $this->compareWith)); + } + + public function testRecursiveDiffShuffleCopy(): void + { + $original = [ + 'a' => [ + 'l' => [], + 'k' => null, + 'e' => 'value2', + 'f' => [ + 'i' => [ + 'j' => 'value5', + ], + 'h' => 'value4', + 'g' => 'value3', + ], + 'm' => '', + 'b' => [ + 'c' => [ + 'd' => 'value1', + ], + ], + ], + ]; + + $this->assertSame([], ArrayHelper::recursiveDiff($original, $this->compareWith)); + } + + public function testRecursiveDiffCopyWithAnotherValues(): void + { + $original = [ + 'a' => [ + 'b' => [ + 'c' => [ + 'd' => 'value1_1', + ], + ], + 'e' => 'value2_2', + 'f' => [ + 'g' => 'value3_3', + 'h' => 'value4_4', + 'i' => [ + 'j' => 'value5_5', + ], + ], + 'k' => [], + 'l' => null, + 'm' => 'value6_6', + ], + ]; + + $this->assertSame([], ArrayHelper::recursiveDiff($original, $this->compareWith)); + } + + public function testRecursiveDiffEmptyCompare(): void + { + $this->assertSame($this->compareWith, ArrayHelper::recursiveDiff($this->compareWith, [])); + } + + public function testRecursiveDiffEmptyOriginal(): void + { + $this->assertSame([], ArrayHelper::recursiveDiff([], $this->compareWith)); + } + + public function testRecursiveDiffCompletelyDifferent(): void + { + $original = [ + 'new_a' => [ + 'new_b' => [ + 'new_c' => [ + 'new_d' => 'value1_1', + ], + ], + 'new_e' => 'value2_2', + 'new_f' => [ + 'new_g' => 'value3_3', + 'new_h' => 'value4_4', + 'new_i' => [ + 'new_j' => 'value5_5', + ], + ], + 'new_k' => [], + 'new_l' => null, + 'new_m' => '', + ], + ]; + + $this->assertSame($original, ArrayHelper::recursiveDiff($original, $this->compareWith)); + } + + public function testRecursiveDiffPartlyDifferent(): void + { + $original = [ + 'a' => [ + 'b' => [ + 'new_c' => [ + 'd' => 'value1', + ], + ], + 'e' => 'value2', + 'f' => [ + 'g' => 'value3', + 'new_h' => 'value4', + 'i' => [ + 'new_j' => 'value5', + ], + ], + 'k' => null, + 'new_l' => [], + 'm' => [ + 'new_n' => '', + ], + ], + ]; + + $diff = [ + 'a' => [ + 'b' => [ + 'new_c' => [ + 'd' => 'value1', + ], + ], + 'f' => [ + 'new_h' => 'value4', + 'i' => [ + 'new_j' => 'value5', + ], + ], + 'm' => [ + 'new_n' => '', + ], + ], + ]; + + $this->assertSame($diff, ArrayHelper::recursiveDiff($original, $this->compareWith)); + } + + public function testRecursiveCountSimple(): void + { + $array = [ + 'a' => 'value1', + 'b' => 'value2', + 'c' => 'value3', + ]; + + $this->assertSame(3, ArrayHelper::recursiveCount($array)); + } + + public function testRecursiveCountNested(): void + { + $array = [ + 'a' => 'value1', + 'b' => [ + 'c' => 'value2', + ], + 'd' => 'value3', + 'e' => [ + 'f' => [ + 'g' => 'value4', + 'h' => [ + 'i' => 'value5', + ], + ], + ], + 'j' => [], + 'k' => null, + ]; + + $this->assertSame(11, ArrayHelper::recursiveCount($array)); + } + + public function testRecursiveCountEqualEmpty(): void + { + $array = [ + 'root' => [ + 'a' => [], + 'b' => false, + 'c' => null, + 'd' => '', + 'e' => 0, + ], + ]; + + $this->assertSame(6, ArrayHelper::recursiveCount($array)); + } +} From 812212333a549d656bdacc070b1f2b766870c7eb Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Fri, 8 Sep 2023 21:14:06 +0300 Subject: [PATCH 35/46] fix: Move methods to array helper class --- .../Translation/LocalizationFinder.php | 56 ++----------------- 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index d601dc67fd36..9152a70da07a 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -13,6 +13,7 @@ use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; +use CodeIgniter\Commands\Translation\LocalizationFinder\ArrayHelper; use Config\App; use Locale; use RecursiveDirectoryIterator; @@ -57,7 +58,7 @@ public function run(array $params) if (ENVIRONMENT === 'testing') { $currentDir = SUPPORTPATH . 'Services/'; - $this->languagePath = SUPPORTPATH . 'Language/'; + $this->languagePath = SUPPORTPATH . 'Language'; } if (is_string($optionLocale)) { @@ -112,7 +113,7 @@ private function findTranslationsInFile($file): array $fileContent = file_get_contents($file->getRealPath()); preg_match_all('/lang\(\'([._a-z0-9\-]+)\'\)/ui', $fileContent, $matches); - if (empty($matches[1])) { + if ([] === $matches[1]) { return []; } @@ -201,38 +202,6 @@ private function buildMultiArray(array $fromKeys, string $lastArrayValue = ''): return $newArray; } - /** - * Compare recursive two arrays and return new array (difference) - */ - private function arrayDiffRecursive(array $originalArray, array $compareArray): array - { - $difference = []; - - if (count($compareArray) < 1) { - return $originalArray; - } - - foreach ($originalArray as $originalKey => $originalValue) { - if (is_array($originalValue)) { - $diffArrays = null; - - if (isset($compareArray[$originalKey])) { - $diffArrays = $this->arrayDiffRecursive($originalValue, $compareArray[$originalKey]); - } else { - $difference[$originalKey] = $originalValue; - } - - if (! empty($diffArrays)) { - $difference[$originalKey] = $diffArrays; - } - } elseif (is_string($originalValue) && ! array_key_exists($originalKey, $compareArray)) { - $difference[$originalKey] = $originalValue; - } - } - - return $difference; - } - /** * Convert multi arrays to specific CLI table rows (flat array) */ @@ -255,19 +224,6 @@ private function arrayToTableRows(string $langFileName, array $array): array return $rows; } - private function arrayCountRecursive(array $array, int $counter = 0): int - { - foreach ($array as $value) { - if (! is_array($value)) { - $counter++; - } else { - $counter = $this->arrayCountRecursive($value, $counter); - } - } - - return $counter; - } - private function languageKeysDump(array $inputArray): string { return var_export($inputArray, true); @@ -317,8 +273,8 @@ private function process(string $currentDir, string $currentLocale): void $languageStoredKeys = require $languageFilePath; } - $languageDiff = $this->arrayDiffRecursive($foundLanguageKeys[$langFileName], $languageStoredKeys); - $countNewKeys += $this->arrayCountRecursive($languageDiff); + $languageDiff = ArrayHelper::recursiveDiff($foundLanguageKeys[$langFileName], $languageStoredKeys); + $countNewKeys += ArrayHelper::recursiveCount($languageDiff); if ($this->showNew) { $tableRows = array_merge($this->arrayToTableRows($langFileName, $languageDiff), $tableRows); @@ -339,7 +295,7 @@ private function process(string $currentDir, string $currentLocale): void } } - if ($this->showNew && ! empty($tableRows)) { + if ($this->showNew && [] !== $tableRows) { sort($tableRows); CLI::table($tableRows, ['File', 'Key']); } From b025df10c462914c00e7336f5316b7bd41a65348 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sat, 9 Sep 2023 22:17:39 +0300 Subject: [PATCH 36/46] fix: Language correction --- system/Commands/Translation/LocalizationFinder.php | 6 +++--- .../Commands/Translation/LocalizationFinder/ArrayHelper.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 9152a70da07a..0c97d2100a9b 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -28,9 +28,9 @@ class LocalizationFinder extends BaseCommand protected $usage = 'lang:find [options]'; protected $arguments = []; protected $options = [ - '--locale' => 'Specify locale (en, ru, etc...) to save files.', - '--dir' => 'Directory for searching for translations (in relation to the APPPATH).', - '--show-new' => 'See only new translations as table. Do not write to files.', + '--locale' => 'Specify locale (en, ru, etc.) to save files.', + '--dir' => 'Directory to search for translations relative to APPPATH.', + '--show-new' => 'Show only new translations in table. Does not write to files.', '--verbose' => 'Output detailed information.', ]; diff --git a/system/Commands/Translation/LocalizationFinder/ArrayHelper.php b/system/Commands/Translation/LocalizationFinder/ArrayHelper.php index b970c4ae65c4..b6cd4d150852 100644 --- a/system/Commands/Translation/LocalizationFinder/ArrayHelper.php +++ b/system/Commands/Translation/LocalizationFinder/ArrayHelper.php @@ -14,8 +14,8 @@ class ArrayHelper { /** - * Compare recursive two associative arrays and return new array (difference) - * Returns keys that exist in $original but not in $compareWith + * Compare recursively two associative arrays and return difference as new array. + * Returns keys that exist in `$original` but not in `$compareWith`. */ public static function recursiveDiff(array $original, array $compareWith): array { @@ -55,7 +55,7 @@ public static function recursiveDiff(array $original, array $compareWith): array } /** - * Count all keys (with nested keys) + * Recursively count all keys. */ public static function recursiveCount(array $array, int $counter = 0): int { From d143a9b472902806bdcf484541cfc344180bd3c1 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sat, 9 Sep 2023 22:56:40 +0300 Subject: [PATCH 37/46] style: Format code --- .../Translation/LocalizationFinder.php | 140 ++++++++---------- 1 file changed, 60 insertions(+), 80 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 0c97d2100a9b..a8d075f0ac83 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -57,7 +57,7 @@ public function run(array $params) $this->languagePath = $currentDir . 'Language'; if (ENVIRONMENT === 'testing') { - $currentDir = SUPPORTPATH . 'Services/'; + $currentDir = SUPPORTPATH . 'Services' . DIRECTORY_SEPARATOR; $this->languagePath = SUPPORTPATH . 'Language'; } @@ -99,6 +99,58 @@ public function run(array $params) return EXIT_SUCCESS; } + private function process(string $currentDir, string $currentLocale): void + { + $tableRows = []; + $countNewKeys = 0; + + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($currentDir)); + $files = iterator_to_array($iterator, true); + ksort($files); + + [$foundLanguageKeys, $countFiles] = $this->findLanguageKeysInFiles($files); + ksort($foundLanguageKeys); + + $languageDiff = []; + $languageFoundGroups = array_unique(array_keys($foundLanguageKeys)); + + foreach ($languageFoundGroups as $langFileName) { + $languageStoredKeys = []; + $languageFilePath = $this->languagePath . DIRECTORY_SEPARATOR . $currentLocale . DIRECTORY_SEPARATOR . $langFileName . '.php'; + + if (is_file($languageFilePath)) { + // Load old localization + $languageStoredKeys = require $languageFilePath; + } + + $languageDiff = ArrayHelper::recursiveDiff($foundLanguageKeys[$langFileName], $languageStoredKeys); + $countNewKeys += ArrayHelper::recursiveCount($languageDiff); + + if ($this->showNew) { + $tableRows = array_merge($this->arrayToTableRows($langFileName, $languageDiff), $tableRows); + } else { + $newLanguageKeys = array_replace_recursive($foundLanguageKeys[$langFileName], $languageStoredKeys); + + if ($languageDiff !== []) { + if (false === file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys))) { + $this->writeIsVerbose('Lang file ' . $langFileName . ' (error write).', 'red'); + } else { + exec('composer cs-fix --quiet ' . $this->languagePath); + $this->writeIsVerbose('Lang file "' . $langFileName . '" successful updated!', 'green'); + } + } + } + } + + if ($this->showNew && $tableRows !== []) { + sort($tableRows); + CLI::table($tableRows, ['File', 'Key']); + } + + $this->writeIsVerbose('Files found: ' . $countFiles); + $this->writeIsVerbose('New translates found: ' . $countNewKeys); + } + /** * @param SplFileInfo|string $file */ @@ -113,16 +165,14 @@ private function findTranslationsInFile($file): array $fileContent = file_get_contents($file->getRealPath()); preg_match_all('/lang\(\'([._a-z0-9\-]+)\'\)/ui', $fileContent, $matches); - if ([] === $matches[1]) { + if ($matches[1] === []) { return []; } foreach ($matches[1] as $phraseKey) { $phraseKeys = explode('.', $phraseKey); - /** - * Language key not have Filename or Lang key - */ + // Language key not have Filename or Lang key if (count($phraseKeys) < 2) { continue; } @@ -136,16 +186,11 @@ private function findTranslationsInFile($file): array continue; } - /** - * Language key as string - */ if (count($phraseKeys) === 1) { - /** - * Add found language keys to temporary array - */ $foundLanguageKeys[$languageFileName][$phraseKeys[0]] = $phraseKey; } else { - $childKeys = $this->buildMultiArray($phraseKeys, $phraseKey); + $childKeys = $this->buildMultiArray($phraseKeys, $phraseKey); + $foundLanguageKeys[$languageFileName] = array_replace_recursive($foundLanguageKeys[$languageFileName] ?? [], $childKeys); } } @@ -159,13 +204,13 @@ private function isIgnoredFile(SplFileInfo $file): bool return true; } - return 'php' !== $file->getExtension(); + return $file->getExtension() !== 'php'; } private function templateFile(array $language = []): string { - if ([] !== $language) { - $languageArrayString = $this->languageKeysDump($language); + if ($language !== []) { + $languageArrayString = var_export($language, true); return <<findLanguageKeysInFiles($files); - ksort($foundLanguageKeys); - - /** - * New translates - */ - $languageDiff = []; - $languageFoundGroups = array_unique(array_keys($foundLanguageKeys)); - - foreach ($languageFoundGroups as $langFileName) { - $languageStoredKeys = []; - $languageFilePath = $this->languagePath . DIRECTORY_SEPARATOR . $currentLocale . DIRECTORY_SEPARATOR . $langFileName . '.php'; - - if (is_file($languageFilePath)) { - /** - * Load old localization - */ - $languageStoredKeys = require $languageFilePath; - } - - $languageDiff = ArrayHelper::recursiveDiff($foundLanguageKeys[$langFileName], $languageStoredKeys); - $countNewKeys += ArrayHelper::recursiveCount($languageDiff); - - if ($this->showNew) { - $tableRows = array_merge($this->arrayToTableRows($langFileName, $languageDiff), $tableRows); - } else { - $newLanguageKeys = array_replace_recursive($foundLanguageKeys[$langFileName], $languageStoredKeys); - - /** - * New translates exists - */ - if ($languageDiff !== []) { - if (false === file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys))) { - $this->writeIsVerbose('Lang file ' . $langFileName . ' (error write).', 'red'); - } else { - exec('composer cs-fix --quiet ' . $this->languagePath); - $this->writeIsVerbose('Lang file "' . $langFileName . '" successful updated!', 'green'); - } - } - } - } - - if ($this->showNew && [] !== $tableRows) { - sort($tableRows); - CLI::table($tableRows, ['File', 'Key']); - } - - $this->writeIsVerbose('Files found: ' . $countFiles); - $this->writeIsVerbose('New translates found: ' . $countNewKeys); - } - /** * @param SplFileInfo[] $files * From 1026314a2f556b06e5d7c524bea5c1bf8ff37511 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sat, 9 Sep 2023 22:56:47 +0300 Subject: [PATCH 38/46] style: Format code --- .../Translation/LocalizationFinder/ArrayHelper.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder/ArrayHelper.php b/system/Commands/Translation/LocalizationFinder/ArrayHelper.php index b6cd4d150852..d747127c3a42 100644 --- a/system/Commands/Translation/LocalizationFinder/ArrayHelper.php +++ b/system/Commands/Translation/LocalizationFinder/ArrayHelper.php @@ -21,16 +21,16 @@ public static function recursiveDiff(array $original, array $compareWith): array { $difference = []; - if ([] === $original) { + if ($original === []) { return []; } - if ([] === $compareWith) { + if ($compareWith === []) { return $original; } foreach ($original as $originalKey => $originalValue) { - if ([] === $originalValue) { + if ($originalValue === []) { continue; } @@ -43,7 +43,7 @@ public static function recursiveDiff(array $original, array $compareWith): array $difference[$originalKey] = $originalValue; } - if ([] !== $diffArrays) { + if ($diffArrays !== []) { $difference[$originalKey] = $diffArrays; } } elseif (is_string($originalValue) && ! array_key_exists($originalKey, $compareWith)) { From db1567edd87cb86571987c58b4b4fb4f75fd29c6 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sat, 9 Sep 2023 23:18:00 +0300 Subject: [PATCH 39/46] tests: Compliance with the project rules --- .../Translation/LocalizationFinderTest.php | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/tests/system/Commands/Translation/LocalizationFinderTest.php b/tests/system/Commands/Translation/LocalizationFinderTest.php index 5d61c4c19bbb..f5cc38ec7b2f 100644 --- a/tests/system/Commands/Translation/LocalizationFinderTest.php +++ b/tests/system/Commands/Translation/LocalizationFinderTest.php @@ -33,7 +33,7 @@ protected function setUp(): void { parent::setUp(); self::$locale = Locale::getDefault(); - self::$languageTestPath = SUPPORTPATH . 'Language/'; + self::$languageTestPath = SUPPORTPATH . 'Language' . DIRECTORY_SEPARATOR; } protected function tearDown(): void @@ -46,11 +46,9 @@ public function testUpdateDefaultLocale(): void { $this->makeLocaleDirectory(); - Services::commands()->run('lang:find', [ - 'dir' => 'Translation', - ]); + command('lang:find --dir Translation'); - $this->realizeAssertion(); + $this->assertTranslationsExistAndHaveTranslatedKeys(); } public function testUpdateWithLocaleOption(): void @@ -58,12 +56,9 @@ public function testUpdateWithLocaleOption(): void self::$locale = config(App::class)->supportedLocales[0]; $this->makeLocaleDirectory(); - Services::commands()->run('lang:find', [ - 'dir' => 'Translation', - 'locale' => self::$locale, - ]); + command('lang:find --dir Translation --locale ' . self::$locale); - $this->realizeAssertion(); + $this->assertTranslationsExistAndHaveTranslatedKeys(); } public function testUpdateWithIncorrectLocaleOption(): void @@ -76,16 +71,16 @@ public function testUpdateWithIncorrectLocaleOption(): void 'locale' => self::$locale, ]); - $this->assertSame($status, EXIT_USER_INPUT); + $this->assertSame(EXIT_USER_INPUT, $status); } public function testUpdateWithEmptyDirOption(): void { $this->makeLocaleDirectory(); - Services::commands()->run('lang:find', []); + command('lang:find'); - $this->realizeAssertion(); + $this->assertTranslationsExistAndHaveTranslatedKeys(); } public function testUpdateWithIncorrectDirOption(): void @@ -96,17 +91,14 @@ public function testUpdateWithIncorrectDirOption(): void 'dir' => 'Translation/NotExistFolder', ]); - $this->assertSame($status, EXIT_USER_INPUT); + $this->assertSame(EXIT_USER_INPUT, $status); } public function testShowNewTranslation(): void { $this->makeLocaleDirectory(); - Services::commands()->run('lang:find', [ - 'dir' => 'Translation', - 'show-new' => null, - ]); + command('lang:find --dir Translation --show-new'); $this->assertStringContainsString($this->getActualTableWithNewKeys(), $this->getStreamFilterBuffer()); } @@ -201,7 +193,7 @@ private function getActualTableWithNewKeys(): string TEXT_WRAP; } - private function realizeAssertion(): void + private function assertTranslationsExistAndHaveTranslatedKeys(): void { $this->assertFileExists(self::$languageTestPath . self::$locale . '/TranslationOne.php'); $this->assertFileExists(self::$languageTestPath . self::$locale . '/TranslationThree.php'); From 1c16cb4a332f2fab248115f22f417155298a2dba Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sat, 9 Sep 2023 23:30:36 +0300 Subject: [PATCH 40/46] fix: Added a warning instead of cs-fix --- system/Commands/Translation/LocalizationFinder.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index a8d075f0ac83..a939b7d178be 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -135,7 +135,6 @@ private function process(string $currentDir, string $currentLocale): void if (false === file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys))) { $this->writeIsVerbose('Lang file ' . $langFileName . ' (error write).', 'red'); } else { - exec('composer cs-fix --quiet ' . $this->languagePath); $this->writeIsVerbose('Lang file "' . $langFileName . '" successful updated!', 'green'); } } @@ -147,6 +146,10 @@ private function process(string $currentDir, string $currentLocale): void CLI::table($tableRows, ['File', 'Key']); } + if (! $this->showNew && $countNewKeys > 0) { + CLI::write('Note: You need to run your linting tool to fix coding standards issues.', 'white', 'red'); + } + $this->writeIsVerbose('Files found: ' . $countFiles); $this->writeIsVerbose('New translates found: ' . $countNewKeys); } From 48f8f6515707f98f19b6ae7c723ae81d4b840a5c Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sat, 9 Sep 2023 23:38:13 +0300 Subject: [PATCH 41/46] style: Format code --- tests/_support/Services/Translation/TranslationTwo.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/_support/Services/Translation/TranslationTwo.php b/tests/_support/Services/Translation/TranslationTwo.php index be40786b5ee2..71e5e663b27e 100644 --- a/tests/_support/Services/Translation/TranslationTwo.php +++ b/tests/_support/Services/Translation/TranslationTwo.php @@ -17,9 +17,7 @@ public function list() { $langKey = 'TranslationTwo.error_key'; - /** - * Error language keys - */ + // Error language keys $translationError1 = lang('TranslationTwo'); $translationError2 = lang(' '); $translationError3 = lang(''); @@ -28,8 +26,6 @@ public function list() $translationError6 = lang('TranslationTwo...'); $translationError7 = lang('..invalid_nested_key..'); - /** - * Empty in comments lang('') lang(' ') - */ + // Empty in comments lang('') lang(' ') } } From 7bb80d90074622f5db2c109ab7f8d8eaf8208fcf Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sun, 10 Sep 2023 09:19:58 +0300 Subject: [PATCH 42/46] fix: Replace array syntax --- .../Translation/LocalizationFinder.php | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index a939b7d178be..94aea443b424 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -215,12 +215,14 @@ private function templateFile(array $language = []): string if ($language !== []) { $languageArrayString = var_export($language, true); - return <<replaceArraySyntax($code); } return <<<'PHP' @@ -231,6 +233,47 @@ private function templateFile(array $language = []): string PHP; } + private function replaceArraySyntax(string $code): string + { + $tokens = token_get_all($code); + $newTokens = $tokens; + + foreach ($tokens as $i => $token) { + if (is_array($token)) { + [$tokenId, $tokenValue] = $token; + + // Replace "array (" + if ( + $tokenId === T_ARRAY + && $tokens[$i + 1][0] === T_WHITESPACE + && $tokens[$i + 2] === '(' + ) { + $newTokens[$i][1] = '['; + $newTokens[$i + 1][1] = ''; + $newTokens[$i + 2] = ''; + } + + // Replace indent + if ($tokenId === T_WHITESPACE) { + if (preg_match('/\n([ ]+)/u', $tokenValue, $matches)) { + $newTokens[$i][1] = "\n{$matches[1]}{$matches[1]}"; + } + } + } // Replace ")" + elseif ($token === ')') { + $newTokens[$i] = ']'; + } + } + + $output = ''; + + foreach ($newTokens as $token) { + $output .= $token[1] ?? $token; + } + + return $output; + } + /** * Create multidimensional array from another keys */ From e22599ae388813c6a5cc2d44f3e893863d7b0657 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Mon, 11 Sep 2023 07:58:40 +0300 Subject: [PATCH 43/46] docs: add formatting advice --- user_guide_src/source/outgoing/localization.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/user_guide_src/source/outgoing/localization.rst b/user_guide_src/source/outgoing/localization.rst index e0c1dd4dc4d1..7fc10d81f752 100644 --- a/user_guide_src/source/outgoing/localization.rst +++ b/user_guide_src/source/outgoing/localization.rst @@ -273,6 +273,8 @@ Previously saved keys do not change. .. literalinclude:: localization/019.php +The language files generated will most likely not conform to your coding standards. +It is recommended to format them. For example, run ``vendor/bin/php-cs-fixer fix ./app/Language`` if ``php-cs-fixer`` is installed. .. note:: The command ignores **app/Language/** path. From d675308b49cd5c0609685c53ba43f5b651497296 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Mon, 11 Sep 2023 08:23:32 +0300 Subject: [PATCH 44/46] style: compliance with phpstan strict rules --- system/Commands/Translation/LocalizationFinder.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 94aea443b424..636a193c7a2f 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -181,9 +181,9 @@ private function findTranslationsInFile($file): array } $languageFileName = array_shift($phraseKeys); - $isEmptyNestedArray = (! empty($languageFileName) && empty($phraseKeys[0])) - || (empty($languageFileName) && ! empty($phraseKeys[0])) - || (empty($languageFileName) && empty($phraseKeys[0])); + $isEmptyNestedArray = ($languageFileName !== '' && $phraseKeys[0] === '') + || ($languageFileName === '' && $phraseKeys[0] !== '') + || ($languageFileName === '' && $phraseKeys[0] === ''); if ($isEmptyNestedArray) { continue; From 935b3878b5f6e680b040547444dc849f531a4926 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Mon, 11 Sep 2023 08:33:37 +0300 Subject: [PATCH 45/46] style: compliance with rector rules --- system/Commands/Translation/LocalizationFinder.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/system/Commands/Translation/LocalizationFinder.php b/system/Commands/Translation/LocalizationFinder.php index 636a193c7a2f..e5147093d2dc 100644 --- a/system/Commands/Translation/LocalizationFinder.php +++ b/system/Commands/Translation/LocalizationFinder.php @@ -20,6 +20,9 @@ use RecursiveIteratorIterator; use SplFileInfo; +/** + * @see \CodeIgniter\Commands\Translation\LocalizationFinderTest + */ class LocalizationFinder extends BaseCommand { protected $group = 'Translation'; @@ -254,10 +257,8 @@ private function replaceArraySyntax(string $code): string } // Replace indent - if ($tokenId === T_WHITESPACE) { - if (preg_match('/\n([ ]+)/u', $tokenValue, $matches)) { - $newTokens[$i][1] = "\n{$matches[1]}{$matches[1]}"; - } + if ($tokenId === T_WHITESPACE && preg_match('/\n([ ]+)/u', $tokenValue, $matches)) { + $newTokens[$i][1] = "\n{$matches[1]}{$matches[1]}"; } } // Replace ")" elseif ($token === ')') { From 070d7e59cc1d450927ec672549240d6ff70447a8 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Thu, 14 Sep 2023 07:59:16 +0300 Subject: [PATCH 46/46] docs: update note in localization.rst --- user_guide_src/source/outgoing/localization.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/outgoing/localization.rst b/user_guide_src/source/outgoing/localization.rst index 7fc10d81f752..837807989b0d 100644 --- a/user_guide_src/source/outgoing/localization.rst +++ b/user_guide_src/source/outgoing/localization.rst @@ -273,12 +273,11 @@ Previously saved keys do not change. .. literalinclude:: localization/019.php +.. note:: When the command scans folders, **app/Language** will be skipped. + The language files generated will most likely not conform to your coding standards. It is recommended to format them. For example, run ``vendor/bin/php-cs-fixer fix ./app/Language`` if ``php-cs-fixer`` is installed. -.. note:: The command ignores **app/Language/** path. - - Before updating, it is possible to preview the translations found by the command: .. code-block:: console