├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── composer.json └── src ├── Classes └── ExportLocalizations.php ├── Console └── Commands │ ├── ExportMessages.php │ └── ExportMessagesToFlat.php ├── Events └── LaravelLocalizationExported.php ├── Facades └── ExportLocalizations.php ├── LaravelLocalizationServiceProvider.php ├── config └── laravel-localization.php └── routes.php /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | .idea -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at office@kg-studio.biz. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Stefan Ninic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Latest Stable Version](https://poser.pugx.org/kg-bot/laravel-localization-to-vue/v/stable)](https://packagist.org/packages/kg-bot/laravel-localization-to-vue) 2 | [![Total Downloads](https://poser.pugx.org/kg-bot/laravel-localization-to-vue/downloads)](https://packagist.org/packages/kg-bot/laravel-localization-to-vue) 3 | [![Latest Unstable Version](https://poser.pugx.org/kg-bot/laravel-localization-to-vue/v/unstable)](https://packagist.org/packages/kg-bot/laravel-localization-to-vue) 4 | [![License](https://poser.pugx.org/kg-bot/laravel-localization-to-vue/license)](https://packagist.org/packages/kg-bot/laravel-localization-to-vue) 5 | [![Monthly Downloads](https://poser.pugx.org/kg-bot/laravel-localization-to-vue/d/monthly)](https://packagist.org/packages/kg-bot/laravel-localization-to-vue) 6 | [![Daily Downloads](https://poser.pugx.org/kg-bot/laravel-localization-to-vue/d/daily)](https://packagist.org/packages/kg-bot/laravel-localization-to-vue) 7 | 8 | 9 | 10 | 11 | # Laravel Localization To Vue/JSON 12 | 13 | This package collects all localizations from resources/lang directory and it's sub-directories and converts them to plain array 14 | which can later be converted to JSON object and used with libraries like Vue, Angular, etc. 15 | 16 | ## Installing 17 | 18 | #### Laravel before version 9 19 | 20 | ``` 21 | composer require kg-bot/laravel-localization-to-vue:^1 22 | ``` 23 | 24 | #### Laravel 9 25 | 26 | ``` 27 | composer require kg-bot/laravel-localization-to-vue:^2 28 | ``` 29 | 30 | 31 | ### Laravel 8+ 32 | Laravel 8 requires minimum of PHP 7.3. make sure you have at least PHP 7.3 before running composer. 33 | 34 | ### Laravel 9+ 35 | Laravel 9 requires minimum of PHP 8.0.2 make sure you have at least PHP 8.0.2 before running composer. 36 | 37 | ### Laravel 5.5+ 38 | 39 | Laravel 5.5 uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider. 40 | 41 | If you don't use auto-discovery, add the ServiceProvider to the providers array in config/app.php 42 | ```php 43 | KgBot\LaravelLocalization\LaravelLocalizationServiceProvider::class 44 | ``` 45 | 46 | and if you want alias add this inside aliases array in config/app.php 47 | ```php 48 | "ExportLocalization" => "KgBot\\LaravelLocalization\\Facades\\ExportLocalizations" 49 | ``` 50 | 51 | ## Settings and configuration 52 | 53 | You can export config by running 54 | 55 | ```php 56 | php artisan vendor:publish --provider="KgBot\LaravelLocalization\LaravelLocalizationServiceProvider" --tag=config 57 | ``` 58 | 59 | if you want to parse multiple language directories or some other directory except `lang_path()` you can add multiple 60 | paths in config `paths.lang_dirs` inside array. 61 | 62 | It can be just one path or multiple paths, for example 63 | ```php 64 | paths => [lang_path(), app_path('lang'), app_path('Modules/Blog/lang')] 65 | ``` 66 | 67 | You can run your own callback function after export, to do that you must register globally accessible function, for example 68 | register `function.php` inside composer files autoload, and add your function inside `config/laravel-localization.php`, 69 | key `export_callback`. Example: 70 | ```php 71 | // helpers/functions.php 72 | 73 | if (! function_exists('testExport')) { 74 | /** 75 | * Change all instances of :argument to {argument} 76 | * 77 | * @param $string 78 | * @return void 79 | * 80 | */ 81 | function testExport($string) { 82 | array_walk_recursive($string, function (&$v, $k) { $v = preg_replace('/:(\w+)/', '{$1}', $v); }); 83 | 84 | return $string; 85 | } 86 | } 87 | 88 | 89 | // composer.json 90 | .... 91 | "autoload": { 92 | "files": [ 93 | "helpers/functions.php" 94 | ], 95 | "psr-4": { 96 | "App\\": "app/" 97 | }, 98 | "classmap": [ 99 | "database/seeds", 100 | "database/factories" 101 | ] 102 | }, 103 | .... 104 | 105 | // config/laravel-localization.php 106 | ... 107 | 'export_callback' => 'testExport', 108 | ... 109 | ``` 110 | 111 | # Usage 112 | 113 | This package can be used in multiple ways, I'll give examples for some of them, but there's really no limitation. 114 | 115 | First example would be to add view composed variable and use it in blade views. 116 | 117 | ```php 118 | // inside ServiceProvider 119 | 120 | // With alias 121 | use ExportLocalization; 122 | 123 | // Without alias 124 | use KgBot\LaravelLocalization\Facades\ExportLocalizations as ExportLocalization; 125 | 126 | 127 | View::composer( 'view.file', function ( $view ) { 128 | 129 | return $view->with( [ 130 | 'messages' => ExportLocalization::export()->toArray(), 131 | ] ); 132 | } ); 133 | ``` 134 | 135 | Second way would be to request it over HTTP just like any other file 136 | 137 | ```html 138 | 141 | ``` 142 | 143 | For this to work, you need to enable the route via `LARAVEL_LOCALIZATION_ROUTE_ENABLE` in your `.env` file or in `config/laravel-localization.php` 144 | 145 | You can also export messages to ECMAScript 6 standard JavaScript module with artisan command 146 | ```` php artisan export:messages ```` 147 | 148 | ## Export for npm localization packages like Lang.js 149 | If you need special format of array that's recognised by some npm localization packages as [Lang.js](https://github.com/rmariuzzo/Lang.js). 150 | 151 | ```php 152 | // Call toFlat() instead of toArray() 153 | ExportLocalization::export()->toFlat() 154 | 155 | or 156 | 157 | // For CLI usage 158 | php artisan export:messages-flat 159 | 160 | ``` 161 | 162 | ## Some examples why would you use this package and messages over Laravel standard localization 163 | 164 | ```html 165 | // Inside blade view 166 | 171 | ``` 172 | 173 | ```js 174 | // And optionaly you can then use it in any JavaScript file or Vue.js component 175 | 176 | // app.js 177 | import Vue from 'vue'; 178 | import Lang from 'lang.js'; 179 | 180 | const default_locale = window.default_language; 181 | const fallback_locale = window.fallback_locale; 182 | const messages = window.messages; 183 | 184 | Vue.prototype.trans = new Lang( { messages, locale: default_locale, fallback: fallback_locale } ); 185 | ``` 186 | 187 | ```html 188 | // Example.vue 189 | 193 | ``` 194 | 195 | ## A note about json files 196 | 197 | Laravel 5.4+ allows localization to be strutured [using a single `.json` file for each language](https://laravel.com/docs/5.7/localization#using-translation-strings-as-keys), in order to use the strings inside the provided json file you must prepend the `__JSON__` key 198 | 199 | ```json 200 | // Assuming that es.json exists and it is the default locale in your app 201 | { 202 | "I love programming": "Me encanta programar" 203 | } 204 | ``` 205 | 206 | ```html 207 | // Example.vue 208 | 209 | ``` 210 | 211 | ## Routing 212 | 213 | This package exposes one route `http://localhost/js/localization.js` by default but you can change the prefix to anything you whish in config file. 214 | 215 | You can also have a nice route name for blade templates or any other route calls, it's `route('assets.lang')` by default but it's customizable by config/environment file. 216 | 217 | ## Proposals, comments, feedback 218 | 219 | Everything of this is highly welcome and appreciated 220 | 221 | ## To-Do 222 | 223 | + Create exclude configuration so not files/directories are collected 224 | 225 | Anything else you can think of please leave me comments, mail me, create issue, whatever you prefer. 226 | 227 | ## License 228 | 229 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 230 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kg-bot/laravel-localization-to-vue", 3 | "description": "Laravel package used to collect all localization files from resources/lang (and custom) directories and sub-directories and make them available as JSON file", 4 | "require": { 5 | "php": "^8.0", 6 | "laravel/framework": "5.*|^6|^7|^8|^9|^10.0|^11.0|^12.0", 7 | "ext-json": "*" 8 | }, 9 | "keywords": [ 10 | "laravel", 11 | "vue.js", 12 | "trans", 13 | "localization", 14 | "json" 15 | ], 16 | "license": "MIT", 17 | "authors": [ 18 | { 19 | "name": "Stefan Ninic", 20 | "email": "ninicstefan94@gmail.com" 21 | } 22 | ], 23 | "autoload": { 24 | "psr-4": { 25 | "KgBot\\LaravelLocalization\\": "src/" 26 | } 27 | }, 28 | "extra": { 29 | "laravel": { 30 | "providers": [ 31 | "KgBot\\LaravelLocalization\\LaravelLocalizationServiceProvider" 32 | ], 33 | "aliases": { 34 | "ExportLocalization": "KgBot\\LaravelLocalization\\Facades\\ExportLocalizations" 35 | } 36 | } 37 | }, 38 | "config": { 39 | "sort-packages": true 40 | }, 41 | "minimum-stability": "dev", 42 | "prefer-stable": true, 43 | "funding": [ 44 | { 45 | "type": "buy-me-a-coffee", 46 | "url": "https://rebrand.ly/laravel-localization-to-vue" 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /src/Classes/ExportLocalizations.php: -------------------------------------------------------------------------------- 1 | phpRegex = $phpRegex; 50 | $this->jsonRegex = $jsonRegex; 51 | } 52 | 53 | /** 54 | * Method to return generate array with contents of parsed language files. 55 | * 56 | * @return object 57 | */ 58 | public function export() 59 | { 60 | // Check if value is cached and set array to cached version 61 | if (Cache::has(config('laravel-localization.caches.key'))) { 62 | $this->strings = Cache::get(config('laravel-localization.caches.key')); 63 | 64 | return $this; 65 | } 66 | 67 | foreach (config('laravel-localization.paths.lang_dirs') as $dir) { 68 | try { 69 | // Collect language files and build array with translations 70 | $files = $this->findLanguageFiles($dir); 71 | 72 | // Parse translations and create final array 73 | array_walk($files['lang'], [$this, 'parseLangFiles'], $dir); 74 | array_walk($files['vendor'], [$this, 'parseVendorFiles'], $dir); 75 | array_walk($files['json'], [$this, 'parseJsonFiles'], $dir); 76 | } catch (\Exception $exception) { 77 | \Log::critical('Can\'t read lang directory '.$dir.', error: '.$exception->getMessage()); 78 | } 79 | } 80 | 81 | // Trigger event for final translated array 82 | event(new LaravelLocalizationExported($this->strings)); 83 | 84 | // If timeout > 0 save array to cache 85 | if (config('laravel-localization.caches.timeout', 0) > 0) { 86 | Cache::store(config('laravel-localization.caches.driver', 'file')) 87 | ->put( 88 | config('laravel-localization.caches.key', 'localization.array'), 89 | $this->strings, 90 | config('laravel-localization.caches.timeout', 60) 91 | ); 92 | } 93 | 94 | return $this; 95 | } 96 | 97 | /** 98 | * Find available language files and parse them to array. 99 | * 100 | * @param string $path 101 | * @return array 102 | */ 103 | protected function findLanguageFiles($path) 104 | { 105 | // Loop through directories 106 | $dirIterator = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); 107 | $recIterator = new \RecursiveIteratorIterator($dirIterator); 108 | 109 | // Fetch only php files - skip others 110 | $phpFiles = array_values( 111 | array_map('current', 112 | iterator_to_array( 113 | new \RegexIterator($recIterator, $this->phpRegex, \RecursiveRegexIterator::GET_MATCH) 114 | ) 115 | ) 116 | ); 117 | 118 | $jsonFiles = array_values( 119 | array_map('current', 120 | iterator_to_array( 121 | new \RegexIterator($recIterator, $this->jsonRegex, \RecursiveRegexIterator::GET_MATCH) 122 | ) 123 | ) 124 | ); 125 | 126 | $files = array_merge($phpFiles, $jsonFiles); 127 | 128 | // Sort array by filepath 129 | sort($files); 130 | 131 | // Remove full path from items 132 | array_walk($files, function (&$item) use ($path) { 133 | $item = str_replace($path, '', $item); 134 | }); 135 | 136 | // Fetch non-vendor files from filtered php files 137 | $nonVendorFiles = array_filter($files, function ($file) { 138 | return strpos($file, $this->excludePath) === false && strpos($file, '.json') === false; 139 | }); 140 | 141 | // Fetch vendor files from filtered php files 142 | $vendorFiles = array_filter(array_diff($files, $nonVendorFiles), function ($file) { 143 | return strpos($file, 'json') === false; 144 | }); 145 | 146 | // Fetch .json files from filtered files 147 | $jsonFiles = array_filter($files, function ($file) { 148 | return strpos($file, '.json') !== false; 149 | }); 150 | 151 | return [ 152 | 'lang' => array_values($nonVendorFiles), 153 | 'vendor' => array_values($vendorFiles), 154 | 'json' => array_values($jsonFiles), 155 | ]; 156 | } 157 | 158 | /** 159 | * Method to return array for json serialization. 160 | * 161 | * @return array 162 | */ 163 | public function jsonSerialize(): mixed 164 | { 165 | return self::executeCallback($this->strings); 166 | } 167 | 168 | /** 169 | * Method to return array. 170 | * 171 | * @return array 172 | */ 173 | public function toArray() 174 | { 175 | return self::executeCallback($this->strings); 176 | } 177 | 178 | /** 179 | * If you need special format of array that's recognised by some npm localization packages as Lang.js 180 | * https://github.com/rmariuzzo/Lang.js use this method. 181 | * 182 | * @param array $array 183 | * @param string $prefix 184 | * @return array 185 | */ 186 | public function toFlat($prefix = '.') 187 | { 188 | $results = []; 189 | $default_locale = config('laravel-localization.js.default_locale'); 190 | $default_json_strings = null; 191 | 192 | foreach ($this->strings as $lang => $strings) { 193 | if ($lang !== 'json') { 194 | foreach ($strings as $lang_array => $lang_messages) { 195 | $key = $lang.$prefix.$lang_array; 196 | $results[$key] = $lang_messages; 197 | } 198 | } else { 199 | foreach ($strings as $json_lang => $json_strings) { 200 | $key = $json_lang.$prefix.'__JSON__'; 201 | if (array_key_exists($key, $results)) { 202 | $results[$key] = $json_strings; 203 | } else { 204 | $results[$key] = $json_strings; 205 | } 206 | 207 | // Pick only the first $json_strings 208 | if (! $default_json_strings) { 209 | $default_json_strings = $json_strings; 210 | } 211 | } 212 | } 213 | } 214 | 215 | // Create a JSON key value pair for the default language 216 | $default_key = $default_locale.$prefix.'__JSON__'; 217 | if (! array_key_exists($default_key, $results)) { 218 | $buffer = array_keys( 219 | $default_json_strings ? get_object_vars($default_json_strings) : [] 220 | ); 221 | 222 | $results[$default_key] = array_combine($buffer, $buffer); 223 | } 224 | 225 | return self::executeCallback($results); 226 | } 227 | 228 | /** 229 | * Method to return array as collection. 230 | * 231 | * @return \Illuminate\Support\Collection 232 | */ 233 | public function toCollection() 234 | { 235 | return collect(self::executeCallback($this->strings)); 236 | } 237 | 238 | /** 239 | * Method to parse language files. 240 | * 241 | * @param string $file 242 | */ 243 | protected function parseLangFiles($file, $key, $dir) 244 | { 245 | // Base package name without file ending 246 | $packageName = basename($file, '.php'); 247 | 248 | // Get package, language and file contents from language file 249 | // //(.php 250 | $language = explode(DIRECTORY_SEPARATOR, $file)[1]; 251 | $fileContents = require $dir.DIRECTORY_SEPARATOR.$file; 252 | 253 | // Check if language already exists in array 254 | if (array_key_exists($language, $this->strings)) { 255 | if (array_key_exists($packageName, $this->strings[$language])) { 256 | $this->strings[$language][$packageName] = 257 | array_replace_recursive((array) $this->strings[$language][$packageName], (array) 258 | $fileContents); 259 | } else { 260 | $this->strings[$language][$packageName] = $fileContents; 261 | } 262 | } else { 263 | $this->strings[$language] = [ 264 | $packageName => $fileContents, 265 | ]; 266 | } 267 | } 268 | 269 | /** 270 | * Method to parse language files from vendor folder. 271 | * 272 | * @param string $file 273 | */ 274 | protected function parseVendorFiles($file, $key, $dir) 275 | { 276 | // Base package name without file ending 277 | $packageName = basename($file, '.php'); 278 | 279 | // Get package, language and file contents from language file 280 | // /vendor///.php 281 | $package = explode(DIRECTORY_SEPARATOR, $file)[2]; 282 | $language = explode(DIRECTORY_SEPARATOR, $file)[3]; 283 | $fileContents = require $dir.DIRECTORY_SEPARATOR.$file; 284 | 285 | // Check if language already exists in array 286 | if (array_key_exists($language, $this->strings)) { 287 | // Check if package already exists in language 288 | if (array_key_exists($package, $this->strings[$language])) { 289 | if (array_key_exists($packageName, $this->strings[$language][$package])) { 290 | $this->strings[$language][$package][$packageName] = 291 | array_replace_recursive((array) $this->strings[$language][$package][$packageName], 292 | (array) 293 | $fileContents); 294 | } else { 295 | $this->strings[$language][$package][$packageName] = $fileContents; 296 | } 297 | } else { 298 | $this->strings[$language][$package] = [$packageName => $fileContents]; 299 | } 300 | } else { 301 | $this->strings[$language] = [ 302 | 303 | $package => [ 304 | 305 | $packageName => $fileContents, 306 | ], 307 | ]; 308 | } 309 | } 310 | 311 | protected function parseJsonFiles($file, $key, $dir) 312 | { 313 | // Base package name without file ending 314 | $language = basename($file, '.json'); 315 | 316 | // Get package, language and file contents from language file 317 | // //(.php 318 | $fileContents = json_decode(file_get_contents($dir.$file)); 319 | 320 | // Check if language already exists in array 321 | if (array_key_exists('json', $this->strings)) { 322 | if (array_key_exists($language, $this->strings['json'])) { 323 | $this->strings['json'][$language] = 324 | array_replace_recursive((array) $this->strings['json'][$language], (array) $fileContents); 325 | } else { 326 | $this->strings['json'][$language] = $fileContents; 327 | } 328 | } else { 329 | $this->strings['json'] = [ 330 | 331 | $language => $fileContents, 332 | ]; 333 | } 334 | } 335 | 336 | public static function exportToArray() 337 | { 338 | return (new self( 339 | config('laravel-localization.file_regexp.php', '/^.+\.php$/i'), 340 | config('laravel-localization.file_regexp.json', '/^.+\.json$/i') 341 | ))->export()->toArray(); 342 | } 343 | 344 | protected static function executeCallback($strings) 345 | { 346 | if ($callback = config('laravel-localization.export_callback')) { 347 | $strings = $callback($strings); 348 | } 349 | 350 | return $strings; 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /src/Console/Commands/ExportMessages.php: -------------------------------------------------------------------------------- 1 | filepath = config('laravel-localization.js.filepath', resource_path('assets/js')); 40 | $this->messages = ExportLocalizations::export()->toArray(); 41 | 42 | $format = $this->argument('format'); 43 | 44 | if ($format === 'javascript') { 45 | return $this->toJavaScript(); 46 | } 47 | if ($format === 'json') { 48 | return $this->toJson(); 49 | } 50 | 51 | $this->error("Format {$format} is not currently supported, you can use callback function if you need additional modification of exported array."); 52 | 53 | return 1; 54 | } 55 | 56 | protected function toJavaScript() 57 | { 58 | $filename = config('laravel-localization.js.filename', 'll_messages.js'); 59 | 60 | $adapter = new Local($this->filepath); 61 | $filesystem = new Filesystem($adapter); 62 | 63 | $contents = 'export default '.json_encode($this->messages); 64 | 65 | if ($filesystem->has($filename)) { 66 | $filesystem->delete($filename); 67 | $filesystem->write($filename, $contents); 68 | } else { 69 | $filesystem->write($filename, $contents); 70 | } 71 | 72 | $this->info('Messages exported to JavaScript file, you can find them at '.$this->filepath.DIRECTORY_SEPARATOR 73 | .$filename); 74 | 75 | return 0; 76 | } 77 | 78 | protected function toJson() 79 | { 80 | foreach ($this->messages as $language_key => $translations) { 81 | foreach ($translations as $translation_key => $translate) { 82 | $filepath = "$this->filepath/$language_key"; 83 | $filename = "$translation_key.json"; 84 | 85 | $adapter = new Local($filepath); 86 | $filesystem = new Filesystem($adapter); 87 | 88 | $contents = json_encode($translate, JSON_PRETTY_PRINT); 89 | 90 | if ($filesystem->has($filename)) { 91 | $filesystem->delete($filename); 92 | $filesystem->write($filename, $contents); 93 | } else { 94 | $filesystem->write($filename, $contents); 95 | } 96 | } 97 | } 98 | 99 | $this->info('Messages exported to JSON files, you can find them at '.$this->filepath); 100 | 101 | return 0; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Console/Commands/ExportMessagesToFlat.php: -------------------------------------------------------------------------------- 1 | toFlat(); 44 | 45 | $filepath = config('laravel-localization.js.filepath', resource_path('assets/js')); 46 | $filename = config('laravel-localization.js.filename', 'll_messages.js'); 47 | 48 | $adapter = new Local($filepath); 49 | $filesystem = new Filesystem($adapter); 50 | 51 | $contents = 'export default '.json_encode($messages); 52 | 53 | if ($filesystem->has($filename)) { 54 | $filesystem->delete($filename); 55 | $filesystem->write($filename, $contents); 56 | } else { 57 | $filesystem->write($filename, $contents); 58 | } 59 | 60 | $this->info('Messages exported to JavaScript file, you can find them at '.$filepath.DIRECTORY_SEPARATOR 61 | .$filename); 62 | 63 | return 0; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Events/LaravelLocalizationExported.php: -------------------------------------------------------------------------------- 1 | messages = $messages; 25 | } 26 | 27 | /** 28 | * Get the channels the event should broadcast on. 29 | * 30 | * @return \Illuminate\Broadcasting\Channel|array 31 | */ 32 | public function broadcastOn() 33 | { 34 | return new PrivateChannel(config('laravel-localization.events.channel', 'channel-name')); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Facades/ExportLocalizations.php: -------------------------------------------------------------------------------- 1 | app->bind(ExportLocalizations::class, function () { 21 | $phpRegex = config('laravel-localization.file_regexp.php', '/^.+\.php$/i'); 22 | $jsonRegex = config('laravel-localization.file_regexp.json', '/^.+\.json$/i'); 23 | 24 | return new ExportLocalizations($phpRegex, $jsonRegex); 25 | }); 26 | 27 | $this->app->alias(ExportLocalizations::class, 'export-localization'); 28 | 29 | /* 30 | * Config 31 | */ 32 | $this->mergeConfigFrom( 33 | __DIR__.'/config/laravel-localization.php', 'laravel-localization' 34 | ); 35 | 36 | $this->publishes([ 37 | __DIR__.'/config/laravel-localization.php' => config_path('laravel-localization.php'), 38 | ], 'config'); 39 | 40 | /* 41 | * Routes 42 | */ 43 | $this->loadRoutesFrom(__DIR__.'/routes.php'); 44 | 45 | if ($this->app->runningInConsole()) { 46 | $this->commands([ 47 | ExportMessages::class, 48 | ExportMessagesToFlat::class, 49 | ]); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/config/laravel-localization.php: -------------------------------------------------------------------------------- 1 | [ 12 | 13 | /** 14 | * Route prefix, example of route http://localhost/js/localizations.js. 15 | */ 16 | 'prefix' => env('LARAVEL_LOCALIZATION_PREFIX', '/js/localization.js'), 17 | 18 | /** 19 | * Route name, defaults to assets.lang. 20 | */ 21 | 'name' => env('LARAVEL_LOCALIZATION_ROUTE_NAME', 'assets.lang'), 22 | 23 | /** 24 | * Middleware used on localization routes. 25 | * 26 | * You can add more middleware with .env directive, example LARAVEL_LOCALIZATION_MIDDLEWARE=web,auth:api, etc. 27 | * 28 | * Don't use space in .env directive after , 29 | */ 30 | 'middleware' => (env('LARAVEL_LOCALIZATION_MIDDLEWARE')) ? 31 | explode(',', env('LARAVEL_LOCALIZATION_MIDDLEWARE')) 32 | : [], 33 | 34 | /** 35 | * Should we enable public URL from which we can access translations. 36 | */ 37 | 'enable' => env('LARAVEL_LOCALIZATION_ROUTE_ENABLE', false), 38 | ], 39 | 'events' => [ 40 | 41 | /** 42 | * This package emits some events after it getters all translation messages. 43 | * 44 | * Here you can change channel on which events will broadcast 45 | */ 46 | 'channel' => env('LARAVEL_LOCALIZATION_EVENTS_CHANNEL', ''), 47 | ], 48 | 'caches' => [ 49 | 50 | /** 51 | * What cache driver do you want to use - more information: https://laravel.com/docs/5.6/cache#driver-prerequisites. 52 | */ 53 | 'driver' => 'file', 54 | 55 | /** 56 | * Key name of the cache entry for the localization array. 57 | */ 58 | 'key' => 'localization.array', 59 | 60 | /** 61 | * Timeout of the cached data in minutes - set to 0 to disable. 62 | */ 63 | 'timeout' => 60, 64 | ], 65 | 'js' => [ 66 | /** 67 | * Default locale for export. 68 | */ 69 | 'default_locale' => 'en', 70 | 71 | /** 72 | * root location to where JavaScript file will be exported. 73 | */ 74 | 'filepath' => resource_path('assets/js'), 75 | 76 | /** 77 | * File name for JavaScript file with exported messages. 78 | */ 79 | 'filename' => 'll_messages.js', 80 | ], 81 | 'paths' => [ 82 | 83 | /** 84 | * You can export more lang files then just files in resources/lang, for example. 85 | * 86 | * In you .env file just add: 87 | * LARAVEL_LOCALIZATION_LANG_DIRS=resources/lang,Modules/Blog/Resources/lang 88 | */ 89 | 'lang_dirs' => [lang_path()], 90 | ], 91 | /** 92 | * You can customize the regexp for lang files to be able to exclude certain files. 93 | */ 94 | 'file_regexp' => [ 95 | 'php' => '/^.+\.php$/i', 96 | 'json' => '/^.+\.json$/i', 97 | ], 98 | /** 99 | * This function will be called every time after export, it should be globally accessible function (eg. Laravel helper function) 100 | * and it should accept (string) argument. 101 | */ 102 | 'export_callback' => null, 103 | 104 | ]; 105 | -------------------------------------------------------------------------------- /src/routes.php: -------------------------------------------------------------------------------- 1 | name(config('laravel-localization.routes.name')) 14 | ->middleware(config('laravel-localization.routes.middleware')); 15 | } 16 | --------------------------------------------------------------------------------