diff --git a/.gitignore b/.gitignore index c5db289..25b1d19 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ composer.lock .php_cs.cache build/ .DS_Store -.cache \ No newline at end of file +*.cache 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/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 7e419e9..d46f825 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -20,5 +20,8 @@ ->setRules([ '@PSR2' => true, '@PhpCsFixer' => true, + 'concat_space' => [ + 'spacing' => 'one' + ] ]) ->setFinder($finder); 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 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" diff --git a/src/Console/Commands/MissingCommand.php b/src/Console/Commands/MissingCommand.php index 5cf2d2b..bbbb455 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 @@ -34,7 +35,7 @@ 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) { diff --git a/src/Console/Commands/UnusedCommand.php b/src/Console/Commands/UnusedCommand.php index 179339a..5b7cfbf 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. @@ -35,7 +36,7 @@ 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) { diff --git a/src/Providers/PackageServiceProvider.php b/src/Providers/PackageServiceProvider.php index a23d6e9..c44e944 100644 --- a/src/Providers/PackageServiceProvider.php +++ b/src/Providers/PackageServiceProvider.php @@ -20,7 +20,7 @@ class PackageServiceProvider extends ServiceProvider /** * Our root directory for this package to make traversal easier. */ - public const PACKAGE_DIR = __DIR__.'/../../'; + public const PACKAGE_DIR = __DIR__ . '/../../'; /** * Name for this package to publish assets. @@ -70,7 +70,7 @@ protected function getPath($path = '') // We get the child class $rc = new ReflectionClass(get_class($this)); - return dirname($rc->getFileName()).'/../../'.$path; + return dirname($rc->getFileName()) . '/../../' . $path; } /** @@ -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 7b698dc..16b9421 100644 --- a/src/TranslationManager.php +++ b/src/TranslationManager.php @@ -113,20 +113,20 @@ 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.'::')) { + 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()); } } - return $this->arrayUniqueRecursive($missingStrings); // if ($autoFix) { // foreach ($missingStrings as $lang => $namespaces) { // foreach ($namespaces as $namespace => $strings) { @@ -149,6 +149,8 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa // } // } // } + + return $this->arrayUniqueRecursive($missingStrings); } /** @@ -156,10 +158,13 @@ public function findMissing($namespaces = null, $languages = null, $autoFix = fa * * @param string $namespaces * @param array|string $languages + * @param null|string $filename Limit research in language file named as given */ - public function findUnused($namespaces = null, $languages = null, bool $autoClean = false): array + 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 @@ -171,10 +176,11 @@ 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) { - if ('' === $namespace || Str::startsWith($key, $namespace.'::')) { + if ('' === $namespace || Str::startsWith($key, $namespace . '::')) { $strings[] = $key; } }); @@ -183,26 +189,33 @@ 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(); 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) { - $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; + $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; } @@ -228,6 +241,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); @@ -278,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}"); @@ -386,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));