diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 32996d54b..7cb5ab908 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -43,6 +43,7 @@ class Kernel extends HttpKernel \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \App\Http\Middleware\HandleInertiaRequests::class, + \App\Http\Middleware\CheckLocale::class, ], 'api' => [ diff --git a/app/Http/Middleware/CheckLocale.php b/app/Http/Middleware/CheckLocale.php new file mode 100644 index 000000000..dddf35f53 --- /dev/null +++ b/app/Http/Middleware/CheckLocale.php @@ -0,0 +1,61 @@ +app = $app; + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ + public function handle(Request $request, Closure $next) + { + $locale = $request->query('lang'); + + if (empty($locale)) { + $locale = $this->getLocale($request); + } + + $this->app->setLocale($locale ?? $this->app['config']->get('app.locale')); + + return $next($request); + } + + /** + * Get the current or default locale. + * + * @param \Illuminate\Http\Request $request + * @return string|null + */ + public function getLocale(Request $request): ?string + { + return ($user = $request->user()) + ? $user->locale + : $this->app['language.detector']->detect(); + } +} diff --git a/app/Listeners/LocaleUpdatedListener.php b/app/Listeners/LocaleUpdatedListener.php new file mode 100644 index 000000000..c0f2f49fe --- /dev/null +++ b/app/Listeners/LocaleUpdatedListener.php @@ -0,0 +1,19 @@ +set('cashier.currency_locale', $event->locale); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index a9f10a631..30c0ac9ef 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,8 +2,10 @@ namespace App\Providers; +use App\Listeners\LocaleUpdatedListener; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; +use Illuminate\Foundation\Events\LocaleUpdated; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Event; @@ -18,6 +20,9 @@ class EventServiceProvider extends ServiceProvider Registered::class => [ SendEmailVerificationNotification::class, ], + LocaleUpdated::class => [ + LocaleUpdatedListener::class, + ], ]; /** diff --git a/composer.json b/composer.json index 12ad73189..ad8ca44cc 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "laravel/scout": "^9.4", "laravel/tinker": "^2.5", "meilisearch/meilisearch-php": "^0.23.2", - "tightenco/ziggy": "^1.0" + "tightenco/ziggy": "^1.0", + "vluzrmos/language-detector": "^2.3" }, "require-dev": { "barryvdh/laravel-ide-helper": "^2.12", diff --git a/composer.lock b/composer.lock index c364c8abb..023e01b6c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3b192e17153bb856a43fe13be354d31b", + "content-hash": "efc65da104294a065a51d76bff968d03", "packages": [ { "name": "asm89/stack-cors", @@ -6088,6 +6088,72 @@ ], "time": "2021-12-12T23:22:04+00:00" }, + { + "name": "vluzrmos/language-detector", + "version": "v2.3.3", + "source": { + "type": "git", + "url": "https://github.com/vluzrmos/laravel-language-detector.git", + "reference": "ed0b70b1a8078a8a550df20ff4e4bcc8fd370dc7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vluzrmos/laravel-language-detector/zipball/ed0b70b1a8078a8a550df20ff4e4bcc8fd370dc7", + "reference": "ed0b70b1a8078a8a550df20ff4e4bcc8fd370dc7", + "shasum": "" + }, + "require": { + "illuminate/config": "~6.0 || ~7.0 || ~8.0 || ~9.0", + "illuminate/cookie": "~6.0 || ~7.0 || ~8.0 || ~9.0", + "illuminate/support": "~6.0 || ~7.0 || ~8.0 || ~9.0", + "illuminate/translation": "~6.0 || ~7.0 || ~8.0 || ~9.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "orchestra/testbench": "^5.0 || ^6.0 || ^7.0", + "phpunit/phpunit": "^8.5 || ^9.0" + }, + "type": "package", + "extra": { + "laravel": { + "providers": [ + "Vluzrmos\\LanguageDetector\\Providers\\LanguageDetectorServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Support/helpers.php" + ], + "psr-4": { + "Vluzrmos\\LanguageDetector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Vagner do Carmo", + "email": "vluzrmos@gmail.com" + } + ], + "description": "Detect the language for your application using browser preferences, subdomains or route prefixes.", + "keywords": [ + "i18n", + "language", + "laravel", + "locale", + "lumen" + ], + "support": { + "issues": "https://github.com/vluzrmos/laravel-language-detector/issues", + "source": "https://github.com/vluzrmos/laravel-language-detector/tree/v2.3.3" + }, + "time": "2022-03-16T21:43:24+00:00" + }, { "name": "voku/portable-ascii", "version": "2.0.1", diff --git a/config/lang-detector.php b/config/lang-detector.php new file mode 100644 index 000000000..5858b8d67 --- /dev/null +++ b/config/lang-detector.php @@ -0,0 +1,48 @@ + env('LANG_DETECTOR_AUTODETECT', false), + + /* + * Default driver to use to detect the request language. + * + * Available: browser, subdomain, uri. + */ + 'driver' => env('LANG_DETECTOR_DRIVER', 'browser'), + + /* + * Used on subdomain and uri drivers. That indicates which segment should be used + * to verify the language. + */ + 'segment' => env('LANG_DETECTOR_SEGMENT', 0), + + /* + * Languages available on the application. + * + * You could use parse_langs_to_array to use the string syntax + * or just use the array of languages with its aliases. + */ + 'languages' => parse_langs_to_array( + env('LANG_DETECTOR_LANGUAGES', [ + 'en', + ]) + ), + + /* + * Indicates if should store detected locale on cookies + */ + 'cookie' => (bool) env('LANG_DETECTOR_COOKIE', true), + + /* + * Indicates if should encrypt cookie + */ + 'cookie_encrypt' => (bool) env('LANG_DETECTOR_COOKIE_ENCRYPT', false), + + /* + * Cookie name + */ + 'cookie_name' => env('LANG_DETECTOR_COOKIE', 'locale'), +]; diff --git a/resources/lang/en/account.php b/lang/en/account.php similarity index 100% rename from resources/lang/en/account.php rename to lang/en/account.php diff --git a/resources/lang/en/app.php b/lang/en/app.php similarity index 100% rename from resources/lang/en/app.php rename to lang/en/app.php diff --git a/resources/lang/en/auth.php b/lang/en/auth.php similarity index 100% rename from resources/lang/en/auth.php rename to lang/en/auth.php diff --git a/resources/lang/en/contact.php b/lang/en/contact.php similarity index 100% rename from resources/lang/en/contact.php rename to lang/en/contact.php diff --git a/resources/lang/en/contact_log.php b/lang/en/contact_log.php similarity index 100% rename from resources/lang/en/contact_log.php rename to lang/en/contact_log.php diff --git a/resources/lang/en/currencies.php b/lang/en/currencies.php similarity index 100% rename from resources/lang/en/currencies.php rename to lang/en/currencies.php diff --git a/resources/lang/en/email.php b/lang/en/email.php similarity index 100% rename from resources/lang/en/email.php rename to lang/en/email.php diff --git a/resources/lang/en/feed.php b/lang/en/feed.php similarity index 100% rename from resources/lang/en/feed.php rename to lang/en/feed.php diff --git a/resources/lang/en/format.php b/lang/en/format.php similarity index 100% rename from resources/lang/en/format.php rename to lang/en/format.php diff --git a/resources/lang/en/log.php b/lang/en/log.php similarity index 100% rename from resources/lang/en/log.php rename to lang/en/log.php diff --git a/resources/lang/en/pagination.php b/lang/en/pagination.php similarity index 100% rename from resources/lang/en/pagination.php rename to lang/en/pagination.php diff --git a/resources/lang/en/passwords.php b/lang/en/passwords.php similarity index 100% rename from resources/lang/en/passwords.php rename to lang/en/passwords.php diff --git a/resources/lang/en/validation.php b/lang/en/validation.php similarity index 100% rename from resources/lang/en/validation.php rename to lang/en/validation.php diff --git a/package.json b/package.json index 7b35e9b8e..6ad8459db 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "husky": "^4.3.0", "eslint-plugin-vue": "^9.1.0", "laravel-mix": "^6.0.44", + "laravel-vue-i18n": "^1.4.3", "lint-staged": "^12.5.0", "lodash": "^4.17.19", "postcss": "^8.4.14", diff --git a/resources/js/Pages/Auth/Login.vue b/resources/js/Pages/Auth/Login.vue index ed05f9555..f00a584d4 100644 --- a/resources/js/Pages/Auth/Login.vue +++ b/resources/js/Pages/Auth/Login.vue @@ -62,7 +62,9 @@
-

