diff --git a/app/Http/Middleware/AcceptLanguage.php b/app/Http/Middleware/AcceptLanguage.php new file mode 100644 index 00000000..1c714190 --- /dev/null +++ b/app/Http/Middleware/AcceptLanguage.php @@ -0,0 +1,44 @@ +parseHttpLocale($request)) { + Log::debug(sprintf('Accept Language changed from [%s] to [%s] from Browser (%s)',app()->getLocale(),$locale,$request->header('Accept-Language'))); + + app()->setLocale($locale); + } + + return $next($request); + } + + private function parseHttpLocale(Request $request): string + { + $list = explode(',',$request->server('HTTP_ACCEPT_LANGUAGE','')); + + $locales = Collection::make($list) + ->map(function ($locale) { + $parts = explode(';',$locale); + $mapping = []; + + $mapping['locale'] = trim($parts[0]); + $mapping['factor'] = isset($parts[1]) + ? Arr::get(explode('=',$parts[1]),1) + : 1; + + return $mapping; + }) + ->sortByDesc(fn($locale)=>$locale['factor']); + + return Arr::get($locales->first(),'locale'); + } +} \ No newline at end of file diff --git a/bootstrap/app.php b/bootstrap/app.php index 2245f6a4..b99a4c60 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -4,7 +4,7 @@ use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; -use App\Http\Middleware\{AllowAnonymous,ApplicationSession,CheckUpdate,SwapinAuthUser,ViewVariables}; +use App\Http\Middleware\{AcceptLanguage,AllowAnonymous,ApplicationSession,CheckUpdate,SwapinAuthUser,ViewVariables}; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( @@ -17,6 +17,7 @@ return Application::configure(basePath: dirname(__DIR__)) group: 'web', middleware: [ ApplicationSession::class, + AcceptLanguage::class, AllowAnonymous::class, SwapinAuthUser::class, ViewVariables::class, diff --git a/composer.json b/composer.json index f8adb6b5..4dd62b6d 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "laravel/ui": "^4.5" }, "require-dev": { + "amirami/localizator": "^0.14@dev", "barryvdh/laravel-debugbar": "^3.6", "fakerphp/faker": "^1.23", "mockery/mockery": "^1.6", diff --git a/composer.lock b/composer.lock index f844b761..aa7074a8 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": "fe524438312a0b5d565ad83c191eac6a", + "content-hash": "baf49824fa0f2a2a61b7915204b18d2c", "packages": [ { "name": "brick/math", @@ -5848,6 +5848,84 @@ } ], "packages-dev": [ + { + "name": "amirami/localizator", + "version": "v0.14.0-alpha", + "source": { + "type": "git", + "url": "https://github.com/amiranagram/localizator.git", + "reference": "30aae5e51d3aaa95105e3834d481b8c37efaab40" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amiranagram/localizator/zipball/30aae5e51d3aaa95105e3834d481b8c37efaab40", + "reference": "30aae5e51d3aaa95105e3834d481b8c37efaab40", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/config": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/console": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/filesystem": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.2.5|^8.0", + "symfony/finder": "^5.1|^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "orchestra/testbench": "^6.6|^7.0|^8.0|^9.0|^10.0", + "phpunit/phpunit": "^9.5|^10.0|^11.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Amirami\\Localizator\\ServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Amirami\\Localizator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Amir Rami", + "email": "me@amirrami.com", + "homepage": "https://amirrami.com", + "role": "Developer" + } + ], + "description": "Localizator is a small tool for Laravel that gives you the ability to extract untranslated strings from project files. It works using the artisan command line and the provided localize command.", + "homepage": "https://github.com/amiranagram/localizator", + "support": { + "issues": "https://github.com/amiranagram/localizator/issues", + "source": "https://github.com/amiranagram/localizator/tree/v0.14.0-alpha" + }, + "funding": [ + { + "url": "https://m.do.co/c/f38828dd20f8", + "type": "custom" + }, + { + "url": "https://www.buymeacoffee.com/amirami/", + "type": "custom" + }, + { + "url": "https://github.com/amiranagram", + "type": "github" + } + ], + "time": "2025-03-06T12:49:02+00:00" + }, { "name": "barryvdh/laravel-debugbar", "version": "v3.15.4", @@ -8461,7 +8539,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "amirami/localizator": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/config/localizator.php b/config/localizator.php new file mode 100644 index 00000000..61587fb3 --- /dev/null +++ b/config/localizator.php @@ -0,0 +1,59 @@ + [ + /** + * Short keys. This is the default for Laravel. + * They are stored in PHP files inside folders name by their locale code. + * Laravel comes with default: auth.php, pagination.php, passwords.php and validation.php + */ + 'default' => true, + + /** + * Translations strings as key. + * They are stored in JSON file for each locale. + */ + 'json' => true, + ], + + /** + * Search criteria for files. + */ + 'search' => [ + /** + * Directories which should be looked inside. + */ + 'dirs' => ['app','resources/views'], + + /** + * Subdirectories which will be excluded. + * The values must be relative to the included directory paths. + */ + 'exclude' => [ + // + ], + + /** + * Patterns by which files should be queried. + * The values can be a regular expression, glob, or just a string. + */ + 'patterns' => ['*.php'], + + /** + * Functions that the strings will be extracted from. + * Add here any custom defined functions. + * NOTE: The translation string should always be the first argument. + */ + 'functions' => ['__', 'trans', '@lang'] + ], + + /** + * Should the localize command sort extracted strings alphabetically? + */ + 'sort' => true, + +]; diff --git a/resources/lang/README.md b/resources/lang/README.md index 60501ef1..b75bf9be 100644 --- a/resources/lang/README.md +++ b/resources/lang/README.md @@ -1,6 +1,45 @@ -This directory contains language translation files for PLA. +This directory contains language translation files for PLA. PLA should automatically detect your language based on your +browser configuration, and if the language is not available it will fall back to the language used internally (English). -Language files named by 2 letter iso language name (suffixed with .json) -represent the translations for that language. +Language files are named by 2 letter iso language name (suffixed with .json) represent the translations for that +language. +Where a language is spoken in multiple countries, but has local country differences (eg: `en-US` vs `en-GB`, +or `zh-CN` vs `zh-TW`), then the language filename is suffixed with `-` and a two letter country, eg: -eg: en.json +* `en.json` for English (General), +* `en-GB.json` for English (Great Britain), +* `zh-CN.json` for Chinese (China), +* `zh-TW.json` for Chinese (Taiwan), etc + +The language file `zz.json` is an example language file, with each translated string prefixed with the letter "Z". Its +used to identify any default language text (in english) that is not in a translated configuration. Text strings enclosed +in `@lang()`, or `__()` functions are translatable to other languages. + +If you want to update the language text for your language, then: + +* If your language file **exists** (eg: `fr.json` for French), then: + * Identify the missing tags (compare it to `zz.json`), + * Insert the missing tags into the language file (eg: `fr.json` for French) - ensure you keep the file in English + Alphabetical order. + +* If your language file **doesnt** exist (eg; `fr.json` for French), then + * Copy the default language file `zz.json` to `fr.json` + * Translate the strings + +The structure of the json files is: + +```json +{ + "Untranslated string1": "Translated string1", + "Untranslated string2": "Translated string2" +} +``` + +Some important notes: +* `Untranslated string` is the string as it appears in PLA, wrapped in either a `__()` or `@lang()` function, normally and english phrase +* `Translated string` is the translation for your language +* Each translated string must be comma terminated *EXCEPT* the last string + +Please submit a pull request with your translations, so that others users can benefit from the translation. + +If you find any strings that you are not translatable, or translated incorrectly, please submit a bug report. diff --git a/resources/lang/dev.json b/resources/lang/dev.json deleted file mode 100644 index d01ebcb8..00000000 --- a/resources/lang/dev.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Email": "DEV:Email", - "Home": "DEV:Home", - "Password": "DEV:Password", - "Please enter your email": "DEV:Please enter your email", - "Please enter your password": "DEV:Please enter your password", - "Server Info": "DEV:Server Info", - "Server Name": "DEV:Server Name", - "Sign in to :server": "DEV:Sign in to :server" -} diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php deleted file mode 100644 index e5506df2..00000000 --- a/resources/lang/en/auth.php +++ /dev/null @@ -1,19 +0,0 @@ - 'These credentials do not match our records.', - 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', - -]; diff --git a/resources/lang/en/pagination.php b/resources/lang/en/pagination.php deleted file mode 100644 index d4814118..00000000 --- a/resources/lang/en/pagination.php +++ /dev/null @@ -1,19 +0,0 @@ - '« Previous', - 'next' => 'Next »', - -]; diff --git a/resources/lang/en/passwords.php b/resources/lang/en/passwords.php deleted file mode 100644 index 2345a56b..00000000 --- a/resources/lang/en/passwords.php +++ /dev/null @@ -1,22 +0,0 @@ - 'Your password has been reset!', - 'sent' => 'We have emailed your password reset link!', - 'throttled' => 'Please wait before retrying.', - 'token' => 'This password reset token is invalid.', - 'user' => "We can't find a user with that email address.", - -]; diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php deleted file mode 100644 index a65914f9..00000000 --- a/resources/lang/en/validation.php +++ /dev/null @@ -1,151 +0,0 @@ - 'The :attribute must be accepted.', - 'active_url' => 'The :attribute is not a valid URL.', - 'after' => 'The :attribute must be a date after :date.', - 'after_or_equal' => 'The :attribute must be a date after or equal to :date.', - 'alpha' => 'The :attribute may only contain letters.', - 'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.', - 'alpha_num' => 'The :attribute may only contain letters and numbers.', - 'array' => 'The :attribute must be an array.', - 'before' => 'The :attribute must be a date before :date.', - 'before_or_equal' => 'The :attribute must be a date before or equal to :date.', - 'between' => [ - 'numeric' => 'The :attribute must be between :min and :max.', - 'file' => 'The :attribute must be between :min and :max kilobytes.', - 'string' => 'The :attribute must be between :min and :max characters.', - 'array' => 'The :attribute must have between :min and :max items.', - ], - 'boolean' => 'The :attribute field must be true or false.', - 'confirmed' => 'The :attribute confirmation does not match.', - 'date' => 'The :attribute is not a valid date.', - 'date_equals' => 'The :attribute must be a date equal to :date.', - 'date_format' => 'The :attribute does not match the format :format.', - 'different' => 'The :attribute and :other must be different.', - 'digits' => 'The :attribute must be :digits digits.', - 'digits_between' => 'The :attribute must be between :min and :max digits.', - 'dimensions' => 'The :attribute has invalid image dimensions.', - 'distinct' => 'The :attribute field has a duplicate value.', - 'email' => 'The :attribute must be a valid email address.', - 'ends_with' => 'The :attribute must end with one of the following: :values.', - 'exists' => 'The selected :attribute is invalid.', - 'file' => 'The :attribute must be a file.', - 'filled' => 'The :attribute field must have a value.', - 'gt' => [ - 'numeric' => 'The :attribute must be greater than :value.', - 'file' => 'The :attribute must be greater than :value kilobytes.', - 'string' => 'The :attribute must be greater than :value characters.', - 'array' => 'The :attribute must have more than :value items.', - ], - 'gte' => [ - 'numeric' => 'The :attribute must be greater than or equal :value.', - 'file' => 'The :attribute must be greater than or equal :value kilobytes.', - 'string' => 'The :attribute must be greater than or equal :value characters.', - 'array' => 'The :attribute must have :value items or more.', - ], - 'image' => 'The :attribute must be an image.', - 'in' => 'The selected :attribute is invalid.', - 'in_array' => 'The :attribute field does not exist in :other.', - 'integer' => 'The :attribute must be an integer.', - 'ip' => 'The :attribute must be a valid IP address.', - 'ipv4' => 'The :attribute must be a valid IPv4 address.', - 'ipv6' => 'The :attribute must be a valid IPv6 address.', - 'json' => 'The :attribute must be a valid JSON string.', - 'lt' => [ - 'numeric' => 'The :attribute must be less than :value.', - 'file' => 'The :attribute must be less than :value kilobytes.', - 'string' => 'The :attribute must be less than :value characters.', - 'array' => 'The :attribute must have less than :value items.', - ], - 'lte' => [ - 'numeric' => 'The :attribute must be less than or equal :value.', - 'file' => 'The :attribute must be less than or equal :value kilobytes.', - 'string' => 'The :attribute must be less than or equal :value characters.', - 'array' => 'The :attribute must not have more than :value items.', - ], - 'max' => [ - 'numeric' => 'The :attribute may not be greater than :max.', - 'file' => 'The :attribute may not be greater than :max kilobytes.', - 'string' => 'The :attribute may not be greater than :max characters.', - 'array' => 'The :attribute may not have more than :max items.', - ], - 'mimes' => 'The :attribute must be a file of type: :values.', - 'mimetypes' => 'The :attribute must be a file of type: :values.', - 'min' => [ - 'numeric' => 'The :attribute must be at least :min.', - 'file' => 'The :attribute must be at least :min kilobytes.', - 'string' => 'The :attribute must be at least :min characters.', - 'array' => 'The :attribute must have at least :min items.', - ], - 'not_in' => 'The selected :attribute is invalid.', - 'not_regex' => 'The :attribute format is invalid.', - 'numeric' => 'The :attribute must be a number.', - 'password' => 'The password is incorrect.', - 'present' => 'The :attribute field must be present.', - 'regex' => 'The :attribute format is invalid.', - 'required' => 'The :attribute field is required.', - 'required_if' => 'The :attribute field is required when :other is :value.', - 'required_unless' => 'The :attribute field is required unless :other is in :values.', - 'required_with' => 'The :attribute field is required when :values is present.', - 'required_with_all' => 'The :attribute field is required when :values are present.', - 'required_without' => 'The :attribute field is required when :values is not present.', - 'required_without_all' => 'The :attribute field is required when none of :values are present.', - 'same' => 'The :attribute and :other must match.', - 'size' => [ - 'numeric' => 'The :attribute must be :size.', - 'file' => 'The :attribute must be :size kilobytes.', - 'string' => 'The :attribute must be :size characters.', - 'array' => 'The :attribute must contain :size items.', - ], - 'starts_with' => 'The :attribute must start with one of the following: :values.', - 'string' => 'The :attribute must be a string.', - 'timezone' => 'The :attribute must be a valid zone.', - 'unique' => 'The :attribute has already been taken.', - 'uploaded' => 'The :attribute failed to upload.', - 'url' => 'The :attribute format is invalid.', - 'uuid' => 'The :attribute must be a valid UUID.', - - /* - |-------------------------------------------------------------------------- - | Custom Validation Language Lines - |-------------------------------------------------------------------------- - | - | Here you may specify custom validation messages for attributes using the - | convention "attribute.rule" to name the lines. This makes it quick to - | specify a specific custom language line for a given attribute rule. - | - */ - - 'custom' => [ - 'attribute-name' => [ - 'rule-name' => 'custom-message', - ], - ], - - /* - |-------------------------------------------------------------------------- - | Custom Validation Attributes - |-------------------------------------------------------------------------- - | - | The following language lines are used to swap our attribute placeholder - | with something more reader friendly such as "E-Mail Address" instead - | of "email". This simply helps us make our message more expressive. - | - */ - - 'attributes' => [], - -]; diff --git a/resources/lang/zz.json b/resources/lang/zz.json new file mode 100644 index 00000000..c5d6ae1a --- /dev/null +++ b/resources/lang/zz.json @@ -0,0 +1,140 @@ +{ + "(no description)": "Z(no description)", + "(none)": "Z(none)", + "(not applicable)": "Z(not applicable)", + "(not specified)": "Z(not specified)", + "(unknown syntax)": "Z(unknown syntax)", + "Add New Attribute": "ZAdd New Attribute", + "Add Objectclass": "ZAdd Objectclass", + "Add Value": "ZAdd Value", + "Aliases": "ZAliases", + "Attributes": "ZAttributes", + "attributes(s)": "Zattributes(s)", + "Attribute Types": "ZAttribute Types", + "Certificate Subject": "ZCertificate Subject", + "Check": "ZCheck", + "Check Password": "ZCheck Password", + "Close": "ZClose", + "Collective": "ZCollective", + "Copy\/Move": "ZCopy\/Move", + "Create Child Entry": "ZCreate Child Entry", + "Created": "ZCreated", + "Create Entry": "ZCreate Entry", + "Create New Entry": "ZCreate New Entry", + "Create new LDAP item here": "ZCreate new LDAP item here", + "Delete": "ZDelete", + "Deleted": "ZDeleted", + "Delete Entry": "ZDelete Entry", + "Deleting this DN will permanently delete it from your LDAP server.": "ZDeleting this DN will permanently delete it from your LDAP server.", + "Description": "ZDescription", + "DN": "ZDN", + "Download": "ZDownload", + "Do you want to make the following changes?": "ZDo you want to make the following changes?", + "dynamic": "Zdynamic", + "Edit Entry": "ZEdit Entry", + "Entry": "ZEntry", + "Entry updated": "ZEntry updated", + "Equality": "ZEquality", + "Error": "ZError", + "Expired": "ZExpired", + "Expires": "ZExpires", + "Export": "ZExport", + "Exported by": "ZExported by", + "Force as MAY by config": "ZForce as MAY by config", + "Generated by": "ZGenerated by", + "Home": "ZHome", + "Ignoring blank value": "ZIgnoring blank value", + "Import Result": "ZImport Result", + "Inherits from": "ZInherits from", + "Internal": "ZInternal", + "Invalid Password": "ZInvalid Password", + "KRB_DISALLOW_ALL_TIX": "ZKRB_DISALLOW_ALL_TIX", + "KRB_DISALLOW_DUP_SKEY": "ZKRB_DISALLOW_DUP_SKEY", + "KRB_DISALLOW_FORWARDABLE": "ZKRB_DISALLOW_FORWARDABLE", + "KRB_DISALLOW_POSTDATED": "ZKRB_DISALLOW_POSTDATED", + "KRB_DISALLOW_PROXIABLE": "ZKRB_DISALLOW_PROXIABLE", + "KRB_DISALLOW_RENEWABLE": "ZKRB_DISALLOW_RENEWABLE", + "KRB_DISALLOW_SVR": "ZKRB_DISALLOW_SVR", + "KRB_DISALLOW_TGT_BASED": "ZKRB_DISALLOW_TGT_BASED", + "KRB_PWCHANGE_SERVICE": "ZKRB_PWCHANGE_SERVICE", + "KRB_REQUIRES_HW_AUTH": "ZKRB_REQUIRES_HW_AUTH", + "KRB_REQUIRES_PRE_AUTH": "ZKRB_REQUIRES_PRE_AUTH", + "KRB_REQUIRES_PWCHANGE": "ZKRB_REQUIRES_PWCHANGE", + "LDAP Authentication Error": "ZLDAP Authentication Error", + "LDAP Entry": "ZLDAP Entry", + "LDAP Server Error Code": "ZLDAP Server Error Code", + "LDAP Server Unavailable": "ZLDAP Server Unavailable", + "LDIF": "ZLDIF", + "LDIF Import": "ZLDIF Import", + "LDIF Import Result": "ZLDIF Import Result", + "Line": "ZLine", + "locale": "ZZ", + "Matching Rules": "ZMatching Rules", + "Maximum file size": "ZMaximum file size", + "Maximum Length": "ZMaximum Length", + "NEW": "ZNEW", + "New Value": "ZNew Value", + "Next": "ZNext", + "No attributes changed": "ZNo attributes changed", + "No description available, can you help with one?": "ZNo description available, can you help with one?", + "No Server Name Yet": "ZNo Server Name Yet", + "NOT DEFINED": "ZNOT DEFINED", + "Not Implemented": "ZNot Implemented", + "Object Classes": "ZObject Classes", + "Object Identifier": "ZObject Identifier", + "Obsolete": "ZObsolete", + "Optional Attributes": "ZOptional Attributes", + "Ordering": "ZOrdering", + "Or upload LDIF file": "ZOr upload LDIF file", + "Parent to": "ZParent to", + "Paste in your LDIF here": "ZPaste in your LDIF here", + "Possible Causes": "ZPossible Causes", + "Process": "ZProcess", + "rdn": "Zrdn", + "RDN is required": "ZRDN is required", + "RDN is required.": "ZRDN is required.", + "RDN value is required.": "ZRDN value is required.", + "Rename": "ZRename", + "Replace": "ZReplace", + "required": "Zrequired", + "Required Attribute by ObjectClass(es)": "ZRequired Attribute by ObjectClass(es)", + "Required Attributes": "ZRequired Attributes", + "Required by ObjectClasses": "ZRequired by ObjectClasses", + "Reset": "ZReset", + "Result": "ZResult", + "Schema Information": "ZSchema Information", + "Search Filter": "ZSearch Filter", + "Search Scope": "ZSearch Scope", + "Select a Structural ObjectClass...": "ZSelect a Structural ObjectClass...", + "Select attribute...": "ZSelect attribute...", + "Select from": "ZSelect from", + "Server": "ZServer", + "Server Info": "ZServer Info", + "Single Valued": "ZSingle Valued", + "Step": "ZStep", + "structural": "Zstructural", + "Substring Rule": "ZSubstring Rule", + "Syntax": "ZSyntax", + "Syntaxes": "ZSyntaxes", + "These are dynamic values present as a result of another attribute": "ZThese are dynamic values present as a result of another attribute", + "This attribute is required for the RDN": "ZThis attribute is required for the RDN", + "To Server": "ZTo Server", + "Total Entries": "ZTotal Entries", + "Type": "ZType", + "Unknown": "ZUnknown", + "Untrapped Error": "ZUntrapped Error", + "Update": "ZUpdate", + "Updated": "ZUpdated", + "Upload JpegPhoto": "ZUpload JpegPhoto", + "Usage": "ZUsage", + "Used by Attributes": "ZUsed by Attributes", + "Used by ObjectClasses": "ZUsed by ObjectClasses", + "User Modification": "ZUser Modification", + "Validation Errors": "ZValidation Errors", + "Version": "ZVersion", + "WARNING": "ZWARNING", + "Your DNS server cannot resolve that hostname": "ZYour DNS server cannot resolve that hostname", + "Your LDAP server hostname is incorrect": "ZYour LDAP server hostname is incorrect", + "Your LDAP server is not connectable": "ZYour LDAP server is not connectable", + "Your Resolver is not pointing to your DNS server": "ZYour Resolver is not pointing to your DNS server" +} \ No newline at end of file diff --git a/resources/themes/architect/views/layouts/app.blade.php b/resources/themes/architect/views/layouts/app.blade.php index e1a646b3..7832fedd 100644 --- a/resources/themes/architect/views/layouts/app.blade.php +++ b/resources/themes/architect/views/layouts/app.blade.php @@ -1,5 +1,5 @@ - + @section('htmlheader') @include('architect::layouts.partials.htmlheader') @show diff --git a/resources/views/debug.blade.php b/resources/views/debug.blade.php index f68bff2c..a7e9d726 100644 --- a/resources/views/debug.blade.php +++ b/resources/views/debug.blade.php @@ -44,6 +44,12 @@