├── .github └── workflows │ ├── deploy.yml │ ├── main.yml │ └── php-cs-fixer.yml ├── .php_cs.dist.php ├── LICENSE.md ├── README.md ├── _ide_helper.php ├── composer.json ├── composer.lock ├── config └── lang-country.php ├── database └── migrations │ └── 2018_04_29_074240_add_lang_country_column_to_users_table.php ├── phpunit.xml └── src ├── .stubs.php ├── Controllers └── LangCountrySwitchController.php ├── LangCountry.php ├── LangCountryData ├── _template.json ├── bg-BG.json ├── bn-BD.json ├── ca-ES.json ├── da-DA.json ├── de-AT.json ├── de-CH.json ├── de-DE.json ├── el-GR.json ├── en-AU.json ├── en-CA.json ├── en-CH.json ├── en-GB.json ├── en-US.json ├── es-CL.json ├── es-CO.json ├── es-ES.json ├── fr-BE.json ├── fr-CA.json ├── fr-CH.json ├── fr-FR.json ├── hu-HU.json ├── id-ID.json ├── it-CH.json ├── it-IT.json ├── lt-LT.json ├── nl-BE.json ├── nl-NL.json ├── pl-PL.json ├── ps-AF.json ├── pt-PT.json └── ru-RU.json ├── LangCountryFacade.php ├── LaravelLangCountryServiceProvider.php ├── Listeners └── UserAuthenticated.php ├── Middleware └── LangCountrySession.php └── Services └── PreferredLanguage.php /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a VitePress site to GitHub Pages 2 | # 3 | name: Deploy VitePress site to Pages 4 | 5 | on: 6 | # Runs on pushes targeting the `main` branch. Change this to `master` if you're 7 | # using the `master` branch as the default branch. 8 | push: 9 | branches: [ master ] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 15 | permissions: 16 | contents: read 17 | pages: write 18 | id-token: write 19 | 20 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 21 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 22 | concurrency: 23 | group: pages 24 | cancel-in-progress: false 25 | 26 | jobs: 27 | # Build job 28 | build: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 # Not needed if lastUpdated is not enabled 35 | # - uses: pnpm/action-setup@v3 # Uncomment this block if you're using pnpm 36 | # with: 37 | # version: 9 # Not needed if you've set "packageManager" in package.json 38 | # - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun 39 | - name: Setup Node 40 | uses: actions/setup-node@v4 41 | with: 42 | node-version: 20 43 | cache: npm # or pnpm / yarn 44 | - name: Setup Pages 45 | uses: actions/configure-pages@v4 46 | - name: Install dependencies 47 | run: npm ci # or pnpm install / yarn install / bun install 48 | - name: Build with VitePress 49 | run: npm run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build 50 | - name: Upload artifact 51 | uses: actions/upload-pages-artifact@v3 52 | with: 53 | path: docs/.vitepress/dist 54 | 55 | # Deployment job 56 | deploy: 57 | environment: 58 | name: github-pages 59 | url: ${{ steps.deployment.outputs.page_url }} 60 | needs: build 61 | runs-on: ubuntu-latest 62 | name: Deploy 63 | steps: 64 | - name: Deploy to GitHub Pages 65 | id: deployment 66 | uses: actions/deploy-pages@v4 -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | push: 5 | branches: [ master,v3 ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | php: [ 8.3, 8.2, 8.1 ] 16 | laravel: [ 12.*,11.*, 10.*, 9.* ] 17 | dependency-version: [ prefer-lowest, prefer-stable ] 18 | include: 19 | - laravel: 12.* 20 | testbench: ^10.0 21 | - laravel: 11.* 22 | testbench: ^9.0 23 | - laravel: 10.* 24 | testbench: ^8.0 25 | - laravel: 9.* 26 | testbench: ^7.0 27 | exclude: 28 | - laravel: 12.* 29 | php: 8.1 30 | - laravel: 11.* 31 | php: 8.1 32 | - laravel: 10.* 33 | dependency-version: prefer-lowest 34 | 35 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} 36 | 37 | steps: 38 | - name: Checkout code 39 | uses: actions/checkout@v3 40 | 41 | - name: Setup PHP 42 | uses: shivammathur/setup-php@v2 43 | with: 44 | php-version: ${{ matrix.php }} 45 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 46 | coverage: pcov 47 | 48 | - name: Setup problem matchers 49 | run: | 50 | echo "::add-matcher::${{ runner.tool_cache }}/php.json" 51 | echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 52 | 53 | - name: Install dependencies Laravel 9 54 | if: ${{ contains(matrix.laravel, '9.*') }} 55 | run: | 56 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "nesbot/carbon:>=2.62.1" --no-interaction --no-update 57 | composer remove --dev "pestphp/pest-plugin-type-coverage" --no-interaction --no-update 58 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction 59 | 60 | - name: Install dependencies Laravel 10 to 12 61 | if: ${{ contains(matrix.laravel, '10.*') || contains(matrix.laravel, '11.*') || contains(matrix.laravel, '12.*') }} 62 | run: | 63 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "nesbot/carbon:>=2.62.1" --no-interaction --no-update 64 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction 65 | 66 | - name: Execute tests 67 | run: composer test:coverage 68 | 69 | - name: Type check 70 | if: ${{ contains(matrix.laravel, '12.*') && contains(matrix.dependency-version, 'prefer-stable') }} 71 | run: composer type:check 72 | -------------------------------------------------------------------------------- /.github/workflows/php-cs-fixer.yml: -------------------------------------------------------------------------------- 1 | name: Check & fix styling 2 | 3 | on: [push] 4 | 5 | jobs: 6 | php-cs-fixer: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v2 12 | with: 13 | ref: ${{ github.head_ref }} 14 | 15 | - name: Run PHP CS Fixer 16 | uses: docker://oskarstark/php-cs-fixer-ga 17 | with: 18 | args: --config=.php_cs.dist.php --allow-risky=yes 19 | 20 | - name: Commit changes 21 | uses: stefanzweifel/git-auto-commit-action@v4 22 | with: 23 | commit_message: Fix styling 24 | -------------------------------------------------------------------------------- /.php_cs.dist.php: -------------------------------------------------------------------------------- 1 | in([ 5 | __DIR__ . '/src', 6 | __DIR__ . '/tests', 7 | ]) 8 | ->name('*.php') 9 | ->notName('*.blade.php') 10 | ->ignoreDotFiles(true) 11 | ->ignoreVCS(true); 12 | 13 | return (new PhpCsFixer\Config()) 14 | ->setRules([ 15 | '@PSR12' => true, 16 | 'array_syntax' => ['syntax' => 'short'], 17 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 18 | 'no_unused_imports' => true, 19 | 'not_operator_with_successor_space' => true, 20 | 'trailing_comma_in_multiline' => true, 21 | 'phpdoc_scalar' => true, 22 | 'unary_operator_spaces' => true, 23 | 'binary_operator_spaces' => true, 24 | 'blank_line_before_statement' => [ 25 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 26 | ], 27 | 'phpdoc_single_line_var_spacing' => true, 28 | 'phpdoc_var_without_name' => true, 29 | 'class_attributes_separation' => [ 30 | 'elements' => [ 31 | 'method' => 'one', 32 | ], 33 | ], 34 | 'method_argument_space' => [ 35 | 'on_multiline' => 'ensure_fully_multiline', 36 | 'keep_multiple_spaces_after_comma' => true, 37 | ], 38 | 'single_trait_insert_per_statement' => true, 39 | ]) 40 | ->setFinder($finder); 41 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Stef Rouschop 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 13 | > all 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 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LaravelLangCountry 2 | 3 | [![Latest Version on Packagist][ico-version]][link-packagist] 4 | [![Software License][ico-license]](LICENSE.md) 5 | 6 | ![laravel-langcountry.png](/docs/public/laravel-langcountry.png) 7 | 8 | # TLDR; 9 | 10 | Laravel has great localisation functionality, but it is all based on the locale. The locale only refers to a language, 11 | not a country. This package adds the ability to localize based on the country. 12 | **Why?** Mainly because dates dates can be pronounced the same in a language, but be formatted differently in different 13 | countries. 14 | The package also adds more convenience functions to get localized country names, currency symbols and more. 15 | 16 | # Have you ever... 17 | 18 | * had the problem that you needed a date format had to be localized? 19 | * needed to show a date in a specific language? 20 | * that you needed to show a country name in the local language? 21 | * that you needed to show a currency symbol in the local currency? 22 | * ... 23 | 24 | **The Laravel LangCountry package is here to help you with just that!** 25 | 26 | # Documentation 27 | 28 | You can find the [official documentation here](https://stefro.github.io/laravel-lang-country/). 29 | 30 | ## Change log 31 | 32 | Please see [CHANGELOG](https://stefro.github.io/laravel-lang-country/getting-started/changelog.html) for more 33 | information on what has changed recently. 34 | 35 | ## Upgrading 36 | 37 | Please see [UPGRADING](https://stefro.github.io/laravel-lang-country/getting-started/upgrade.html) for detailed upgrade 38 | instructions. 39 | 40 | ## Contributing 41 | 42 | Great! Please 43 | see [the contributing guide in the documentation](https://stefro.github.io/laravel-lang-country/contribute/contribution.html) 44 | for details. 45 | 46 | ## ToDo 47 | 48 | * Caching to reduce file lookups. 49 | 50 | ## Security 51 | 52 | If you discover any security related issues, please email github@rouschop.com instead of using the issue tracker. 53 | 54 | ## Credits 55 | 56 | - [Stef Rouschop](https://github.com/stefro) 57 | - [Jayenne Montana](https://github.com/jayenne): For adding local country names and currency. 58 | - Ronald Huijgen: Background vocals, choreography and funny business. 59 | - [All Contributors][link-contributors] 60 | 61 | ## License 62 | 63 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 64 | 65 | [ico-version]: https://img.shields.io/packagist/v/stefro/laravel-lang-country.svg?style=flat-square 66 | 67 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 68 | 69 | [ico-downloads]: https://img.shields.io/packagist/dt/stefro/laravel-lang-country.svg?style=flat-square 70 | 71 | [link-packagist]: https://packagist.org/packages/stefro/laravel-lang-country 72 | 73 | [link-author]: https://github.com/stefro 74 | 75 | [link-contributors]: ../../contributors 76 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stefro/laravel-lang-country", 3 | "description": "Setting the locale is not enough most of the time, some countries use more than one languages. Also, different countries use different date notation formats, names, flags and currencies. This package is here to help you with that!", 4 | "keywords": [ 5 | "LaravelLangCountry" 6 | ], 7 | "homepage": "https://github.com/stefro/laravel-lang-country", 8 | "license": "MIT", 9 | "type": "library", 10 | "authors": [ 11 | { 12 | "name": "Stef Rouschop", 13 | "email": "github@rouschop.com", 14 | "role": "Developer" 15 | } 16 | ], 17 | "require": { 18 | "php": "^8.1|^8.2", 19 | "illuminate/support": "^9.0|^10.0|^11.0|^12.0" 20 | }, 21 | "require-dev": { 22 | "barryvdh/laravel-ide-helper": "^2.13|^3.0", 23 | "friendsofphp/php-cs-fixer": "^3.22", 24 | "orchestra/testbench": "^7.0|^8.0|^9.0", 25 | "pestphp/pest": "^1.21 | ^2.0 | ^3.0", 26 | "pestphp/pest-plugin-type-coverage": "^2.0|^3.0", 27 | "spatie/laravel-ray": "^1.32" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "Stefro\\LaravelLangCountry\\": "src" 32 | } 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "Stefro\\LaravelLangCountry\\Tests\\": "tests" 37 | } 38 | }, 39 | "scripts": { 40 | "post-autoload-dump": [ 41 | "@php vendor/bin/testbench package:discover --ansi" 42 | ], 43 | "test": "vendor/bin/pest", 44 | "test:fast": "vendor/bin/pest --parallel", 45 | "test:coverage": "vendor/bin/pest --coverage --min=100 --coverage-clover=coverage.xml", 46 | "test:coverage-herd": "herd coverage vendor/bin/pest --coverage --min=100 --coverage-clover=coverage.xml", 47 | "type:check": "vendor/bin/pest --type-coverage --min=100", 48 | "format": "vendor/bin/php-cs-fixer fix src --allow-risky=yes" 49 | }, 50 | "extra": { 51 | "laravel": { 52 | "providers": [ 53 | "Stefro\\LaravelLangCountry\\LaravelLangCountryServiceProvider" 54 | ], 55 | "aliases": { 56 | "LangCountry": "Stefro\\LaravelLangCountry\\LangCountryFacade" 57 | } 58 | } 59 | }, 60 | "minimum-stability": "dev", 61 | "prefer-stable": true, 62 | "config": { 63 | "sort-packages": true, 64 | "allow-plugins": { 65 | "pestphp/pest-plugin": true 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /config/lang-country.php: -------------------------------------------------------------------------------- 1 | 'en-GB', 5 | 6 | 'allowed' => [ 7 | 'bn-BD', 8 | 'bg-BG', 9 | 'ca-ES', 10 | 'da-DA', 11 | 'de-AT', 12 | 'de-CH', 13 | 'de-DE', 14 | 'en-AU', 15 | 'en-CA', 16 | 'en-CH', 17 | 'en-GB', 18 | 'en-US', 19 | 'el-GR', 20 | 'es-CL', 21 | 'es-CO', 22 | 'es-ES', 23 | 'fr-BE', 24 | 'fr-CA', 25 | 'fr-CH', 26 | 'fr-FR', 27 | 'hu-HU', 28 | 'id-ID', 29 | 'it-CH', 30 | 'it-IT', 31 | 'lt-LT', 32 | 'nl-BE', 33 | 'nl-NL', 34 | 'ps-AF', 35 | 'pt-PT', 36 | 'pl-PL', 37 | 'ru-RU', 38 | ], 39 | 40 | 'lang_switcher_middleware' => ['web'], 41 | 42 | 'lang_switcher_uri' => 'change_lang_country', 43 | 44 | 'fallback_based_on_current_locale' => false, 45 | ]; 46 | -------------------------------------------------------------------------------- /database/migrations/2018_04_29_074240_add_lang_country_column_to_users_table.php: -------------------------------------------------------------------------------- 1 | string('lang_country', 5)->after('email')->nullable(); 19 | }); 20 | } 21 | } 22 | 23 | /**a 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::table('users', function (Blueprint $table) { 31 | $table->dropColumn('lang_country'); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | tests 13 | 14 | 15 | 16 | 17 | 18 | ./src 19 | 20 | 21 | ./src/.stubs.php 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/.stubs.php: -------------------------------------------------------------------------------- 1 | back(); 14 | } 15 | 16 | \LangCountry::setAllSessions($lang_country); 17 | 18 | if (\Auth::user() && array_key_exists('lang_country', \Auth::user()->getAttributes())) { 19 | \Auth::user()->lang_country = $lang_country; 20 | \Auth::user()->save(); 21 | } 22 | 23 | return redirect()->back(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/LangCountry.php: -------------------------------------------------------------------------------- 1 | has('lang_country')) { 21 | $lang = new PreferredLanguage(app()->getLocale()); 22 | $this->lang_country = $lang->lang_country; 23 | } else { 24 | $this->lang_country = session('lang_country', config('lang-country.fallback')); 25 | } 26 | $this->data = $this->getDataFromFile($this->lang_country); 27 | } 28 | 29 | /** 30 | * @throws Exception 31 | */ 32 | public function overrideSession(string $lang_country): void 33 | { 34 | $lang = new PreferredLanguage($lang_country); 35 | 36 | $this->lang_country = $lang->lang_country; 37 | $this->data = $this->getDataFromFile($this->lang_country); 38 | } 39 | 40 | /** 41 | * Will return the current LangCountry value 42 | **/ 43 | public function currentLangCountry(): string 44 | { 45 | return $this->lang_country; 46 | } 47 | 48 | /** 49 | * It will return the right language. This can be a two char representation (ex. "nl", Dutch) 50 | * or a four char representation (ex. es_CO; Spanish-colombian). 51 | */ 52 | public function lang(string|bool $override = false): string 53 | { 54 | if ($override) { 55 | $this->overrideSession($override); 56 | } 57 | 58 | return $this->data['lang']; 59 | } 60 | 61 | public function withTime(): self 62 | { 63 | $this->withTime = true; 64 | 65 | return $this; 66 | } 67 | 68 | protected function createFormatString(string $format): string 69 | { 70 | if ($this->withTime) { 71 | $format .= ' ' . $this->timeFormat(); 72 | } 73 | 74 | $this->withTime = false; // Reset the time flag, so it doesn't affect the next call. 75 | 76 | return $format; 77 | } 78 | 79 | /** 80 | * It will return the two character code representation of the country. 81 | */ 82 | public function country(): string 83 | { 84 | return $this->data['country']; 85 | } 86 | 87 | /** 88 | * It will return the name of the country represented by the country code. 89 | */ 90 | public function countryName(): string 91 | { 92 | return $this->data['country_name']; 93 | } 94 | 95 | /** 96 | * It will return the two character code representation of the country. 97 | */ 98 | public function countryNameLocal(): string 99 | { 100 | return $this->data['country_name_local']; 101 | } 102 | 103 | /** 104 | * It will return the name of the language TRANSLATED IN THE LANGUAGE IN QUESTION. 105 | * You can use this for nice country-selectors in your app. 106 | */ 107 | public function name(): string 108 | { 109 | return $this->data['name']; 110 | } 111 | 112 | /** 113 | * String representation of the dateformat with only numbers. 114 | * Ex: "Y-m-d". 115 | */ 116 | public function dateNumbersFormat(): string 117 | { 118 | return $this->createFormatString($this->data['date_numbers']); 119 | } 120 | 121 | /** 122 | * String representation of the date with only numbers from the Carbon instance provided. 123 | * Ex: "2018-04-24". 124 | */ 125 | public function dateNumbers(Carbon $carbon, string|bool $override = false): string 126 | { 127 | if ($override) { 128 | $this->overrideSession($override); 129 | } 130 | 131 | return $carbon->locale($this->data['lang'])->translatedFormat($this->dateNumbersFormat()); 132 | } 133 | 134 | /** 135 | * String representation of the dateformat with only capitals, some javascript date selectors use this. 136 | * Ex: "DD-MM-YYYY". 137 | */ 138 | public function dateNumbersFullCapitalsFormat(): string 139 | { 140 | return $this->createFormatString($this->data['date_numbers_full_capitals']); 141 | } 142 | 143 | /** 144 | * String representation of the dateformat with words but without the day. 145 | * Ex: "F jS Y". 146 | */ 147 | public function dateWordsWithoutDayFormat(): string 148 | { 149 | return $this->createFormatString($this->data['date_words_without_day']); 150 | } 151 | 152 | /** 153 | * String representation of the date in words but without the day. 154 | * Ex: "April 24th 2018". 155 | */ 156 | public function dateWordsWithoutDay(Carbon $carbon, string|bool $override = false): string 157 | { 158 | if ($override) { 159 | $this->overrideSession($override); 160 | } 161 | 162 | return $carbon->locale($this->data['lang'])->translatedFormat($this->dateWordsWithoutDayFormat()); 163 | } 164 | 165 | /** 166 | * String representation of the dateformat with words but without the day. 167 | * Ex: "l F jS Y". 168 | */ 169 | public function dateWordsWithDayFormat(): string 170 | { 171 | return $this->createFormatString($this->data['date_words_with_day']); 172 | } 173 | 174 | /** 175 | * String representation of the date with words but without the day. 176 | * Ex: "Tuesday April 24th 2018". 177 | */ 178 | public function dateWordsWithDay(Carbon $carbon, string|bool $override = false): string 179 | { 180 | if ($override) { 181 | $this->overrideSession($override); 182 | } 183 | 184 | return $carbon->locale($this->data['lang'])->translatedFormat($this->dateWordsWithDayFormat()); 185 | } 186 | 187 | /** 188 | * String representation of the dateformat for a birthday. 189 | * Ex: "F jS". 190 | */ 191 | public function dateBirthdayFormat(): string 192 | { 193 | return $this->data['date_birthday']; 194 | } 195 | 196 | /** 197 | * String representation of a birthday date. 198 | * Ex: "April 24th". 199 | */ 200 | public function dateBirthday(Carbon $carbon, string|bool $override = false): string 201 | { 202 | if ($override) { 203 | $this->overrideSession($override); 204 | } 205 | 206 | return $carbon->locale($this->data['lang'])->translatedFormat($this->dateBirthdayFormat()); 207 | } 208 | 209 | /** 210 | * String representation of the time-format. 211 | * Ex: "h:i a". 212 | */ 213 | public function timeFormat(): string 214 | { 215 | return $this->data['time_format']; 216 | } 217 | 218 | /** 219 | * String representation of time. 220 | * Ex: "12:00 pm". 221 | */ 222 | public function time(Carbon $carbon, string|bool $override = false): string 223 | { 224 | if ($override) { 225 | $this->overrideSession($override); 226 | } 227 | 228 | return $carbon->locale($this->data['lang'])->translatedFormat($this->timeFormat()); 229 | } 230 | 231 | /** 232 | * Emoji representation of language country flag. 233 | * Ex: "🇱🇹". 234 | */ 235 | public function emojiFlag(): string 236 | { 237 | return $this->data['emoji_flag']; 238 | } 239 | 240 | public function allLanguages(): Collection 241 | { 242 | return collect(config('lang-country.allowed')) 243 | ->map(function (string $item) { 244 | $file = $this->getDataFromFile($item); 245 | 246 | return [ 247 | 'country' => $file['country'], 248 | 'country_name' => $file['country_name'], 249 | 'country_name_local' => $file['country_name_local'], 250 | 'lang' => $file['lang'], 251 | 'name' => $file['name'], 252 | 'lang_country' => $item, 253 | 'emoji_flag' => $file['emoji_flag'], 254 | 'currency_code' => $file['currency_code'], 255 | 'currency_symbol' => $file['currency_symbol'], 256 | 'currency_symbol_local' => $file['currency_symbol_local'], 257 | 'currency_name' => $file['currency_name'], 258 | 'currency_name_local' => $file['currency_name_local'], 259 | ]; 260 | }); 261 | } 262 | 263 | /** 264 | * It will return the iso code of the country's currency. 265 | */ 266 | public function currencyCode(): string 267 | { 268 | return $this->data['currency_code']; 269 | } 270 | 271 | /** 272 | * It will return the iso symbol of the country's currency. 273 | */ 274 | public function currencySymbol(): string 275 | { 276 | return $this->data['currency_symbol']; 277 | } 278 | 279 | /** 280 | * It will return the iso symbol of the country's currency, prefixed with localization. 281 | */ 282 | public function currencySymbolLocal(): string 283 | { 284 | return $this->data['currency_symbol_local']; 285 | } 286 | 287 | /** 288 | * It will return the name of the country's currency. 289 | */ 290 | public function currencyName(): string 291 | { 292 | return $this->data['currency_name']; 293 | } 294 | 295 | /** 296 | * It will return the name of the country's currency as spoken locally by language code. 297 | */ 298 | public function currencyNameLocal(): string 299 | { 300 | return $this->data['currency_name_local']; 301 | } 302 | 303 | /** 304 | * It will return a collection with the current language, country and name 305 | * and also the other available language, country and name. 306 | */ 307 | public function langSelectorHelper(): array 308 | { 309 | return $this->allLanguages()->reduce(function (?array $carry, array $item) { 310 | if ($item['lang_country'] != session('lang_country')) { 311 | $carry['available'][] = $item; 312 | } else { 313 | $carry['current'] = $item; 314 | } 315 | 316 | return $carry; 317 | }); 318 | } 319 | 320 | public function setAllSessions(?string $preferred_lang): void 321 | { 322 | $lang = new PreferredLanguage($preferred_lang); 323 | session(['lang_country' => $lang->lang_country]); 324 | session(['locale' => $lang->locale]); 325 | } 326 | 327 | /** 328 | * @throws Exception 329 | */ 330 | private function getDataFromFile(?string $lang_country): array 331 | { 332 | if ($lang_country === null) { 333 | throw new Exception('The lang_country session is not set'); 334 | } 335 | 336 | $filename = $lang_country . '.json'; 337 | if (file_exists(lang_path('lang-country-overrides/' . $filename))) { 338 | $resource = lang_path('lang-country-overrides/' . $filename); 339 | } else { 340 | $resource = __DIR__ . '/LangCountryData/' . $filename; 341 | } 342 | 343 | return json_decode(file_get_contents($resource), true); 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /src/LangCountryData/_template.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "", 3 | "country_name": "", 4 | "country_name_local": "", 5 | "lang": "", 6 | "name": "", 7 | "date_numbers": "", 8 | "date_numbers_full_capitals": "", 9 | "date_words_without_day": "", 10 | "date_words_with_day": "", 11 | "date_birthday": "", 12 | "time_format": "", 13 | "emoji_flag": "", 14 | "currency_code": "", 15 | "currency_symbol": "", 16 | "currency_symbol_local": "", 17 | "currency_name": "", 18 | "currency_name_local": "" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/bg-BG.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "BG", 3 | "country_name": "Bulgaria", 4 | "country_name_local": "България", 5 | "lang": "bg", 6 | "name": "Bulgarian", 7 | "date_numbers": "d.m.Y", 8 | "date_numbers_full_capitals": "DD.MM.YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l, j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇧🇬", 14 | "currency_code": "BGN", 15 | "currency_symbol": "лв", 16 | "currency_symbol_local": "лв", 17 | "currency_name": "Lev", 18 | "currency_name_local": "Български лев" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/bn-BD.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "BD", 3 | "country_name": "Bangladesh", 4 | "country_name_local": "বাংলাদেশ", 5 | "lang": "bn", 6 | "name": "Bengali", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇧🇩", 14 | "currency_code": "BDT", 15 | "currency_symbol": "৳", 16 | "currency_symbol_local": "৳", 17 | "currency_name": "Taka", 18 | "currency_name_local": "টাকা" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/ca-ES.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "ES", 3 | "country_name": "Spain", 4 | "country_name_local": "España", 5 | "lang": "ca", 6 | "name": "Català", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇪🇸", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "€", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euro" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/da-DA.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "DK", 3 | "country_name": "Denmark", 4 | "country_name_local": "Danmark", 5 | "lang": "da", 6 | "name": "Danish", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j. F Y", 10 | "date_words_with_day": "l d. F Y", 11 | "date_birthday": "j. F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇩🇰", 14 | "currency_code": "DKK", 15 | "currency_symbol": "kr", 16 | "currency_symbol_local": "kr.", 17 | "currency_name": "Krone", 18 | "currency_name_local": "Dansk Krone" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/de-AT.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "AT", 3 | "country_name": "Austria", 4 | "country_name_local": "Österreich", 5 | "lang": "de", 6 | "name": "Österreich", 7 | "date_numbers": "d.m.Y", 8 | "date_numbers_full_capitals": "DD.MM.YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇦🇹", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "Euro", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euro" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/de-CH.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "CH", 3 | "country_name": "Switzerland", 4 | "country_name_local": "Schweiz", 5 | "lang": "de", 6 | "name": "Schweiz - Deutsch", 7 | "date_numbers": "d.m.Y", 8 | "date_numbers_full_capitals": "DD.MM.YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇩🇪", 14 | "currency_code": "CHF", 15 | "currency_symbol": "₣", 16 | "currency_symbol_local": "S₣r", 17 | "currency_name": "Swiss Franc", 18 | "currency_name_local": "Schweizer Franken" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/de-DE.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "DE", 3 | "country_name": "Germany", 4 | "country_name_local": "Deutschland", 5 | "lang": "de", 6 | "name": "Deutsch", 7 | "date_numbers": "d.m.Y", 8 | "date_numbers_full_capitals": "DD.MM.YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇩🇪", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "€", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euro" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/el-GR.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "GR", 3 | "country_name": "Greece", 4 | "country_name_local": "Ελλάδα", 5 | "lang": "el", 6 | "name": "Greek", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l, j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇬🇷", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "€", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Ευρώ" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/en-AU.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "AU", 3 | "country_name": "Australia", 4 | "country_name_local": "Australia", 5 | "lang": "en", 6 | "name": "English", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "h:i a", 13 | "emoji_flag": "🇦🇺", 14 | "currency_code": "AUD", 15 | "currency_symbol": "$", 16 | "currency_symbol_local": "$", 17 | "currency_name": "Dollar", 18 | "currency_name_local": "Australian Dollar" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/en-CA.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "CA", 3 | "country_name": "Canada", 4 | "country_name_local": "Canada", 5 | "lang": "en", 6 | "name": "Canadian English", 7 | "date_numbers": "m/d/Y", 8 | "date_numbers_full_capitals": "MM/DD/YYYY", 9 | "date_words_without_day": "F jS Y", 10 | "date_words_with_day": "l F jS Y", 11 | "date_birthday": "F jS", 12 | "time_format": "h:i a", 13 | "emoji_flag": "🇨🇦", 14 | "currency_code": "CAD", 15 | "currency_symbol": "$", 16 | "currency_symbol_local": "CA$", 17 | "currency_name": "Canadian Dollar", 18 | "currency_name_local": "Canadian Dollar" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/en-CH.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "CH", 3 | "country_name": "Switzerland", 4 | "country_name_local": "Switzerland", 5 | "lang": "en", 6 | "name": "Switzerland - English", 7 | "date_numbers": "d.m.Y", 8 | "date_numbers_full_capitals": "DD.MM.YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇨🇭", 14 | "currency_code": "CHF", 15 | "currency_symbol": "₣", 16 | "currency_symbol_local": "S₣r", 17 | "currency_name": "Swiss Franc", 18 | "currency_name_local": "Swiss Franc" 19 | } -------------------------------------------------------------------------------- /src/LangCountryData/en-GB.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "GB", 3 | "country_name": "United Kingdom", 4 | "country_name_local": "United Kingdom", 5 | "lang": "en", 6 | "name": "English", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "h:i a", 13 | "emoji_flag": "🇬🇧", 14 | "currency_code": "GBP", 15 | "currency_symbol": "£", 16 | "currency_symbol_local": "£", 17 | "currency_name": "Pound Stirling", 18 | "currency_name_local": "Pound" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "US", 3 | "country_name": "United States of America", 4 | "country_name_local": "America", 5 | "lang": "en", 6 | "name": "American English", 7 | "date_numbers": "m/d/Y", 8 | "date_numbers_full_capitals": "MM/DD/YYYY", 9 | "date_words_without_day": "F jS Y", 10 | "date_words_with_day": "l F jS Y", 11 | "date_birthday": "F jS", 12 | "time_format": "h:i a", 13 | "emoji_flag": "🇺🇸", 14 | "currency_code": "USD", 15 | "currency_symbol": "$", 16 | "currency_symbol_local": "US$", 17 | "currency_name": "Dollar", 18 | "currency_name_local": "US Dollar" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/es-CL.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "CL", 3 | "country_name": "Chile", 4 | "country_name_local": "Chile", 5 | "lang": "es", 6 | "name": "Chile", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇨🇱", 14 | "currency_code": "CLP", 15 | "currency_symbol": "$", 16 | "currency_symbol_local": "$", 17 | "currency_name": "Peso Chileno", 18 | "currency_name_local": "Peso Chileno" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/es-CO.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "CO", 3 | "country_name": "Colombia", 4 | "country_name_local": "Colombia", 5 | "lang": "es_CO", 6 | "name": "Colombia", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇨🇴", 14 | "currency_code": "COP", 15 | "currency_symbol": "$", 16 | "currency_symbol_local": "COP$", 17 | "currency_name": "Peso", 18 | "currency_name_local": "Peso" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/es-ES.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "ES", 3 | "country_name": "Spain", 4 | "country_name_local": "España", 5 | "lang": "es", 6 | "name": "Español", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇪🇸", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "€", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euro" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/fr-BE.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "BE", 3 | "country_name": "Belgium", 4 | "country_name_local": "Belgique", 5 | "lang": "fr", 6 | "name": "Belgique - Français", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇧🇪", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "€", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euro" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/fr-CA.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "CA", 3 | "country_name": "Canada", 4 | "country_name_local": "Canada", 5 | "lang": "fr", 6 | "name": "Canada - Français", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "h:i a", 13 | "emoji_flag": "🇨🇦", 14 | "currency_code": "CAD", 15 | "currency_symbol": "$", 16 | "currency_symbol_local": "CA$", 17 | "currency_name": "Canadian Dollar", 18 | "currency_name_local": "Canadian Dollar" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/fr-CH.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "CH", 3 | "country_name": "Switzerland", 4 | "country_name_local": "Suisse", 5 | "lang": "fr", 6 | "name": "Suisse - Français", 7 | "date_numbers": "d.m.Y", 8 | "date_numbers_full_capitals": "DD.MM.YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇨🇭", 14 | "currency_code": "CHF", 15 | "currency_symbol": "₣", 16 | "currency_symbol_local": "S₣r", 17 | "currency_name": "Swiss Franc", 18 | "currency_name_local": "franc suisse" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/fr-FR.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "FR", 3 | "country_name": "France", 4 | "country_name_local": "France", 5 | "lang": "fr", 6 | "name": "Français", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇫🇷", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "Euro", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euro" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/hu-HU.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "HU", 3 | "country_name": "Hungary", 4 | "country_name_local": "Magyarország", 5 | "lang": "hu", 6 | "name": "Hungarian", 7 | "date_numbers": "Y.m.d.", 8 | "date_numbers_full_capitals": "YYYY.MM.DD.", 9 | "date_words_without_day": "Y. MMMM d.", 10 | "date_words_with_day": "l, Y. MMMM d.", 11 | "date_birthday": "MMMM d.", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇭🇺", 14 | "currency_code": "HUF", 15 | "currency_symbol": "Ft", 16 | "currency_symbol_local": "Ft", 17 | "currency_name": "Hungarian Forint", 18 | "currency_name_local": "Forint" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/id-ID.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "ID", 3 | "country_name": "Indonesia", 4 | "country_name_local": "Indonesia", 5 | "lang": "id", 6 | "name": "Indonesia", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇮🇩", 14 | "currency_code": "IDR", 15 | "currency_symbol": "Rp", 16 | "currency_symbol_local": "Rp", 17 | "currency_name": "Rupiah", 18 | "currency_name_local": "Rupiah" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/it-CH.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "CH", 3 | "country_name": "Switzerland", 4 | "country_name_local": "Svizzera", 5 | "lang": "it", 6 | "name": "Svizzera - Italia", 7 | "date_numbers": "d.m.Y", 8 | "date_numbers_full_capitals": "DD.MM.YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇨🇭", 14 | "currency_code": "CHF", 15 | "currency_symbol": "₣", 16 | "currency_symbol_local": "S₣r", 17 | "currency_name": "Swiss Franc", 18 | "currency_name_local": "Franco Svizzero" 19 | } -------------------------------------------------------------------------------- /src/LangCountryData/it-IT.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "IT", 3 | "country_name": "Italy", 4 | "country_name_local": "Italia", 5 | "lang": "it", 6 | "name": "Italia", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇮🇹", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "€", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euro" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/lt-LT.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "LT", 3 | "country_name": "Lithuania", 4 | "country_name_local": "Lietuva", 5 | "lang": "lt", 6 | "name": "Lithuanian", 7 | "date_numbers": "Y-m-d", 8 | "date_numbers_full_capitals": "YYYY-MM-DD", 9 | "date_words_without_day": "Y\\m. F d\\d.", 10 | "date_words_with_day": "Y\\m. F d\\d., l", 11 | "date_birthday": "F j", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇱🇹", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "€", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euras" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/nl-BE.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "BE", 3 | "country_name": "Belgium", 4 | "country_name_local": "België", 5 | "lang": "nl", 6 | "name": "België - Vlaams", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD/MM/YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇧🇪", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "€", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euro" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/nl-NL.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "NL", 3 | "country_name": "The Netherlands", 4 | "country_name_local": "Nederland", 5 | "lang": "nl", 6 | "name": "Nederlands", 7 | "date_numbers": "d-m-Y", 8 | "date_numbers_full_capitals": "DD-MM-YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇳🇱", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "€", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euro" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/pl-PL.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "PL", 3 | "country_name": "Poland", 4 | "country_name_local": "Polska", 5 | "lang": "pl", 6 | "name": "Polski", 7 | "date_numbers": "d.m.Y", 8 | "date_numbers_full_capitals": "DD.MM.YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇵🇱", 14 | "currency_code": "PLN", 15 | "currency_symbol": "zł", 16 | "currency_symbol_local": "zł", 17 | "currency_name": "Polish Złoty", 18 | "currency_name_local": "Złoty" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/ps-AF.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "AF", 3 | "country_name": "Afghanistan", 4 | "country_name_local": "افغانستان", 5 | "lang": "ps", 6 | "name": "Pashto", 7 | "date_numbers": "Y/m/d", 8 | "date_numbers_full_capitals": "YYYY/MM/DD", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇦🇫", 14 | "currency_code": "AFN", 15 | "currency_symbol": "؋", 16 | "currency_symbol_local": "؋", 17 | "currency_name": "Afghani", 18 | "currency_name_local": "افغانۍ" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/pt-PT.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "PT", 3 | "country_name": "Portugal", 4 | "country_name_local": "Portugal", 5 | "lang": "pt", 6 | "name": "Portuguese", 7 | "date_numbers": "d/m/Y", 8 | "date_numbers_full_capitals": "DD-MM-YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇵🇹", 14 | "currency_code": "EUR", 15 | "currency_symbol": "€", 16 | "currency_symbol_local": "€", 17 | "currency_name": "Euro", 18 | "currency_name_local": "Euro" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryData/ru-RU.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "RU", 3 | "country_name": "Russia", 4 | "country_name_local": "Россия", 5 | "lang": "ru", 6 | "name": "Русский", 7 | "date_numbers": "d.m.Y", 8 | "date_numbers_full_capitals": "DD.MM.YYYY", 9 | "date_words_without_day": "j F Y", 10 | "date_words_with_day": "l j F Y", 11 | "date_birthday": "j F", 12 | "time_format": "H:i", 13 | "emoji_flag": "🇷🇺", 14 | "currency_code": "RUB", 15 | "currency_symbol": "₽", 16 | "currency_symbol_local": "₽", 17 | "currency_name": "Ruble", 18 | "currency_name_local": "Рубль" 19 | } 20 | -------------------------------------------------------------------------------- /src/LangCountryFacade.php: -------------------------------------------------------------------------------- 1 | publishes([ 21 | __DIR__ . '/../config/lang-country.php' => config_path('lang-country.php'), 22 | ], 'config'); 23 | 24 | $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); 25 | 26 | $this->app['router']->aliasMiddleware('lang_country', LangCountrySession::class); 27 | 28 | $this->app['router'] 29 | ->middleware(config('lang-country.lang_switcher_middleware')) 30 | ->get('/' . config('lang-country.lang_switcher_uri', 'change_lang_country') . '/{lang_country}', 'Stefro\LaravelLangCountry\Controllers\LangCountrySwitchController@switch') 31 | ->name('lang_country.switch'); 32 | 33 | \Event::listen(Login::class, UserAuthenticated::class); 34 | } 35 | 36 | /** 37 | * Register any package services. 38 | * 39 | * @return void 40 | */ 41 | public function register(): void 42 | { 43 | Carbon::macro('langCountryDateNumbers', function (bool|string $override = false, bool $withTime = false) { 44 | if ($withTime) { 45 | return \LangCountry::withTime()->dateNumbers($this, $override); 46 | } 47 | 48 | return \LangCountry::dateNumbers($this, $override); 49 | }); 50 | 51 | Carbon::macro('langCountryDateWordsWithoutDay', function (bool|string $override = false, bool $withTime = false) { 52 | if ($withTime) { 53 | return \LangCountry::withTime()->dateWordsWithoutDay($this, $override); 54 | } 55 | 56 | return \LangCountry::dateWordsWithoutDay($this, $override); 57 | }); 58 | 59 | Carbon::macro('langCountryDateWordsWithDay', function (bool|string $override = false, bool $withTime = false) { 60 | if ($withTime) { 61 | return \LangCountry::withTime()->dateWordsWithDay($this, $override); 62 | } 63 | 64 | return \LangCountry::dateWordsWithDay($this, $override); 65 | }); 66 | 67 | Carbon::macro('langCountryDateBirthday', function (bool|string $override = false) { 68 | return \LangCountry::dateBirthday($this, $override); 69 | }); 70 | 71 | Carbon::macro('langCountryTime', function (bool|string $override = false) { 72 | return \LangCountry::time($this, $override); 73 | }); 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Listeners/UserAuthenticated.php: -------------------------------------------------------------------------------- 1 | user->lang_country) { 22 | \LangCountry::setAllSessions($event->user->lang_country); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Middleware/LangCountrySession.php: -------------------------------------------------------------------------------- 1 | has('lang_country') || ! session()->has('locale')) { 14 | 15 | if (\Auth::user() && \Auth::user()?->lang_country !== null) { 16 | $preferred_lang = \Auth::user()->lang_country; 17 | } else { 18 | $preferred_lang = $request->server('HTTP_ACCEPT_LANGUAGE'); 19 | } 20 | 21 | \LangCountry::setAllSessions($preferred_lang); 22 | 23 | if (\Auth::user() && array_key_exists('lang_country', \Auth::user()->getAttributes()) && \Auth::user()->lang_country === null) { 24 | \Auth::user()->lang_country = session('lang_country'); 25 | \Auth::user()->save(); 26 | } 27 | } 28 | 29 | App::setLocale(session('locale')); 30 | 31 | 32 | return $next($request); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Services/PreferredLanguage.php: -------------------------------------------------------------------------------- 1 | allowed = $allowed ?? collect(config('lang-country.allowed')); 22 | $this->fallback = $fallback ?? config('lang-country.fallback'); 23 | $this->client_preferred = $this->clientPreferredLanguages(); 24 | $this->lang_country = $this->getLangCountry(); 25 | $this->locale = $this->getLocale(); 26 | } 27 | 28 | /** 29 | * It will return a list of preferred languages of the browser in order of preference. 30 | */ 31 | public function clientPreferredLanguages(): Collection 32 | { 33 | // regex inspired from @GabrielAnderson on http://stackoverflow.com/questions/6038236/http-accept-language 34 | preg_match_all( 35 | '/([a-z]{1,8}(-[a-z]{1,8})*)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', 36 | $this->preferred_languages, 37 | $lang_parse 38 | ); 39 | 40 | $langs = $lang_parse[1]; 41 | 42 | // Make sure the country chars (when available) are uppercase. 43 | $langs = collect($langs)->map(function (string $lang) { 44 | if (5 == strlen($lang)) { 45 | $lang = explode('-', $lang); 46 | $lang = implode('-', [$lang[0], strtoupper($lang[1])]); 47 | } 48 | 49 | return $lang; 50 | })->toArray(); 51 | 52 | $ranks = $lang_parse[4]; 53 | 54 | // (create an associative array 'language' => 'preference') 55 | $lang2pref = []; 56 | for ($i = 0; $i < count($langs); $i++) { 57 | $lang2pref[$langs[$i]] = (float)(! empty($ranks[$i]) ? $ranks[$i] : 1); 58 | } 59 | 60 | // (comparison function for uksort) 61 | $cmpLangs = function (string $a, string $b) use ($lang2pref) { 62 | if ($lang2pref[$a] > $lang2pref[$b]) { 63 | return -1; 64 | } elseif ($lang2pref[$a] < $lang2pref[$b]) { 65 | return 1; 66 | } elseif (strlen($a) > strlen($b)) { 67 | return -1; 68 | } elseif (strlen($a) < strlen($b)) { 69 | return 1; 70 | } else { 71 | return 0; 72 | } 73 | }; 74 | 75 | // sort the languages by preferred language and by the most specific region 76 | uksort($lang2pref, $cmpLangs); 77 | 78 | return collect($lang2pref); 79 | } 80 | 81 | /** 82 | * It will find the first best match for lang_country according to the allowed list (from config file). 83 | */ 84 | protected function getLangCountry(): string 85 | { 86 | $preferred = $this->findExactMatchForFourCharsOrReturnNull(); 87 | 88 | if (null === $preferred) { 89 | $preferred = $this->findFirstMatchBasedOnOnlyTheLangChars(); 90 | } 91 | 92 | // Get fallback if no results 93 | if (null === $preferred) { 94 | $preferred = $this->fallback; 95 | } 96 | 97 | return $preferred; 98 | } 99 | 100 | public function findExactMatchForFourCharsOrReturnNull(): ?string 101 | { 102 | return $this->client_preferred->keys()->filter(function (string $value) { 103 | return 5 == strlen($value); 104 | })->first(function (string $value) { 105 | return $this->allowed->contains($value); 106 | }); 107 | } 108 | 109 | public function findFirstMatchBasedOnOnlyTheLangChars(): ?string 110 | { 111 | return $this->client_preferred->keys()->filter(function (string $value) { 112 | return 2 === strlen($value); 113 | })->map(function (string $item) { 114 | return $this->allowed->filter(function (string $value) use ($item) { 115 | return $item === explode('-', $value)[0]; 116 | })->first(); 117 | })->reject(function (?string $value) { 118 | return $value === null; 119 | })->first(); 120 | } 121 | 122 | /** 123 | * Check if 4 char language (ex. en-US.json) file exists in /resources/lang/ dir. 124 | * If not, just return the first two chars (represents the language). 125 | */ 126 | private function getLocale(): string 127 | { 128 | $path = lang_path() . $this->lang_country . '.json'; 129 | 130 | if (file_exists($path)) { 131 | return $this->lang_country; 132 | } else { 133 | return substr($this->lang_country, 0, 2); 134 | } 135 | } 136 | } 137 | --------------------------------------------------------------------------------