├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── config └── config.php ├── gulpfile.js ├── package.json ├── phpunit.xml ├── public ├── .gitkeep └── js │ └── localization.min.js ├── resources ├── js │ ├── config.js │ └── localization.js └── views │ └── head.blade.php ├── src ├── Caching │ ├── AbstractCachingService.php │ ├── ConfigCachingService.php │ └── MessageCachingService.php ├── Console │ ├── ExportCommand.php │ └── RefreshCommand.php ├── Exceptions │ ├── ConfigException.php │ └── FileNotFoundException.php ├── Facades │ ├── ConfigCachingService.php │ ├── JsLocalizationHelper.php │ └── MessageCachingService.php ├── Http │ ├── Controllers │ │ └── JsLocalizationController.php │ ├── Responses │ │ └── StaticFileResponse.php │ └── routes.php ├── JsLocalizationServiceProvider.php ├── Utils │ └── Helper.php └── bindings.php └── tests ├── .gitkeep ├── JsLocalization ├── Caching │ ├── ConfigCachingServiceTest.php │ └── MessageCachingServiceTest.php ├── Console │ └── RefreshCommandTest.php ├── Http │ ├── Controllers │ │ └── JsLocalizationControllerTest.php │ ├── LocalizationScriptTest.php │ └── Responses │ │ └── StaticFileResponseTest.php ├── JsLocalizationServiceProviderTest.php └── Utils │ └── JsLocalizationHelperTest.php └── TestCase.php /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /vendor 3 | _ide_fix.php 4 | composer.phar 5 | .DS_Store 6 | .idea 7 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: [vendor/*, app/*, web/*] 3 | 4 | before_commands: 5 | - export COMPOSER_PROCESS_TIMEOUT=600 6 | - composer install --dev --prefer-dist 7 | 8 | tools: 9 | php_cpd: true 10 | php_pdepend: 11 | excluded_dirs: [tests, vendor] 12 | 13 | php_code_coverage: 14 | enabled: true 15 | filter: 16 | excluded_paths: [vendor/*] 17 | external_code_coverage: true 18 | 19 | checks: 20 | php: 21 | code_rating: true 22 | duplication: true 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | sudo: false 3 | 4 | php: 5 | - 7.2 6 | - 7.3 7 | - 7.4 8 | 9 | before_script: 10 | - composer install --dev 11 | - ./vendor/bin/phpunit --coverage-clover=coverage.clover 12 | - wget https://scrutinizer-ci.com/ocular.phar 13 | - php ocular.phar code-coverage:upload --format=php-clover coverage.clover 14 | 15 | script: ./vendor/bin/phpunit 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Andy Wermke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | laravel-js-localization 2 | ======================= 3 | [![Build Status](https://travis-ci.org/andywer/laravel-js-localization.svg?branch=laravel-5)](https://travis-ci.org/andywer/laravel-js-localization) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/andywer/laravel-js-localization/badges/quality-score.png?b=laravel-5)](https://scrutinizer-ci.com/g/andywer/laravel-js-localization/?branch=laravel-5) [![Code Coverage](https://scrutinizer-ci.com/g/andywer/laravel-js-localization/badges/coverage.png?b=laravel-5)](https://scrutinizer-ci.com/g/andywer/laravel-js-localization/?branch=laravel-5) [![Total Downloads](https://poser.pugx.org/andywer/js-localization/downloads.svg)](https://packagist.org/packages/andywer/js-localization) 4 | 5 | 6 | Simple, ease-to-use and flexible package for the [Laravel](http://laravel.com/) web framework. Allows you to use localized messages of the Laravel webapp (see `resources/lang` directory) in your Javascript code. You may easily configure which messages you need to export. 7 | 8 | **⚠️ Looking for a new maintainer. Please contact me if you are interested.** 9 | 10 | 11 | Branches 12 | -------- 13 | 14 | Laravel | Branch 15 | :----------|:------- 16 | 8 | laravel-8 17 | 7 | laravel-7 18 | 6 | laravel-6 19 | 5.8 | laravel-5.8 20 | 5.0 - 5.7 | laravel-5 (end of life) 21 | 22 | 23 | Installation 24 | ------------ 25 | 26 | Add the following line to the `require` section of your Laravel webapp's `composer.json` file: 27 | 28 | ```javascript 29 | "require": { 30 | "andywer/js-localization": "dev-laravel-6" // see table above 31 | } 32 | ``` 33 | 34 | 35 | Run `composer update` to install the package. 36 | 37 | 38 | Finally add the following line to the `providers` array of your `app/config/app.php` file: 39 | 40 | ```php 41 | 'providers' => [ 42 | /* ... */ 43 | JsLocalization\JsLocalizationServiceProvider::class 44 | ] 45 | ``` 46 | 47 | 48 | Configuration 49 | ------------- 50 | 51 | Run `php artisan vendor:publish` first. This command copies the package's default configuration to `config/js-localization.php`. 52 | 53 | You may now edit this file to define the messages you need in your Javascript code. Just edit the `messages` array in the config file. 54 | 55 | Example (exports all reminder messages): 56 | 57 | ```php 58 | ['en'], 63 | 64 | // Set the keys of the messages you want to use in javascript 65 | 'messages' => [ 66 | 'passwords' => [ 67 | 'password', 'user', 'token' 68 | ] 69 | ], 70 | 71 | /* 72 | * in short: 73 | * 'messages' => ['passwords'] 74 | * 75 | * 76 | * you could also use: 77 | * 78 | * 'messages' => [ 79 | * 'passwords.password', 80 | * 'passwords.user', 81 | * 'passwords.token' 82 | * ] 83 | */ 84 | 85 | // Set the keys of config properties you want to use in javascript. 86 | // Caution: Do not expose any configuration values that should be kept privately! 87 | 'config' => [ 88 | 'app.debug' 89 | ], 90 | 91 | // Disables the config cache if set to true, so you don't have to run `php artisan js-localization:refresh` 92 | // each time you change configuration files. 93 | // Attention: Should not be used in production mode due to decreased performance. 94 | 'disable_config_cache' => false, 95 | 96 | // Split up the exported messages.js file into separate files for each locale. 97 | // This is to ensue faster loading times so one doesn't have to load translations for _all_ languages. 98 | 'split_export_files' => true, 99 | ]; 100 | ``` 101 | 102 | __Important:__ 103 | 104 | The messages configuration will be cached when the JsLocalizationController is used for the first time. After changing the messages configuration you will need to call __`php artisan js-localization:refresh`__ to refresh that cache. That also affects the config properties you export to javascript, since they are cached, too. 105 | 106 | 107 | Usage 108 | ----- 109 | 110 | The translation resources for JavaScript can either be served by your Laravel app at run-time or they can be pre-generated as static JavaScript files, allowing you to serve them straight from your web server or CDN or to be included in your build process. 111 | 112 | ### Run-time generation 113 | 114 | You just need to add the necessary ` 130 |

