├── src ├── Providers │ └── FindMissingTranslationsProvider.php └── Commands │ └── FindMissingTranslations.php ├── composer.json └── README.md /src/Providers/FindMissingTranslationsProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 18 | $this->commands([ 19 | FindMissingTranslations::class 20 | ]); 21 | } 22 | } 23 | 24 | /** 25 | * Register services. 26 | * 27 | * @return void 28 | */ 29 | public function register() 30 | { 31 | // 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "name": "veton-muhaxhiri/laravel-command-to-find-missing-translations", 4 | "description": "Response control for Laravel/Lumen 5 and up.", 5 | "homepage": "https://github.com/VetonMuhaxhiri/Laravel-find-missing-translations", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Veton Muhaxhiri", 10 | "email": "veto.on@hotmail.com" 11 | } 12 | ], 13 | "autoload": { 14 | "psr-4": { 15 | "VetonMuhaxhiri\\Laravelfindmissingtranslations\\": "src/" 16 | } 17 | }, 18 | "require": { 19 | "illuminate/support": "~5.6.34|~5.7.0|~5.8.0|^6.0|^7.0|^8.0" 20 | }, 21 | "extra": { 22 | "laravel": { 23 | "providers": [ 24 | "VetonMuhaxhiri\\Laravelfindmissingtranslations\\Providers\\FindMissingTranslationsProvider" 25 | ] 26 | } 27 | }, 28 | "config": { 29 | "sort-packages": true 30 | }, 31 | "minimum-stability": "dev", 32 | "prefer-stable": true 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # One command to find missing translations in Laravel application 2 | 3 | Laravel artisan command to list all your untranslated words. 4 | 5 | ## Prerequisites 6 | 7 | There is no prerequisites for this command to work, you just need to follow the installing process. 8 | 9 | ## Installing 10 | 11 | Installing this command is easy, you can use composer or do it manually : 12 | ### Composer 13 | 14 | ``` 15 | composer require veton-muhaxhiri/laravel-command-to-find-missing-translations 16 | ``` 17 | By running composer command you will have everything setup and ready to be used. 18 | 19 | ### Manually 20 | 1. git clone the repository, or just download it. 21 | 2. copy "src/Commands/FindMissingTranslations.php" file to /app/Console/Commands of your laravel project. 22 | 23 | By completing this steps the command should work, unless the command is not loaded automaticly. 24 | To load the command go to app/Console/Kernel.php and add the class name to the $commands property. 25 | 26 | ``` 27 | protected $commands = [ 28 | Commands\FindMissingTranslations::class 29 | ]; 30 | ``` 31 | ### Lumen 32 | In Laravel Lumen we just need one extra step, go open ```bootstrap/app.php``` file and register the service provider by adding the line 33 | ```$app->register(VetonMuhaxhiri\Laravelfindmissingtranslations\Providers\FindMissingTranslationsProvider::class);``` 34 | 35 | ## Running the command 36 | 37 | The command expects two arguments : 38 | 1. **language directory** - Relative path of language directory for ex. /resources/lang is a directory that contains all supported language in your laravel app. 39 | 2. **base language** - Base language for ex. "en". All other languages are compared to this language. 40 | 41 | Inside of **language directory** 42 | ![Language directory](https://i.imgur.com/eXGlUI8.png) 43 | 44 | While **base language** should be one of the language listed in the picture. 45 | 46 | ### Example 47 | ``` 48 | $ php artisan translations:missing /resources/lang en 49 | ``` 50 | ![Proof of concept](https://imgur.com/PNxv82D.png) 51 | 52 | ## Features 53 | #### Recursive 54 | Detects missing words in multilevel array for ex. 55 | ![Multilevel array](https://imgur.com/Hn4YQB7.png) 56 | 57 | #### Missing files 58 | Detects missing files, for ex. if file with translation named "posts.php" exists in english but not in deutsch. 59 | ## Authors 60 | 61 | * **Veton Muhaxhiri** - [LinkedIn](https://www.linkedin.com/in/veton-muhaxhiri-815113196) 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/Commands/FindMissingTranslations.php: -------------------------------------------------------------------------------- 1 | argument('language directory'); 43 | $baseLanguageDirectory = $languageDir. '/'. $this->argument('base language'); 44 | 45 | try { 46 | $directoriesOfLanguages = File::directories($languageDir); 47 | $baseLanguageFiles = $this->getFilenames($baseLanguageDirectory); 48 | } catch (\Exception $e) { 49 | $this->line(''); 50 | $this->error($e->getMessage()); 51 | 52 | exit; 53 | } 54 | 55 | foreach ($directoriesOfLanguages as $languageDirectory) { 56 | $languageFiles = $this->getFilenames($languageDirectory); 57 | 58 | $baseLanguageName = explode("/", $baseLanguageDirectory); 59 | $baseLanguageName = explode("\\", $baseLanguageName[count($baseLanguageName)-1]); 60 | $baseLanguageName = $baseLanguageName[count($baseLanguageName)-1]; 61 | 62 | $languageName = explode("/", $languageDirectory); 63 | $languageName = explode("\\", $languageName[count($languageName)-1]); 64 | $languageName = $languageName[count($languageName)-1]; 65 | 66 | if ($baseLanguageName == $languageName) { 67 | //Skip the base language, we don't have to compare language by it's self 68 | continue; 69 | } 70 | 71 | $this->info('Comparing '. $baseLanguageName. ' to '. $languageName. '.'); 72 | 73 | $this->compareLanguages($baseLanguageDirectory, $baseLanguageFiles, $languageDirectory, $languageFiles, $languageName); 74 | 75 | $this->info(''); 76 | $this->info('Successfuly compared all languages'); 77 | } 78 | } 79 | 80 | /** 81 | * Comparing languages 82 | * 83 | * @param String $baseLanguagePath 84 | * @param String $baseLanguageFiles 85 | * @param String $languagePath 86 | * @param Array $languageFiles The list of language files 87 | * @param String $languageName 88 | * 89 | * @return void 90 | */ 91 | private function compareLanguages($baseLanguagePath, $baseLanguageFiles, $languagePath, $languageFiles, $languageName) 92 | { 93 | foreach ($baseLanguageFiles as $languageFile) { 94 | $baseLanguageFile = File::getRequire($baseLanguagePath. '/'. $languageFile); 95 | 96 | if (!in_array($languageFile, $languageFiles)) { 97 | $this->line(''); 98 | $this->comment('Comparing translations in '. $languageFile. '.'); 99 | $this->error($languageName. '/'. $languageFile. ' file is missing.'); 100 | continue; 101 | } 102 | $secondLanguageFile = File::getRequire($languagePath. '/'. $languageFile); 103 | 104 | $this->compareFileKeys($baseLanguageFile, $secondLanguageFile, $languageName, $languageFile); 105 | } 106 | } 107 | 108 | /** 109 | * Compare files and display missing translations 110 | * 111 | * @param Array $baseLanguageFileKeys 112 | * @param Array $secondLanguageFileKeys 113 | * @param String $languageName 114 | * @param String $filename 115 | * 116 | * @return void 117 | */ 118 | private function compareFileKeys($baseLanguageFileKeys, $secondLanguageFileKeys, $languageName, $filename) 119 | { 120 | $missingKeys = $this->arrayDiffRecursive($baseLanguageFileKeys, $secondLanguageFileKeys); 121 | 122 | if (is_array($missingKeys)) { 123 | if (count($missingKeys)) { 124 | $this->line(''); 125 | $this->comment('Comparing translations in '. $filename. '.'); 126 | $this->error('Found missing translations in /' . $languageName. '/'. $filename. '.'); 127 | 128 | foreach ($missingKeys as $key) { 129 | $this->line('"'.$key.'" is not translated to /'. $languageName. '/'. $filename); 130 | } 131 | } 132 | } else { 133 | $this->info('Bad file, cannot proccess!'); 134 | } 135 | } 136 | 137 | /** 138 | * Compare array keys recursivly 139 | * 140 | * @param array $firstArray 141 | * @param array $secondArray 142 | * 143 | * @return array 144 | */ 145 | private function arrayDiffRecursive($firstArray, $secondArray) 146 | { 147 | $outputDiff = []; 148 | 149 | foreach ($firstArray as $key => $value) { 150 | if (array_key_exists($key, $secondArray)) { 151 | if (is_array($value)) { 152 | $recursiveDiff = $this->arrayDiffRecursive($value, $secondArray[$key]); 153 | if (count($recursiveDiff)) { 154 | foreach ($recursiveDiff as $diff) { 155 | $outputDiff[] = $diff; 156 | } 157 | } 158 | } 159 | } else { 160 | $outputDiff[] = $key; 161 | } 162 | } 163 | return $outputDiff; 164 | } 165 | 166 | /** 167 | * Get filenames of directory 168 | * 169 | * @param String $directory 170 | * @return array 171 | */ 172 | private function getFilenames($directory) 173 | { 174 | $fileNames = []; 175 | 176 | $filesInFolder = File::files($directory); 177 | 178 | foreach ($filesInFolder as $path) { 179 | $file = pathinfo($path); 180 | $fileNames[] = $file['basename']; 181 | } 182 | 183 | return $fileNames; 184 | } 185 | } --------------------------------------------------------------------------------