👋 Hi friend

+

+ 👋 {{ $t('Please choose a product first') }} +

diff --git a/resources/js/app.js b/resources/js/app.js index d40918d3b..b3d2d45ea 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -9,6 +9,7 @@ import 'ant-design-vue/lib/dropdown/style/index.css'; import 'ant-design-vue/lib/tooltip/style/index.css'; import 'v-calendar/dist/style.css'; import VCalendar from 'v-calendar'; +import { i18nVue } from 'laravel-vue-i18n'; const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel'; @@ -18,6 +19,9 @@ createInertiaApp({ setup({ el, app, props, plugin }) { return createApp({ render: () => h(app, props) }) .use(plugin) + .use(i18nVue, { + resolve: (lang) => import(`../../lang/${lang}.json`), + }) .use(Antd) .use(VCalendar) .mixin({ methods: _.assign({ route }, require('./methods').default) }) diff --git a/webpack.mix.js b/webpack.mix.js index 530937d84..ebeec6604 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -1,4 +1,17 @@ const mix = require('laravel-mix'); +const path = require('path'); +require('laravel-vue-i18n/mix'); + +/* + |-------------------------------------------------------------------------- + | Mix Asset Management + |-------------------------------------------------------------------------- + | + | Mix provides a clean, fluent API for defining some Webpack build steps + | for your Laravel applications. By default, we are compiling the CSS + | file for the application as well as bundling up all the JS files. + | + */ mix .js('resources/js/app.js', 'public/js') @@ -8,7 +21,13 @@ mix require('tailwindcss'), require('autoprefixer'), ]) - .webpackConfig(require('./webpack.config')); + .alias({ + vue$: path.join(__dirname, 'node_modules/vue/dist/vue.esm-bundler.js'), + '@': 'resources/js', + }) + .sourceMaps(process.env.MIX_PROD_SOURCE_MAPS || false, 'eval-cheap-module-source-map', 'source-map') + .setResourceRoot('../') + .i18n(); if (mix.inProduction()) { mix.version(); diff --git a/yarn.lock b/yarn.lock index 8b3ae2915..b2093e25a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1324,6 +1324,16 @@ estree-walker "^2.0.2" source-map "^0.6.1" +"@vue/compiler-core@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.37.tgz#b3c42e04c0e0f2c496ff1784e543fbefe91e215a" + integrity sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/shared" "3.2.37" + estree-walker "^2.0.2" + source-map "^0.6.1" + "@vue/compiler-dom@3.2.36": version "3.2.36" resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.36.tgz#16d911ff163ed5fc8087a01645bf14bb7f325401" @@ -1332,6 +1342,14 @@ "@vue/compiler-core" "3.2.36" "@vue/shared" "3.2.36" +"@vue/compiler-dom@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5" + integrity sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ== + dependencies: + "@vue/compiler-core" "3.2.37" + "@vue/shared" "3.2.37" + "@vue/compiler-sfc@3.2.36", "@vue/compiler-sfc@^3.2.36": version "3.2.36" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.36.tgz#e5065e7c0e5170ffa750e3c3dd93a29db109d0f2" @@ -1348,6 +1366,22 @@ postcss "^8.1.10" source-map "^0.6.1" +"@vue/compiler-sfc@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4" + integrity sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/compiler-core" "3.2.37" + "@vue/compiler-dom" "3.2.37" + "@vue/compiler-ssr" "3.2.37" + "@vue/reactivity-transform" "3.2.37" + "@vue/shared" "3.2.37" + estree-walker "^2.0.2" + magic-string "^0.25.7" + postcss "^8.1.10" + source-map "^0.6.1" + "@vue/compiler-ssr@3.2.36": version "3.2.36" resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.36.tgz#314f3a9424db58142c3608f48cbda7aa05fc66cb" @@ -1356,6 +1390,14 @@ "@vue/compiler-dom" "3.2.36" "@vue/shared" "3.2.36" +"@vue/compiler-ssr@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff" + integrity sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw== + dependencies: + "@vue/compiler-dom" "3.2.37" + "@vue/shared" "3.2.37" + "@vue/reactivity-transform@3.2.36": version "3.2.36" resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.36.tgz#8426a941b0b09d1b94fc162d4642758183b5d133" @@ -1367,6 +1409,17 @@ estree-walker "^2.0.2" magic-string "^0.25.7" +"@vue/reactivity-transform@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz#0caa47c4344df4ae59f5a05dde2a8758829f8eca" + integrity sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/compiler-core" "3.2.37" + "@vue/shared" "3.2.37" + estree-walker "^2.0.2" + magic-string "^0.25.7" + "@vue/reactivity@3.2.36": version "3.2.36" resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.36.tgz#026b14e716febffe80cd284fd8a2b33378968646" @@ -1374,6 +1427,13 @@ dependencies: "@vue/shared" "3.2.36" +"@vue/reactivity@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.37.tgz#5bc3847ac58828e2b78526e08219e0a1089f8848" + integrity sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A== + dependencies: + "@vue/shared" "3.2.37" + "@vue/runtime-core@3.2.36": version "3.2.36" resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.36.tgz#be5115e665679c26bf3807d2326675dc1d847134" @@ -1382,6 +1442,14 @@ "@vue/reactivity" "3.2.36" "@vue/shared" "3.2.36" +"@vue/runtime-core@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.37.tgz#7ba7c54bb56e5d70edfc2f05766e1ca8519966e3" + integrity sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ== + dependencies: + "@vue/reactivity" "3.2.37" + "@vue/shared" "3.2.37" + "@vue/runtime-dom@3.2.36": version "3.2.36" resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.36.tgz#cd5d403ea23c18ee7c17767103a1b2f8263c54bb" @@ -1391,6 +1459,15 @@ "@vue/shared" "3.2.36" csstype "^2.6.8" +"@vue/runtime-dom@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz#002bdc8228fa63949317756fb1e92cdd3f9f4bbd" + integrity sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw== + dependencies: + "@vue/runtime-core" "3.2.37" + "@vue/shared" "3.2.37" + csstype "^2.6.8" + "@vue/server-renderer@3.2.36": version "3.2.36" resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.36.tgz#1e7c1cf63bd17df7828d04e8c780ee6ca7a9ed7c" @@ -1399,11 +1476,24 @@ "@vue/compiler-ssr" "3.2.36" "@vue/shared" "3.2.36" +"@vue/server-renderer@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.37.tgz#840a29c8dcc29bddd9b5f5ffa22b95c0e72afdfc" + integrity sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA== + dependencies: + "@vue/compiler-ssr" "3.2.37" + "@vue/shared" "3.2.37" + "@vue/shared@3.2.36": version "3.2.36" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.36.tgz#35e11200542cf29068ba787dad57da9bdb82f644" integrity sha512-JtB41wXl7Au3+Nl3gD16Cfpj7k/6aCroZ6BbOiCMFCMvrOpkg/qQUXTso2XowaNqBbnkuGHurLAqkLBxNGc1hQ== +"@vue/shared@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.37.tgz#8e6adc3f2759af52f0e85863dfb0b711ecc5c702" + integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw== + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -4381,6 +4471,14 @@ laravel-mix@^6.0.44: webpackbar "^5.0.0-3" yargs "^17.2.1" +laravel-vue-i18n@^1.4.3: + version "1.4.4" + resolved "https://registry.yarnpkg.com/laravel-vue-i18n/-/laravel-vue-i18n-1.4.4.tgz#8781f5e29bc1b53f5a09cd826c5285250af0b823" + integrity sha512-/BqljTnWmITPBHnp2WPOe2YsiDcutEf4r4xHOxVjwKvuKMdl9lHBmprK5iXvHipPapyR5Q8QvIqUJmmWtnkIaQ== + dependencies: + php-parser "3.1.0-beta.4" + vue "^3.2.37" + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -5181,6 +5279,11 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +php-parser@3.1.0-beta.4: + version "3.1.0-beta.4" + resolved "https://registry.yarnpkg.com/php-parser/-/php-parser-3.1.0-beta.4.tgz#a56e3c90e0371e41fb9c1a09f4459b5adad2b03b" + integrity sha512-fLW5tSV8PK0Kizn+Wx1LrhJIFW5eYME4RXoHwIIXfArTBRs7RQJhoU016rXJTPQZIKAXo93ddVyojVvCOETxiA== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -6739,6 +6842,17 @@ vue@^3.2.36: "@vue/server-renderer" "3.2.36" "@vue/shared" "3.2.36" +vue@^3.2.37: + version "3.2.37" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.37.tgz#da220ccb618d78579d25b06c7c21498ca4e5452e" + integrity sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ== + dependencies: + "@vue/compiler-dom" "3.2.37" + "@vue/compiler-sfc" "3.2.37" + "@vue/runtime-dom" "3.2.37" + "@vue/server-renderer" "3.2.37" + "@vue/shared" "3.2.37" + vuedraggable@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-4.1.0.tgz#edece68adb8a4d9e06accff9dfc9040e66852270"