131 | 132 | 133 | ``` 134 | 135 | Remember it's best to not put the `@yield('js-localization.head')` in the `` as it contains the ` 3 | 4 | 5 | @if(Config::get('js-localization.config')) 6 | 7 | @endif 8 | 9 | 12 | @stop 13 | 14 | @section('js-localization.head.all_in_one') 15 | 16 | 19 | @stop 20 | -------------------------------------------------------------------------------- /src/Caching/AbstractCachingService.php: -------------------------------------------------------------------------------- 1 | cacheKey = $cacheKey; 39 | $this->cacheTimestampKey = $cacheTimestampKey; 40 | } 41 | 42 | /** 43 | * Returns the timestamp of the last refreshCache() call. 44 | * 45 | * @return DateTime 46 | */ 47 | public function getLastRefreshTimestamp() 48 | { 49 | $unixTime = Cache::get($this->cacheTimestampKey); 50 | 51 | $dateTime = new DateTime; 52 | $dateTime->setTimestamp($unixTime); 53 | 54 | return $dateTime; 55 | } 56 | 57 | /** 58 | * Refresh the cached data. 59 | * 60 | * @param mixed $data 61 | */ 62 | public function refreshCacheUsing($data) 63 | { 64 | Cache::forever($this->cacheKey, $data); 65 | Cache::forever($this->cacheTimestampKey, time()); 66 | } 67 | 68 | /** 69 | * Create up-to-date data and update cache using refreshCacheUsing(). 70 | * Trigger some event. 71 | * 72 | * @return void 73 | */ 74 | abstract public function refreshCache(); 75 | 76 | /** 77 | * Return the cached data. Refresh cache if cache has not yet been initialized. 78 | * 79 | * @return mixed 80 | */ 81 | public function getData() 82 | { 83 | if (!Cache::has($this->cacheKey)) { 84 | $this->refreshCache(); 85 | } 86 | 87 | return Cache::get($this->cacheKey); 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /src/Caching/ConfigCachingService.php: -------------------------------------------------------------------------------- 1 | createConfigJson(); 47 | $this->refreshCacheUsing($configJson); 48 | } 49 | 50 | /** 51 | * Returns the config (already JSON encoded). 52 | * Refreshes the cache if necessary. 53 | * 54 | * @param bool $noCache (optional) Defines if cache should be ignored. 55 | * @return mixed|string string The JSON-encoded config exports. 56 | */ 57 | public function getConfigJson($noCache = false) 58 | { 59 | if ($noCache || $this->isDisabled()) { 60 | return $this->createConfigJson(); 61 | } else { 62 | return $this->getData(); 63 | } 64 | } 65 | 66 | /** 67 | * @return bool Is config caching disabled? `true` means that this class does not cache, but create the data on the fly. 68 | */ 69 | public function isDisabled() 70 | { 71 | return Config::get('js-localization.disable_config_cache', false); 72 | } 73 | 74 | /** 75 | * @return string 76 | */ 77 | protected function createConfigJson() 78 | { 79 | $propertyNames = Config::get('js-localization.config', []); 80 | $configArray = []; 81 | 82 | foreach ($propertyNames as $propertyName) { 83 | $configArray[$propertyName] = Config::get($propertyName); 84 | } 85 | 86 | return json_encode((object)$configArray); 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /src/Caching/MessageCachingService.php: -------------------------------------------------------------------------------- 1 | createMessagesJson(); 49 | } else { 50 | return $this->getData(); 51 | } 52 | } 53 | 54 | /** 55 | * Refreshes the cache item containing the JSON encoded 56 | * messages object. 57 | * Fires the 'JsLocalization.registerMessages' event. 58 | * 59 | * @return void 60 | */ 61 | public function refreshCache() 62 | { 63 | Event::dispatch('JsLocalization.registerMessages'); 64 | 65 | $messagesJSON = $this->createMessagesJson(); 66 | $this->refreshCacheUsing($messagesJSON); 67 | } 68 | 69 | /** 70 | * @return string 71 | */ 72 | protected function createMessagesJson() 73 | { 74 | $locales = $this->getLocales(); 75 | $messageKeys = $this->getMessageKeys(); 76 | $translatedMessages = $this->getTranslatedMessagesForLocales($messageKeys, $locales); 77 | 78 | return json_encode($translatedMessages); 79 | } 80 | 81 | /** 82 | * Returns the locales we need to consider. 83 | * 84 | * @return array Locales. 85 | */ 86 | protected function getLocales() 87 | { 88 | return Config::get('js-localization.locales'); 89 | } 90 | 91 | /** 92 | * Returns the translated messages for the given keys and locales. 93 | * 94 | * @param array $messageKeys 95 | * @param array $locales 96 | * @return array The translated messages as [ => [ => , ... ], ...] 97 | */ 98 | protected function getTranslatedMessagesForLocales(array $messageKeys, array $locales) 99 | { 100 | $translatedMessages = []; 101 | 102 | foreach ($locales as $locale) { 103 | if (!isset($translatedMessages[$locale])) { 104 | $translatedMessages[$locale] = []; 105 | } 106 | 107 | $translatedMessages[$locale] = $this->getTranslatedMessages($messageKeys, $locale); 108 | } 109 | 110 | return $translatedMessages; 111 | } 112 | 113 | /** 114 | * Returns the translated messages for the given keys. 115 | * 116 | * @param array $messageKeys 117 | * @param $locale 118 | * @return array The translated messages as [ => , ... ] 119 | */ 120 | protected function getTranslatedMessages(array $messageKeys, $locale) 121 | { 122 | $translatedMessages = []; 123 | 124 | foreach ($messageKeys as $key) { 125 | $translation = __($key, [], $locale); 126 | 127 | if (is_array($translation)) { 128 | $flattened = $this->flattenTranslations($translation, $key.'.'); 129 | $translatedMessages = array_merge($translatedMessages, $flattened); 130 | } else { 131 | $translatedMessages[$key] = $translation; 132 | } 133 | } 134 | 135 | return $translatedMessages; 136 | } 137 | 138 | /** 139 | * Transforms an array of nested translation messages into a "flat" (not nested) array. 140 | * 141 | * @param array $nestedMessages 142 | * @param string $keyPrefix 143 | * @return array Flattened translations array. 144 | */ 145 | protected function flattenTranslations(array $nestedMessages, $keyPrefix='') 146 | { 147 | $flattened = []; 148 | 149 | foreach ($nestedMessages as $key => $message) { 150 | if (is_array($message)) { 151 | $flattenedMessages = $this->flattenTranslations($message, $keyPrefix . $key . '.'); 152 | $flattened = array_merge($flattened, $flattenedMessages); 153 | } else { 154 | $flattened[$keyPrefix.$key] = $message; 155 | } 156 | } 157 | 158 | return $flattened; 159 | } 160 | 161 | /** 162 | * Returns the message keys of all messages 163 | * that are supposed to be sent to the browser. 164 | * 165 | * @return array Array of message keys. 166 | */ 167 | protected function getMessageKeys() 168 | { 169 | $messageKeys = Config::get('js-localization.messages'); 170 | $messageKeys = JsLocalizationHelper::resolveMessageKeyArray($messageKeys); 171 | 172 | $messageKeys = array_unique( 173 | array_merge($messageKeys, JsLocalizationHelper::getAdditionalMessages()) 174 | ); 175 | 176 | return $messageKeys; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/Console/ExportCommand.php: -------------------------------------------------------------------------------- 1 | option('no-cache'); 57 | if ($noCache === true) $this->line('Exporting messages and config...'); 58 | else $this->line('Refreshing and exporting the message and config cache...'); 59 | 60 | $locales = Config::get('js-localization.locales'); 61 | 62 | if(!is_array($locales)) { 63 | throw new ConfigException('Please set the "locales" config! See https://github.com/andywer/laravel-js-localization#configuration'); 64 | } 65 | 66 | if ($noCache === false) MessageCachingService::refreshCache(); 67 | $messagesFilePath = $this->createPath('messages.js'); 68 | $this->generateMessagesFile($messagesFilePath, $noCache); 69 | 70 | if ($noCache === false) ConfigCachingService::refreshCache(); 71 | $configFilePath = $this->createPath('config.js'); 72 | $this->generateConfigFile($configFilePath, $noCache); 73 | } 74 | 75 | /** 76 | * Execute the console command. 77 | * Compatibility with previous Laravel 5.x versions. 78 | * 79 | * @return void 80 | * @throws ConfigException 81 | */ 82 | public function fire() 83 | { 84 | $this->handle(); 85 | } 86 | 87 | /** 88 | * Create full file path. 89 | * This method will also generate the directories if they don't exist already. 90 | * 91 | * @var string $filename 92 | * 93 | * @return string $path 94 | */ 95 | public function createPath($filename) 96 | { 97 | $dir = Config::get('js-localization.storage_path'); 98 | if (!is_dir($dir)) { 99 | mkdir($dir, 0777, true); 100 | } 101 | 102 | return $dir . $filename; 103 | } 104 | 105 | /** 106 | * Generate the messages file. 107 | * 108 | * @param string $path 109 | * @param bool $noCache 110 | */ 111 | public function generateMessagesFile($path, $noCache = false) 112 | { 113 | $splitFiles = Config::get('js-localization.split_export_files'); 114 | $messages = MessageCachingService::getMessagesJson($noCache); 115 | 116 | if ($splitFiles) { 117 | $this->generateMessageFiles(File::dirname($path), $messages); 118 | } 119 | 120 | $contents = 'Lang.addMessages(' . $messages . ');'; 121 | 122 | File::put($path, $contents); 123 | 124 | $this->line("Generated $path"); 125 | } 126 | 127 | /** 128 | * Generate the lang-{locale}.js files 129 | * 130 | * @param string $path Directory to where we will store the files 131 | * @param string $messages JSON string of messages 132 | */ 133 | protected function generateMessageFiles(string $path, string $messages) 134 | { 135 | $locales = Config::get('js-localization.locales'); 136 | $messages = json_decode($messages, true); 137 | 138 | foreach ($locales as $locale) { 139 | $fileName = $path . "/lang-{$locale}.js"; 140 | 141 | if (key_exists($locale, $messages)) { 142 | $content = 'Lang.addMessages(' . json_encode([$locale => $messages[$locale]], JSON_PRETTY_PRINT) . ');'; 143 | 144 | File::put($fileName, $content); 145 | $this->line("Generated $fileName"); 146 | } 147 | } 148 | } 149 | 150 | /** 151 | * Generate the config file. 152 | * 153 | * @param string $path 154 | * @param bool $noCache 155 | */ 156 | public function generateConfigFile($path, $noCache = false) 157 | { 158 | $config = ConfigCachingService::getConfigJson($noCache); 159 | if ($config === '{}') { 160 | $this->line('No config specified. Config not written to file.'); 161 | return; 162 | } 163 | 164 | $contents = 'Config.addConfig(' . $config . ');'; 165 | 166 | File::put($path, $contents); 167 | 168 | $this->line("Generated $path"); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/Console/RefreshCommand.php: -------------------------------------------------------------------------------- 1 | line('Refreshing the message cache...'); 37 | 38 | $locales = Config::get('js-localization.locales'); 39 | 40 | if(!is_array($locales)) { 41 | throw new ConfigException('Please set the "locales" config! See https://github.com/andywer/laravel-js-localization#configuration'); 42 | } 43 | 44 | MessageCachingService::refreshCache(); 45 | ConfigCachingService::refreshCache(); 46 | } 47 | 48 | /** 49 | * Execute the console command. 50 | * Compatibility with previous Laravel 5.x versions. 51 | * 52 | * @return void 53 | * @throws ConfigException 54 | */ 55 | public function fire() 56 | { 57 | $this->handle(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Exceptions/ConfigException.php: -------------------------------------------------------------------------------- 1 | getMessagesJson(); 21 | 22 | return response($contents) 23 | ->header('Content-Type', 'text/javascript') 24 | ->setLastModified(MessageCachingService::getLastRefreshTimestamp()); 25 | } 26 | 27 | /** 28 | * @return \Illuminate\Http\Response 29 | */ 30 | public function createJsConfig() 31 | { 32 | $contents = $this->getConfigJson(); 33 | 34 | /** @var \Illuminate\Http\Response $response */ 35 | $response = response($contents); 36 | $response->header('Content-Type', 'text/javascript'); 37 | 38 | if (ConfigCachingService::isDisabled()) { 39 | $response->setEtag(md5($contents)); 40 | } else { 41 | $response->setLastModified(ConfigCachingService::getLastRefreshTimestamp()); 42 | } 43 | 44 | return $response; 45 | } 46 | 47 | /** 48 | * Deliver the Framework for getting the translation in JS 49 | * 50 | * @return \Illuminate\Http\Response 51 | */ 52 | public function deliverLocalizationJS() 53 | { 54 | $response = new StaticFileResponse( __DIR__."/../../../public/js/localization.min.js" ); 55 | $response->setPublic(); 56 | $response->header('Content-Type', 'application/javascript'); 57 | 58 | return $response; 59 | } 60 | 61 | /** 62 | * Deliver one file that combines messages and framework. 63 | * Saves one additional HTTP-Request 64 | * 65 | * @return \Illuminate\Http\Response 66 | */ 67 | public function deliverAllInOne() 68 | { 69 | $contents = file_get_contents( __DIR__."/../../../public/js/localization.min.js" ); 70 | $contents .= "\n"; 71 | $contents .= $this->getMessagesJson(); 72 | $contents .= $this->getConfigJson(); 73 | 74 | /** @var \Illuminate\Http\Response $response */ 75 | $response = response($contents); 76 | $response->header('Content-Type', 'text/javascript'); 77 | 78 | if (ConfigCachingService::isDisabled()) { 79 | $response->setEtag(md5($contents)); 80 | } else { 81 | $response->setLastModified(MessageCachingService::getLastRefreshTimestamp()); 82 | } 83 | 84 | return $response; 85 | } 86 | 87 | /** 88 | * Transforms the cached data to stay compatible to old versions of the package. 89 | * 90 | * @param string $messages 91 | * @return string 92 | */ 93 | protected function ensureBackwardsCompatibility($messages) 94 | { 95 | if (preg_match('/^\\{"[a-z]{2}":/', $messages)) { 96 | return $messages; 97 | } else { 98 | return '{"' . app()->getLocale() . '":' . $messages . '}'; 99 | } 100 | } 101 | 102 | /** 103 | * Get the configured messages from the translation files 104 | * 105 | * @return string 106 | */ 107 | protected function getMessagesJson() 108 | { 109 | $messages = MessageCachingService::getMessagesJson(); 110 | $messages = $this->ensureBackwardsCompatibility($messages); 111 | 112 | $contents = 'Lang.addMessages(' . $messages . ');'; 113 | 114 | return $contents; 115 | } 116 | 117 | /** 118 | * Get the JSON-encoded config properties that shall be passed to the client. 119 | * 120 | * @return string 121 | */ 122 | protected function getConfigJson() 123 | { 124 | $config = ConfigCachingService::getConfigJson(); 125 | 126 | $contents = 'Config.addConfig(' . $config . ');'; 127 | 128 | return $contents; 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/Http/Responses/StaticFileResponse.php: -------------------------------------------------------------------------------- 1 | setContent($fileContent); 22 | 23 | $lastModified = new DateTime(); 24 | $lastModified->setTimestamp( File::lastModified($filePath) ); 25 | 26 | $this->setLastModified($lastModified); 27 | 28 | $this->isNotModified(App::make('request')); 29 | } 30 | } -------------------------------------------------------------------------------- /src/Http/routes.php: -------------------------------------------------------------------------------- 1 | '\JsLocalization\Http\Controllers' ], function() 5 | { 6 | Route::get('/js-localization/messages', 'JsLocalizationController@createJsMessages'); 7 | Route::get('/js-localization/config', 'JsLocalizationController@createJsConfig'); 8 | Route::get('/js-localization/localization.js', 'JsLocalizationController@deliverLocalizationJS'); 9 | 10 | Route::get('/js-localization/all.js', 'JsLocalizationController@deliverAllInOne'); 11 | }); 12 | -------------------------------------------------------------------------------- /src/JsLocalizationServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 30 | __DIR__.'/../config/config.php' => config_path('js-localization.php') 31 | ]); 32 | 33 | $this->publishes([ 34 | __DIR__.'/../public/js/localization.min.js' => public_path('vendor/js-localization/js-localization.min.js'), 35 | ], 'public'); 36 | 37 | $this->mergeConfigFrom( 38 | __DIR__.'/../config/config.php', 'js-localization' 39 | ); 40 | 41 | $this->loadViewsFrom(__DIR__.'/../resources/views', 'js-localization'); 42 | 43 | $this->registerRefreshCommand(); 44 | $this->registerExportCommand(); 45 | } 46 | 47 | /** 48 | * Register the service provider. 49 | * 50 | * @return void 51 | */ 52 | public function register() 53 | { 54 | require __DIR__.'/bindings.php'; 55 | require __DIR__.'/Http/routes.php'; 56 | } 57 | 58 | /** 59 | * Get the services provided by the provider. 60 | * 61 | * @return array 62 | */ 63 | public function provides() 64 | { 65 | return ['js-localization']; 66 | } 67 | 68 | /** 69 | * Register js-localization.refresh 70 | */ 71 | private function registerRefreshCommand() 72 | { 73 | $this->app->singleton('js-localization.refresh', function() 74 | { 75 | return new RefreshCommand; 76 | }); 77 | 78 | $this->commands('js-localization.refresh'); 79 | } 80 | 81 | /** 82 | * Register js-localization.export 83 | */ 84 | private function registerExportCommand() 85 | { 86 | $this->app->singleton('js-localization.export', function() 87 | { 88 | return new ExportCommand; 89 | }); 90 | 91 | $this->commands('js-localization.export'); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/Utils/Helper.php: -------------------------------------------------------------------------------- 1 | messagesToExport = array_unique( 35 | array_merge( 36 | $this->messagesToExport, 37 | $this->resolveMessageKeyArray($messageKeys) 38 | ) 39 | ); 40 | } 41 | 42 | /** 43 | * Similar to addMessagesToExport(), but does not 44 | * register an array of message keys, but the 45 | * messages of a whole language file (one of the 46 | * PHP files in app/lang). 47 | * 48 | * @param string $filePath Path to the message file. 49 | * @param string $prefix Optional. Prefix to prepend before the message keys. 50 | * @return void 51 | * @throws FileNotFoundException 52 | */ 53 | public function addMessageFileToExport($filePath, $prefix="") 54 | { 55 | if (!File::isFile($filePath)) { 56 | throw new FileNotFoundException("File not found: $filePath"); 57 | } 58 | 59 | $messages = require_once $filePath; 60 | $prefix = $this->prefix($prefix); 61 | $prefix .= preg_replace('/\.php$/i', '', basename($filePath)) . '.'; 62 | 63 | $this->messagesToExport = array_unique( 64 | array_merge( 65 | $this->messagesToExport, 66 | $this->resolveMessageArrayToMessageKeys($messages, $prefix) 67 | ) 68 | ); 69 | } 70 | 71 | /** 72 | * Returns the message keys previously registered 73 | * by addMessagesToExport(). Nested arrays have 74 | * already been resolved to a single flat array. 75 | * 76 | * @return array 77 | * Array of message keys to export to the JS code. 78 | */ 79 | public function getAdditionalMessages() 80 | { 81 | return $this->messagesToExport; 82 | } 83 | 84 | /** 85 | * Takes an array of message keys with nested 86 | * sub-arrays and returns a flat array of 87 | * fully qualified message keys. 88 | * 89 | * @param array $messageKeys Complex array of message keys. 90 | * @return array Flat array of fully qualified message keys. 91 | */ 92 | public function resolveMessageKeyArray(array $messageKeys) 93 | { 94 | $flatArray = []; 95 | 96 | foreach ($messageKeys as $index=>$key) { 97 | $this->resolveMessageKey($key, $index, function($qualifiedKey) use(&$flatArray) 98 | { 99 | $flatArray[] = $qualifiedKey; 100 | }); 101 | } 102 | 103 | return $flatArray; 104 | } 105 | 106 | /** 107 | * Resolves a message array with nested 108 | * sub-arrays to a flat array of fully 109 | * qualified message keys. 110 | * 111 | * @param array $messages Complex message array (like the ones in the app/lang/* files). 112 | * @return array Flat array of fully qualified message keys. 113 | */ 114 | public function resolveMessageArrayToMessageKeys(array $messages, $prefix="") 115 | { 116 | $flatArray = []; 117 | 118 | foreach ($messages as $key=>$message) { 119 | $this->resolveMessageToKeys($message, $key, function($qualifiedKey) use(&$flatArray) 120 | { 121 | $flatArray[] = $qualifiedKey; 122 | }, $prefix); 123 | } 124 | 125 | return $flatArray; 126 | } 127 | 128 | /** 129 | * Returns the concatenation of prefix and key if the key 130 | * is a string. If the key is an array then the function 131 | * will recurse. 132 | * 133 | * @param mixed $key An array item read from the configuration ('messages' array). 134 | * @param mixed $keyIndex The array index of $key. Is necessary if $key is an array. 135 | * @param callable $callback A callback function: function($fullyQualifiedKey). 136 | * @param string $prefix Optional key prefix. 137 | */ 138 | private function resolveMessageKey($key, $keyIndex, $callback, $prefix="") 139 | { 140 | if (is_array($key)) { 141 | $_prefix = $prefix ? $prefix.$keyIndex."." : $keyIndex."."; 142 | 143 | foreach ($key as $_index=>$_key) { 144 | $this->resolveMessageKey($_key, $_index, $callback, $_prefix); 145 | } 146 | 147 | } else { 148 | $callback($prefix.$key); 149 | } 150 | } 151 | 152 | /** 153 | * Returns the concatenation of prefix and key if the value 154 | * is a message. If the value is an array then the function 155 | * will recurse. 156 | * 157 | * @param mixed $message An array item read from a message file array. 158 | * @param mixed $key The array key of $message. 159 | * @param callable $callback A callback function: function($fullyQualifiedKey). 160 | * @param string $prefix Optional key prefix. 161 | */ 162 | private function resolveMessageToKeys($message, $key, $callback, $prefix="") 163 | { 164 | if (is_array($message)) { 165 | $_prefix = $prefix ? $prefix.$key."." : $key."."; 166 | 167 | foreach ($message as $_key=>$_message) { 168 | $this->resolveMessageToKeys($_message, $_key, $callback, $_prefix); 169 | } 170 | 171 | } else { 172 | $callback($prefix.$key); 173 | } 174 | } 175 | 176 | /** 177 | * Appends a dot to the prefix if necessary. 178 | * 179 | * @param string $prefix Prefix to validate and possibly append dot to. 180 | * @return string Processed prefix. 181 | */ 182 | private function prefix($prefix) 183 | { 184 | if ($prefix) { 185 | $prefixLastChar = substr($prefix, -1); 186 | 187 | if ($prefixLastChar != '.' && $prefixLastChar != ':') { 188 | $prefix .= '.'; 189 | } 190 | } 191 | 192 | return $prefix; 193 | } 194 | 195 | } 196 | -------------------------------------------------------------------------------- /src/bindings.php: -------------------------------------------------------------------------------- 1 | assertFalse(ConfigCachingService::isDisabled()); 27 | 28 | Config::set('js-localization.disable_config_cache', true); 29 | $this->assertTrue(ConfigCachingService::isDisabled()); 30 | } 31 | 32 | public function testGetConfigJson() 33 | { 34 | $expectedJson = json_encode($this->testConfigExportFlat); 35 | $this->assertEquals($expectedJson, ConfigCachingService::getConfigJson()); 36 | 37 | // Test that getConfigJson() returns the old value after Config::set() 38 | Config::set('js-localization.test-config', 'new value'); 39 | $this->assertEquals($expectedJson, ConfigCachingService::getConfigJson()); 40 | } 41 | 42 | public function testGetConfigJsonWithCacheDisabled() 43 | { 44 | Config::set('js-localization.disable_config_cache', true); 45 | 46 | $expectedJson = json_encode($this->testConfigExportFlat); 47 | $this->assertEquals($expectedJson, ConfigCachingService::getConfigJson()); 48 | 49 | // Test that getConfigJson() returns the new value after Config::set() 50 | $this->testConfigExportFlat['js-localization.test-config'] = 'new value'; 51 | $expectedJson = json_encode($this->testConfigExportFlat); 52 | 53 | Config::set('js-localization.test-config', 'new value'); 54 | $this->assertEquals($expectedJson, ConfigCachingService::getConfigJson()); 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /tests/JsLocalization/Caching/MessageCachingServiceTest.php: -------------------------------------------------------------------------------- 1 | assertMessagesJsonEquals($this->testMessagesFlat); 28 | 29 | // Add another string, but without refreshing the cache: 30 | 31 | $originalTestMessages = $this->testMessagesFlat; 32 | $this->addTestMessage('en','test.new_message', "This is a new message."); 33 | 34 | $this->assertMessagesJsonEquals($originalTestMessages); 35 | 36 | // Now refresh the cache: 37 | 38 | MessageCachingService::refreshCache(); 39 | 40 | $this->assertMessagesJsonEquals($this->testMessagesFlat); 41 | } 42 | 43 | public function testGetLastRefreshTimestamp() 44 | { 45 | $timestamp = MessageCachingService::getLastRefreshTimestamp()->getTimestamp(); 46 | $this->assertEquals(0, $timestamp); 47 | 48 | MessageCachingService::refreshCache(); 49 | $refreshTime = time(); 50 | 51 | $timestamp = MessageCachingService::getLastRefreshTimestamp()->getTimestamp(); 52 | $this->assertEquals($refreshTime, $timestamp); 53 | } 54 | 55 | public function testRefreshMessageCacheEvent() 56 | { 57 | $this->addToAssertionCount( 58 | \Mockery::getContainer()->mockery_getExpectationCount() 59 | ); 60 | 61 | Event::shouldReceive('dispatch')->once()->with('JsLocalization.registerMessages'); 62 | 63 | MessageCachingService::refreshCache(); 64 | } 65 | 66 | private function assertMessagesJsonEquals(array $expectedMessages) 67 | { 68 | $messagesJson = MessageCachingService::getMessagesJson(); 69 | $this->assertJson($messagesJson); 70 | 71 | $messages = json_decode($messagesJson, true); 72 | $this->assertEquals($expectedMessages, $messages); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /tests/JsLocalization/Console/RefreshCommandTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('get')->with('js-localization.locales') 21 | ->andReturn(null); 22 | 23 | $this->expectException(Exception::class); 24 | 25 | $this->runCommand(); 26 | } 27 | 28 | protected function runCommand() 29 | { 30 | $cmd = new RefreshCommand(); 31 | 32 | $cmd->setLaravel(app(\Illuminate\Contracts\Foundation\Application::class)); 33 | 34 | $cmd->run( 35 | new Symfony\Component\Console\Input\ArrayInput([]), 36 | new Symfony\Component\Console\Output\NullOutput 37 | ); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tests/JsLocalization/Http/Controllers/JsLocalizationControllerTest.php: -------------------------------------------------------------------------------- 1 | mockLang($locale = 'en'); 13 | Artisan::call('js-localization:refresh'); 14 | 15 | $response = $this->call('GET', '/js-localization/messages'); 16 | $content = $response->getContent(); 17 | 18 | $this->assertTrue($response->isOk()); 19 | 20 | // Test for Lang.addMessages() 21 | 22 | $this->assertLangAddMessages($content, $this->testMessagesFlat); 23 | } 24 | 25 | public function testCreateJsConfig() 26 | { 27 | // Prepare & Request 28 | 29 | $this->mockLang($locale = 'en'); 30 | Artisan::call('js-localization:refresh'); 31 | 32 | $response = $this->call('GET', '/js-localization/config'); 33 | $content = $response->getContent(); 34 | 35 | $this->assertTrue($response->isOk()); 36 | 37 | $this->assertNotNull($response->getLastModified()); 38 | $this->assertNull($response->getEtag()); 39 | 40 | // Test for Config.addConfig() 41 | 42 | $this->assertConfigAddConfig($content, $this->testConfigExportFlat); 43 | } 44 | 45 | public function testCreateJsConfigWithCacheDisabled() 46 | { 47 | // Prepare & Request 48 | 49 | $this->mockLang($locale = 'en'); 50 | Config::set('js-localization.disable_config_cache', true); 51 | 52 | $response = $this->call('GET', '/js-localization/config'); 53 | $content = $response->getContent(); 54 | 55 | $this->assertTrue($response->isOk()); 56 | 57 | $this->assertNull($response->getLastModified()); 58 | $this->assertEquals('"'.md5($content).'"', $response->getEtag()); 59 | 60 | // Test for Config.addConfig() 61 | 62 | $this->assertConfigAddConfig($content, $this->testConfigExportFlat); 63 | } 64 | 65 | public function testBackwardsCompatibility() 66 | { 67 | // Prepare & Request 68 | 69 | $this->mockLang($locale = 'en'); 70 | $this->mockMessageCachingService($this->testMessagesFlat['en']); 71 | 72 | $response = $this->call('GET', '/js-localization/messages'); 73 | $content = $response->getContent(); 74 | 75 | $this->assertTrue($response->isOk()); 76 | 77 | // Test for Lang.addMessages() 78 | 79 | $this->assertLangAddMessages($content, $this->testMessagesFlat); 80 | } 81 | 82 | private function mockMessageCachingService(array $messages) 83 | { 84 | $service = m::mock('CachingServiceMock'); 85 | JsLocalization\Facades\MessageCachingService::swap($service); 86 | 87 | $service->shouldReceive('getMessagesJson') 88 | ->andReturn(json_encode($messages)); 89 | 90 | $service->shouldReceive('getLastRefreshTimestamp') 91 | ->andReturn(new DateTime); 92 | } 93 | 94 | private function assertLangAddMessages($jsContent, array $expectedMessages) 95 | { 96 | $this->assertJsCall($jsContent, 'Lang.addMessages', $expectedMessages); 97 | } 98 | 99 | protected function assertConfigAddConfig($jsContent, array $expectedConfig) 100 | { 101 | $this->assertJsCall($jsContent, 'Config.addConfig', $expectedConfig); 102 | } 103 | 104 | protected function assertJsCall($jsContent, $functionName, $functionParam) 105 | { 106 | $functionName = str_replace('.', '\\.', $functionName); 107 | $functionNameRegex = '/' . $functionName . '\( (\{.*?\}) \);/x'; 108 | 109 | preg_match($functionNameRegex, $jsContent, $matches); 110 | $paramJson = $matches[1]; 111 | 112 | $this->assertJson($paramJson); 113 | $parsedParam = json_decode($paramJson, true); 114 | 115 | $this->assertEquals($functionParam, $parsedParam); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /tests/JsLocalization/Http/LocalizationScriptTest.php: -------------------------------------------------------------------------------- 1 | call('GET', '/js-localization/localization.js'); 8 | 9 | $this->assertTrue($response->isOk()); 10 | $content = $response->getContent(); 11 | 12 | // Test for JS content 13 | 14 | $this->assertRegExp('/^!?\(?function\(.*\);/', $content); 15 | } 16 | 17 | public function testScriptAndTranslationCombinedRetrieval() 18 | { 19 | $response = $this->call('GET', '/js-localization/all.js'); 20 | 21 | $this->assertTrue($response->isOk()); 22 | $content = $response->getContent(); 23 | 24 | // Test for JS content 25 | 26 | $this->assertRegExp('/^!?\(?function\(.*\);/', $content); 27 | 28 | // Test for Lang.addMessages() 29 | 30 | $addMessagesRegex = '/Lang\.addMessages\( (\{.*?\}) \);/x'; 31 | $this->assertRegExp($addMessagesRegex, $content); 32 | 33 | // Test for Config.addConfig() 34 | 35 | $addConfigRegex = '/Config\.addConfig\( (\{.*?\}) \);/x'; 36 | $this->assertRegExp($addConfigRegex, $content); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/JsLocalization/Http/Responses/StaticFileResponseTest.php: -------------------------------------------------------------------------------- 1 | testFilePath = "/tmp/laravel-static"; 16 | $this->testFileContent = "Test contents!"; 17 | 18 | File::put($this->testFilePath, $this->testFileContent); 19 | } 20 | 21 | public function testServingFile() 22 | { 23 | $response = new StaticFileResponse($this->testFilePath); 24 | 25 | $lastModified = new DateTime(); 26 | $lastModified->setTimestamp( File::lastModified($this->testFilePath) ); 27 | 28 | $this->assertEquals($response->getStatusCode(), 200); 29 | $this->assertEquals($response->getContent(), $this->testFileContent); 30 | $this->assertEquals($response->getLastModified()->getTimestamp(), $lastModified->getTimestamp()); 31 | } 32 | 33 | public function testExceptionHandling() 34 | { 35 | $filePath = "/tmp/x/y/z/does-not-exist"; 36 | 37 | $this->expectException(Exception::class, "Cannot read file: $filePath"); 38 | 39 | new StaticFileResponse($filePath); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/JsLocalization/JsLocalizationServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | app); 17 | 18 | $this->assertEquals( $service->provides(), ['js-localization'] ); 19 | } 20 | 21 | public function testRegisteredNamespaces() 22 | { 23 | $this->assertEquals(['en'], Config::get('js-localization.locales')); 24 | $this->assertEquals([], Config::get('js-localization.messages')); 25 | 26 | $this->assertArrayHasKey( 27 | 'js-localization', View::getFinder()->getHints(), 28 | 'View namespace not registered: js-localization' 29 | ); 30 | } 31 | 32 | public function testRegisteredCommands() 33 | { 34 | $allCommands = Artisan::all(); 35 | 36 | /** @var \Symfony\Component\Console\Command\Command $command */ 37 | foreach ($allCommands as $command) { 38 | if ($command->getName() === 'js-localization:refresh') { break; } 39 | } 40 | 41 | $refreshCommand = $command; 42 | $this->assertInstanceOf('JsLocalization\Console\RefreshCommand', $refreshCommand); 43 | 44 | $commandTester = new CommandTester($refreshCommand); 45 | $commandTester->execute([]); 46 | $this->assertEquals("Refreshing the message cache...\n", $commandTester->getDisplay()); 47 | } 48 | 49 | public function testBindings() 50 | { 51 | $helper = App::make('JsLocalizationHelper'); 52 | $this->assertInstanceOf('JsLocalization\Utils\Helper', $helper); 53 | 54 | $cachingService = App::make('JsLocalizationMessageCachingService'); 55 | $this->assertInstanceOf('JsLocalization\Caching\MessageCachingService', $cachingService); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /tests/JsLocalization/Utils/JsLocalizationHelperTest.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'message1', 13 | 'message2' 14 | ] 15 | ]; 16 | 17 | protected $additionalMessageKeysFlat = [ 18 | 'additional.message1', 'additional.message2' 19 | ]; 20 | 21 | 22 | protected $testMessagesFlat = [ 23 | 'en' => [ 24 | 'test1' => "Text for test1", 25 | 'prefix1' => [ 26 | 'prefix2' => [ 27 | 'test2' => "Text for test2", 28 | 'test3' => "Text for test3" 29 | ], 30 | 'test4' => "Text for test4" 31 | ] 32 | ] 33 | ]; 34 | 35 | protected $testKeys = [ 36 | 'test1', 37 | 'prefix1' => [ 38 | 'prefix2' => [ 39 | 'test2', 'test3' 40 | ], 41 | 'test4' 42 | ] 43 | ]; 44 | 45 | protected $testKeysFlat = [ 46 | 'test1', 47 | 'prefix1.prefix2.test2', 48 | 'prefix1.prefix2.test3', 49 | 'prefix1.test4' 50 | ]; 51 | 52 | protected function setUpTestMessagesFile($filePath) 53 | { 54 | $fileContents = 'testMessagesFlat['en'], true) . ';'; 55 | file_put_contents($filePath, $fileContents); 56 | 57 | $prefix = preg_replace('/\.php$/i', '', basename($filePath)); 58 | 59 | return $prefix; 60 | } 61 | 62 | public function setUp(): void 63 | { 64 | parent::setUp(); 65 | 66 | $this->tmpFilePath = tempnam('/tmp', ''); 67 | unlink($this->tmpFilePath); 68 | 69 | $this->tmpFilePath .= '.php'; 70 | touch($this->tmpFilePath); 71 | } 72 | 73 | public function tearDown(): void 74 | { 75 | unlink($this->tmpFilePath); 76 | 77 | parent::tearDown(); 78 | } 79 | 80 | public function testResolveMessageKeyArray() 81 | { 82 | $this->assertEquals($this->testKeysFlat, JsLocalizationHelper::resolveMessageKeyArray($this->testKeys)); 83 | } 84 | 85 | public function testResolveMessageArrayToMessageKeys() 86 | { 87 | $this->assertEquals($this->testKeysFlat, JsLocalizationHelper::resolveMessageArrayToMessageKeys($this->testMessagesFlat['en'])); 88 | } 89 | 90 | public function testAddingRetrieving() 91 | { 92 | JsLocalizationHelper::addMessagesToExport($this->additionalMessageKeys); 93 | 94 | $this->assertEquals( 95 | $this->additionalMessageKeysFlat, 96 | JsLocalizationHelper::getAdditionalMessages() 97 | ); 98 | 99 | 100 | $this->addTestMessage('en', 'another', 'Another test text.'); 101 | 102 | JsLocalizationHelper::addMessagesToExport(['another']); 103 | 104 | $this->assertEquals( 105 | array_merge($this->additionalMessageKeysFlat, ['another']), 106 | JsLocalizationHelper::getAdditionalMessages() 107 | ); 108 | } 109 | 110 | public function testEventBasedAdding() 111 | { 112 | $additionalMessageKeys = $this->additionalMessageKeys; 113 | 114 | 115 | Event::listen('JsLocalization.registerMessages', function() 116 | use($additionalMessageKeys) 117 | { 118 | JsLocalizationHelper::addMessagesToExport($additionalMessageKeys); 119 | }); 120 | 121 | $this->assertEquals([], JsLocalizationHelper::getAdditionalMessages()); 122 | 123 | Event::dispatch('JsLocalization.registerMessages'); 124 | 125 | $this->assertEquals( 126 | $this->additionalMessageKeysFlat, 127 | JsLocalizationHelper::getAdditionalMessages() 128 | ); 129 | 130 | 131 | $this->addTestMessage('en', 'another', 'Another test text.'); 132 | 133 | Event::listen('JsLocalization.registerMessages', function() 134 | { 135 | JsLocalizationHelper::addMessagesToExport(['another']); 136 | }); 137 | 138 | Event::dispatch('JsLocalization.registerMessages'); 139 | 140 | $this->assertEquals( 141 | array_merge($this->additionalMessageKeysFlat, ['another']), 142 | JsLocalizationHelper::getAdditionalMessages() 143 | ); 144 | } 145 | 146 | public function testAddMessageFileToExport() 147 | { 148 | $prefix = 'xyz::' . $this->setUpTestMessagesFile($this->tmpFilePath); 149 | JsLocalizationHelper::addMessageFileToExport($this->tmpFilePath, 'xyz::'); 150 | 151 | // since we just tested the method using a prefix without the trailing '.' 152 | $prefix .= '.'; 153 | 154 | $testKeysFlat = $this->testKeysFlat; 155 | array_walk($testKeysFlat, function(&$key) use($prefix) 156 | { 157 | $key = $prefix . $key; 158 | }); 159 | 160 | $this->assertEquals($testKeysFlat, JsLocalizationHelper::getAdditionalMessages()); 161 | } 162 | 163 | public function testAddMessageFileToExportExceptionHandling() 164 | { 165 | $filePath = "/tmp/x/y/z/does-not-exist"; 166 | 167 | $this->expectException( 168 | JsLocalization\Exceptions\FileNotFoundException::class, 169 | "File not found: $filePath" 170 | ); 171 | 172 | JsLocalizationHelper::addMessageFileToExport($filePath, 'xyz::'); 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | sh [ 15 | 'test_string' => 'This is: test_string', 16 | 'test' => [ 17 | 'nested' => [ 18 | 'leaf' => 'I am deeply nested!' 19 | ], 20 | 'string' => 'This is: test.string' 21 | ], 22 | 23 | 'test.string' => 'This is: test.string' 24 | ] 25 | ]; 26 | 27 | protected $testMessagesFlat = [ 28 | 'en' => [ 29 | 'test_string' => 'This is: test_string', 30 | 'test.nested.leaf' => 'I am deeply nested!', 31 | 'test.string' => 'This is: test.string' 32 | ] 33 | ]; 34 | 35 | protected $testConfigExportFlat = [ 36 | 'js-localization.test-config' => 'some test property value' 37 | ]; 38 | 39 | 40 | public function setUp(): void 41 | { 42 | parent::setUp(); 43 | 44 | $this->updateMessagesConfig($this->testMessagesConfig); 45 | $this->updateConfigExportConfig($this->testConfigExportFlat); 46 | $this->mockLang(); 47 | } 48 | 49 | public function tearDown(): void 50 | { 51 | m::close(); 52 | 53 | parent::tearDown(); 54 | } 55 | 56 | protected function getPackageProviders($app) 57 | { 58 | return ['JsLocalization\JsLocalizationServiceProvider']; 59 | } 60 | 61 | protected function updateMessagesConfig(array $config) 62 | { 63 | Config::set('js-localization.messages', $config); 64 | } 65 | 66 | protected function updateConfigExportConfig(array $configData) 67 | { 68 | Config::set('js-localization.config', array_keys($configData)); 69 | 70 | foreach ($configData as $propertyName => $propertyValue) { 71 | Config::set($propertyName, $propertyValue); 72 | } 73 | } 74 | 75 | protected function mockLang($locale = "en") 76 | { 77 | Illuminate\Support\Facades\Lang::swap($lang = m::mock('LangMock')); 78 | 79 | $lang->shouldReceive('setLocale'); 80 | $lang->shouldReceive('locale')->andReturn($locale); 81 | 82 | foreach ($this->testMessages[$locale] as $key=>$message) { 83 | $lang->shouldReceive('get') 84 | ->with($key)->andReturn($message); 85 | $lang->shouldReceive('get') 86 | ->with($key, m::any(), $locale)->andReturn($message); 87 | } 88 | } 89 | 90 | protected function addTestMessage($locale, $messageKey, $message) 91 | { 92 | $this->testMessagesConfig[] = $messageKey; 93 | 94 | $this->testMessages[$locale][$messageKey] = $message; 95 | $this->testMessagesFlat[$locale][$messageKey] = $message; 96 | 97 | $keys = explode('.', $messageKey); 98 | if (count($keys) == 2) { 99 | if (!isset($this->testMessages[$locale][$keys[0]])) { 100 | $this->testMessages[$locale][$keys[0]] = []; 101 | } 102 | $this->testMessages[$locale][$keys[0]][$keys[1]] = $message; 103 | } 104 | 105 | $this->updateMessagesConfig($this->testMessagesConfig); 106 | $this->mockLang(); 107 | } 108 | 109 | } 110 | --------------------------------------------------------------------------------