diff --git a/lib/lang.min.js b/lib/lang.min.js index e77de10..316c4ba 100644 --- a/lib/lang.min.js +++ b/lib/lang.min.js @@ -6,4 +6,4 @@ * @site https://github.com/rmariuzzo/Lang.js * @author Rubens Mariuzzo */ -(function(root,factory){"use strict";if(typeof define==="function"&&define.amd){define([],factory)}else if(typeof exports==="object"){module.exports=factory()}else{root.Lang=factory()}})(this,function(){"use strict";function inferLocale(){if(typeof document!=="undefined"&&document.documentElement){return document.documentElement.lang}}function convertNumber(str){if(str==="-Inf"){return-Infinity}else if(str==="+Inf"||str==="Inf"||str==="*"){return Infinity}return parseInt(str,10)}var intervalRegexp=/^({\s*(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)\s*})|([\[\]])\s*(-Inf|\*|\-?\d+(\.\d+)?)\s*,\s*(\+?Inf|\*|\-?\d+(\.\d+)?)\s*([\[\]])$/;var anyIntervalRegexp=/({\s*(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)\s*})|([\[\]])\s*(-Inf|\*|\-?\d+(\.\d+)?)\s*,\s*(\+?Inf|\*|\-?\d+(\.\d+)?)\s*([\[\]])/;var defaults={locale:"en"};var Lang=function(options){options=options||{};this.locale=options.locale||inferLocale()||defaults.locale;this.fallback=options.fallback;this.messages=options.messages};Lang.prototype.setMessages=function(messages){this.messages=messages};Lang.prototype.getLocale=function(){return this.locale||this.fallback};Lang.prototype.setLocale=function(locale){this.locale=locale};Lang.prototype.getFallback=function(){return this.fallback};Lang.prototype.setFallback=function(fallback){this.fallback=fallback};Lang.prototype.has=function(key,locale){if(typeof key!=="string"||!this.messages){return false}return this._getMessage(key,locale)!==null};Lang.prototype.get=function(key,replacements,locale){if(!this.has(key,locale)){return key}var message=this._getMessage(key,locale);if(message===null){return key}if(replacements){message=this._applyReplacements(message,replacements)}return message};Lang.prototype.trans=function(key,replacements){return this.get(key,replacements)};Lang.prototype.choice=function(key,number,replacements,locale){replacements=typeof replacements!=="undefined"?replacements:{};replacements.count=number;var message=this.get(key,replacements,locale);if(message===null||message===undefined){return message}var messageParts=message.split("|");var explicitRules=[];for(var i=0;i=leftNumber:count>leftNumber)&&(rightDelimiter==="]"?count<=rightNumber:count=2&&count%10<=4&&(count%100<10||count%100>=20)?1:2;case"cs":case"sk":return count==1?0:count>=2&&count<=4?1:2;case"ga":return count==1?0:count==2?1:2;case"lt":return count%10==1&&count%100!=11?0:count%10>=2&&(count%100<10||count%100>=20)?1:2;case"sl":return count%100==1?0:count%100==2?1:count%100==3||count%100==4?2:3;case"mk":return count%10==1?0:1;case"mt":return count==1?0:count===0||count%100>1&&count%100<11?1:count%100>10&&count%100<20?2:3;case"lv":return count===0?0:count%10==1&&count%100!=11?1:2;case"pl":return count==1?0:count%10>=2&&count%10<=4&&(count%100<12||count%100>14)?1:2;case"cy":return count==1?0:count==2?1:count==8||count==11?2:3;case"ro":return count==1?0:count===0||count%100>0&&count%100<20?1:2;case"ar":return count===0?0:count==1?1:count==2?2:count%100>=3&&count%100<=10?3:count%100>=11&&count%100<=99?4:5;default:return 0}};return Lang}); \ No newline at end of file +(function(root,factory){"use strict";if(typeof define==="function"&&define.amd){define([],factory)}else if(typeof exports==="object"){module.exports=factory()}else{root.Lang=factory()}})(this,function(){"use strict";function inferLocale(){if(typeof document!=="undefined"&&document.documentElement){return document.documentElement.lang}}function convertNumber(str){if(str==="-Inf"){return-Infinity}else if(str==="+Inf"||str==="Inf"||str==="*"){return Infinity}return parseInt(str,10)}var intervalRegexp=/^({\s*(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)\s*})|([\[\]])\s*(-Inf|\*|\-?\d+(\.\d+)?)\s*,\s*(\+?Inf|\*|\-?\d+(\.\d+)?)\s*([\[\]])$/;var anyIntervalRegexp=/({\s*(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)\s*})|([\[\]])\s*(-Inf|\*|\-?\d+(\.\d+)?)\s*,\s*(\+?Inf|\*|\-?\d+(\.\d+)?)\s*([\[\]])/;var defaults={locale:"en"};var Lang=function(options){options=options||{};this.locale=options.locale||inferLocale()||defaults.locale;this.fallback=options.fallback;this.messages=options.messages};Lang.prototype.setMessages=function(messages){this.messages=messages};Lang.prototype.getLocale=function(){return this.locale||this.fallback};Lang.prototype.setLocale=function(locale){this.locale=locale};Lang.prototype.getFallback=function(){return this.fallback};Lang.prototype.setFallback=function(fallback){this.fallback=fallback};Lang.prototype.has=function(key,locale){if(typeof key!=="string"||!this.messages){return false}return this._getMessage(key,locale)!==null};Lang.prototype.get=function(key,replacements,locale){if(!this.has(key,locale)){return key}var message=this._getMessage(key,locale);if(message===null){return key}if(replacements){message=this._applyReplacements(message,replacements)}return message};Lang.prototype.trans=function(key,replacements){return this.get(key,replacements)};Lang.prototype.choice=function(key,number,replacements,locale){replacements=typeof replacements!=="undefined"?replacements:{};replacements.count=number;var message=this.get(key,replacements,locale);if(message===null||message===undefined){return message}var messageParts=message.split("|");var explicitRules=[];for(var i=0;i=leftNumber:count>leftNumber)&&(rightDelimiter==="]"?count<=rightNumber:count=2&&count%10<=4&&(count%100<10||count%100>=20)?1:2;case"cs":case"sk":return count==1?0:count>=2&&count<=4?1:2;case"ga":return count==1?0:count==2?1:2;case"lt":return count%10==1&&count%100!=11?0:count%10>=2&&(count%100<10||count%100>=20)?1:2;case"sl":return count%100==1?0:count%100==2?1:count%100==3||count%100==4?2:3;case"mk":return count%10==1?0:1;case"mt":return count==1?0:count===0||count%100>1&&count%100<11?1:count%100>10&&count%100<20?2:3;case"lv":return count===0?0:count%10==1&&count%100!=11?1:2;case"pl":return count==1?0:count%10>=2&&count%10<=4&&(count%100<12||count%100>14)?1:2;case"cy":return count==1?0:count==2?1:count==8||count==11?2:3;case"ro":return count==1?0:count===0||count%100>0&&count%100<20?1:2;case"ar":return count===0?0:count==1?1:count==2?2:count%100>=3&&count%100<=10?3:count%100>=11&&count%100<=99?4:5;default:return 0}};return Lang}); diff --git a/src/Mariuzzo/LaravelJsLocalization/Commands/LangJsCommand.php b/src/Mariuzzo/LaravelJsLocalization/Commands/LangJsCommand.php index b98111c..389c270 100644 --- a/src/Mariuzzo/LaravelJsLocalization/Commands/LangJsCommand.php +++ b/src/Mariuzzo/LaravelJsLocalization/Commands/LangJsCommand.php @@ -67,8 +67,23 @@ public function handle() 'no-lib' => $this->option('no-lib'), 'source' => $this->option('source'), 'no-sort' => $this->option('no-sort'), + 'autodetect' => $this->option('autodetect'), ]; + + if ($options['autodetect']) { + $this->info('Autodetect enabled... If this takes too long, edit the glob\'s in config/js-localization.php to be more specific'); + $usageSearchFiles = $this->generator->usageSearchFiles($this->getUsageSearchFiles()); + $bar = $this->output->createProgressBar(count($usageSearchFiles)); + $bar->start(); + foreach ($usageSearchFiles as $file){ + $this->generator->usageSearch($file); + $bar->advance(); + } + $bar->finish(); + $this->info("\n"); + } + if ($this->generator->generate($target, $options)) { $this->info("Created: {$target}"); @@ -100,6 +115,20 @@ protected function getDefaultPath() return Config::get('localization-js.path', public_path('messages.js')); } + /** + * Return the glob's to search. + * + * @return array + */ + protected function getUsageSearchFiles() + { + return Config::get('localization-js.usageSearchFiles', [ + 'public/**/*.js', + 'resources/assets/**/*.js', + 'resources/views/**/*', + ]); + } + /** * Return all command options. * @@ -113,6 +142,7 @@ protected function getOptions() ['json', 'j', InputOption::VALUE_NONE, 'Only output the messages json.', null], ['source', 's', InputOption::VALUE_REQUIRED, 'Specifying a custom source folder', null], ['no-sort', 'ns', InputOption::VALUE_NONE, 'Do not sort the messages', null], + ['autodetect', 'a', InputOption::VALUE_NONE, 'Autodetect which messages to include', null], ]; } } diff --git a/src/Mariuzzo/LaravelJsLocalization/Generators/LangJsGenerator.php b/src/Mariuzzo/LaravelJsLocalization/Generators/LangJsGenerator.php index 8e2a945..9cd0d3e 100644 --- a/src/Mariuzzo/LaravelJsLocalization/Generators/LangJsGenerator.php +++ b/src/Mariuzzo/LaravelJsLocalization/Generators/LangJsGenerator.php @@ -35,6 +35,14 @@ class LangJsGenerator */ protected $messagesIncluded = []; + + /** + * List of files/folders the autodetector should search through. + * + * @var array + */ + public $keepMessages = []; + /** * Name of the domain in which all string-translation should be stored under. * More about string-translation: https://laravel.com/docs/master/localization#retrieving-translation-strings @@ -48,6 +56,7 @@ class LangJsGenerator * * @param File $file The file service instance. * @param string $sourcePath The source path of the language files. + * @param array $messagesIncluded List of messages should be included in build. */ public function __construct(File $file, $sourcePath, $messagesIncluded = []) { @@ -71,6 +80,9 @@ public function generate($target, $options) } $messages = $this->getMessages($options['no-sort']); + if ($options['autodetect']) { + $this->filterKeepMessages($messages,$this->keepMessages); + } $this->prepareTarget($target); if ($options['no-lib']) { @@ -108,6 +120,86 @@ protected function sortMessages(&$messages) } } + /** + * Returns array of filenames from input glob's. + * @param array $globs Globs to parse + * @return array + */ + public function usageSearchFiles($globs) + { + $files = []; + foreach ($globs as $glob) { + $files += $this->file->glob($this->sourcePath.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.$glob); + } + return $files; + } + + /** + * Searches a file for Lang.get / Lang.has / Lang.choice etc. occurances. + * Stores an occurence in $this->keepMessages; + * @param string $file Absolute path of file to open + * @return void + */ + public function usageSearch($file) + { + try { + $content = $this->file->get($file); + preg_match_all("/Lang\.(?:get|has|choice|trans|transChoice)\(['\"]([^'\"]+)/", $content, $matches); + foreach ($matches[1] as $match) { + $chain = []; + $ref = &$chain; + foreach (explode(".", $match) as $value) { + $ref[$value] = []; + $ref = &$ref[$value]; + } + if(array_key_exists($this->stringsDomain,$chain)){ + $this->keepMessages[$this->stringsDomain][substr($match, strlen($this->stringsDomain)+1)] = ""; + }else { + $this->keepMessages = array_merge_recursive($this->keepMessages, $chain); + $this->keepMessages[$this->stringsDomain][$match] = ""; + } + } + } catch (\Exception $exception) { + return; + } + } + + /** + * Recursively executes array_intersect_key($array1, $array2); + * @param array $array1 Array of master keys + * @param array $array2 Array to check keys against + * @see array_intersect_key() + * @return array + */ + protected function array_intersect_key_recursive( $array1, $array2 ) { + $array1 = array_intersect_key( $array1, $array2 ); + foreach ( $array1 as $key => $value ) { + if ( is_array( $value ) && is_array( $array2[ $key ] ) ) { + $array1[ $key ] = $this->array_intersect_key_recursive( $value, $array2[ $key ] ); + } + } + return $array1; + } + + /** + * Filters language keys in $messages to only keep keys in $this->keepMessages; + * @param array &$messages Array of master keys + * @param array &$keep Array of keys to keep + * @return void + */ + protected function filterKeepMessages(&$messages, &$keep){ + $messages = array_filter($messages,function($key) use ($keep) { + if(array_key_exists($key,$keep)||array_key_exists(substr(strstr($key, '.'),1),$keep)){ + return true; + } + return false; + },ARRAY_FILTER_USE_KEY); + foreach($messages as $key=>$array){ + $skey = substr(strstr($key, '.'),1); + $messages[$key] = $this->array_intersect_key_recursive($array,$keep[$skey]); + } + } + /** * Return all language messages. * diff --git a/src/config/config.php b/src/config/config.php index 77d5500..9220bcc 100644 --- a/src/config/config.php +++ b/src/config/config.php @@ -19,4 +19,15 @@ * The default path to use for the generated javascript. */ 'path' => public_path('messages.js'), + + /* + * Specify the files you want the autodetector to search through. + * The autodetector looks for Lang.get / Lang.has / Lang.choice usages in your Javascript files. + * Use glob format. https://en.wikipedia.org/wiki/Glob_(programming) + */ + 'usageSearchFiles' => [ + 'public/**/*.js', + 'resources/assets/**/*.js', + 'resources/views/**/*', + ], ];