From 23c2cd6ad5de287d82554b1374d40b7273a28cc0 Mon Sep 17 00:00:00 2001 From: Gaetan <72258504+gaetan-hexadog@users.noreply.github.com> Date: Wed, 17 Nov 2021 19:28:15 +0100 Subject: [PATCH 01/10] feat(*): add new filename option in unused cmd #1 : New option to process only language files with the given name --- src/Console/Commands/MissingCommand.php | 5 ++-- src/Console/Commands/UnusedCommand.php | 7 +++--- src/TranslationManager.php | 32 ++++++++++++++++--------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/Console/Commands/MissingCommand.php b/src/Console/Commands/MissingCommand.php index 3a8fa71..611a0cb 100644 --- a/src/Console/Commands/MissingCommand.php +++ b/src/Console/Commands/MissingCommand.php @@ -2,6 +2,7 @@ namespace Hexadog\TranslationManager\Console\Commands; +use Hexadog\TranslationManager\Facades\TranslationManager; use Illuminate\Console\Command; class MissingCommand extends Command @@ -35,9 +36,9 @@ public function handle() { $result = []; - $strings = \TranslationManager::findMissing($this->option('namespace'), $this->option('lang'), boolval($this->option('fix'))); + $strings = TranslationManager::findMissing($this->option('namespace'), $this->option('lang'), boolval($this->option('fix'))); $total = 0; - + foreach ($strings as $lang => $namespaces) { foreach ($namespaces as $namespace => $strings) { foreach ($strings as $string) { diff --git a/src/Console/Commands/UnusedCommand.php b/src/Console/Commands/UnusedCommand.php index 5ec9a80..64bafe4 100644 --- a/src/Console/Commands/UnusedCommand.php +++ b/src/Console/Commands/UnusedCommand.php @@ -2,6 +2,7 @@ namespace Hexadog\TranslationManager\Console\Commands; +use Hexadog\TranslationManager\Facades\TranslationManager; use Illuminate\Console\Command; use Illuminate\Support\Arr; @@ -12,7 +13,7 @@ class UnusedCommand extends Command * * @var string */ - protected $signature = 'translation:unused {--namespace=*} {--l|lang=*}'; + protected $signature = 'translation:unused {--namespace=*} {--l|lang=*} {--f|filename=} '; /** * The console command description. @@ -36,13 +37,13 @@ public function handle() { $result = []; - $strings = \TranslationManager::findUnused($this->option('namespace'), $this->option('lang')); + $strings = TranslationManager::findUnused($this->option('namespace'), $this->option('lang'), $this->option('filename')); $total = 0; foreach ($strings as $lang => $namespaces) { foreach ($namespaces as $namespace => $translations) { foreach ($translations as $key => $string) { - if (! is_array($string)) { + if (!is_array($string)) { $result[] = [ 'lang' => $lang, 'namespace' => $namespace, diff --git a/src/TranslationManager.php b/src/TranslationManager.php index b8a6950..a3f31ec 100644 --- a/src/TranslationManager.php +++ b/src/TranslationManager.php @@ -80,12 +80,12 @@ public function getSupportedLanguages() */ public function addSupportedLanguage($lang) { - if (! is_array($lang)) { + if (!is_array($lang)) { $lang = [$lang]; } foreach ($lang as $l) { - if (! in_array($l, $this->languages)) { + if (!in_array($l, $this->languages)) { $this->languages[] = $l; } } @@ -107,7 +107,7 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa $usedStrings = $this->extractor->extract(); // If no language provided, search for all languages supported by application - if (is_null($languages) || (is_array($languages) && ! count($languages))) { + if (is_null($languages) || (is_array($languages) && !count($languages))) { $languages = $this->getSupportedLanguages(); } @@ -116,10 +116,11 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa // Check if translation exists for each languages foreach ($languages as $language) { $missingStrings[$language] = []; + foreach ($hints as $namespace => $path) { $missingStrings[$language][$namespace] = $this->sortIfEnabled($usedStrings->filter(function ($key) use ($language, $namespace) { if ($namespace === '' || Str::startsWith($key, $namespace . '::')) { - return ! $this->translator->hasForLocale($key, $language); + return !$this->translator->hasForLocale($key, $language); } return false; @@ -162,17 +163,17 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa * * @param string $namespaces * @param array|string $languages - * @param bool $autoClean + * @param string|null $filename Limit research in language file named as given * * @return array */ - public function findUnused($namespaces = null, $languages = null, bool $autoClean = false): array + public function findUnused($namespaces = null, $languages = null, $filename = null): array { $unusedStrings = []; $usedStrings = $this->extractor->extract(); // If no language provided, search for all languages supported by application - if (is_null($languages) || (is_array($languages) && ! count($languages))) { + if (is_null($languages) || (is_array($languages) && !count($languages))) { $languages = $this->getSupportedLanguages(); } @@ -180,6 +181,7 @@ public function findUnused($namespaces = null, $languages = null, bool $autoClea // Filter used strings based on requested namespaces $strings = []; + foreach ($hints as $namespace => $path) { // Filter used strings to only keep requested namespaces $usedStrings->each(function ($key) use ($namespace, &$strings) { @@ -192,11 +194,16 @@ public function findUnused($namespaces = null, $languages = null, bool $autoClea // check translation usage all supported languages foreach ($languages as $language) { $unusedStrings[$language] = []; + foreach ($hints as $namespace => $path) { $unusedStrings[$language][$namespace] = []; $files = $this->finder->find("$path/$language", "php"); foreach ($files as $file) { + if (!is_null($filename) && $file->getBasename('.php') !== $filename) { + continue; + } + // Get all strings in namespace $translations = include $file->getPathname(); @@ -206,13 +213,15 @@ public function findUnused($namespaces = null, $languages = null, bool $autoClea if (is_array($value)) { foreach (Arr::dot($value) as $k => $val) { $searchKey = $namespace !== '' ? $namespace . '::' . $key . '.' . $k : $key . '.' . $k; - if (! in_array($searchKey, $strings)) { + + if (!in_array($searchKey, $strings)) { $unusedStrings[$language][$namespace][$key . '.' . $k] = $val; } } } else { $searchKey = $namespace !== '' ? $namespace . '::' . $key : $key; - if (! in_array($searchKey, $strings)) { + + if (!in_array($searchKey, $strings)) { $unusedStrings[$language][$namespace][$key] = $value; } } @@ -278,6 +287,7 @@ protected function prepareNamespaces($namespaces = null): Collection // Get Translator namespaces $loader = $this->translator->getLoader(); + if ($loader) { foreach ($loader->namespaces() as $hint => $path) { $namespacesCollection->put($hint, $path); @@ -336,7 +346,7 @@ protected function getTranslationFilePath($key, $locale = 'en') } $filePath = "{$hintPath}/{$group}.php"; - if (! $this->files->exists($filePath)) { + if (!$this->files->exists($filePath)) { // TODO: create file if not exists yet File::put($filePath, "stringLineMaker($translations); $content .= "\n];"; - + return file_put_contents($path, $content); } From f25c623513f6d2ace42b236b612a3637b4981577 Mon Sep 17 00:00:00 2001 From: Gaetan <72258504+gaetan-hexadog@users.noreply.github.com> Date: Wed, 17 Nov 2021 19:51:14 +0100 Subject: [PATCH 02/10] chore(style): update php-cs-fixer dependency --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4a8662f..fac9a52 100755 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "symfony/finder": "^5.1" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.18", + "friendsofphp/php-cs-fixer": "^3.1", "squizlabs/php_codesniffer": "3.*", "phpunit/phpunit": "^7.0|^8.0|^9.0", "orchestra/testbench": "~5.2" From ab23f2b71977287615d59c89c4f8e5d0e323bbd8 Mon Sep 17 00:00:00 2001 From: Gaetan <72258504+gaetan-hexadog@users.noreply.github.com> Date: Wed, 17 Nov 2021 19:51:52 +0100 Subject: [PATCH 03/10] chore(style): update php-cs-fixer config file --- .php-cs-fixer.dist.php | 27 +++++++++++++++++++++++++ .php_cs.dist | 45 ------------------------------------------ 2 files changed, 27 insertions(+), 45 deletions(-) create mode 100644 .php-cs-fixer.dist.php delete mode 100644 .php_cs.dist diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..d46f825 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,27 @@ +notPath('vendor/*') + ->notPath('resources/*') + ->notPath('database/*') + ->notPath('storage/*') + ->notPath('node_modules/*') + ->in([ + __DIR__ . '/config', + __DIR__ . '/src', + ]) + ->name('*.php') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +$config = new PhpCsFixer\Config(); +return $config + ->setRules([ + '@PSR2' => true, + '@PhpCsFixer' => true, + 'concat_space' => [ + 'spacing' => 'one' + ] + ]) + ->setFinder($finder); diff --git a/.php_cs.dist b/.php_cs.dist deleted file mode 100644 index cdb2272..0000000 --- a/.php_cs.dist +++ /dev/null @@ -1,45 +0,0 @@ -notPath('vendor/*') - ->notPath('resources/*') - ->notPath('database/*') - ->notPath('storage/*') - ->notPath('node_modules/*') - ->in([ - __DIR__ . '/config', - __DIR__ . '/src', - ]) - ->name('*.php') - ->notName('*.blade.php') - ->ignoreDotFiles(true) - ->ignoreVCS(true); - -return PhpCsFixer\Config::create() - ->setRules([ - '@PSR2' => true, - 'array_syntax' => ['syntax' => 'short'], - 'ordered_imports' => ['sortAlgorithm' => 'alpha'], - 'no_unused_imports' => true, - 'not_operator_with_successor_space' => true, - 'trailing_comma_in_multiline_array' => true, - 'phpdoc_scalar' => true, - 'unary_operator_spaces' => true, - 'binary_operator_spaces' => true, - 'blank_line_before_statement' => [ - 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], - ], - 'phpdoc_single_line_var_spacing' => true, - 'phpdoc_var_without_name' => true, - 'class_attributes_separation' => [ - 'elements' => [ - 'method', - ], - ], - 'method_argument_space' => [ - 'on_multiline' => 'ensure_fully_multiline', - 'keep_multiple_spaces_after_comma' => true, - ], - 'single_trait_insert_per_statement' => true, - ]) - ->setFinder($finder); From 32f8069f6c32326a09b21acbeadbf781d4c986ba Mon Sep 17 00:00:00 2001 From: Gaetan <72258504+gaetan-hexadog@users.noreply.github.com> Date: Wed, 17 Nov 2021 19:52:35 +0100 Subject: [PATCH 04/10] chore(git): update gitignore rules --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c5db289..1dd899c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ composer.lock .php_cs.cache build/ .DS_Store -.cache \ No newline at end of file +.cache +*.cache From 19374714a071c33e14e4cf022ebdfea39ce935d3 Mon Sep 17 00:00:00 2001 From: Gaetan <72258504+gaetan-hexadog@users.noreply.github.com> Date: Wed, 17 Nov 2021 19:52:55 +0100 Subject: [PATCH 05/10] chore(style): fix code style --- src/Console/Commands/MissingCommand.php | 5 +- src/Console/Commands/UnusedCommand.php | 5 +- src/Extractor.php | 10 +- src/Finder.php | 9 +- src/Parser.php | 15 +-- src/Providers/PackageServiceProvider.php | 65 +++++----- src/TranslationManager.php | 148 +++++++++++------------ 7 files changed, 120 insertions(+), 137 deletions(-) diff --git a/src/Console/Commands/MissingCommand.php b/src/Console/Commands/MissingCommand.php index 611a0cb..bbbb455 100644 --- a/src/Console/Commands/MissingCommand.php +++ b/src/Console/Commands/MissingCommand.php @@ -29,8 +29,7 @@ class MissingCommand extends Command protected $headers = ['Lang', 'Namespace', 'String']; /** - * Prompt for module's alias name - * + * Prompt for module's alias name. */ public function handle() { @@ -48,7 +47,7 @@ public function handle() 'string' => $string, ]; - $total++; + ++$total; } } } diff --git a/src/Console/Commands/UnusedCommand.php b/src/Console/Commands/UnusedCommand.php index 64bafe4..5b7cfbf 100644 --- a/src/Console/Commands/UnusedCommand.php +++ b/src/Console/Commands/UnusedCommand.php @@ -30,8 +30,7 @@ class UnusedCommand extends Command protected $headers = ['Lang', 'Namespace', 'Key', 'String']; /** - * Prompt for module's alias name - * + * Prompt for module's alias name. */ public function handle() { @@ -61,7 +60,7 @@ public function handle() } } - $total++; + ++$total; } } } diff --git a/src/Extractor.php b/src/Extractor.php index bedfcdf..ffefdb9 100644 --- a/src/Extractor.php +++ b/src/Extractor.php @@ -10,14 +10,14 @@ class Extractor implements ContractsExtractor { /** - * Extractor Finder + * Extractor Finder. * * @var Finder */ protected $finder; - + /** - * Extractor parser + * Extractor parser. * * @var Parser */ @@ -31,8 +31,6 @@ public function __construct(Finder $finder, Parser $parser) /** * Extract translatable strings from the files. - * - * @return Collection */ public function extract(): Collection { @@ -40,7 +38,7 @@ public function extract(): Collection // List files $files = $this->finder->find(); - + // Get all translatable strings from files foreach ($files as $file) { $strings = array_merge($strings, $this->parser->parse($file)); diff --git a/src/Finder.php b/src/Finder.php index 9cf17b1..273b874 100644 --- a/src/Finder.php +++ b/src/Finder.php @@ -9,14 +9,14 @@ class Finder implements ContractsFinder { /** - * Directories to search in + * Directories to search in. * * @var array */ protected $directories; /** - * File Extensions to search for + * File Extensions to search for. * * @var array */ @@ -34,7 +34,10 @@ public function __construct() /** * Find all files that can contain translatable strings. * - * @return SymfonyFinder|null + * @param null|mixed $directories + * @param null|mixed $extensions + * + * @return null|SymfonyFinder */ public function find($directories = null, $extensions = null) { diff --git a/src/Parser.php b/src/Parser.php index 6e2eb3d..e929f9f 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -9,14 +9,14 @@ class Parser implements ContractsParser { /** - * Translation function names + * Translation function names. * * @var array */ protected $functions; /** - * Translation function pattern + * Translation function pattern. * * @var string */ @@ -33,24 +33,21 @@ public function __construct() /** * Parse a file in order to find translatable strings. - * - * @param \Symfony\Component\Finder\SplFileInfo $file - * @return array */ public function parse(SplFileInfo $file): array { $strings = []; $data = $file->getContents(); - - if (! preg_match_all($this->pattern, $data, $matches, PREG_OFFSET_CAPTURE)) { + + if (!preg_match_all($this->pattern, $data, $matches, PREG_OFFSET_CAPTURE)) { // If pattern not found return return $strings; } - + foreach (current($matches) as $match) { preg_match($this->pattern, $match[0], $string); - + $strings[] = $string[2]; } diff --git a/src/Providers/PackageServiceProvider.php b/src/Providers/PackageServiceProvider.php index 8fb8e40..c44e944 100644 --- a/src/Providers/PackageServiceProvider.php +++ b/src/Providers/PackageServiceProvider.php @@ -18,48 +18,22 @@ class PackageServiceProvider extends ServiceProvider { /** - * Our root directory for this package to make traversal easier + * Our root directory for this package to make traversal easier. */ - const PACKAGE_DIR = __DIR__ . '/../../'; + public const PACKAGE_DIR = __DIR__ . '/../../'; /** - * Name for this package to publish assets + * Name for this package to publish assets. */ - const PACKAGE_NAME = 'translation-manager'; + public const PACKAGE_NAME = 'translation-manager'; /** - * Pblishers list + * Pblishers list. */ protected $publishers = []; - /** - * Get Package absolute path - * - * @param string $path - * @return void - */ - protected function getPath($path = '') - { - // We get the child class - $rc = new ReflectionClass(get_class($this)); - - return dirname($rc->getFileName()) . '/../../' . $path; - } - - /** - * Get Module normalized namespace - * - * @return void - */ - protected function getNormalizedNamespace($prefix = '') - { - return Str::start(Str::lower(self::PACKAGE_NAME), $prefix); - } - /** * Bootstrap the application events. - * - * @return void */ public function boot(Router $router) { @@ -87,7 +61,30 @@ public function register(): void } /** - * Bootstrap our Configs + * Get Package absolute path. + * + * @param string $path + */ + protected function getPath($path = '') + { + // We get the child class + $rc = new ReflectionClass(get_class($this)); + + return dirname($rc->getFileName()) . '/../../' . $path; + } + + /** + * Get Module normalized namespace. + * + * @param mixed $prefix + */ + protected function getNormalizedNamespace($prefix = '') + { + return Str::start(Str::lower(self::PACKAGE_NAME), $prefix); + } + + /** + * Bootstrap our Configs. */ protected function registerConfigs() { @@ -101,7 +98,7 @@ protected function registerConfigs() protected function strapCommands() { - if ($this->app->runningInConsole() || config('app.env') == 'testing') { + if ($this->app->runningInConsole() || 'testing' == config('app.env')) { $this->commands([ Commands\MissingCommand::class, Commands\UnusedCommand::class, @@ -110,7 +107,7 @@ protected function strapCommands() } /** - * Bootstrap our Publishers + * Bootstrap our Publishers. */ protected function strapPublishers() { diff --git a/src/TranslationManager.php b/src/TranslationManager.php index a3f31ec..5c7d93a 100644 --- a/src/TranslationManager.php +++ b/src/TranslationManager.php @@ -15,36 +15,35 @@ class TranslationManager extends NamespacedItemResolver { - /** - * Strings extractor - * - * @var Extractor - */ - private $extractor; - /** * The filesystem instance. * * @var \Illuminate\Filesystem\Filesystem */ protected $files; + /** + * Strings extractor. + * + * @var Extractor + */ + private $extractor; /** - * Files Finder + * Files Finder. * * @var Finder */ private $finder; /** - * Supported languages + * Supported languages. * * @var array */ private $languages = ['en']; /** - * Undocumented variable + * Undocumented variable. * * @var [type] */ @@ -73,10 +72,9 @@ public function getSupportedLanguages() } /** - * Undocumented function + * Undocumented function. * * @param [type] $lang - * @return void */ public function addSupportedLanguage($lang) { @@ -92,12 +90,11 @@ public function addSupportedLanguage($lang) } /** - * Find missing translations for namespace(s) by language(s) + * Find missing translations for namespace(s) by language(s). * * @param string $namespaces * @param string $languages - * - * @return array + * @param mixed $autoFix */ public function findMissing($namespaces = null, $languages = null, $autoFix = false): array { @@ -119,19 +116,18 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa foreach ($hints as $namespace => $path) { $missingStrings[$language][$namespace] = $this->sortIfEnabled($usedStrings->filter(function ($key) use ($language, $namespace) { - if ($namespace === '' || Str::startsWith($key, $namespace . '::')) { + if ('' === $namespace || Str::startsWith($key, $namespace . '::')) { return !$this->translator->hasForLocale($key, $language); } return false; })->mapWithKeys(function ($value, $key) use ($namespace) { - return [$key => $namespace !== '' ? preg_replace('/^' . $namespace . '::/', '', $value) : '']; + return [$key => '' !== $namespace ? preg_replace('/^' . $namespace . '::/', '', $value) : '']; })->toArray()); } } - $missingStrings = $this->arrayUniqueRecursive($missingStrings); - + return $this->arrayUniqueRecursive($missingStrings); // if ($autoFix) { // foreach ($missingStrings as $lang => $namespaces) { // foreach ($namespaces as $namespace => $strings) { @@ -154,18 +150,14 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa // } // } // } - - return $missingStrings; } /** - * Find all unused strings declared in resources files + * Find all unused strings declared in resources files. * - * @param string $namespaces + * @param string $namespaces * @param array|string $languages - * @param string|null $filename Limit research in language file named as given - * - * @return array + * @param null|string $filename Limit research in language file named as given */ public function findUnused($namespaces = null, $languages = null, $filename = null): array { @@ -185,7 +177,7 @@ public function findUnused($namespaces = null, $languages = null, $filename = nu foreach ($hints as $namespace => $path) { // Filter used strings to only keep requested namespaces $usedStrings->each(function ($key) use ($namespace, &$strings) { - if ($namespace === '' || Str::startsWith($key, $namespace . '::')) { + if ('' === $namespace || Str::startsWith($key, $namespace . '::')) { $strings[] = $key; } }); @@ -197,7 +189,7 @@ public function findUnused($namespaces = null, $languages = null, $filename = nu foreach ($hints as $namespace => $path) { $unusedStrings[$language][$namespace] = []; - $files = $this->finder->find("$path/$language", "php"); + $files = $this->finder->find("{$path}/{$language}", 'php'); foreach ($files as $file) { if (!is_null($filename) && $file->getBasename('.php') !== $filename) { @@ -212,14 +204,14 @@ public function findUnused($namespaces = null, $languages = null, $filename = nu if (is_array($value)) { foreach (Arr::dot($value) as $k => $val) { - $searchKey = $namespace !== '' ? $namespace . '::' . $key . '.' . $k : $key . '.' . $k; + $searchKey = '' !== $namespace ? $namespace . '::' . $key . '.' . $k : $key . '.' . $k; if (!in_array($searchKey, $strings)) { $unusedStrings[$language][$namespace][$key . '.' . $k] = $val; } } } else { - $searchKey = $namespace !== '' ? $namespace . '::' . $key : $key; + $searchKey = '' !== $namespace ? $namespace . '::' . $key : $key; if (!in_array($searchKey, $strings)) { $unusedStrings[$language][$namespace][$key] = $value; @@ -234,50 +226,9 @@ public function findUnused($namespaces = null, $languages = null, $filename = nu } /** - * Sort strings array either by key or value + * Prepare namespaces. * - * @param array $data - * @param bool $byKey - * - * @return array - */ - private function sortIfEnabled($data = [], $byKey = false): array - { - if (Config::get('translation-manager.sort-keys', true)) { - return Arr::sort($data, function ($value, $key) use ($byKey) { - return $byKey ? strtolower($key) : (is_array($value) ? $this->sortIfEnabled($value, $byKey) : strtolower($value)); - }); - } - - return $data; - } - - /** - * Get unique value recursively - * - * @param array $array - * - * @return array - */ - private function arrayUniqueRecursive($array) - { - $array = array_unique($array, SORT_REGULAR); - - foreach ($array as $key => $elem) { - if (is_array($elem)) { - $array[$key] = $this->arrayUniqueRecursive($elem); - } - } - - return $array; - } - - /** - * Prepare namespaces - * - * @param string|array $namespaces - * - * @return Collection + * @param array|string $namespaces */ protected function prepareNamespaces($namespaces = null): Collection { @@ -316,7 +267,7 @@ protected function prepareNamespaces($namespaces = null): Collection } /** - * Find translation file from translatable key + * Find translation file from translatable key. * * @param string $key * @param string $locale @@ -330,7 +281,7 @@ protected function getTranslationFilePath($key, $locale = 'en') [$namespace, $group, $item] = $this->parseKey($key); - if (is_null($namespace) || $namespace === '*') { + if (is_null($namespace) || '*' === $namespace) { // Search into default lang folder $hintPath = resource_path("lang/{$locale}"); } else { @@ -355,9 +306,10 @@ protected function getTranslationFilePath($key, $locale = 'en') } /** - * Convert given string to associative + * Convert given string to associative. * * @param string $string + * @param mixed $value * * @return array */ @@ -365,7 +317,7 @@ protected function undotStringToArray($value) { $array = []; - if (strpos($value, '.') === false) { + if (false === strpos($value, '.')) { $array[$value] = $value; } else { $keys = explode('.', $value); @@ -381,9 +333,9 @@ protected function undotStringToArray($value) * Write a language file from array. * * @param string $path - * @param array $translations + * @param array $translations * - * @return int|false + * @return false|int */ protected function writeTranslationsToFile($translations, $path) { @@ -394,10 +346,48 @@ protected function writeTranslationsToFile($translations, $path) return file_put_contents($path, $content); } + /** + * Sort strings array either by key or value. + * + * @param array $data + * @param bool $byKey + */ + private function sortIfEnabled($data = [], $byKey = false): array + { + if (Config::get('translation-manager.sort-keys', true)) { + return Arr::sort($data, function ($value, $key) use ($byKey) { + return $byKey ? strtolower($key) : (is_array($value) ? $this->sortIfEnabled($value, $byKey) : strtolower($value)); + }); + } + + return $data; + } + + /** + * Get unique value recursively. + * + * @param array $array + * + * @return array + */ + private function arrayUniqueRecursive($array) + { + $array = array_unique($array, SORT_REGULAR); + + foreach ($array as $key => $elem) { + if (is_array($elem)) { + $array[$key] = $this->arrayUniqueRecursive($elem); + } + } + + return $array; + } + /** * Write the lines of the inner array of the language file. * * @param $array + * @param mixed $prepend * * @return string */ From 2b037a0ddfb49a3f2853a74a2a241b6bac1aabc9 Mon Sep 17 00:00:00 2001 From: Gaetan <72258504+gaetan-hexadog@users.noreply.github.com> Date: Fri, 19 Nov 2021 13:15:06 +0100 Subject: [PATCH 06/10] chore(git): update gitignore rules --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1dd899c..25b1d19 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,4 @@ composer.lock .php_cs.cache build/ .DS_Store -.cache *.cache From eb3d3f65b86b375939e6ffe45587104ed0fec03b Mon Sep 17 00:00:00 2001 From: Gaetan <72258504+gaetan-hexadog@users.noreply.github.com> Date: Fri, 19 Nov 2021 13:15:25 +0100 Subject: [PATCH 07/10] chore(manager): add missing comments --- src/TranslationManager.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/TranslationManager.php b/src/TranslationManager.php index 5c7d93a..16b9421 100644 --- a/src/TranslationManager.php +++ b/src/TranslationManager.php @@ -127,7 +127,6 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa } } - return $this->arrayUniqueRecursive($missingStrings); // if ($autoFix) { // foreach ($missingStrings as $lang => $namespaces) { // foreach ($namespaces as $namespace => $strings) { @@ -150,6 +149,8 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa // } // } // } + + return $this->arrayUniqueRecursive($missingStrings); } /** @@ -162,6 +163,8 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa public function findUnused($namespaces = null, $languages = null, $filename = null): array { $unusedStrings = []; + + // Retreive all used strings $usedStrings = $this->extractor->extract(); // If no language provided, search for all languages supported by application From 592ba38eba770a0f5a7da04fb003e0fe2f8ef2a1 Mon Sep 17 00:00:00 2001 From: Gaetan <72258504+gaetan-hexadog@users.noreply.github.com> Date: Fri, 19 Nov 2021 13:17:47 +0100 Subject: [PATCH 08/10] chore(ci): update php-cs-fixer github workflow action --- .github/workflows/php-cs-fixer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/php-cs-fixer.yml b/.github/workflows/php-cs-fixer.yml index d96bd63..73ca5e6 100644 --- a/.github/workflows/php-cs-fixer.yml +++ b/.github/workflows/php-cs-fixer.yml @@ -17,7 +17,7 @@ jobs: - name: Run php-cs-fixer uses: docker://oskarstark/php-cs-fixer-ga with: - args: --config=.php_cs.dist --allow-risky=yes + args: --config=.php-cs-fixer.dist.php --allow-risky=yes - name: Commit changes uses: stefanzweifel/git-auto-commit-action@v4.10.0 From aed8d6b9324cc325ef24c6c58dddb0f1fcd70491 Mon Sep 17 00:00:00 2001 From: Gaetan <72258504+gaetan-hexadog@users.noreply.github.com> Date: Fri, 19 Nov 2021 19:47:58 +0100 Subject: [PATCH 09/10] chore(readme): update readme with new filename option --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e5f454e..387bc2f 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ Translation Manager has many features to help you working with translation - [Configuration](#configuration) - [Artisan Commands](#artisan-commands) - - [Find unused translation](#find-unused-translation) - - [Find missing translation](#find-missing-translation) + - [Find unused translation](#find-unused-translation) + - [Find missing translation](#find-missing-translation) ### Configuration This is the default contents of the configuration: @@ -91,6 +91,11 @@ Find all unused translation in your app for specifig language php artisan translation:unused --lang=fr ``` +Find all unused translation in your app for a specific language file +```shell +php artisan translation:unused --filename=buttons +``` +__This example will search unused translation only in file `buttons.php` for all languages in `directories` configured.__ #### Find missing translation Find all missing translation in your app From 240e04318fe5c6aab075c8c20608ce332aa1f8ad Mon Sep 17 00:00:00 2001 From: gaetan-hexadog Date: Fri, 19 Nov 2021 18:52:11 +0000 Subject: [PATCH 10/10] Automatically applied php-cs-fixer changes --- .php-cs-fixer.cache | 2 +- src/Providers/PackageServiceProvider.php | 2 +- src/TranslationManager.php | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 8c42532..210d216 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.0.5","version":"3.0.0","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":{"allow_single_line_anonymous_class_with_empty_body":true,"allow_single_line_closure":true},"class_definition":{"single_line":true},"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"encoding":true,"full_opening_tag":true,"align_multiline_comment":true,"array_indentation":true,"blank_line_before_statement":{"statements":["break","case","continue","declare","default","exit","goto","include","include_once","require","require_once","return","switch","throw","try"]},"combine_consecutive_issets":true,"combine_consecutive_unsets":true,"escape_implicit_backslashes":true,"explicit_indirect_variable":true,"explicit_string_variable":true,"heredoc_to_nowdoc":true,"method_chaining_indentation":true,"multiline_comment_opening_closing":true,"multiline_whitespace_before_semicolons":{"strategy":"new_line_for_chained_calls"},"no_extra_blank_lines":{"tokens":["break","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","return","square_brace_block","switch","throw","use","use_trait"]},"no_null_property_initialization":true,"no_superfluous_elseif":true,"no_useless_else":true,"no_useless_return":true,"operator_linebreak":{"only_booleans":true},"ordered_class_elements":true,"php_unit_internal_class":true,"php_unit_test_class_requires_covers":true,"phpdoc_add_missing_param_annotation":true,"phpdoc_no_empty_return":true,"phpdoc_order":true,"phpdoc_order_by_value":true,"phpdoc_types_order":true,"phpdoc_var_annotation_correct_order":true,"return_assignment":true,"simple_to_complex_string_variable":true,"single_line_comment_style":true,"array_syntax":true,"backtick_to_shell_exec":true,"binary_operator_spaces":true,"cast_spaces":true,"class_attributes_separation":{"elements":{"method":"one"}},"clean_namespace":true,"concat_space":true,"echo_tag_syntax":true,"fully_qualified_strict_types":true,"function_typehint_space":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"include":true,"increment_style":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"native_function_casing":true,"native_function_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_mixed":true,"allow_unused_params":true},"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","return","switch_case","yield","yield_from"]},"no_unneeded_curly_braces":{"namespaces":true},"no_unset_cast":true,"no_unused_imports":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"object_operator_without_whitespace":true,"ordered_imports":true,"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_var_without_name":true,"protected_to_private":true,"semicolon_after_instruction":true,"single_quote":true,"single_space_after_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"blank_line_after_opening_tag":true,"compact_nullable_typehint":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"return_type_declaration":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true},"hashes":{"config\/config.php":913059430,"src\/TranslationManager.php":2845577052,"src\/Console\/Commands\/MissingCommand.php":1036640037,"src\/Console\/Commands\/UnusedCommand.php":195623235,"src\/Providers\/PackageServiceProvider.php":483164339,"src\/Contracts\/Parser.php":1204543224,"src\/Contracts\/Extractor.php":1680923030,"src\/Contracts\/Finder.php":2313434851,"src\/Parser.php":149104290,"src\/Facades\/TranslationManager.php":569518627,"src\/Extractor.php":4044305742,"src\/Finder.php":2070407001}} \ No newline at end of file +{"php":"8.0.12","version":"3.2.1","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":{"allow_single_line_anonymous_class_with_empty_body":true,"allow_single_line_closure":true},"class_definition":{"single_line":true},"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"encoding":true,"full_opening_tag":true,"align_multiline_comment":true,"array_indentation":true,"blank_line_before_statement":{"statements":["break","case","continue","declare","default","exit","goto","include","include_once","require","require_once","return","switch","throw","try"]},"combine_consecutive_issets":true,"combine_consecutive_unsets":true,"empty_loop_body":true,"escape_implicit_backslashes":true,"explicit_indirect_variable":true,"explicit_string_variable":true,"heredoc_to_nowdoc":true,"method_chaining_indentation":true,"multiline_comment_opening_closing":true,"multiline_whitespace_before_semicolons":{"strategy":"new_line_for_chained_calls"},"no_extra_blank_lines":{"tokens":["break","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","return","square_brace_block","switch","throw","use"]},"no_null_property_initialization":true,"no_superfluous_elseif":true,"no_useless_else":true,"no_useless_return":true,"operator_linebreak":{"only_booleans":true},"ordered_class_elements":true,"php_unit_internal_class":true,"php_unit_test_class_requires_covers":true,"phpdoc_add_missing_param_annotation":true,"phpdoc_no_empty_return":true,"phpdoc_order":true,"phpdoc_order_by_value":true,"phpdoc_types_order":true,"phpdoc_var_annotation_correct_order":true,"return_assignment":true,"simple_to_complex_string_variable":true,"single_line_comment_style":true,"array_syntax":true,"backtick_to_shell_exec":true,"binary_operator_spaces":true,"cast_spaces":true,"class_attributes_separation":{"elements":{"method":"one"}},"clean_namespace":true,"concat_space":{"spacing":"one"},"echo_tag_syntax":true,"empty_loop_condition":true,"fully_qualified_strict_types":true,"function_typehint_space":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"native_function_casing":true,"native_function_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_mixed":true,"allow_unused_params":true},"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","return","switch_case","yield","yield_from"]},"no_unneeded_curly_braces":{"namespaces":true},"no_unset_cast":true,"no_unused_imports":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"object_operator_without_whitespace":true,"ordered_imports":true,"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_var_without_name":true,"protected_to_private":true,"semicolon_after_instruction":true,"single_quote":true,"single_space_after_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"types_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"blank_line_after_opening_tag":true,"compact_nullable_typehint":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"return_type_declaration":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true},"hashes":{"config\/config.php":913059430,"src\/Providers\/PackageServiceProvider.php":2896054117,"src\/TranslationManager.php":271954458,"src\/Parser.php":149104290,"src\/Facades\/TranslationManager.php":569518627,"src\/Finder.php":2070407001,"src\/Extractor.php":4044305742,"src\/Contracts\/Parser.php":1204543224,"src\/Contracts\/Finder.php":2313434851,"src\/Contracts\/Extractor.php":1680923030,"src\/Console\/Commands\/UnusedCommand.php":1390662346,"src\/Console\/Commands\/MissingCommand.php":1736749268}} \ No newline at end of file diff --git a/src/Providers/PackageServiceProvider.php b/src/Providers/PackageServiceProvider.php index f82c4c8..c44e944 100644 --- a/src/Providers/PackageServiceProvider.php +++ b/src/Providers/PackageServiceProvider.php @@ -114,7 +114,7 @@ protected function strapPublishers() $configPath = $this->getPath('config'); $this->publishes([ - "{$configPath}/config.php" => config_path($this->getNormalizedNamespace().'.php'), + "{$configPath}/config.php" => config_path($this->getNormalizedNamespace() . '.php'), ], 'config'); } } diff --git a/src/TranslationManager.php b/src/TranslationManager.php index 5361072..16b9421 100644 --- a/src/TranslationManager.php +++ b/src/TranslationManager.php @@ -149,7 +149,7 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa // } // } // } - + return $this->arrayUniqueRecursive($missingStrings); } @@ -203,7 +203,7 @@ public function findUnused($namespaces = null, $languages = null, $filename = nu $translations = include $file->getPathname(); foreach ($translations as $key => $value) { - $key = $file->getBasename('.php').'.'.$key; + $key = $file->getBasename('.php') . '.' . $key; if (is_array($value)) { foreach (Arr::dot($value) as $k => $val) { @@ -292,7 +292,7 @@ protected function getTranslationFilePath($key, $locale = 'en') // Check if hint exists for namespace if (array_key_exists($namespace, $hints)) { - $hintPath = Arr::get($hints, $namespace)."/{$locale}"; + $hintPath = Arr::get($hints, $namespace) . "/{$locale}"; } else { // TODO: are we sure we create file in default path ??? $hintPath = resource_path("lang/{$locale}"); @@ -400,7 +400,7 @@ private function stringLineMaker($array, $prepend = '') foreach ($array as $key => $value) { if (is_array($value)) { - $value = $this->stringLineMaker($value, $prepend.' '); + $value = $this->stringLineMaker($value, $prepend . ' '); $output .= "\n{$prepend} '{$key}' => [{$value}\n{$prepend} ],"; } else { $value = str_replace('\"', '"', addslashes($value));