Browse Source

Merge branch 'user-authentication' of kyle/autocde2.0 into dev

User authentication and multilingual components
Mo 1 year ago
parent
commit
e520d2ed05

+ 57 - 0
.env.testing

@@ -0,0 +1,57 @@
+APP_NAME=Laravel
+APP_ENV=local
+APP_KEY=base64:hIJv76Q0WaJOgjaaoBY22yHbn/ayrqQPOOzakcNQzEk=
+APP_DEBUG=true
+APP_URL=http://localhost
+
+LOG_CHANNEL=stack
+LOG_DEPRECATIONS_CHANNEL=null
+LOG_LEVEL=debug
+
+DB_CONNECTION=sqlite
+DB_DATABASE=:memory:
+
+LOCALE=zh_CN
+
+BROADCAST_DRIVER=log
+CACHE_DRIVER=file
+FILESYSTEM_DISK=local
+QUEUE_CONNECTION=sync
+SESSION_DRIVER=file
+SESSION_LIFETIME=120
+
+MEMCACHED_HOST=127.0.0.1
+
+REDIS_HOST=127.0.0.1
+REDIS_PASSWORD=null
+REDIS_PORT=6379
+
+MAIL_MAILER=smtp
+MAIL_HOST=mailpit
+MAIL_PORT=1025
+MAIL_USERNAME=null
+MAIL_PASSWORD=null
+MAIL_ENCRYPTION=null
+MAIL_FROM_ADDRESS="hello@example.com"
+MAIL_FROM_NAME="${APP_NAME}"
+
+AWS_ACCESS_KEY_ID=
+AWS_SECRET_ACCESS_KEY=
+AWS_DEFAULT_REGION=us-east-1
+AWS_BUCKET=
+AWS_USE_PATH_STYLE_ENDPOINT=false
+
+PUSHER_APP_ID=
+PUSHER_APP_KEY=
+PUSHER_APP_SECRET=
+PUSHER_HOST=
+PUSHER_PORT=443
+PUSHER_SCHEME=https
+PUSHER_APP_CLUSTER=mt1
+
+VITE_APP_NAME="${APP_NAME}"
+VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
+VITE_PUSHER_HOST="${PUSHER_HOST}"
+VITE_PUSHER_PORT="${PUSHER_PORT}"
+VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
+VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

+ 49 - 0
app/Http/Controllers/API/AuthController.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace App\Http\Controllers\API;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\API\User\LoginRequest;
+use App\Models\User;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Validation\ValidationException;
+use Laravel\Sanctum\PersonalAccessToken;
+
+class AuthController extends Controller
+{
+    public function login(LoginRequest $request)
+    {
+        $user = User::query()
+            ->where("username", $request->username)
+            ->orWhere("email", $request->username)
+            ->first();
+
+        if (! $user || ! Hash::check($request->password, $user->password)) {
+            throw ValidationException::withMessages([
+                'username' => [__("auth.failed")],
+            ]);
+        }
+
+        //Duplicate logins are not allowed
+        PersonalAccessToken::query()->where("tokenable_type", User::class)
+            ->where("name", 'user')
+            ->where("tokenable_id", $user->id)
+            ->delete();
+
+        return $this->success([
+            'data' => [
+                'token' => $user->createToken('user')->plainTextToken,
+            ]
+        ]);
+    }
+
+
+    public function logout()
+    {
+        Auth::user()->currentAccessToken()->delete();
+
+        return $this->noContent();
+    }
+}

+ 1 - 1
app/Http/Controllers/Controller.php

@@ -8,5 +8,5 @@ use Illuminate\Routing\Controller as BaseController;
 
 class Controller extends BaseController
 {
-    use AuthorizesRequests, ValidatesRequests;
+    use AuthorizesRequests, ValidatesRequests, JsonResponseHelper;
 }

+ 115 - 0
app/Http/Controllers/JsonResponseHelper.php

@@ -0,0 +1,115 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Response;
+
+trait JsonResponseHelper
+{
+    /**
+     * 201
+     *
+     * @param array|string|null $content
+     * @return string
+     */
+    protected function created(array|string $content = null): string
+    {
+        return new Response($content, Response::HTTP_CREATED);
+    }
+
+    /**
+     * 202
+     *
+     * @return Response
+     */
+    protected function accepted(): Response
+    {
+        return new Response('', Response::HTTP_ACCEPTED);
+    }
+
+    /**
+     * 204
+     *
+     * @return Response
+     */
+    protected function noContent(): Response
+    {
+        return new Response('', Response::HTTP_NO_CONTENT);
+    }
+
+    /**
+     * 400
+     *
+     * @param string $message
+     * @param array $headers
+     * @param int $options
+     * @return JsonResponse
+     */
+    protected function badRequest(string $message, array $headers = [], int $options = 0): JsonResponse
+    {
+        return response()->json([
+            'message' => $message
+        ], Response::HTTP_BAD_REQUEST, $headers, $options);
+    }
+
+    /**
+     * 401
+     *
+     * @param string $message
+     * @param array $headers
+     * @param int $options
+     * @return JsonResponse
+     */
+    protected function unauthorized(string $message = '', array $headers = [], int $options = 0): JsonResponse
+    {
+        return response()->json([
+            'message' => $message ? $message : 'Token Signature could not be verified.'
+        ], Response::HTTP_UNAUTHORIZED, $headers, $options);
+    }
+
+    /**
+     * 403
+     *
+     * @param string $message
+     * @param array $headers
+     * @param int $options
+     * @return JsonResponse
+     */
+    protected function forbidden(string $message = '', array $headers = [], int $options = 0): JsonResponse
+    {
+        return response()->json([
+            'message' => $message ? $message : 'Insufficient permissions.'
+        ], Response::HTTP_FORBIDDEN, $headers, $options);
+    }
+
+    /**
+     * 422
+     *
+     * @param array $errors
+     * @param array $headers
+     * @param string $message
+     * @param $options
+     * @return JsonResponse
+     */
+    protected function unprocesableEtity(array $errors = [], array $headers = [], string $message = '', $options = 0): JsonResponse
+    {
+        return response()->json([
+            'message' => $message ? $message : '422 Unprocessable Entity',
+            'errors' => $errors
+        ], Response::HTTP_UNPROCESSABLE_ENTITY, $headers, $options);
+    }
+
+    /**
+     * 200
+     *
+     * @param array $data
+     * @param array $headers
+     * @param $options
+     * @return JsonResponse
+     */
+    protected function success(array $data, array $headers = [], $options = 0): JsonResponse
+    {
+        return response()->json($data, Response::HTTP_OK, $headers, $options);
+    }
+}

+ 1 - 1
app/Http/Kernel.php

@@ -39,7 +39,7 @@ class Kernel extends HttpKernel
         ],
 
         'api' => [
-            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
+            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
             \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
             \Illuminate\Routing\Middleware\SubstituteBindings::class,
         ],

+ 29 - 0
app/Http/Requests/API/User/LoginRequest.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Http\Requests\API\User;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class LoginRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     */
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
+     */
+    public function rules(): array
+    {
+        return [
+            'username' => 'required',
+            'password' => 'required',
+        ];
+    }
+}

+ 1 - 0
app/Models/User.php

@@ -19,6 +19,7 @@ class User extends Authenticatable
      */
     protected $fillable = [
         'name',
+        'username',
         'email',
         'password',
     ];

+ 3 - 1
composer.json

@@ -13,6 +13,7 @@
     },
     "require-dev": {
         "fakerphp/faker": "^1.9.1",
+        "laravel-lang/common": "^6.1",
         "laravel/pint": "^1.0",
         "laravel/sail": "^1.18",
         "mockery/mockery": "^1.4.4",
@@ -38,7 +39,8 @@
             "@php artisan package:discover --ansi"
         ],
         "post-update-cmd": [
-            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
+            "@php artisan vendor:publish --tag=laravel-assets --ansi --force",
+            "@php artisan lang:update"
         ],
         "post-root-package-install": [
             "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""

File diff suppressed because it is too large
+ 963 - 43
composer.lock


+ 1 - 1
config/app.php

@@ -83,7 +83,7 @@ return [
     |
     */
 
-    'locale' => 'en',
+    'locale' => env("LOCALE", 'en'),
 
     /*
     |--------------------------------------------------------------------------

+ 1 - 1
config/sanctum.php

@@ -46,7 +46,7 @@ return [
     |
     */
 
-    'expiration' => null,
+    'expiration' => env("TOKEN_EXPIRATION", 2 * 30 * 24 * 60),
 
     /*
     |--------------------------------------------------------------------------

+ 28 - 0
database/migrations/2024_01_14_134918_add_username_to_user_table.php

@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->string("username", 30)->unique();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn("username");
+        });
+    }
+};

+ 208 - 0
lang/ar.json

@@ -0,0 +1,208 @@
+{
+    "(and :count more error)": "(و :count خطأ إضافي)",
+    "(and :count more errors)": "(و :count أخطاء إضافية)",
+    "A Timeout Occurred": "انتهت المهلة",
+    "Accept": "موافقة",
+    "Accepted": "تمت الموافقة",
+    "Action": "الإجراء",
+    "Actions": "الإجراءات",
+    "Add": "إضافة",
+    "Admin": "مدير",
+    "Agree": "موافقة",
+    "All rights reserved.": "جميع الحقوق محفوظة.",
+    "Already Reported": "تم التبليغ مسبقاً",
+    "Archive": "أرشفة",
+    "Assign": "تعيين",
+    "Attach": "إرفاق",
+    "Bad Gateway": "بوابة غير صالحة",
+    "Bad Request": "طلب غير صالح",
+    "Bandwidth Limit Exceeded": "تم تجاوز حد النطاق الترددي",
+    "Browse": "تصفح",
+    "Cancel": "إلغاء",
+    "Choose": "اختر",
+    "Choose :name": "اختر :name",
+    "Choose File": "اختر ملف",
+    "Choose Image": "اختر صورة",
+    "Click to copy": "اضغط للنسخ",
+    "Client Closed Request": "أغلق العميل الطلب",
+    "Close": "إغلاق",
+    "Collapse": "طي",
+    "Collapse All": "طي الكل",
+    "Comment": "تعليق",
+    "Confirm": "تأكيد",
+    "Conflict": "تعارض",
+    "Connect": "اتصال",
+    "Connection Closed Without Response": "تم إغلاق الاتصال دون استجابة",
+    "Connection Timed Out": "انتهت مهلة الاتصال",
+    "Continue": "استمرار",
+    "Create": "إنشاء",
+    "Created": "تم الإنشاء",
+    "Delete": "حذف",
+    "Detach": "إلغاء الإرفاق",
+    "Details": "التفاصيل",
+    "Disable": "تعطيل",
+    "Discard": "تجاهل",
+    "Done": "مكتمل",
+    "Down": "أسفل",
+    "Duplicate": "نسخ",
+    "Duplicate: name": "نسخ: الاسم",
+    "Edit": "تعديل",
+    "Edit :name": "تعديل :name",
+    "Enable": "تفعيل",
+    "Expand": "توسيع",
+    "Expand All": "توسيع الكل",
+    "Expectation Failed": "فشل التوقع",
+    "Explanation": "توضيح",
+    "Export": "تصدير",
+    "Failed Dependency": "فشل التبعية",
+    "File": "ملف",
+    "Files": "ملفات",
+    "Forbidden": "محظور",
+    "Found": "تم العثور",
+    "Gateway Timeout": "مهلة البوابة",
+    "Go Home": "الرئيسية",
+    "Go to page :page": "الإنتقال إلى الصفحة :page",
+    "Gone": "ذهب",
+    "Hello!": "أهلاً بك!",
+    "Hide": "إخفاء",
+    "Hide :name": "إخفاء :name",
+    "Home": "الرئيسية",
+    "HTTP Version Not Supported": "إصدار HTTP غير مدعوم",
+    "I'm a teapot": "أنا إبريق الشاي",
+    "If you did not create an account, no further action is required.": "إذا لم تقم بإنشاء حساب ، فلا يلزم اتخاذ أي إجراء آخر.",
+    "If you did not request a password reset, no further action is required.": "إذا لم تقم بطلب استعادة كلمة المرور، لا تحتاج القيام بأي إجراء.",
+    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "إذا كنت تواجه مشكلة في النقر على الزر \":actionText \"، يمكنك نسخ عنوان URL أدناه\n وألصقه في متصفح الويب:",
+    "IM Used": "IM مستخدمة",
+    "Image": "صورة",
+    "Impersonate": "انتحال الهوية",
+    "Impersonation": "التقليد",
+    "Import": "استيراد",
+    "Import :name": "استيراد :name",
+    "Insufficient Storage": "مساحة تخزين غير كافية",
+    "Internal Server Error": "خطأ داخلي في الخادم",
+    "Introduction": "مقدمة",
+    "Invalid JSON was returned from the route.": "تم إرجاع JSON غير صالح من المسار.",
+    "Invalid SSL Certificate": "شهادة SSL غير صالحة",
+    "Length Required": "المدة المطلوبة",
+    "Like": "إعجاب",
+    "Load": "تحميل",
+    "Localize": "اللغة",
+    "Locked": "مقفل",
+    "Login": "تسجيل الدخول",
+    "Logout": "تسجيل الخروج",
+    "Loop Detected": "تم الكشف عن حلقة",
+    "Maintenance Mode": "نمط الصيانة",
+    "Method Not Allowed": "الطريقة غير مسموحة",
+    "Misdirected Request": "طلب توجيه خاطئ",
+    "Moved Permanently": "انتقل بشكل دائم",
+    "Multi-Status": "حالات متعددة",
+    "Multiple Choices": "خيارات متعددة",
+    "Network Authentication Required": "مصادقة الشبكة المطلوبة",
+    "Network Connect Timeout Error": "خطأ مهلة الاتصال بالشبكة",
+    "Network Read Timeout Error": "خطأ في مهلة قراءة الشبكة",
+    "New": "جديد",
+    "New :name": "إضافة :name",
+    "No": "ﻻ",
+    "No Content": "لا يوجد محتوى",
+    "Non-Authoritative Information": "معلومات غير موثوق بها",
+    "Not Acceptable": "غير مقبول",
+    "Not Extended": "غير موسعة",
+    "Not Found": "غير متوفر",
+    "Not Implemented": "غير منفذة",
+    "Not Modified": "غير معدل",
+    "of": "من",
+    "OK": "حسنا",
+    "Open": "فتح",
+    "Open in a current window": "فتح في النافذة الحالية",
+    "Open in a new window": "افتح في نافذة جديدة",
+    "Open in a parent frame": "فتح في إطار الأصل",
+    "Open in the topmost frame": "افتح في الإطار العلوي",
+    "Open on the website": "فتح على الموقع",
+    "Origin Is Unreachable": "لا يمكن الوصول إلى المنشأ",
+    "Page Expired": "الصفحة منتهية الصلاحية",
+    "Pagination Navigation": "التنقل بين الصفحات",
+    "Partial Content": "محتوى جزئي",
+    "Payload Too Large": "الحمولة كبيرة جدا",
+    "Payment Required": "مطلوب الدفع",
+    "Permanent Redirect": "إعادة توجيه دائمة",
+    "Please click the button below to verify your email address.": "يرجى النقر على الزر أدناه للتحقق من عنوان بريدك الإلكتروني.",
+    "Precondition Failed": "فشل الشرط المسبق",
+    "Precondition Required": "الشرط المسبق مطلوب",
+    "Preview": "معاينة",
+    "Price": "السعر",
+    "Processing": "جاري المعالجة",
+    "Proxy Authentication Required": "مصادقة الوكيل مطلوبة",
+    "Railgun Error": "خطأ Railgun",
+    "Range Not Satisfiable": "نطاق غير مقبول",
+    "Regards": "مع التحية",
+    "Register": "تسجيل",
+    "Request Header Fields Too Large": "حقول عنوان الطلب كبيرة جدا",
+    "Request Timeout": "انتهت مهلة الطلب",
+    "Reset Content": "إعادة تعيين المحتوى",
+    "Reset Password": "استعادة كلمة المرور",
+    "Reset Password Notification": "تنبيه استعادة كلمة المرور",
+    "Restore": "استعادة",
+    "Restore :name": "استعادة :name",
+    "results": "نتيجة",
+    "Retry With": "إعادة المحاولة مع",
+    "Save": "حفظ",
+    "Save & Close": "حفظ وإغلاق",
+    "Save & Return": "حفظ وعودة",
+    "Save :name": "حفظ :name",
+    "Search": "بحث",
+    "Search :name": "بحث :name",
+    "See Other": "انظر الآخر",
+    "Select": "اختر",
+    "Select All": "اختر الكل",
+    "Send": "إرسال",
+    "Server Error": "خطأ في الإستضافة",
+    "Service Unavailable": "الخدمة غير متوفرة",
+    "Session Has Expired": "انتهت الجلسة",
+    "Settings": "إعدادات",
+    "Show": "عرض",
+    "Show :name": "عرض :name",
+    "Show All": "عرض الكل",
+    "Showing": "عرض",
+    "Solve": "حل",
+    "SSL Handshake Failed": "فشلت مصافحة SSL",
+    "Submit": "إرسال",
+    "Subscribe": "اشتراك",
+    "Switch": "تحويل",
+    "Switch To Role": "التحويل إلى صلاحية",
+    "Switching Protocols": "تبديل البروتوكولات",
+    "Tag": "علامة",
+    "Tags": "العلامات",
+    "Temporary Redirect": "إعادة توجيه مؤقتة",
+    "The given data was invalid.": "البيانات المدخلة غير صالحة.",
+    "The response is not a streamed response.": "الاستجابة ليست استجابة متدفقة.",
+    "The response is not a view.": "الاستجابة ليست صفحة عرض.",
+    "This password reset link will expire in :count minutes.": "ستنتهي صلاحية رابط استعادة كلمة المرور خلال :count دقيقة.",
+    "to": "إلى",
+    "Toggle navigation": "إظهار/إخفاء القائمة",
+    "Too Early": "مبكر جدا",
+    "Too Many Requests": "طلبات كثيرة جدًا",
+    "Translate": "ترجمة",
+    "Translate It": "ترجمة",
+    "Unauthorized": "غير مصرّح",
+    "Unavailable For Legal Reasons": "غير متوفر لأسباب قانونية",
+    "Unknown Error": "خطأ غير معروف",
+    "Unpack": "فك الضغط",
+    "Unprocessable Entity": "كيان غير قابل للمعالجة",
+    "Unsubscribe": "إلغاء الاشتراك",
+    "Unsupported Media Type": "نوع وسائط غير مدعوم",
+    "Up": "أعلى",
+    "Update": "تحديث",
+    "Update :name": "تحديث :name",
+    "Upgrade Required": "الترقية مطلوبة",
+    "URI Too Long": "URI طويلة جدا",
+    "Use Proxy": "استخدام الوكيل",
+    "User": "مستخدم",
+    "Variant Also Negotiates": "البديل يتفاوض أيضا",
+    "Verify Email Address": "التحقق من عنوان البريد الإلكتروني",
+    "View": "عرض",
+    "View :name": "عرض :name",
+    "Web Server is Down": "خادم الويب متوقف",
+    "Whoops!": "عذراً!",
+    "Yes": "نعم",
+    "You are receiving this email because we received a password reset request for your account.": "لقد استلمت هذا الإيميل لأننا استقبلنا طلباً لاستعادة كلمة مرور حسابك."
+}

+ 108 - 0
lang/ar/actions.php

@@ -0,0 +1,108 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'accept'          => 'موافقة',
+    'action'          => 'إجراء',
+    'actions'         => 'أجراءات',
+    'add'             => 'إضافة',
+    'admin'           => 'مدير',
+    'agree'           => 'موافقة',
+    'archive'         => 'أرشفة',
+    'assign'          => 'تعيين',
+    'attach'          => 'إرفاق',
+    'browse'          => 'تصفح',
+    'cancel'          => 'إلغاء',
+    'choose'          => 'اختر',
+    'choose_file'     => 'اختر ملف',
+    'choose_image'    => 'اختر صورة',
+    'click_to_copy'   => 'اضغط للنسخ',
+    'close'           => 'إغلاق',
+    'collapse'        => 'طي',
+    'collapse_all'    => 'طي الكل',
+    'comment'         => 'تعليق',
+    'confirm'         => 'تأكيد',
+    'connect'         => 'اتصال',
+    'create'          => 'إنشاء',
+    'delete'          => 'حذف',
+    'detach'          => 'فصل',
+    'details'         => 'تفاصيل',
+    'disable'         => 'إلغاء التفعيل',
+    'discard'         => 'تجاهل',
+    'done'            => 'مكتمل',
+    'down'            => 'أسفل',
+    'duplicate'       => 'نسخ',
+    'edit'            => 'تعديل',
+    'enable'          => 'تفعيل',
+    'expand'          => 'توسيع',
+    'expand_all'      => 'توسيع الكل',
+    'explanation'     => 'توضيح',
+    'export'          => 'تصدير',
+    'file'            => 'الحقل :attribute يجب أن يكون ملفا.',
+    'files'           => 'ملفات',
+    'go_home'         => 'الانتقال للرئيسية',
+    'hide'            => 'إخفاء',
+    'home'            => 'الرئيسية',
+    'image'           => 'يجب أن يكون حقل :attribute صورةً.',
+    'Impersonate'     => 'انتحال الهوية',
+    'Impersonation'   => 'التقليد',
+    'import'          => 'استيراد',
+    'introduction'    => 'مقدمة',
+    'like'            => 'إعجاب',
+    'load'            => 'تحميل',
+    'localize'        => 'اللغة',
+    'named'           => [
+        'choose'    => 'اختر :name',
+        'duplicate' => 'مكرر: الاسم',
+        'edit'      => 'تعديل :name',
+        'hide'      => 'إخفاء :name',
+        'import'    => 'استيراد :name',
+        'new'       => ':name جديد',
+        'restore'   => 'استعادة :name',
+        'save'      => 'حفظ :name',
+        'search'    => 'بحث :name',
+        'show'      => 'عرض :name',
+        'update'    => 'تحديث :name',
+        'view'      => 'عرض :name',
+    ],
+    'new'             => 'جديد',
+    'no'              => 'لا',
+    'open'            => 'فتح',
+    'open_website'    => 'فتح على الموقع',
+    'preview'         => 'معاينة',
+    'price'           => 'السعر',
+    'restore'         => 'استعادة',
+    'save'            => 'حفظ',
+    'save_and_close'  => 'حفظ وإغلاق',
+    'save_and_return' => 'حفظ وعودة',
+    'search'          => 'بحث',
+    'select'          => 'اختر',
+    'select_all'      => 'اختر الكل',
+    'send'            => 'إرسال',
+    'settings'        => 'إعدادات',
+    'show'            => 'عرض',
+    'show_all'        => 'عرض الكل',
+    'solve'           => 'حل',
+    'submit'          => 'إرسال',
+    'subscribe'       => 'اشتراك',
+    'switch'          => 'تحويل',
+    'switch_to_role'  => 'التحويل إلى صلاحية',
+    'tag'             => 'علامة',
+    'tags'            => 'العلامات',
+    'target_link'     => [
+        'blank'  => 'افتح في نافذة جديدة',
+        'parent' => 'فتح في إطار الأصل',
+        'self'   => 'فتح في النافذة الحالية',
+        'top'    => 'افتح في الإطار العلوي',
+    ],
+    'translate'       => 'ترجمة',
+    'translate_it'    => 'ترجمة',
+    'unpack'          => 'فك الضغط',
+    'unsubscribe'     => 'إلغاء الاشتراك',
+    'up'              => 'أعلى',
+    'update'          => 'تحديث',
+    'user'            => 'لم يتم العثور على أيّ حسابٍ بهذا العنوان الإلكتروني.',
+    'view'            => 'عرض',
+    'yes'             => 'نعم',
+];

+ 9 - 0
lang/ar/auth.php

@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'failed'   => 'بيانات الاعتماد هذه غير متطابقة مع البيانات المسجلة لدينا.',
+    'password' => 'كلمة المرور غير صحيحة.',
+    'throttle' => 'عدد كبير جدا من محاولات الدخول. يرجى المحاولة مرة أخرى بعد :seconds ثانية.',
+];

+ 84 - 0
lang/ar/http-statuses.php

@@ -0,0 +1,84 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    '0'            => 'خطأ غير معروف',
+    '100'          => 'استمرار',
+    '101'          => 'تبديل البروتوكولات',
+    '102'          => 'جاري المعالجة',
+    '200'          => 'حسنا',
+    '201'          => 'تم الإنشاء',
+    '202'          => 'تمت الموافقة',
+    '203'          => 'معلومات غير موثقة',
+    '204'          => 'لا يوجد محتوى',
+    '205'          => 'إعادة تعيين المحتوى',
+    '206'          => 'محتوى جزئي',
+    '207'          => 'حالات متعددة',
+    '208'          => 'تم التبليغ مسبقاً',
+    '226'          => 'IM مستخدمة',
+    '300'          => 'خيارات متعددة',
+    '301'          => 'انتقل بشكل دائم',
+    '302'          => 'تم العثور',
+    '303'          => 'انظر الآخر',
+    '304'          => 'غير معدّل',
+    '305'          => 'استخدام Proxy',
+    '307'          => 'إعادة توجيه مؤقتة',
+    '308'          => 'إعادة توجيه دائمة',
+    '400'          => 'طلب غير صالح',
+    '401'          => 'غير مصرّح',
+    '402'          => 'الدفع مطلوب',
+    '403'          => 'محظور',
+    '404'          => 'الصفحة غير موجودة',
+    '405'          => 'الطريقة غير مسموحة',
+    '406'          => 'غير مقبول',
+    '407'          => 'مصادقة الوكيل مطلوبة',
+    '408'          => 'انتهت مهلة الطلب',
+    '409'          => 'تعارض',
+    '410'          => 'ذهب',
+    '411'          => 'المدة المطلوبة',
+    '412'          => 'فشل الشرط المسبق',
+    '413'          => 'الحمولة كبيرة جدا',
+    '414'          => 'URI طويل جدا',
+    '415'          => 'نوع الوسائط غير مدعوم',
+    '416'          => 'نطاق غير مقبول',
+    '417'          => 'فشل التوقع',
+    '418'          => 'أنا إبريق الشاي',
+    '419'          => 'انتهت الجلسة',
+    '421'          => 'طلب توجيه خاطئ',
+    '422'          => 'كيان غير قابل للمعاملة',
+    '423'          => 'مقفل',
+    '424'          => 'فشل التبعية',
+    '425'          => 'مبكر جدا',
+    '426'          => 'الترقية مطلوبة',
+    '428'          => 'الشرط المسبق مطلوب',
+    '429'          => 'طلبات كثيرة جدا',
+    '431'          => 'حقول عنوان الطلب كبيرة جدا',
+    '444'          => 'تم إغلاق الاتصال دون استجابة',
+    '449'          => 'إعادة المحاولة مع',
+    '451'          => 'غير متوفر لأسباب قانونية',
+    '499'          => 'أغلق العميل الطلب',
+    '500'          => 'خطأ داخلي في الخادم',
+    '501'          => 'غير منفذة',
+    '502'          => 'بوابة غير صالحة',
+    '503'          => 'نمط الصيانة',
+    '504'          => 'مهلة البوابة',
+    '505'          => 'إصدار HTTP غير مدعوم',
+    '506'          => 'البديل يتفاوض أيضا',
+    '507'          => 'مساحة تخزين غير كافية',
+    '508'          => 'تم الكشف عن حلقة',
+    '509'          => 'تم تجاوز حد النطاق الترددي',
+    '510'          => 'غير موسعة',
+    '511'          => 'مصادقة الشبكة المطلوبة',
+    '520'          => 'خطأ غير معروف',
+    '521'          => 'خادم الويب متوقف',
+    '522'          => 'انتهت مهلة الاتصال',
+    '523'          => 'لا يمكن الوصول إلى المنشأ',
+    '524'          => 'انتهت المهلة',
+    '525'          => 'فشلت مصافحة SSL',
+    '526'          => 'شهادة SSL غير صالحة',
+    '527'          => 'خطأ Railgun',
+    '598'          => 'خطأ في مهلة قراءة الشبكة',
+    '599'          => 'خطأ مهلة الاتصال بالشبكة',
+    'unknownError' => 'خطأ غير معروف',
+];

+ 8 - 0
lang/ar/pagination.php

@@ -0,0 +1,8 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'next'     => 'التالي &raquo;',
+    'previous' => '&laquo; السابق',
+];

+ 11 - 0
lang/ar/passwords.php

@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'reset'     => 'تمت إعادة تعيين كلمة المرور!',
+    'sent'      => 'تم إرسال تفاصيل استعادة كلمة المرور الخاصة بك إلى بريدك الإلكتروني!',
+    'throttled' => 'الرجاء الانتظار قبل إعادة المحاولة.',
+    'token'     => 'رمز استعادة كلمة المرور الذي أدخلته غير صحيح.',
+    'user'      => 'لم يتم العثور على أيّ حسابٍ بهذا العنوان الإلكتروني.',
+];

+ 236 - 0
lang/ar/validation.php

@@ -0,0 +1,236 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'accepted'             => 'يجب قبول :attribute.',
+    'accepted_if'          => 'يجب قبول :attribute في حالة :other يساوي :value.',
+    'active_url'           => 'حقل :attribute لا يُمثّل رابطًا صحيحًا.',
+    'after'                => 'يجب على حقل :attribute أن يكون تاريخًا لاحقًا للتاريخ :date.',
+    'after_or_equal'       => 'حقل :attribute يجب أن يكون تاريخاً لاحقاً أو مطابقاً للتاريخ :date.',
+    'alpha'                => 'يجب أن لا يحتوي حقل :attribute سوى على حروف.',
+    'alpha_dash'           => 'يجب أن لا يحتوي حقل :attribute سوى على حروف، أرقام ومطّات.',
+    'alpha_num'            => 'يجب أن يحتوي حقل :attribute على حروفٍ وأرقامٍ فقط.',
+    'array'                => 'يجب أن يكون حقل :attribute ًمصفوفة.',
+    'ascii'                => 'يجب أن يحتوي الحقل :attribute فقط على أحرف أبجدية رقمية أحادية البايت ورموز.',
+    'before'               => 'يجب على حقل :attribute أن يكون تاريخًا سابقًا للتاريخ :date.',
+    'before_or_equal'      => 'حقل :attribute يجب أن يكون تاريخا سابقا أو مطابقا للتاريخ :date.',
+    'between'              => [
+        'array'   => 'يجب أن يحتوي حقل :attribute على عدد من العناصر بين :min و :max.',
+        'file'    => 'يجب أن يكون حجم ملف حقل :attribute بين :min و :max كيلوبايت.',
+        'numeric' => 'يجب أن تكون قيمة حقل :attribute بين :min و :max.',
+        'string'  => 'يجب أن يكون عدد حروف نّص حقل :attribute بين :min و :max.',
+    ],
+    'boolean'              => 'يجب أن تكون قيمة حقل :attribute إما true أو false .',
+    'can'                  => 'الحقل :attribute يحتوي على قيمة غير مصرّح بها.',
+    'confirmed'            => 'حقل التأكيد غير مُطابق للحقل :attribute.',
+    'current_password'     => 'كلمة المرور غير صحيحة.',
+    'date'                 => 'حقل :attribute ليس تاريخًا صحيحًا.',
+    'date_equals'          => 'يجب أن يكون حقل :attribute مطابقاً للتاريخ :date.',
+    'date_format'          => 'لا يتوافق حقل :attribute مع الشكل :format.',
+    'decimal'              => 'يجب أن يحتوي الحقل :attribute على :decimal منزلة/منازل عشرية.',
+    'declined'             => 'يجب رفض :attribute.',
+    'declined_if'          => 'يجب رفض :attribute عندما يكون :other بقيمة :value.',
+    'different'            => 'يجب أن يكون الحقلان :attribute و :other مُختلفين.',
+    'digits'               => 'يجب أن يحتوي حقل :attribute على :digits رقمًا/أرقام.',
+    'digits_between'       => 'يجب أن يحتوي حقل :attribute بين :min و :max رقمًا/أرقام .',
+    'dimensions'           => 'الحقل:attribute يحتوي على أبعاد صورة غير صالحة.',
+    'distinct'             => 'للحقل :attribute قيمة مُكرّرة.',
+    'doesnt_end_with'      => 'الحقل :attribute يجب ألّا ينتهي بأحد القيم التالية: :values.',
+    'doesnt_start_with'    => 'الحقل :attribute يجب ألّا يبدأ بأحد القيم التالية: :values.',
+    'email'                => 'يجب أن يكون حقل :attribute عنوان بريد إلكتروني صحيح البُنية.',
+    'ends_with'            => 'يجب أن ينتهي حقل :attribute بأحد القيم التالية: :values',
+    'enum'                 => 'حقل :attribute المختار غير صالح.',
+    'exists'               => 'القيمة المحددة :attribute غير موجودة.',
+    'extensions'           => 'يجب أن يحتوي الحقل :attribute على أحد الإمتدادات التالية: :values.',
+    'file'                 => 'الحقل :attribute يجب أن يكون ملفا.',
+    'filled'               => 'حقل :attribute إجباري.',
+    'gt'                   => [
+        'array'   => 'يجب أن يحتوي حقل :attribute على أكثر من :value عناصر/عنصر.',
+        'file'    => 'يجب أن يكون حجم ملف حقل :attribute أكبر من :value كيلوبايت.',
+        'numeric' => 'يجب أن تكون قيمة حقل :attribute أكبر من :value.',
+        'string'  => 'يجب أن يكون طول نّص حقل :attribute أكثر من :value حروفٍ/حرفًا.',
+    ],
+    'gte'                  => [
+        'array'   => 'يجب أن يحتوي حقل :attribute على الأقل على :value عُنصرًا/عناصر.',
+        'file'    => 'يجب أن يكون حجم ملف حقل :attribute على الأقل :value كيلوبايت.',
+        'numeric' => 'يجب أن تكون قيمة حقل :attribute مساوية أو أكبر من :value.',
+        'string'  => 'يجب أن يكون طول نص حقل :attribute على الأقل :value حروفٍ/حرفًا.',
+    ],
+    'hex_color'            => 'يجب أن يحتوي الحقل :attribute على صيغة لون HEX صالحة',
+    'image'                => 'يجب أن يكون حقل :attribute صورةً.',
+    'in'                   => 'حقل :attribute غير موجود.',
+    'in_array'             => 'حقل :attribute غير موجود في :other.',
+    'integer'              => 'يجب أن يكون حقل :attribute عددًا صحيحًا.',
+    'ip'                   => 'يجب أن يكون حقل :attribute عنوان IP صحيحًا.',
+    'ipv4'                 => 'يجب أن يكون حقل :attribute عنوان IPv4 صحيحًا.',
+    'ipv6'                 => 'يجب أن يكون حقل :attribute عنوان IPv6 صحيحًا.',
+    'json'                 => 'يجب أن يكون حقل :attribute نصًا من نوع JSON.',
+    'lowercase'            => 'يجب أن يحتوي الحقل :attribute على حروف صغيرة.',
+    'lt'                   => [
+        'array'   => 'يجب أن يحتوي حقل :attribute على أقل من :value عناصر/عنصر.',
+        'file'    => 'يجب أن يكون حجم ملف حقل :attribute أصغر من :value كيلوبايت.',
+        'numeric' => 'يجب أن تكون قيمة حقل :attribute أصغر من :value.',
+        'string'  => 'يجب أن يكون طول نّص حقل :attribute أقل من :value حروفٍ/حرفًا.',
+    ],
+    'lte'                  => [
+        'array'   => 'يجب أن لا يحتوي حقل :attribute على أكثر من :value عناصر/عنصر.',
+        'file'    => 'يجب أن لا يتجاوز حجم ملف حقل :attribute :value كيلوبايت.',
+        'numeric' => 'يجب أن تكون قيمة حقل :attribute مساوية أو أصغر من :value.',
+        'string'  => 'يجب أن لا يتجاوز طول نّص حقل :attribute :value حروفٍ/حرفًا.',
+    ],
+    'mac_address'          => 'الحقل :attribute يجب أن يكون عنوان MAC صالحاً.',
+    'max'                  => [
+        'array'   => 'يجب أن لا يحتوي حقل :attribute على أكثر من :max عناصر/عنصر.',
+        'file'    => 'يجب أن لا يتجاوز حجم ملف حقل :attribute :max كيلوبايت.',
+        'numeric' => 'يجب أن تكون قيمة حقل :attribute مساوية أو أصغر من :max.',
+        'string'  => 'يجب أن لا يتجاوز طول نّص حقل :attribute :max حروفٍ/حرفًا.',
+    ],
+    'max_digits'           => 'يجب ألا يحتوي الحقل :attribute على أكثر من :max رقم/أرقام.',
+    'mimes'                => 'يجب أن يكون ملفًا من نوع : :values.',
+    'mimetypes'            => 'يجب أن يكون ملفًا من نوع : :values.',
+    'min'                  => [
+        'array'   => 'يجب أن يحتوي حقل :attribute على الأقل على :min عُنصرًا/عناصر.',
+        'file'    => 'يجب أن يكون حجم ملف حقل :attribute على الأقل :min كيلوبايت.',
+        'numeric' => 'يجب أن تكون قيمة حقل :attribute مساوية أو أكبر من :min.',
+        'string'  => 'يجب أن يكون طول نص حقل :attribute على الأقل :min حروفٍ/حرفًا.',
+    ],
+    'min_digits'           => 'يجب أن يحتوي الحقل :attribute على الأقل :min رقم/أرقام.',
+    'missing'              => 'يجب أن يكون الحقل :attribute مفقوداً.',
+    'missing_if'           => 'يجب أن يكون الحقل :attribute مفقوداً عندما :other يساوي :value.',
+    'missing_unless'       => 'يجب أن يكون الحقل :attribute مفقوداً ما لم يكن :other يساوي :value.',
+    'missing_with'         => 'يجب أن يكون الحقل :attribute مفقوداً عند توفر :values.',
+    'missing_with_all'     => 'يجب أن يكون الحقل :attribute مفقوداً عند توفر :values.',
+    'multiple_of'          => 'حقل :attribute يجب أن يكون من مضاعفات :value',
+    'not_in'               => 'عنصر الحقل :attribute غير صحيح.',
+    'not_regex'            => 'صيغة حقل :attribute غير صحيحة.',
+    'numeric'              => 'يجب على حقل :attribute أن يكون رقمًا.',
+    'password'             => [
+        'letters'       => 'يجب أن يحتوي حقل :attribute على حرف واحد على الأقل.',
+        'mixed'         => 'يجب أن يحتوي حقل :attribute على حرف كبير وحرف صغير على الأقل.',
+        'numbers'       => 'يجب أن يحتوي حقل :attribute على رقمٍ واحدٍ على الأقل.',
+        'symbols'       => 'يجب أن يحتوي حقل :attribute على رمزٍ واحدٍ على الأقل.',
+        'uncompromised' => 'حقل :attribute ظهر في بيانات مُسربة. الرجاء اختيار :attribute مختلف.',
+    ],
+    'present'              => 'يجب تقديم حقل :attribute.',
+    'present_if'           => 'يجب أن يكون الحقل :attribute متوفراً عندما :other يساوي :value.',
+    'present_unless'       => 'يجب أن يكون الحقل :attribute متوفراً ما لم يكن :other يساوي :value.',
+    'present_with'         => 'يجب أن يكون الحقل :attribute متوفراً عند توفر :values.',
+    'present_with_all'     => 'يجب أن يكون الحقل :attribute متوفراً عند توفر :values.',
+    'prohibited'           => 'حقل :attribute محظور.',
+    'prohibited_if'        => 'حقل :attribute محظور إذا كان :other هو :value.',
+    'prohibited_unless'    => 'حقل :attribute محظور ما لم يكن :other ضمن :values.',
+    'prohibits'            => 'الحقل :attribute يحظر تواجد الحقل :other.',
+    'regex'                => 'صيغة حقل :attribute غير صحيحة.',
+    'required'             => 'حقل :attribute مطلوب.',
+    'required_array_keys'  => 'الحقل :attribute يجب أن يحتوي على مدخلات لـ: :values.',
+    'required_if'          => 'حقل :attribute مطلوب في حال ما إذا كان :other يساوي :value.',
+    'required_if_accepted' => 'الحقل :attribute مطلوب عند قبول الحقل :other.',
+    'required_unless'      => 'حقل :attribute مطلوب في حال ما لم يكن :other يساوي :values.',
+    'required_with'        => 'حقل :attribute مطلوب إذا توفّر :values.',
+    'required_with_all'    => 'حقل :attribute مطلوب إذا توفّر :values.',
+    'required_without'     => 'حقل :attribute مطلوب إذا لم يتوفّر :values.',
+    'required_without_all' => 'حقل :attribute مطلوب إذا لم يتوفّر :values.',
+    'same'                 => 'يجب أن يتطابق حقل :attribute مع :other.',
+    'size'                 => [
+        'array'   => 'يجب أن يحتوي حقل :attribute على :size عنصرٍ/عناصر بالضبط.',
+        'file'    => 'يجب أن يكون حجم ملف حقل :attribute :size كيلوبايت.',
+        'numeric' => 'يجب أن تكون قيمة حقل :attribute مساوية لـ :size.',
+        'string'  => 'يجب أن يحتوي نص حقل :attribute على :size حروفٍ/حرفًا بالضبط.',
+    ],
+    'starts_with'          => 'يجب أن يبدأ حقل :attribute بأحد القيم التالية: :values',
+    'string'               => 'يجب أن يكون حقل :attribute نصًا.',
+    'timezone'             => 'يجب أن يكون حقل :attribute نطاقًا زمنيًا صحيحًا.',
+    'ulid'                 => 'حقل :attribute يجب أن يكون بصيغة ULID سليمة.',
+    'unique'               => 'قيمة حقل :attribute مُستخدمة من قبل.',
+    'uploaded'             => 'فشل في تحميل الـ :attribute.',
+    'uppercase'            => 'يجب أن يحتوي الحقل :attribute على حروف كبيرة.',
+    'url'                  => 'صيغة رابط حقل :attribute غير صحيحة.',
+    'uuid'                 => 'حقل :attribute يجب أن يكون بصيغة UUID سليمة.',
+    'attributes'           => [
+        'address'                  => 'العنوان',
+        'affiliate_url'            => 'رابط الأفلييت',
+        'age'                      => 'العمر',
+        'amount'                   => 'الكمية',
+        'area'                     => 'المنطقة',
+        'available'                => 'مُتاح',
+        'birthday'                 => 'عيد الميلاد',
+        'body'                     => 'المُحتوى',
+        'city'                     => 'المدينة',
+        'content'                  => 'المُحتوى',
+        'country'                  => 'الدولة',
+        'created_at'               => 'تاريخ الإنشاء',
+        'creator'                  => 'المنشئ',
+        'currency'                 => 'العملة',
+        'current_password'         => 'كلمة المرور الحالية',
+        'customer'                 => 'عميل',
+        'date'                     => 'التاريخ',
+        'date_of_birth'            => 'تاريخ الميلاد',
+        'day'                      => 'اليوم',
+        'deleted_at'               => 'تاريخ الحذف',
+        'description'              => 'الوصف',
+        'district'                 => 'الحي',
+        'duration'                 => 'المدة',
+        'email'                    => 'البريد الالكتروني',
+        'excerpt'                  => 'المُلخص',
+        'filter'                   => 'تصفية',
+        'first_name'               => 'الاسم الأول',
+        'gender'                   => 'النوع',
+        'group'                    => 'مجموعة',
+        'hour'                     => 'ساعة',
+        'image'                    => 'صورة',
+        'is_subscribed'            => 'مشترك',
+        'items'                    => 'العناصر',
+        'last_name'                => 'اسم العائلة',
+        'lesson'                   => 'الدرس',
+        'line_address_1'           => 'العنوان 1',
+        'line_address_2'           => 'العنوان 2',
+        'message'                  => 'الرسالة',
+        'middle_name'              => 'الاسم الأوسط',
+        'minute'                   => 'دقيقة',
+        'mobile'                   => 'الجوال',
+        'month'                    => 'الشهر',
+        'name'                     => 'الاسم',
+        'national_code'            => 'الرمز الدولي',
+        'number'                   => 'الرقم',
+        'password'                 => 'كلمة المرور',
+        'password_confirmation'    => 'تأكيد كلمة المرور',
+        'phone'                    => 'الهاتف',
+        'photo'                    => 'الصورة',
+        'postal_code'              => 'الرمز البريدي',
+        'preview'                  => 'معاينة',
+        'price'                    => 'السعر',
+        'product_id'               => 'معرف المنتج',
+        'product_uid'              => 'معرف المنتج',
+        'product_uuid'             => 'معرف المنتج',
+        'promo_code'               => 'رمز ترويجي',
+        'province'                 => 'المحافظة',
+        'quantity'                 => 'الكمية',
+        'recaptcha_response_field' => 'حقل استجابة recaptcha',
+        'remember'                 => 'تذكير',
+        'restored_at'              => 'تاريخ الاستعادة',
+        'result_text_under_image'  => 'نص النتيجة أسفل الصورة',
+        'role'                     => 'الصلاحية',
+        'second'                   => 'ثانية',
+        'sex'                      => 'الجنس',
+        'shipment'                 => 'الشحنة',
+        'short_text'               => 'نص مختصر',
+        'size'                     => 'الحجم',
+        'state'                    => 'الولاية',
+        'street'                   => 'الشارع',
+        'student'                  => 'طالب',
+        'subject'                  => 'الموضوع',
+        'teacher'                  => 'معلّم',
+        'terms'                    => 'الأحكام',
+        'test_description'         => 'وصف الاختبار',
+        'test_locale'              => 'لغة الاختبار',
+        'test_name'                => 'اسم الاختبار',
+        'text'                     => 'نص',
+        'time'                     => 'الوقت',
+        'title'                    => 'اللقب',
+        'updated_at'               => 'تاريخ التحديث',
+        'user'                     => 'مستخدم',
+        'username'                 => 'اسم المُستخدم',
+        'year'                     => 'السنة',
+    ],
+];

+ 208 - 0
lang/en.json

@@ -0,0 +1,208 @@
+{
+    "(and :count more error)": "(and :count more error)",
+    "(and :count more errors)": "(and :count more errors)",
+    "A Timeout Occurred": "A Timeout Occurred",
+    "Accept": "Accept",
+    "Accepted": "Accepted",
+    "Action": "Action",
+    "Actions": "Actions",
+    "Add": "Add",
+    "Admin": "Admin",
+    "Agree": "Agree",
+    "All rights reserved.": "All rights reserved.",
+    "Already Reported": "Already Reported",
+    "Archive": "Archive",
+    "Assign": "Assign",
+    "Attach": "Attach",
+    "Bad Gateway": "Bad Gateway",
+    "Bad Request": "Bad Request",
+    "Bandwidth Limit Exceeded": "Bandwidth Limit Exceeded",
+    "Browse": "Browse",
+    "Cancel": "Cancel",
+    "Choose": "Choose",
+    "Choose :name": "Choose :name",
+    "Choose File": "Choose File",
+    "Choose Image": "Choose Image",
+    "Click to copy": "Click to copy",
+    "Client Closed Request": "Client Closed Request",
+    "Close": "Close",
+    "Collapse": "Collapse",
+    "Collapse All": "Collapse All",
+    "Comment": "Comment",
+    "Confirm": "Confirm",
+    "Conflict": "Conflict",
+    "Connect": "Connect",
+    "Connection Closed Without Response": "Connection Closed Without Response",
+    "Connection Timed Out": "Connection Timed Out",
+    "Continue": "Continue",
+    "Create": "Create",
+    "Created": "Created",
+    "Delete": "Delete",
+    "Detach": "Detach",
+    "Details": "Details",
+    "Disable": "Disable",
+    "Discard": "Discard",
+    "Done": "Done",
+    "Down": "Down",
+    "Duplicate": "Duplicate",
+    "Duplicate: name": "Duplicate: name",
+    "Edit": "Edit",
+    "Edit :name": "Edit :name",
+    "Enable": "Enable",
+    "Expand": "Expand",
+    "Expand All": "Expand All",
+    "Expectation Failed": "Expectation Failed",
+    "Explanation": "Explanation",
+    "Export": "Export",
+    "Failed Dependency": "Failed Dependency",
+    "File": "File",
+    "Files": "Files",
+    "Forbidden": "Forbidden",
+    "Found": "Found",
+    "Gateway Timeout": "Gateway Timeout",
+    "Go Home": "Go Home",
+    "Go to page :page": "Go to page :page",
+    "Gone": "Gone",
+    "Hello!": "Hello!",
+    "Hide": "Hide",
+    "Hide :name": "Hide :name",
+    "Home": "Home",
+    "HTTP Version Not Supported": "HTTP Version Not Supported",
+    "I'm a teapot": "I'm a teapot",
+    "If you did not create an account, no further action is required.": "If you did not create an account, no further action is required.",
+    "If you did not request a password reset, no further action is required.": "If you did not request a password reset, no further action is required.",
+    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:",
+    "IM Used": "IM Used",
+    "Image": "Image",
+    "Impersonate": "Impersonate",
+    "Impersonation": "Impersonation",
+    "Import": "Import",
+    "Import :name": "Import :name",
+    "Insufficient Storage": "Insufficient Storage",
+    "Internal Server Error": "Internal Server Error",
+    "Introduction": "Introduction",
+    "Invalid JSON was returned from the route.": "Invalid JSON was returned from the route.",
+    "Invalid SSL Certificate": "Invalid SSL Certificate",
+    "Length Required": "Length Required",
+    "Like": "Like",
+    "Load": "Load",
+    "Localize": "Localize",
+    "Locked": "Locked",
+    "Login": "Login",
+    "Logout": "Logout",
+    "Loop Detected": "Loop Detected",
+    "Maintenance Mode": "Maintenance Mode",
+    "Method Not Allowed": "Method Not Allowed",
+    "Misdirected Request": "Misdirected Request",
+    "Moved Permanently": "Moved Permanently",
+    "Multi-Status": "Multi-Status",
+    "Multiple Choices": "Multiple Choices",
+    "Network Authentication Required": "Network Authentication Required",
+    "Network Connect Timeout Error": "Network Connect Timeout Error",
+    "Network Read Timeout Error": "Network Read Timeout Error",
+    "New": "New",
+    "New :name": "New :name",
+    "No": "No",
+    "No Content": "No Content",
+    "Non-Authoritative Information": "Non-Authoritative Information",
+    "Not Acceptable": "Not Acceptable",
+    "Not Extended": "Not Extended",
+    "Not Found": "Not Found",
+    "Not Implemented": "Not Implemented",
+    "Not Modified": "Not Modified",
+    "of": "of",
+    "OK": "OK",
+    "Open": "Open",
+    "Open in a current window": "Open in a current window",
+    "Open in a new window": "Open in a new window",
+    "Open in a parent frame": "Open in a parent frame",
+    "Open in the topmost frame": "Open in the topmost frame",
+    "Open on the website": "Open on the website",
+    "Origin Is Unreachable": "Origin Is Unreachable",
+    "Page Expired": "Page Expired",
+    "Pagination Navigation": "Pagination Navigation",
+    "Partial Content": "Partial Content",
+    "Payload Too Large": "Payload Too Large",
+    "Payment Required": "Payment Required",
+    "Permanent Redirect": "Permanent Redirect",
+    "Please click the button below to verify your email address.": "Please click the button below to verify your email address.",
+    "Precondition Failed": "Precondition Failed",
+    "Precondition Required": "Precondition Required",
+    "Preview": "Preview",
+    "Price": "Price",
+    "Processing": "Processing",
+    "Proxy Authentication Required": "Proxy Authentication Required",
+    "Railgun Error": "Railgun Error",
+    "Range Not Satisfiable": "Range Not Satisfiable",
+    "Regards": "Regards",
+    "Register": "Register",
+    "Request Header Fields Too Large": "Request Header Fields Too Large",
+    "Request Timeout": "Request Timeout",
+    "Reset Content": "Reset Content",
+    "Reset Password": "Reset Password",
+    "Reset Password Notification": "Reset Password Notification",
+    "Restore": "Restore",
+    "Restore :name": "Restore :name",
+    "results": "results",
+    "Retry With": "Retry With",
+    "Save": "Save",
+    "Save & Close": "Save & Close",
+    "Save & Return": "Save & Return",
+    "Save :name": "Save :name",
+    "Search": "Search",
+    "Search :name": "Search :name",
+    "See Other": "See Other",
+    "Select": "Select",
+    "Select All": "Select All",
+    "Send": "Send",
+    "Server Error": "Server Error",
+    "Service Unavailable": "Service Unavailable",
+    "Session Has Expired": "Session Has Expired",
+    "Settings": "Settings",
+    "Show": "Show",
+    "Show :name": "Show :name",
+    "Show All": "Show All",
+    "Showing": "Showing",
+    "Solve": "Solve",
+    "SSL Handshake Failed": "SSL Handshake Failed",
+    "Submit": "Submit",
+    "Subscribe": "Subscribe",
+    "Switch": "Switch",
+    "Switch To Role": "Switch To Role",
+    "Switching Protocols": "Switching Protocols",
+    "Tag": "Tag",
+    "Tags": "Tags",
+    "Temporary Redirect": "Temporary Redirect",
+    "The given data was invalid.": "The given data was invalid.",
+    "The response is not a streamed response.": "The response is not a streamed response.",
+    "The response is not a view.": "The response is not a view.",
+    "This password reset link will expire in :count minutes.": "This password reset link will expire in :count minutes.",
+    "to": "to",
+    "Toggle navigation": "Toggle navigation",
+    "Too Early": "Too Early",
+    "Too Many Requests": "Too Many Requests",
+    "Translate": "Translate",
+    "Translate It": "Translate It",
+    "Unauthorized": "Unauthorized",
+    "Unavailable For Legal Reasons": "Unavailable For Legal Reasons",
+    "Unknown Error": "Unknown Error",
+    "Unpack": "Unpack",
+    "Unprocessable Entity": "Unprocessable Entity",
+    "Unsubscribe": "Unsubscribe",
+    "Unsupported Media Type": "Unsupported Media Type",
+    "Up": "Up",
+    "Update": "Update",
+    "Update :name": "Update :name",
+    "Upgrade Required": "Upgrade Required",
+    "URI Too Long": "URI Too Long",
+    "Use Proxy": "Use Proxy",
+    "User": "User",
+    "Variant Also Negotiates": "Variant Also Negotiates",
+    "Verify Email Address": "Verify Email Address",
+    "View": "View",
+    "View :name": "View :name",
+    "Web Server is Down": "Web Server is Down",
+    "Whoops!": "Whoops!",
+    "Yes": "Yes",
+    "You are receiving this email because we received a password reset request for your account.": "You are receiving this email because we received a password reset request for your account."
+}

+ 108 - 0
lang/en/actions.php

@@ -0,0 +1,108 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'accept'          => 'Accept',
+    'action'          => 'Action',
+    'actions'         => 'Actions',
+    'add'             => 'Add',
+    'admin'           => 'Admin',
+    'agree'           => 'Agree',
+    'archive'         => 'Archive',
+    'assign'          => 'Assign',
+    'attach'          => 'Attach',
+    'browse'          => 'Browse',
+    'cancel'          => 'Cancel',
+    'choose'          => 'Choose',
+    'choose_file'     => 'Choose File',
+    'choose_image'    => 'Choose Image',
+    'click_to_copy'   => 'Click to copy',
+    'close'           => 'Close',
+    'collapse'        => 'Collapse',
+    'collapse_all'    => 'Collapse All',
+    'comment'         => 'Comment',
+    'confirm'         => 'Confirm',
+    'connect'         => 'Connect',
+    'create'          => 'Create',
+    'delete'          => 'Delete',
+    'detach'          => 'Detach',
+    'details'         => 'Details',
+    'disable'         => 'Disable',
+    'discard'         => 'Discard',
+    'done'            => 'Done',
+    'down'            => 'Down',
+    'duplicate'       => 'Duplicate',
+    'edit'            => 'Edit',
+    'enable'          => 'Enable',
+    'expand'          => 'Expand',
+    'expand_all'      => 'Expand All',
+    'explanation'     => 'Explanation',
+    'export'          => 'Export',
+    'file'            => 'The :attribute must be a file.',
+    'files'           => 'Files',
+    'go_home'         => 'Go Home',
+    'hide'            => 'Hide',
+    'home'            => 'Home',
+    'image'           => 'The :attribute must be an image.',
+    'Impersonate'     => 'Impersonate',
+    'Impersonation'   => 'Impersonation',
+    'import'          => 'Import',
+    'introduction'    => 'Introduction',
+    'like'            => 'Like',
+    'load'            => 'Load',
+    'localize'        => 'Localize',
+    'named'           => [
+        'choose'    => 'Choose :name',
+        'duplicate' => 'Duplicate: name',
+        'edit'      => 'Edit :name',
+        'hide'      => 'Hide :name',
+        'import'    => 'Import :name',
+        'new'       => 'New :name',
+        'restore'   => 'Restore :name',
+        'save'      => 'Save :name',
+        'search'    => 'Search :name',
+        'show'      => 'Show :name',
+        'update'    => 'Update :name',
+        'view'      => 'View :name',
+    ],
+    'new'             => 'New',
+    'no'              => 'No',
+    'open'            => 'Open',
+    'open_website'    => 'Open on the website',
+    'preview'         => 'Preview',
+    'price'           => 'Price',
+    'restore'         => 'Restore',
+    'save'            => 'Save',
+    'save_and_close'  => 'Save & Close',
+    'save_and_return' => 'Save & Return',
+    'search'          => 'Search',
+    'select'          => 'Select',
+    'select_all'      => 'Select All',
+    'send'            => 'Send',
+    'settings'        => 'Settings',
+    'show'            => 'Show',
+    'show_all'        => 'Show All',
+    'solve'           => 'Solve',
+    'submit'          => 'Submit',
+    'subscribe'       => 'Subscribe',
+    'switch'          => 'Switch',
+    'switch_to_role'  => 'Switch To Role',
+    'tag'             => 'Tag',
+    'tags'            => 'Tags',
+    'target_link'     => [
+        'blank'  => 'Open in a new window',
+        'parent' => 'Open in a parent frame',
+        'self'   => 'Open in a current window',
+        'top'    => 'Open in the topmost frame',
+    ],
+    'translate'       => 'Translate',
+    'translate_it'    => 'Translate It',
+    'unpack'          => 'Unpack',
+    'unsubscribe'     => 'Unsubscribe',
+    'up'              => 'Up',
+    'update'          => 'Update',
+    'user'            => 'We can\'t find a user with that email address.',
+    'view'            => 'View',
+    'yes'             => 'Yes',
+];

+ 9 - 0
lang/en/auth.php

@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'failed'   => 'These credentials do not match our records.',
+    'password' => 'The password is incorrect.',
+    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
+];

+ 84 - 0
lang/en/http-statuses.php

@@ -0,0 +1,84 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    '0'            => 'Unknown Error',
+    '100'          => 'Continue',
+    '101'          => 'Switching Protocols',
+    '102'          => 'Processing',
+    '200'          => 'OK',
+    '201'          => 'Created',
+    '202'          => 'Accepted',
+    '203'          => 'Non-Authoritative Information',
+    '204'          => 'No Content',
+    '205'          => 'Reset Content',
+    '206'          => 'Partial Content',
+    '207'          => 'Multi-Status',
+    '208'          => 'Already Reported',
+    '226'          => 'IM Used',
+    '300'          => 'Multiple Choices',
+    '301'          => 'Moved Permanently',
+    '302'          => 'Found',
+    '303'          => 'See Other',
+    '304'          => 'Not Modified',
+    '305'          => 'Use Proxy',
+    '307'          => 'Temporary Redirect',
+    '308'          => 'Permanent Redirect',
+    '400'          => 'Bad Request',
+    '401'          => 'Unauthorized',
+    '402'          => 'Payment Required',
+    '403'          => 'Forbidden',
+    '404'          => 'Not Found',
+    '405'          => 'Method Not Allowed',
+    '406'          => 'Not Acceptable',
+    '407'          => 'Proxy Authentication Required',
+    '408'          => 'Request Timeout',
+    '409'          => 'Conflict',
+    '410'          => 'Gone',
+    '411'          => 'Length Required',
+    '412'          => 'Precondition Failed',
+    '413'          => 'Payload Too Large',
+    '414'          => 'URI Too Long',
+    '415'          => 'Unsupported Media Type',
+    '416'          => 'Range Not Satisfiable',
+    '417'          => 'Expectation Failed',
+    '418'          => 'I\'m a teapot',
+    '419'          => 'Session Has Expired',
+    '421'          => 'Misdirected Request',
+    '422'          => 'Unprocessable Entity',
+    '423'          => 'Locked',
+    '424'          => 'Failed Dependency',
+    '425'          => 'Too Early',
+    '426'          => 'Upgrade Required',
+    '428'          => 'Precondition Required',
+    '429'          => 'Too Many Requests',
+    '431'          => 'Request Header Fields Too Large',
+    '444'          => 'Connection Closed Without Response',
+    '449'          => 'Retry With',
+    '451'          => 'Unavailable For Legal Reasons',
+    '499'          => 'Client Closed Request',
+    '500'          => 'Internal Server Error',
+    '501'          => 'Not Implemented',
+    '502'          => 'Bad Gateway',
+    '503'          => 'Maintenance Mode',
+    '504'          => 'Gateway Timeout',
+    '505'          => 'HTTP Version Not Supported',
+    '506'          => 'Variant Also Negotiates',
+    '507'          => 'Insufficient Storage',
+    '508'          => 'Loop Detected',
+    '509'          => 'Bandwidth Limit Exceeded',
+    '510'          => 'Not Extended',
+    '511'          => 'Network Authentication Required',
+    '520'          => 'Unknown Error',
+    '521'          => 'Web Server is Down',
+    '522'          => 'Connection Timed Out',
+    '523'          => 'Origin Is Unreachable',
+    '524'          => 'A Timeout Occurred',
+    '525'          => 'SSL Handshake Failed',
+    '526'          => 'Invalid SSL Certificate',
+    '527'          => 'Railgun Error',
+    '598'          => 'Network Read Timeout Error',
+    '599'          => 'Network Connect Timeout Error',
+    'unknownError' => 'Unknown Error',
+];

+ 8 - 0
lang/en/pagination.php

@@ -0,0 +1,8 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'next'     => 'Next &raquo;',
+    'previous' => '&laquo; Previous',
+];

+ 11 - 0
lang/en/passwords.php

@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'reset'     => '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.',
+];

+ 236 - 0
lang/en/validation.php

@@ -0,0 +1,236 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'accepted'             => 'The :attribute must be accepted.',
+    'accepted_if'          => 'The :attribute must be accepted when :other is :value.',
+    '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 must only contain letters.',
+    'alpha_dash'           => 'The :attribute must only contain letters, numbers, dashes and underscores.',
+    'alpha_num'            => 'The :attribute must only contain letters and numbers.',
+    'array'                => 'The :attribute must be an array.',
+    'ascii'                => 'The :attribute field must only contain single-byte alphanumeric characters and symbols.',
+    'before'               => 'The :attribute must be a date before :date.',
+    'before_or_equal'      => 'The :attribute must be a date before or equal to :date.',
+    'between'              => [
+        'array'   => 'The :attribute must have between :min and :max items.',
+        'file'    => 'The :attribute must be between :min and :max kilobytes.',
+        'numeric' => 'The :attribute must be between :min and :max.',
+        'string'  => 'The :attribute must be between :min and :max characters.',
+    ],
+    'boolean'              => 'The :attribute field must be true or false.',
+    'can'                  => 'The :attribute field contains an unauthorized value.',
+    'confirmed'            => 'The :attribute confirmation does not match.',
+    'current_password'     => 'The password is incorrect.',
+    '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.',
+    'decimal'              => 'The :attribute field must have :decimal decimal places.',
+    'declined'             => 'The :attribute must be declined.',
+    'declined_if'          => 'The :attribute must be declined when :other is :value.',
+    '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.',
+    'doesnt_end_with'      => 'The :attribute field must not end with one of the following: :values.',
+    'doesnt_start_with'    => 'The :attribute field must not start with one of the following: :values.',
+    'email'                => 'The :attribute must be a valid email address.',
+    'ends_with'            => 'The :attribute must end with one of the following: :values.',
+    'enum'                 => 'The selected :attribute is invalid.',
+    'exists'               => 'The selected :attribute is invalid.',
+    'extensions'           => 'The :attribute field must have one of the following extensions: :values.',
+    'file'                 => 'The :attribute must be a file.',
+    'filled'               => 'The :attribute field must have a value.',
+    'gt'                   => [
+        'array'   => 'The :attribute must have more than :value items.',
+        'file'    => 'The :attribute must be greater than :value kilobytes.',
+        'numeric' => 'The :attribute must be greater than :value.',
+        'string'  => 'The :attribute must be greater than :value characters.',
+    ],
+    'gte'                  => [
+        'array'   => 'The :attribute must have :value items or more.',
+        'file'    => 'The :attribute must be greater than or equal to :value kilobytes.',
+        'numeric' => 'The :attribute must be greater than or equal to :value.',
+        'string'  => 'The :attribute must be greater than or equal to :value characters.',
+    ],
+    'hex_color'            => 'The :attribute field must be a valid hexadecimal color.',
+    '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.',
+    'lowercase'            => 'The :attribute field must be lowercase.',
+    'lt'                   => [
+        'array'   => 'The :attribute must have less than :value items.',
+        'file'    => 'The :attribute must be less than :value kilobytes.',
+        'numeric' => 'The :attribute must be less than :value.',
+        'string'  => 'The :attribute must be less than :value characters.',
+    ],
+    'lte'                  => [
+        'array'   => 'The :attribute must not have more than :value items.',
+        'file'    => 'The :attribute must be less than or equal to :value kilobytes.',
+        'numeric' => 'The :attribute must be less than or equal to :value.',
+        'string'  => 'The :attribute must be less than or equal to :value characters.',
+    ],
+    'mac_address'          => 'The :attribute must be a valid MAC address.',
+    'max'                  => [
+        'array'   => 'The :attribute must not have more than :max items.',
+        'file'    => 'The :attribute must not be greater than :max kilobytes.',
+        'numeric' => 'The :attribute must not be greater than :max.',
+        'string'  => 'The :attribute must not be greater than :max characters.',
+    ],
+    'max_digits'           => 'The :attribute field must not have more than :max digits.',
+    'mimes'                => 'The :attribute must be a file of type: :values.',
+    'mimetypes'            => 'The :attribute must be a file of type: :values.',
+    'min'                  => [
+        'array'   => 'The :attribute must have at least :min items.',
+        'file'    => 'The :attribute must be at least :min kilobytes.',
+        'numeric' => 'The :attribute must be at least :min.',
+        'string'  => 'The :attribute must be at least :min characters.',
+    ],
+    'min_digits'           => 'The :attribute field must have at least :min digits.',
+    'missing'              => 'The :attribute field must be missing.',
+    'missing_if'           => 'The :attribute field must be missing when :other is :value.',
+    'missing_unless'       => 'The :attribute field must be missing unless :other is :value.',
+    'missing_with'         => 'The :attribute field must be missing when :values is present.',
+    'missing_with_all'     => 'The :attribute field must be missing when :values are present.',
+    'multiple_of'          => 'The :attribute must be a multiple of :value.',
+    'not_in'               => 'The selected :attribute is invalid.',
+    'not_regex'            => 'The :attribute format is invalid.',
+    'numeric'              => 'The :attribute must be a number.',
+    'password'             => [
+        'letters'       => 'The :attribute field must contain at least one letter.',
+        'mixed'         => 'The :attribute field must contain at least one uppercase and one lowercase letter.',
+        'numbers'       => 'The :attribute field must contain at least one number.',
+        'symbols'       => 'The :attribute field must contain at least one symbol.',
+        'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.',
+    ],
+    'present'              => 'The :attribute field must be present.',
+    'present_if'           => 'The :attribute field must be present when :other is :value.',
+    'present_unless'       => 'The :attribute field must be present unless :other is :value.',
+    'present_with'         => 'The :attribute field must be present when :values is present.',
+    'present_with_all'     => 'The :attribute field must be present when :values are present.',
+    'prohibited'           => 'The :attribute field is prohibited.',
+    'prohibited_if'        => 'The :attribute field is prohibited when :other is :value.',
+    'prohibited_unless'    => 'The :attribute field is prohibited unless :other is in :values.',
+    'prohibits'            => 'The :attribute field prohibits :other from being present.',
+    'regex'                => 'The :attribute format is invalid.',
+    'required'             => 'The :attribute field is required.',
+    'required_array_keys'  => 'The :attribute field must contain entries for: :values.',
+    'required_if'          => 'The :attribute field is required when :other is :value.',
+    'required_if_accepted' => 'The :attribute field is required when :other is accepted.',
+    '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'                 => [
+        'array'   => 'The :attribute must contain :size items.',
+        'file'    => 'The :attribute must be :size kilobytes.',
+        'numeric' => 'The :attribute must be :size.',
+        'string'  => 'The :attribute must be :size characters.',
+    ],
+    '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 timezone.',
+    'ulid'                 => 'The :attribute field must be a valid ULID.',
+    'unique'               => 'The :attribute has already been taken.',
+    'uploaded'             => 'The :attribute failed to upload.',
+    'uppercase'            => 'The :attribute field must be uppercase.',
+    'url'                  => 'The :attribute must be a valid URL.',
+    'uuid'                 => 'The :attribute must be a valid UUID.',
+    'attributes'           => [
+        'address'                  => 'address',
+        'affiliate_url'            => 'affiliate URL',
+        'age'                      => 'age',
+        'amount'                   => 'amount',
+        'area'                     => 'area',
+        'available'                => 'available',
+        'birthday'                 => 'birthday',
+        'body'                     => 'body',
+        'city'                     => 'city',
+        'content'                  => 'content',
+        'country'                  => 'country',
+        'created_at'               => 'created at',
+        'creator'                  => 'creator',
+        'currency'                 => 'currency',
+        'current_password'         => 'current password',
+        'customer'                 => 'customer',
+        'date'                     => 'date',
+        'date_of_birth'            => 'date of birth',
+        'day'                      => 'day',
+        'deleted_at'               => 'deleted at',
+        'description'              => 'description',
+        'district'                 => 'district',
+        'duration'                 => 'duration',
+        'email'                    => 'email',
+        'excerpt'                  => 'excerpt',
+        'filter'                   => 'filter',
+        'first_name'               => 'first name',
+        'gender'                   => 'gender',
+        'group'                    => 'group',
+        'hour'                     => 'hour',
+        'image'                    => 'image',
+        'is_subscribed'            => 'is subscribed',
+        'items'                    => 'items',
+        'last_name'                => 'last name',
+        'lesson'                   => 'lesson',
+        'line_address_1'           => 'line address 1',
+        'line_address_2'           => 'line address 2',
+        'message'                  => 'message',
+        'middle_name'              => 'middle name',
+        'minute'                   => 'minute',
+        'mobile'                   => 'mobile',
+        'month'                    => 'month',
+        'name'                     => 'name',
+        'national_code'            => 'national code',
+        'number'                   => 'number',
+        'password'                 => 'password',
+        'password_confirmation'    => 'password confirmation',
+        'phone'                    => 'phone',
+        'photo'                    => 'photo',
+        'postal_code'              => 'postal code',
+        'preview'                  => 'preview',
+        'price'                    => 'price',
+        'product_id'               => 'product ID',
+        'product_uid'              => 'product UID',
+        'product_uuid'             => 'product UUID',
+        'promo_code'               => 'promo code',
+        'province'                 => 'province',
+        'quantity'                 => 'quantity',
+        'recaptcha_response_field' => 'recaptcha response field',
+        'remember'                 => 'remember',
+        'restored_at'              => 'restored at',
+        'result_text_under_image'  => 'result text under image',
+        'role'                     => 'role',
+        'second'                   => 'second',
+        'sex'                      => 'sex',
+        'shipment'                 => 'shipment',
+        'short_text'               => 'short text',
+        'size'                     => 'size',
+        'state'                    => 'state',
+        'street'                   => 'street',
+        'student'                  => 'student',
+        'subject'                  => 'subject',
+        'teacher'                  => 'teacher',
+        'terms'                    => 'terms',
+        'test_description'         => 'test description',
+        'test_locale'              => 'test locale',
+        'test_name'                => 'test name',
+        'text'                     => 'text',
+        'time'                     => 'time',
+        'title'                    => 'title',
+        'updated_at'               => 'updated at',
+        'user'                     => 'user',
+        'username'                 => 'username',
+        'year'                     => 'year',
+    ],
+];

+ 208 - 0
lang/zh_CN.json

@@ -0,0 +1,208 @@
+{
+    "(and :count more error)": "(还有 :count 个错误)",
+    "(and :count more errors)": "(以及另外 :count 个错误)",
+    "A Timeout Occurred": "发生超时",
+    "Accept": "接受",
+    "Accepted": "已接受",
+    "Action": "操作",
+    "Actions": "操作",
+    "Add": "添加",
+    "Admin": "行政",
+    "Agree": "同意",
+    "All rights reserved.": "版权所有。",
+    "Already Reported": "已上报",
+    "Archive": "档案",
+    "Assign": "分配",
+    "Attach": "附加",
+    "Bad Gateway": "网关错误",
+    "Bad Request": "请求错误",
+    "Bandwidth Limit Exceeded": "超出带宽限制",
+    "Browse": "浏览",
+    "Cancel": "取消",
+    "Choose": "选择",
+    "Choose :name": "选择:name",
+    "Choose File": "选择文件",
+    "Choose Image": "选择图片",
+    "Click to copy": "点击复制",
+    "Client Closed Request": "客户端关闭请求",
+    "Close": "关闭",
+    "Collapse": "坍塌",
+    "Collapse All": "全部收缩",
+    "Comment": "评论",
+    "Confirm": "确认",
+    "Conflict": "冲突",
+    "Connect": "连接",
+    "Connection Closed Without Response": "连接关闭无响应",
+    "Connection Timed Out": "连接超时",
+    "Continue": "继续请求",
+    "Create": "创建",
+    "Created": "已创建",
+    "Delete": "删除",
+    "Detach": "分离",
+    "Details": "详情",
+    "Disable": "禁用",
+    "Discard": "丢弃",
+    "Done": "完毕",
+    "Down": "向下",
+    "Duplicate": "复制",
+    "Duplicate: name": "重复:名称",
+    "Edit": "编辑",
+    "Edit :name": "编辑:name",
+    "Enable": "启用",
+    "Expand": "扩张",
+    "Expand All": "展开全部",
+    "Expectation Failed": "期望不满足",
+    "Explanation": "解释",
+    "Export": "出口",
+    "Failed Dependency": "失败的依赖",
+    "File": "文件",
+    "Files": "文件",
+    "Forbidden": "访问被拒绝",
+    "Found": "临时移动",
+    "Gateway Timeout": "网关超时",
+    "Go Home": "回首页",
+    "Go to page :page": "前往第 :page 页",
+    "Gone": "不可用",
+    "Hello!": "您好!",
+    "Hide": "隐藏",
+    "Hide :name": "隐藏 :name",
+    "Home": "家",
+    "HTTP Version Not Supported": "HTTP版本不支持",
+    "I'm a teapot": "我是一个茶壶",
+    "If you did not create an account, no further action is required.": "如果您未注册帐号,请忽略此邮件。",
+    "If you did not request a password reset, no further action is required.": "如果您未申请重设密码,请忽略此邮件。",
+    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "如果您单击「:actionText」按钮时遇到问题,请复制下方链接到浏览器中访问:",
+    "IM Used": "IM已使用",
+    "Image": "图像",
+    "Impersonate": "模拟登录",
+    "Impersonation": "冒充",
+    "Import": "进口",
+    "Import :name": "进口 :name",
+    "Insufficient Storage": "存储空间不足",
+    "Internal Server Error": "内部服务器错误",
+    "Introduction": "介绍",
+    "Invalid JSON was returned from the route.": "从路由返回无效的 JSON。",
+    "Invalid SSL Certificate": "无效的SSL证书",
+    "Length Required": "长度要求",
+    "Like": "喜欢",
+    "Load": "加载",
+    "Localize": "本地化",
+    "Locked": "锁定",
+    "Login": "登录",
+    "Logout": "登出",
+    "Loop Detected": "检测到环路",
+    "Maintenance Mode": "服务不可用",
+    "Method Not Allowed": "方法不允许",
+    "Misdirected Request": "错误的请求",
+    "Moved Permanently": "已永久移动",
+    "Multi-Status": "多状态",
+    "Multiple Choices": "多种选择",
+    "Network Authentication Required": "需要网络验证",
+    "Network Connect Timeout Error": "网络连接超时",
+    "Network Read Timeout Error": "网络读取超时",
+    "New": "新建",
+    "New :name": "新 :name",
+    "No": "不",
+    "No Content": "无内容",
+    "Non-Authoritative Information": "非权威信息",
+    "Not Acceptable": "无法接受",
+    "Not Extended": "未延期",
+    "Not Found": "页面不存在",
+    "Not Implemented": "未实现",
+    "Not Modified": "未修改",
+    "of": "于",
+    "OK": "请求成功",
+    "Open": "打开",
+    "Open in a current window": "在当前窗口中打开",
+    "Open in a new window": "在新窗口中打开",
+    "Open in a parent frame": "在父框架中打开",
+    "Open in the topmost frame": "在最上面的框架中打开",
+    "Open on the website": "在网站上打开",
+    "Origin Is Unreachable": "原点无法到达",
+    "Page Expired": "页面会话已超时",
+    "Pagination Navigation": "分页导航",
+    "Partial Content": "部分内容",
+    "Payload Too Large": "请求实体过大",
+    "Payment Required": "需要付款",
+    "Permanent Redirect": "永久重定向",
+    "Please click the button below to verify your email address.": "请点击下面按钮验证您的 E-mail:",
+    "Precondition Failed": "前提条件未满足",
+    "Precondition Required": "前提要求",
+    "Preview": "预览",
+    "Price": "价格",
+    "Processing": "处理中",
+    "Proxy Authentication Required": "需要代理验证",
+    "Railgun Error": "轨道炮错误",
+    "Range Not Satisfiable": "请求范围不符合",
+    "Regards": "致敬",
+    "Register": "注册",
+    "Request Header Fields Too Large": "请求标头字段太大",
+    "Request Timeout": "请求超时",
+    "Reset Content": "重置内容",
+    "Reset Password": "重置密码",
+    "Reset Password Notification": "重置密码通知",
+    "Restore": "恢复",
+    "Restore :name": "恢复:name",
+    "results": "结果",
+    "Retry With": "重试",
+    "Save": "保存",
+    "Save & Close": "保存并关闭",
+    "Save & Return": "保存并返回",
+    "Save :name": "节省 :name",
+    "Search": "搜索",
+    "Search :name": "搜索 :name",
+    "See Other": "见其他",
+    "Select": "选择",
+    "Select All": "全选",
+    "Send": "发送",
+    "Server Error": "服务器错误",
+    "Service Unavailable": "服务不可用",
+    "Session Has Expired": "会话已过期",
+    "Settings": "设置",
+    "Show": "展示",
+    "Show :name": "显示 :name",
+    "Show All": "显示所有",
+    "Showing": "显示中",
+    "Solve": "解决",
+    "SSL Handshake Failed": "SSL握手失败",
+    "Submit": "提交",
+    "Subscribe": "订阅",
+    "Switch": "转变",
+    "Switch To Role": "切换角色",
+    "Switching Protocols": "切换协议",
+    "Tag": "标签",
+    "Tags": "标签",
+    "Temporary Redirect": "临时重定向",
+    "The given data was invalid.": "给定的数据无效。",
+    "The response is not a streamed response.": "该响应不是流式响应。",
+    "The response is not a view.": "响应不是视图。",
+    "This password reset link will expire in :count minutes.": "这个重设密码链接将会在 :count 分钟后失效。",
+    "to": "至",
+    "Toggle navigation": "切换导航",
+    "Too Early": "太早了",
+    "Too Many Requests": "请求次数过多。",
+    "Translate": "翻译",
+    "Translate It": "翻译它",
+    "Unauthorized": "未授权",
+    "Unavailable For Legal Reasons": "法律原因不可用",
+    "Unknown Error": "未知错误",
+    "Unpack": "打开包装",
+    "Unprocessable Entity": "不可处理的实体",
+    "Unsubscribe": "退订",
+    "Unsupported Media Type": "不支持的媒体类型",
+    "Up": "向上",
+    "Update": "更新",
+    "Update :name": "更新:name",
+    "Upgrade Required": "需要升级",
+    "URI Too Long": "URI太长了",
+    "Use Proxy": "使用代理",
+    "User": "用户",
+    "Variant Also Negotiates": "变体协商",
+    "Verify Email Address": "验证 E-mail",
+    "View": "查看",
+    "View :name": "查看 :name",
+    "Web Server is Down": "Web服务器已关闭",
+    "Whoops!": "哎呀!",
+    "Yes": "是的",
+    "You are receiving this email because we received a password reset request for your account.": "您收到此电子邮件是因为我们收到了您帐户的密码重设请求。"
+}

+ 108 - 0
lang/zh_CN/actions.php

@@ -0,0 +1,108 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'accept'          => '接受',
+    'action'          => '行动',
+    'actions'         => '行动',
+    'add'             => '添加',
+    'admin'           => '行政',
+    'agree'           => '同意',
+    'archive'         => '档案',
+    'assign'          => '分配',
+    'attach'          => '附',
+    'browse'          => '浏览',
+    'cancel'          => '取消',
+    'choose'          => '选择',
+    'choose_file'     => '选择文件',
+    'choose_image'    => '选择图片',
+    'click_to_copy'   => '点击复制',
+    'close'           => '关闭',
+    'collapse'        => '坍塌',
+    'collapse_all'    => '全部收缩',
+    'comment'         => '评论',
+    'confirm'         => '确认',
+    'connect'         => '连接',
+    'create'          => '创造',
+    'delete'          => '删除',
+    'detach'          => '分离',
+    'details'         => '细节',
+    'disable'         => '禁用',
+    'discard'         => '丢弃',
+    'done'            => '完毕',
+    'down'            => '向下',
+    'duplicate'       => '复制',
+    'edit'            => '编辑',
+    'enable'          => '使能够',
+    'expand'          => '扩张',
+    'expand_all'      => '展开全部',
+    'explanation'     => '解释',
+    'export'          => '出口',
+    'file'            => ':Attribute 必须是文件。',
+    'files'           => '文件',
+    'go_home'         => '回家',
+    'hide'            => '隐藏',
+    'home'            => '家',
+    'image'           => ':Attribute 必须是图片。',
+    'Impersonate'     => '模拟登录',
+    'Impersonation'   => '冒充',
+    'import'          => '进口',
+    'introduction'    => '介绍',
+    'like'            => '喜欢',
+    'load'            => '加载',
+    'localize'        => '本地化',
+    'named'           => [
+        'choose'    => '选择:name',
+        'duplicate' => '重复:名称',
+        'edit'      => '编辑:name',
+        'hide'      => '隐藏 :name',
+        'import'    => '进口 :name',
+        'new'       => '新 :name',
+        'restore'   => '恢复:name',
+        'save'      => '节省 :name',
+        'search'    => '搜索 :name',
+        'show'      => '显示 :name',
+        'update'    => '更新:name',
+        'view'      => '查看 :name',
+    ],
+    'new'             => '新的',
+    'no'              => '不',
+    'open'            => '打开',
+    'open_website'    => '在网站上打开',
+    'preview'         => '预览',
+    'price'           => '价格',
+    'restore'         => '恢复',
+    'save'            => '节省',
+    'save_and_close'  => '保存并关闭',
+    'save_and_return' => '保存并返回',
+    'search'          => '搜索',
+    'select'          => '选择',
+    'select_all'      => '全选',
+    'send'            => '发送',
+    'settings'        => '设置',
+    'show'            => '展示',
+    'show_all'        => '显示所有',
+    'solve'           => '解决',
+    'submit'          => '提交',
+    'subscribe'       => '订阅',
+    'switch'          => '转变',
+    'switch_to_role'  => '切换角色',
+    'tag'             => '标签',
+    'tags'            => '标签',
+    'target_link'     => [
+        'blank'  => '在新窗口中打开',
+        'parent' => '在父框架中打开',
+        'self'   => '在当前窗口中打开',
+        'top'    => '在最上面的框架中打开',
+    ],
+    'translate'       => '翻译',
+    'translate_it'    => '翻译它',
+    'unpack'          => '打开包装',
+    'unsubscribe'     => '退订',
+    'up'              => '向上',
+    'update'          => '更新',
+    'user'            => '找不到该邮箱对应的用户。',
+    'view'            => '看法',
+    'yes'             => '是的',
+];

+ 9 - 0
lang/zh_CN/auth.php

@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'failed'   => '用户名或密码错误。',
+    'password' => '密码错误',
+    'throttle' => '您尝试的登录次数过多,请 :seconds 秒后再试。',
+];

+ 84 - 0
lang/zh_CN/http-statuses.php

@@ -0,0 +1,84 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    '0'            => '未知错误',
+    '100'          => '继续请求',
+    '101'          => '切换协议',
+    '102'          => '处理中',
+    '200'          => '请求成功',
+    '201'          => '已创建',
+    '202'          => '已接受',
+    '203'          => '非权威信息',
+    '204'          => '无内容',
+    '205'          => '重置内容',
+    '206'          => '部分内容',
+    '207'          => '多状态',
+    '208'          => '已上报',
+    '226'          => 'IM已使用',
+    '300'          => '多种选择',
+    '301'          => '已永久移动',
+    '302'          => '临时移动',
+    '303'          => '见其他',
+    '304'          => '未修改',
+    '305'          => '使用代理',
+    '307'          => '临时重定向',
+    '308'          => '永久重定向',
+    '400'          => '请求错误',
+    '401'          => '未授权',
+    '402'          => '需要付款',
+    '403'          => '禁止',
+    '404'          => '未找到',
+    '405'          => '方法不允许',
+    '406'          => '无法接受',
+    '407'          => '需要代理验证',
+    '408'          => '请求超时',
+    '409'          => '冲突',
+    '410'          => '不可用',
+    '411'          => '长度要求',
+    '412'          => '前提条件未满足',
+    '413'          => '请求实体过大',
+    '414'          => 'URI太长了',
+    '415'          => '不支持的媒体类型',
+    '416'          => '请求范围不符合',
+    '417'          => '期望不满足',
+    '418'          => '我是一个茶壶',
+    '419'          => '会话已过期',
+    '421'          => '错误的请求',
+    '422'          => '不可处理的实体',
+    '423'          => '锁定',
+    '424'          => '失败的依赖',
+    '425'          => '太早了',
+    '426'          => '需要升级',
+    '428'          => '前提要求',
+    '429'          => '请求太多',
+    '431'          => '请求标头字段太大',
+    '444'          => '连接关闭无响应',
+    '449'          => '重试',
+    '451'          => '法律原因不可用',
+    '499'          => '客户端关闭请求',
+    '500'          => '内部服务器错误',
+    '501'          => '未实现',
+    '502'          => '网关错误',
+    '503'          => '服务不可用',
+    '504'          => '网关超时',
+    '505'          => 'HTTP版本不支持',
+    '506'          => '变体协商',
+    '507'          => '存储空间不足',
+    '508'          => '检测到环路',
+    '509'          => '超出带宽限制',
+    '510'          => '未延期',
+    '511'          => '需要网络验证',
+    '520'          => '未知错误',
+    '521'          => 'Web服务器已关闭',
+    '522'          => '连接超时',
+    '523'          => '原点无法到达',
+    '524'          => '发生超时',
+    '525'          => 'SSL握手失败',
+    '526'          => '无效的SSL证书',
+    '527'          => '轨道炮错误',
+    '598'          => '网络读取超时',
+    '599'          => '网络连接超时',
+    'unknownError' => '未知错误',
+];

+ 8 - 0
lang/zh_CN/pagination.php

@@ -0,0 +1,8 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'next'     => '下一页 &raquo;',
+    'previous' => '&laquo; 上一页',
+];

+ 11 - 0
lang/zh_CN/passwords.php

@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'reset'     => '密码重置成功!',
+    'sent'      => '密码重置邮件已发送!',
+    'throttled' => '请稍候再试。',
+    'token'     => '密码重置令牌无效。',
+    'user'      => '找不到该邮箱对应的用户。',
+];

+ 236 - 0
lang/zh_CN/validation.php

@@ -0,0 +1,236 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'accepted'             => '您必须接受 :attribute。',
+    'accepted_if'          => '当 :other 为 :value 时,必须接受 :attribute。',
+    'active_url'           => ':Attribute 不是一个有效的网址。',
+    'after'                => ':Attribute 必须要晚于 :date。',
+    'after_or_equal'       => ':Attribute 必须要等于 :date 或更晚。',
+    'alpha'                => ':Attribute 只能由字母组成。',
+    'alpha_dash'           => ':Attribute 只能由字母、数字、短划线(-)和下划线(_)组成。',
+    'alpha_num'            => ':Attribute 只能由字母和数字组成。',
+    'array'                => ':Attribute 必须是一个数组。',
+    'ascii'                => ':Attribute 必须仅包含单字节字母数字字符和符号。',
+    'before'               => ':Attribute 必须要早于 :date。',
+    'before_or_equal'      => ':Attribute 必须要等于 :date 或更早。',
+    'between'              => [
+        'array'   => ':Attribute 必须只有 :min - :max 个单元。',
+        'file'    => ':Attribute 必须介于 :min - :max KB 之间。',
+        'numeric' => ':Attribute 必须介于 :min - :max 之间。',
+        'string'  => ':Attribute 必须介于 :min - :max 个字符之间。',
+    ],
+    'boolean'              => ':Attribute 必须为布尔值。',
+    'can'                  => ':Attribute 字段包含未经授权的值。',
+    'confirmed'            => ':Attribute 两次输入不一致。',
+    'current_password'     => '密码错误。',
+    'date'                 => ':Attribute 不是一个有效的日期。',
+    'date_equals'          => ':Attribute 必须要等于 :date。',
+    'date_format'          => ':Attribute 的格式必须为 :format。',
+    'decimal'              => ':Attribute 必须有 :decimal 位小数。',
+    'declined'             => ':Attribute 必须是拒绝的。',
+    'declined_if'          => '当 :other 为 :value 时字段 :attribute 必须是拒绝的。',
+    'different'            => ':Attribute 和 :other 必须不同。',
+    'digits'               => ':Attribute 必须是 :digits 位数字。',
+    'digits_between'       => ':Attribute 必须是介于 :min 和 :max 位的数字。',
+    'dimensions'           => ':Attribute 图片尺寸不正确。',
+    'distinct'             => ':Attribute 已经存在。',
+    'doesnt_end_with'      => ':Attribute 不能以以下之一结尾: :values。',
+    'doesnt_start_with'    => ':Attribute 不能以下列之一开头: :values。',
+    'email'                => ':Attribute 不是一个合法的邮箱。',
+    'ends_with'            => ':Attribute 必须以 :values 为结尾。',
+    'enum'                 => ':Attribute 值不正确。',
+    'exists'               => ':Attribute 不存在。',
+    'extensions'           => ':attribute 字段必须具有以下扩展名之一::values。',
+    'file'                 => ':Attribute 必须是文件。',
+    'filled'               => ':Attribute 不能为空。',
+    'gt'                   => [
+        'array'   => ':Attribute 必须多于 :value 个元素。',
+        'file'    => ':Attribute 必须大于 :value KB。',
+        'numeric' => ':Attribute 必须大于 :value。',
+        'string'  => ':Attribute 必须多于 :value 个字符。',
+    ],
+    'gte'                  => [
+        'array'   => ':Attribute 必须多于或等于 :value 个元素。',
+        'file'    => ':Attribute 必须大于或等于 :value KB。',
+        'numeric' => ':Attribute 必须大于或等于 :value。',
+        'string'  => ':Attribute 必须多于或等于 :value 个字符。',
+    ],
+    'hex_color'            => ':attribute 字段必须是有效的十六进制颜色。',
+    'image'                => ':Attribute 必须是图片。',
+    'in'                   => '已选的属性 :attribute 无效。',
+    'in_array'             => ':Attribute 必须在 :other 中。',
+    'integer'              => ':Attribute 必须是整数。',
+    'ip'                   => ':Attribute 必须是有效的 IP 地址。',
+    'ipv4'                 => ':Attribute 必须是有效的 IPv4 地址。',
+    'ipv6'                 => ':Attribute 必须是有效的 IPv6 地址。',
+    'json'                 => ':Attribute 必须是正确的 JSON 格式。',
+    'lowercase'            => ':Attribute 必须小写。',
+    'lt'                   => [
+        'array'   => ':Attribute 必须少于 :value 个元素。',
+        'file'    => ':Attribute 必须小于 :value KB。',
+        'numeric' => ':Attribute 必须小于 :value。',
+        'string'  => ':Attribute 必须少于 :value 个字符。',
+    ],
+    'lte'                  => [
+        'array'   => ':Attribute 必须少于或等于 :value 个元素。',
+        'file'    => ':Attribute 必须小于或等于 :value KB。',
+        'numeric' => ':Attribute 必须小于或等于 :value。',
+        'string'  => ':Attribute 必须少于或等于 :value 个字符。',
+    ],
+    'mac_address'          => ':Attribute 必须是一个有效的 MAC 地址。',
+    'max'                  => [
+        'array'   => ':Attribute 最多只有 :max 个单元。',
+        'file'    => ':Attribute 不能大于 :max KB。',
+        'numeric' => ':Attribute 不能大于 :max。',
+        'string'  => ':Attribute 不能大于 :max 个字符。',
+    ],
+    'max_digits'           => ':Attribute 不能超过 :max 位数。',
+    'mimes'                => ':Attribute 必须是一个 :values 类型的文件。',
+    'mimetypes'            => ':Attribute 必须是一个 :values 类型的文件。',
+    'min'                  => [
+        'array'   => ':Attribute 至少有 :min 个单元。',
+        'file'    => ':Attribute 大小不能小于 :min KB。',
+        'numeric' => ':Attribute 必须大于等于 :min。',
+        'string'  => ':Attribute 至少为 :min 个字符。',
+    ],
+    'min_digits'           => ':Attribute 必须至少有 :min 位数。',
+    'missing'              => '必须缺少 :attribute 字段。',
+    'missing_if'           => '当 :other 为 :value 时,必须缺少 :attribute 字段。',
+    'missing_unless'       => '必须缺少 :attribute 字段,除非 :other 是 :value。',
+    'missing_with'         => '存在 :values 时,必须缺少 :attribute 字段。',
+    'missing_with_all'     => '存在 :values 时,必须缺少 :attribute 字段。',
+    'multiple_of'          => ':Attribute 必须是 :value 中的多个值。',
+    'not_in'               => '已选的属性 :attribute 非法。',
+    'not_regex'            => ':Attribute 的格式错误。',
+    'numeric'              => ':Attribute 必须是一个数字。',
+    'password'             => [
+        'letters'       => ':Attribute 必须至少包含一个字母。',
+        'mixed'         => ':Attribute 必须至少包含一个大写字母和一个小写字母。',
+        'numbers'       => ':Attribute 必须至少包含一个数字。',
+        'symbols'       => ':Attribute 必须至少包含一个符号。',
+        'uncompromised' => '给定的 :attribute 出现在已经泄漏的密码中。请选择不同的 :attribute。',
+    ],
+    'present'              => ':Attribute 必须存在。',
+    'present_if'           => '当 :other 等于 :value 时,必须存在 :attribute 字段。',
+    'present_unless'       => '除非 :other 等于 :value,否则 :attribute 字段必须存在。',
+    'present_with'         => '当 :values 存在时,:attribute 字段必须存在。',
+    'present_with_all'     => '当存在 :values 时,必须存在 :attribute 字段。',
+    'prohibited'           => ':Attribute 字段被禁止。',
+    'prohibited_if'        => '当 :other 为 :value 时,禁止 :attribute 字段。',
+    'prohibited_unless'    => ':Attribute 字段被禁止,除非 :other 位于 :values 中。',
+    'prohibits'            => ':Attribute 字段禁止出现 :other。',
+    'regex'                => ':Attribute 格式不正确。',
+    'required'             => ':Attribute 不能为空。',
+    'required_array_keys'  => ':Attribute 至少包含指定的键::values.',
+    'required_if'          => '当 :other 为 :value 时 :attribute 不能为空。',
+    'required_if_accepted' => '当 :other 存在时,:attribute 不能为空。',
+    'required_unless'      => '当 :other 不为 :values 时 :attribute 不能为空。',
+    'required_with'        => '当 :values 存在时 :attribute 不能为空。',
+    'required_with_all'    => '当 :values 存在时 :attribute 不能为空。',
+    'required_without'     => '当 :values 不存在时 :attribute 不能为空。',
+    'required_without_all' => '当 :values 都不存在时 :attribute 不能为空。',
+    'same'                 => ':Attribute 和 :other 必须相同。',
+    'size'                 => [
+        'array'   => ':Attribute 必须为 :size 个单元。',
+        'file'    => ':Attribute 大小必须为 :size KB。',
+        'numeric' => ':Attribute 大小必须为 :size。',
+        'string'  => ':Attribute 必须是 :size 个字符。',
+    ],
+    'starts_with'          => ':Attribute 必须以 :values 为开头。',
+    'string'               => ':Attribute 必须是一个字符串。',
+    'timezone'             => ':Attribute 必须是一个合法的时区值。',
+    'ulid'                 => ':Attribute 必须是有效的 ULID。',
+    'unique'               => ':Attribute 已经存在。',
+    'uploaded'             => ':Attribute 上传失败。',
+    'uppercase'            => ':Attribute 必须大写',
+    'url'                  => ':Attribute 格式不正确。',
+    'uuid'                 => ':Attribute 必须是有效的 UUID。',
+    'attributes'           => [
+        'address'                  => '地址',
+        'affiliate_url'            => '附属网址',
+        'age'                      => '年龄',
+        'amount'                   => '数额',
+        'area'                     => '区域',
+        'available'                => '可用的',
+        'birthday'                 => '生日',
+        'body'                     => '身体',
+        'city'                     => '城市',
+        'content'                  => '内容',
+        'country'                  => '国家',
+        'created_at'               => '创建于',
+        'creator'                  => '创建者',
+        'currency'                 => '货币',
+        'current_password'         => '当前密码',
+        'customer'                 => '顾客',
+        'date'                     => '日期',
+        'date_of_birth'            => '出生日期',
+        'day'                      => '天',
+        'deleted_at'               => '删除于',
+        'description'              => '描述',
+        'district'                 => '地区',
+        'duration'                 => '期间',
+        'email'                    => '邮箱',
+        'excerpt'                  => '摘要',
+        'filter'                   => '过滤',
+        'first_name'               => '名',
+        'gender'                   => '性别',
+        'group'                    => '组',
+        'hour'                     => '时',
+        'image'                    => '图像',
+        'is_subscribed'            => '已订阅',
+        'items'                    => '项目',
+        'last_name'                => '姓',
+        'lesson'                   => '课程',
+        'line_address_1'           => '线路地址 1',
+        'line_address_2'           => '线路地址 2',
+        'message'                  => '信息',
+        'middle_name'              => '中间名字',
+        'minute'                   => '分',
+        'mobile'                   => '手机',
+        'month'                    => '月',
+        'name'                     => '名称',
+        'national_code'            => '国家代码',
+        'number'                   => '数字',
+        'password'                 => '密码',
+        'password_confirmation'    => '确认密码',
+        'phone'                    => '电话',
+        'photo'                    => '照片',
+        'postal_code'              => '邮政编码',
+        'preview'                  => '预览',
+        'price'                    => '价格',
+        'product_id'               => '产品编号',
+        'product_uid'              => '产品UID',
+        'product_uuid'             => '产品UUID',
+        'promo_code'               => '促销代码',
+        'province'                 => '省',
+        'quantity'                 => '数量',
+        'recaptcha_response_field' => '重复验证码响应字段',
+        'remember'                 => '记住',
+        'restored_at'              => '恢复于',
+        'result_text_under_image'  => '图像下的结果文本',
+        'role'                     => '角色',
+        'second'                   => '秒',
+        'sex'                      => '性别',
+        'shipment'                 => '运输',
+        'short_text'               => '短文本',
+        'size'                     => '大小',
+        'state'                    => '状态',
+        'street'                   => '街道',
+        'student'                  => '学生',
+        'subject'                  => '主题',
+        'teacher'                  => '教师',
+        'terms'                    => '条款',
+        'test_description'         => '测试说明',
+        'test_locale'              => '测试语言环境',
+        'test_name'                => '测试名称',
+        'text'                     => '文本',
+        'time'                     => '时间',
+        'title'                    => '标题',
+        'updated_at'               => '更新于',
+        'user'                     => '用户',
+        'username'                 => '用户名',
+        'year'                     => '年',
+    ],
+];

+ 208 - 0
lang/zh_TW.json

@@ -0,0 +1,208 @@
+{
+    "(and :count more error)": "(還有 :count 個錯誤)",
+    "(and :count more errors)": "(還有 :count 多個錯誤)",
+    "A Timeout Occurred": "發生超時",
+    "Accept": "接受",
+    "Accepted": "公認",
+    "Action": "開始!",
+    "Actions": "動作",
+    "Add": "新增",
+    "Admin": "行政",
+    "Agree": "同意",
+    "All rights reserved.": "版權所有。",
+    "Already Reported": "已舉報",
+    "Archive": "檔案",
+    "Assign": "分配",
+    "Attach": "附加",
+    "Bad Gateway": "錯誤的網關",
+    "Bad Request": "錯誤的請求",
+    "Bandwidth Limit Exceeded": "超出帶寬限制",
+    "Browse": "瀏覽",
+    "Cancel": "取消",
+    "Choose": "選擇",
+    "Choose :name": "選擇:name",
+    "Choose File": "選擇檔案",
+    "Choose Image": "選擇圖片",
+    "Click to copy": "點選複製",
+    "Client Closed Request": "客戶端關閉請求",
+    "Close": "關閉",
+    "Collapse": "坍塌",
+    "Collapse All": "全部收縮",
+    "Comment": "評論",
+    "Confirm": "確認",
+    "Conflict": "衝突",
+    "Connect": "連接",
+    "Connection Closed Without Response": "連接關閉無響應",
+    "Connection Timed Out": "連接超時",
+    "Continue": "繼續",
+    "Create": "建立",
+    "Created": "已創建",
+    "Delete": "刪除",
+    "Detach": "分離",
+    "Details": "詳情",
+    "Disable": "停用",
+    "Discard": "丟棄",
+    "Done": "完畢",
+    "Down": "向下",
+    "Duplicate": "複製",
+    "Duplicate: name": "重複:名稱",
+    "Edit": "編輯",
+    "Edit :name": "編輯:name",
+    "Enable": "啟用",
+    "Expand": "擴張",
+    "Expand All": "展開全部",
+    "Expectation Failed": "期望失敗",
+    "Explanation": "解釋",
+    "Export": "出口",
+    "Failed Dependency": "失敗的依賴",
+    "File": "文件",
+    "Files": "文件",
+    "Forbidden": "拒絕存取",
+    "Found": "成立",
+    "Gateway Timeout": "網關超時",
+    "Go Home": "回首頁",
+    "Go to page :page": "前往第 :page 頁",
+    "Gone": "走了",
+    "Hello!": "您好!",
+    "Hide": "隱藏",
+    "Hide :name": "隱藏 :name",
+    "Home": "家",
+    "HTTP Version Not Supported": "不支持 HTTP 版本",
+    "I'm a teapot": "我是茶壺",
+    "If you did not create an account, no further action is required.": "如果您未註冊帳號,請忽略此郵件。",
+    "If you did not request a password reset, no further action is required.": "如果您未要求重設密碼,請忽略此郵件。",
+    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "如果您點擊「:actionText」按鈕時出現問題,請複製下方連結至瀏覽器中貼上:",
+    "IM Used": "使用即時通訊",
+    "Image": "影像",
+    "Impersonate": "模擬登入",
+    "Impersonation": "冒充",
+    "Import": "進口",
+    "Import :name": "進口 :name",
+    "Insufficient Storage": "存儲空間不足",
+    "Internal Server Error": "內部服務器錯誤",
+    "Introduction": "介紹",
+    "Invalid JSON was returned from the route.": "路由回傳了無效的 JSON。",
+    "Invalid SSL Certificate": "無效的 SSL 證書",
+    "Length Required": "所需長度",
+    "Like": "喜歡",
+    "Load": "載入",
+    "Localize": "在地化",
+    "Locked": "鎖定",
+    "Login": "登入",
+    "Logout": "登出",
+    "Loop Detected": "檢測到環路",
+    "Maintenance Mode": "維護模式",
+    "Method Not Allowed": "方法不允許",
+    "Misdirected Request": "錯誤的請求",
+    "Moved Permanently": "永久移動",
+    "Multi-Status": "多狀態",
+    "Multiple Choices": "多項選擇",
+    "Network Authentication Required": "需要網絡身份驗證",
+    "Network Connect Timeout Error": "網絡連接超時錯誤",
+    "Network Read Timeout Error": "網絡讀取超時錯誤",
+    "New": "新增",
+    "New :name": "新 :name",
+    "No": "否",
+    "No Content": "無內容",
+    "Non-Authoritative Information": "非權威信息",
+    "Not Acceptable": "不能接受的",
+    "Not Extended": "未擴展",
+    "Not Found": "找不到頁面",
+    "Not Implemented": "未實現",
+    "Not Modified": "未修改",
+    "of": "於",
+    "OK": "好的",
+    "Open": "打開",
+    "Open in a current window": "在目前視窗中開啟",
+    "Open in a new window": "在新視窗中開啟",
+    "Open in a parent frame": "在父框架中打開",
+    "Open in the topmost frame": "在最上面的框架中打開",
+    "Open on the website": "在網站上打開",
+    "Origin Is Unreachable": "原點不可達",
+    "Page Expired": "頁面已過期",
+    "Pagination Navigation": "分頁導覽",
+    "Partial Content": "部分內容",
+    "Payload Too Large": "有效載荷太大",
+    "Payment Required": "需要付款",
+    "Permanent Redirect": "永久重定向",
+    "Please click the button below to verify your email address.": "請點擊下方按鈕驗證您的電子郵件地址:",
+    "Precondition Failed": "前置條件失敗",
+    "Precondition Required": "需要先決條件",
+    "Preview": "預覽",
+    "Price": "價格",
+    "Processing": "加工",
+    "Proxy Authentication Required": "需要代理身份驗證",
+    "Railgun Error": "軌道炮錯誤",
+    "Range Not Satisfiable": "範圍不可滿足",
+    "Regards": "致敬",
+    "Register": "註冊",
+    "Request Header Fields Too Large": "請求標頭字段太大",
+    "Request Timeout": "請求超時",
+    "Reset Content": "重置內容",
+    "Reset Password": "重設密碼",
+    "Reset Password Notification": "重設密碼通知",
+    "Restore": "還原",
+    "Restore :name": "恢復:name",
+    "results": "結果",
+    "Retry With": "重試",
+    "Save": "儲存",
+    "Save & Close": "儲存並關閉",
+    "Save & Return": "儲存並返回",
+    "Save :name": "節省 :name",
+    "Search": "搜尋",
+    "Search :name": "搜尋 :name",
+    "See Other": "查看其他",
+    "Select": "選擇",
+    "Select All": "全選",
+    "Send": "傳送",
+    "Server Error": "伺服器錯誤",
+    "Service Unavailable": "暫時不提供服務",
+    "Session Has Expired": "會話已過期",
+    "Settings": "設定",
+    "Show": "展示",
+    "Show :name": "顯示 :name",
+    "Show All": "顯示所有",
+    "Showing": "顯示中",
+    "Solve": "解決",
+    "SSL Handshake Failed": "SSL 握手失敗",
+    "Submit": "提交",
+    "Subscribe": "訂閱",
+    "Switch": "轉變",
+    "Switch To Role": "切換角色",
+    "Switching Protocols": "切換協議",
+    "Tag": "標籤",
+    "Tags": "標籤",
+    "Temporary Redirect": "臨時重定向",
+    "The given data was invalid.": "給定的數據無效。",
+    "The response is not a streamed response.": "該響應不是流式響應。",
+    "The response is not a view.": "響應不是視圖。",
+    "This password reset link will expire in :count minutes.": "重設密碼連結將會在 :count 分鐘後失效。",
+    "to": "至",
+    "Toggle navigation": "切換導覽",
+    "Too Early": "太早了",
+    "Too Many Requests": "要求次數過多。",
+    "Translate": "翻譯",
+    "Translate It": "翻譯它",
+    "Unauthorized": "未授權",
+    "Unavailable For Legal Reasons": "因法律原因無法使用",
+    "Unknown Error": "未知錯誤",
+    "Unpack": "打開包裝",
+    "Unprocessable Entity": "不可處理的實體",
+    "Unsubscribe": "退訂",
+    "Unsupported Media Type": "不支持的媒體類型",
+    "Up": "向上",
+    "Update": "更新",
+    "Update :name": "更新:name",
+    "Upgrade Required": "需要升級",
+    "URI Too Long": "URI 太長",
+    "Use Proxy": "使用代理服務器",
+    "User": "使用者",
+    "Variant Also Negotiates": "變體也協商",
+    "Verify Email Address": "驗證電子郵件地址",
+    "View": "檢視",
+    "View :name": "查看 :name",
+    "Web Server is Down": "Web 服務器已關閉",
+    "Whoops!": "哎呀!",
+    "Yes": "是",
+    "You are receiving this email because we received a password reset request for your account.": "您收到此電子郵件是因為我們收到了重設密碼的要求。"
+}

+ 108 - 0
lang/zh_TW/actions.php

@@ -0,0 +1,108 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'accept'          => '接受',
+    'action'          => '行動',
+    'actions'         => '行動',
+    'add'             => '添加',
+    'admin'           => '行政',
+    'agree'           => '同意',
+    'archive'         => '檔案',
+    'assign'          => '分配',
+    'attach'          => '附',
+    'browse'          => '瀏覽',
+    'cancel'          => '取消',
+    'choose'          => '選擇',
+    'choose_file'     => '選擇文件',
+    'choose_image'    => '選擇圖片',
+    'click_to_copy'   => '點選複製',
+    'close'           => '關閉',
+    'collapse'        => '坍塌',
+    'collapse_all'    => '全部收縮',
+    'comment'         => '評論',
+    'confirm'         => '確認',
+    'connect'         => '連接',
+    'create'          => '創造',
+    'delete'          => '刪除',
+    'detach'          => '分離',
+    'details'         => '細節',
+    'disable'         => '停用',
+    'discard'         => '丟棄',
+    'done'            => '完畢',
+    'down'            => '向下',
+    'duplicate'       => '複製',
+    'edit'            => '編輯',
+    'enable'          => '使能夠',
+    'expand'          => '擴張',
+    'expand_all'      => '展開全部',
+    'explanation'     => '解釋',
+    'export'          => '出口',
+    'file'            => ':Attribute 必須是有效的檔案。',
+    'files'           => '文件',
+    'go_home'         => '回家',
+    'hide'            => '隱藏',
+    'home'            => '家',
+    'image'           => ':Attribute 必須是一張圖片。',
+    'Impersonate'     => '模擬登入',
+    'Impersonation'   => '冒充',
+    'import'          => '進口',
+    'introduction'    => '介紹',
+    'like'            => '喜歡',
+    'load'            => '載入',
+    'localize'        => '在地化',
+    'named'           => [
+        'choose'    => '選擇:name',
+        'duplicate' => '重複:名稱',
+        'edit'      => '編輯:name',
+        'hide'      => '隱藏 :name',
+        'import'    => '進口 :name',
+        'new'       => '新 :name',
+        'restore'   => '恢復:name',
+        'save'      => '節省 :name',
+        'search'    => '搜尋 :name',
+        'show'      => '顯示 :name',
+        'update'    => '更新:name',
+        'view'      => '查看 :name',
+    ],
+    'new'             => '新的',
+    'no'              => '不',
+    'open'            => '打開',
+    'open_website'    => '在網站上打開',
+    'preview'         => '預覽',
+    'price'           => '價格',
+    'restore'         => '恢復',
+    'save'            => '節省',
+    'save_and_close'  => '儲存並關閉',
+    'save_and_return' => '儲存並返回',
+    'search'          => '搜尋',
+    'select'          => '選擇',
+    'select_all'      => '全選',
+    'send'            => '傳送',
+    'settings'        => '設定',
+    'show'            => '展示',
+    'show_all'        => '顯示所有',
+    'solve'           => '解決',
+    'submit'          => '提交',
+    'subscribe'       => '訂閱',
+    'switch'          => '轉變',
+    'switch_to_role'  => '切換角色',
+    'tag'             => '標籤',
+    'tags'            => '標籤',
+    'target_link'     => [
+        'blank'  => '在新視窗中開啟',
+        'parent' => '在父框架中打開',
+        'self'   => '在目前視窗中開啟',
+        'top'    => '在最上面的框架中打開',
+    ],
+    'translate'       => '翻譯',
+    'translate_it'    => '翻譯它',
+    'unpack'          => '打開包裝',
+    'unsubscribe'     => '退訂',
+    'up'              => '向上',
+    'update'          => '更新',
+    'user'            => '找不到該 E-mail 對應的使用者。',
+    'view'            => '看法',
+    'yes'             => '是的',
+];

+ 9 - 0
lang/zh_TW/auth.php

@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'failed'   => '使用者名稱或密碼錯誤。',
+    'password' => '密碼錯誤',
+    'throttle' => '嘗試登入太多次,請在 :seconds 秒後再試。',
+];

+ 84 - 0
lang/zh_TW/http-statuses.php

@@ -0,0 +1,84 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    '0'            => '未知錯誤',
+    '100'          => '繼續',
+    '101'          => '切換協議',
+    '102'          => '加工',
+    '200'          => '好的',
+    '201'          => '已創建',
+    '202'          => '公認',
+    '203'          => '非權威信息',
+    '204'          => '無內容',
+    '205'          => '重置內容',
+    '206'          => '部分內容',
+    '207'          => '多狀態',
+    '208'          => '已舉報',
+    '226'          => '使用即時通訊',
+    '300'          => '多項選擇',
+    '301'          => '永久移動',
+    '302'          => '成立',
+    '303'          => '查看其他',
+    '304'          => '未修改',
+    '305'          => '使用代理服務器',
+    '307'          => '臨時重定向',
+    '308'          => '永久重定向',
+    '400'          => '錯誤的請求',
+    '401'          => '未經授權',
+    '402'          => '需要付款',
+    '403'          => '禁止',
+    '404'          => '未找到',
+    '405'          => '方法不允許',
+    '406'          => '不能接受的',
+    '407'          => '需要代理身份驗證',
+    '408'          => '請求超時',
+    '409'          => '衝突',
+    '410'          => '走了',
+    '411'          => '所需長度',
+    '412'          => '前置條件失敗',
+    '413'          => '有效載荷太大',
+    '414'          => 'URI 太長',
+    '415'          => '不支持的媒體類型',
+    '416'          => '範圍不可滿足',
+    '417'          => '期望失敗',
+    '418'          => '我是茶壺',
+    '419'          => '會話已過期',
+    '421'          => '錯誤的請求',
+    '422'          => '不可處理的實體',
+    '423'          => '鎖定',
+    '424'          => '失敗的依賴',
+    '425'          => '太早了',
+    '426'          => '需要升級',
+    '428'          => '需要先決條件',
+    '429'          => '請求太多',
+    '431'          => '請求標頭字段太大',
+    '444'          => '連接關閉無響應',
+    '449'          => '重試',
+    '451'          => '因法律原因無法使用',
+    '499'          => '客戶端關閉請求',
+    '500'          => '內部服務器錯誤',
+    '501'          => '未實現',
+    '502'          => '錯誤的網關',
+    '503'          => '維護模式',
+    '504'          => '網關超時',
+    '505'          => '不支持 HTTP 版本',
+    '506'          => '變體也協商',
+    '507'          => '存儲空間不足',
+    '508'          => '檢測到環路',
+    '509'          => '超出帶寬限制',
+    '510'          => '未擴展',
+    '511'          => '需要網絡身份驗證',
+    '520'          => '未知錯誤',
+    '521'          => 'Web 服務器已關閉',
+    '522'          => '連接超時',
+    '523'          => '原點不可達',
+    '524'          => '發生超時',
+    '525'          => 'SSL 握手失敗',
+    '526'          => '無效的 SSL 證書',
+    '527'          => '軌道炮錯誤',
+    '598'          => '網絡讀取超時錯誤',
+    '599'          => '網絡連接超時錯誤',
+    'unknownError' => '未知錯誤',
+];

+ 8 - 0
lang/zh_TW/pagination.php

@@ -0,0 +1,8 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'next'     => '下一頁 &raquo;',
+    'previous' => '&laquo; 上一頁',
+];

+ 11 - 0
lang/zh_TW/passwords.php

@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'reset'     => '密碼已成功重設!',
+    'sent'      => '密碼重設郵件已發送!',
+    'throttled' => '請稍候再試。',
+    'token'     => '密碼重設碼無效。',
+    'user'      => '找不到該 E-mail 對應的使用者。',
+];

+ 236 - 0
lang/zh_TW/validation.php

@@ -0,0 +1,236 @@
+<?php
+
+declare(strict_types=1);
+
+return [
+    'accepted'             => '必須接受 :attribute。',
+    'accepted_if'          => '當 :other 為 :value 時,:attribute 必須接受。',
+    'active_url'           => ':Attribute 不是有效的網址。',
+    'after'                => ':Attribute 必須要晚於 :date。',
+    'after_or_equal'       => ':Attribute 必須要等於 :date 或更晚。',
+    'alpha'                => ':Attribute 只能以字母組成。',
+    'alpha_dash'           => ':Attribute 只能以字母、數字、連接線(-)及底線(_)組成。',
+    'alpha_num'            => ':Attribute 只能以字母及數字組成。',
+    'array'                => ':Attribute 必須為陣列。',
+    'ascii'                => ':Attribute 必須僅包含單字節字母數字字符和符號。',
+    'before'               => ':Attribute 必須要早於 :date。',
+    'before_or_equal'      => ':Attribute 必須要等於 :date 或更早。',
+    'between'              => [
+        'array'   => ':Attribute: 必須有 :min - :max 個元素。',
+        'file'    => ':Attribute 必須介於 :min 至 :max KB 之間。',
+        'numeric' => ':Attribute 必須介於 :min 至 :max 之間。',
+        'string'  => ':Attribute 必須介於 :min 至 :max 個字元之間。',
+    ],
+    'boolean'              => ':Attribute 必須為布林值。',
+    'can'                  => ':Attribute 字段包含未經授權的值。',
+    'confirmed'            => ':Attribute 確認欄位的輸入不一致。',
+    'current_password'     => '當前密碼不正確。',
+    'date'                 => ':Attribute 不是有效的日期。',
+    'date_equals'          => ':Attribute 必須等於 :date。',
+    'date_format'          => ':Attribute 不符合 :format 的格式。',
+    'decimal'              => ':Attribute 必須有 :decimal 位小數。',
+    'declined'             => ':Attribute 必須拒絕。',
+    'declined_if'          => '當 :other 為 :value 時,:attribute 必須拒絕。',
+    'different'            => ':Attribute 與 :other 必須不同。',
+    'digits'               => ':Attribute 必須是 :digits 位數字。',
+    'digits_between'       => ':Attribute 必須介於 :min 至 :max 位數字。',
+    'dimensions'           => ':Attribute 圖片尺寸不正確。',
+    'distinct'             => ':Attribute 已經存在。',
+    'doesnt_end_with'      => ':Attribute 不能以下列之一結尾::values。',
+    'doesnt_start_with'    => ':Attribute 不能以下列之一開頭::values。',
+    'email'                => ':Attribute 必須是有效的 E-mail。',
+    'ends_with'            => ':Attribute 結尾必須包含下列之一::values。',
+    'enum'                 => ':Attribute 的值不正確。',
+    'exists'               => ':Attribute 不存在。',
+    'extensions'           => ':attribute 欄位必須具有以下副檔名之一::values。',
+    'file'                 => ':Attribute 必須是有效的檔案。',
+    'filled'               => ':Attribute 不能留空。',
+    'gt'                   => [
+        'array'   => ':Attribute 必須多於 :value 個元素。',
+        'file'    => ':Attribute 必須大於 :value KB。',
+        'numeric' => ':Attribute 必須大於 :value。',
+        'string'  => ':Attribute 必須多於 :value 個字元。',
+    ],
+    'gte'                  => [
+        'array'   => ':Attribute 必須多於或等於 :value 個元素。',
+        'file'    => ':Attribute 必須大於或等於 :value KB。',
+        'numeric' => ':Attribute 必須大於或等於 :value。',
+        'string'  => ':Attribute 必須多於或等於 :value 個字元。',
+    ],
+    'hex_color'            => ':attribute 字段必須是有效的十六進位顏色。',
+    'image'                => ':Attribute 必須是一張圖片。',
+    'in'                   => '所選擇的 :attribute 選項無效。',
+    'in_array'             => ':Attribute 沒有在 :other 中。',
+    'integer'              => ':Attribute 必須是一個整數。',
+    'ip'                   => ':Attribute 必須是一個有效的 IP 位址。',
+    'ipv4'                 => ':Attribute 必須是一個有效的 IPv4 位址。',
+    'ipv6'                 => ':Attribute 必須是一個有效的 IPv6 位址。',
+    'json'                 => ':Attribute 必須是正確的 JSON 字串。',
+    'lowercase'            => ':Attribute 必須小寫。',
+    'lt'                   => [
+        'array'   => ':Attribute 必須少於 :value 個元素。',
+        'file'    => ':Attribute 必須小於 :value KB。',
+        'numeric' => ':Attribute 必須小於 :value。',
+        'string'  => ':Attribute 必須少於 :value 個字元。',
+    ],
+    'lte'                  => [
+        'array'   => ':Attribute 必須少於或等於 :value 個元素。',
+        'file'    => ':Attribute 必須小於或等於 :value KB。',
+        'numeric' => ':Attribute 必須小於或等於 :value。',
+        'string'  => ':Attribute 必須少於或等於 :value 個字元。',
+    ],
+    'mac_address'          => ':Attribute 必須是一個有效的 MAC 位址。',
+    'max'                  => [
+        'array'   => ':Attribute 最多有 :max 個元素。',
+        'file'    => ':Attribute 不能大於 :max KB。',
+        'numeric' => ':Attribute 不能大於 :max。',
+        'string'  => ':Attribute 不能多於 :max 個字元。',
+    ],
+    'max_digits'           => ':Attribute 不得超過 :max 位。',
+    'mimes'                => ':Attribute 必須為 :values 的檔案。',
+    'mimetypes'            => ':Attribute 必須為 :values 的檔案。',
+    'min'                  => [
+        'array'   => ':Attribute 至少有 :min 個元素。',
+        'file'    => ':Attribute 不能小於 :min KB。',
+        'numeric' => ':Attribute 不能小於 :min。',
+        'string'  => ':Attribute 不能小於 :min 個字元。',
+    ],
+    'min_digits'           => ':Attribute 必須至少有 :min 位數字。',
+    'missing'              => '必須缺少 :attribute 字段。',
+    'missing_if'           => '當 :other 為 :value 時,必須缺少 :attribute 字段。',
+    'missing_unless'       => '必須缺少 :attribute 字段,除非 :other 是 :value。',
+    'missing_with'         => '存在 :values 時,必須缺少 :attribute 字段。',
+    'missing_with_all'     => '存在 :values 時,必須缺少 :attribute 字段。',
+    'multiple_of'          => '所選擇的 :attribute 必須為 :value 中的多個。',
+    'not_in'               => '所選擇的 :attribute 選項無效。',
+    'not_regex'            => ':Attribute 的格式錯誤。',
+    'numeric'              => ':Attribute 必須為一個數字。',
+    'password'             => [
+        'letters'       => ':Attribute 必須至少包含一個字母。',
+        'mixed'         => ':Attribute 必須至少包含一個大寫字母和一個小寫字母。',
+        'numbers'       => ':Attribute 必須至少包含一個數字。',
+        'symbols'       => ':Attribute 必須包含至少一個符號。',
+        'uncompromised' => '給定的 :attribute 已出現數據洩漏。請選擇不同的 :attribute。',
+    ],
+    'present'              => ':Attribute 必須存在。',
+    'present_if'           => '當 :other 等於 :value 時,必須存在 :attribute 個欄位。',
+    'present_unless'       => '除非 :other 等於 :value,否則 :attribute 個字段必須存在。',
+    'present_with'         => '當 :values 存在時,:attribute 個字段必須存在。',
+    'present_with_all'     => '當存在 :values 時,必須存在 :attribute 個字段。',
+    'prohibited'           => ':Attribute 字段被禁止。',
+    'prohibited_if'        => '当 :other 为 :value 时,:attribute字段被禁止。',
+    'prohibited_unless'    => ':Attribute 字段被禁止,除非 :other 在 :values 中。',
+    'prohibits'            => ':Attribute 字段禁止包含 :other。',
+    'regex'                => ':Attribute 的格式錯誤。',
+    'required'             => ':Attribute 不能留空。',
+    'required_array_keys'  => ':Attribute 必須包含 :values 中的一個鍵。',
+    'required_if'          => '當 :other 是 :value 時 :attribute 不能留空。',
+    'required_if_accepted' => '接受 :other 時需要 :attribute 字段。',
+    'required_unless'      => '當 :other 不是 :values 時 :attribute 不能留空。',
+    'required_with'        => '當 :values 出現時 :attribute 不能留空。',
+    'required_with_all'    => '當 :values 出現時 :attribute 不能為空。',
+    'required_without'     => '當 :values 留空時 :attribute field 不能留空。',
+    'required_without_all' => '當 :values 都不出現時 :attribute 不能留空。',
+    'same'                 => ':Attribute 與 :other 必須相同。',
+    'size'                 => [
+        'array'   => ':Attribute 必須是 :size 個元素。',
+        'file'    => ':Attribute 的大小必須是 :size KB。',
+        'numeric' => ':Attribute 的大小必須是 :size。',
+        'string'  => ':Attribute 必須是 :size 個字元。',
+    ],
+    'starts_with'          => ':Attribute 開頭必須包含下列之一::values。',
+    'string'               => ':Attribute 必須是一個字串。',
+    'timezone'             => ':Attribute 必須是一個正確的時區值。',
+    'ulid'                 => ':Attribute 必須是有效的 ULID。',
+    'unique'               => ':Attribute 已經存在。',
+    'uploaded'             => ':Attribute 上傳失敗。',
+    'uppercase'            => ':Attribute 必須大寫。',
+    'url'                  => ':Attribute 的格式錯誤。',
+    'uuid'                 => ':Attribute 必須是有效的 UUID。',
+    'attributes'           => [
+        'address'                  => '地址',
+        'affiliate_url'            => '附屬網址',
+        'age'                      => '年齡',
+        'amount'                   => '數量',
+        'area'                     => '區域',
+        'available'                => '可用的',
+        'birthday'                 => '生日',
+        'body'                     => '身體',
+        'city'                     => '城市',
+        'content'                  => '內容',
+        'country'                  => '國家',
+        'created_at'               => '創建於',
+        'creator'                  => '創造者',
+        'currency'                 => '貨幣',
+        'current_password'         => '當前密碼',
+        'customer'                 => '顧客',
+        'date'                     => '日期',
+        'date_of_birth'            => '出生日期',
+        'day'                      => '天',
+        'deleted_at'               => '刪除於',
+        'description'              => '描述',
+        'district'                 => '區',
+        'duration'                 => '期間',
+        'email'                    => 'e-mail',
+        'excerpt'                  => '摘要',
+        'filter'                   => '篩選',
+        'first_name'               => '名',
+        'gender'                   => '性別',
+        'group'                    => '團體',
+        'hour'                     => '時',
+        'image'                    => '圖片',
+        'is_subscribed'            => '已訂閱',
+        'items'                    => '專案',
+        'last_name'                => '姓',
+        'lesson'                   => '課',
+        'line_address_1'           => '行地址 1',
+        'line_address_2'           => '行地址 2',
+        'message'                  => '信息',
+        'middle_name'              => '中間名字',
+        'minute'                   => '分',
+        'mobile'                   => '手機',
+        'month'                    => '月',
+        'name'                     => '名稱',
+        'national_code'            => '國家代碼',
+        'number'                   => '數字',
+        'password'                 => '密碼',
+        'password_confirmation'    => '確認密碼',
+        'phone'                    => '電話',
+        'photo'                    => '照片',
+        'postal_code'              => '郵政編碼',
+        'preview'                  => '預覽',
+        'price'                    => '價格',
+        'product_id'               => '產品編號',
+        'product_uid'              => '產品UID',
+        'product_uuid'             => '產品UUID',
+        'promo_code'               => '促銷代碼',
+        'province'                 => '省',
+        'quantity'                 => '數量',
+        'recaptcha_response_field' => '重新驗證響應字段',
+        'remember'                 => '記住',
+        'restored_at'              => '恢復於',
+        'result_text_under_image'  => '圖片下方的結果文本',
+        'role'                     => '角色',
+        'second'                   => '秒',
+        'sex'                      => '性別',
+        'shipment'                 => '運輸',
+        'short_text'               => '短文',
+        'size'                     => '大小',
+        'state'                    => '狀態',
+        'street'                   => '街道',
+        'student'                  => '學生',
+        'subject'                  => '主題',
+        'teacher'                  => '老師',
+        'terms'                    => '條款',
+        'test_description'         => '測試說明',
+        'test_locale'              => '測試語言環境',
+        'test_name'                => '測試名稱',
+        'text'                     => '文本',
+        'time'                     => '時間',
+        'title'                    => '標題',
+        'updated_at'               => '更新於',
+        'user'                     => '使用者',
+        'username'                 => '使用者名稱',
+        'year'                     => '年',
+    ],
+];

+ 6 - 0
routes/api.php

@@ -17,3 +17,9 @@ use Illuminate\Support\Facades\Route;
 Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
     return $request->user();
 });
+
+Route::post("/login", [\App\Http\Controllers\API\AuthController::class, "login"]);
+
+Route::middleware(['auth:sanctum'])->group(function () {
+    Route::post("/logout", [\App\Http\Controllers\API\AuthController::class, "logout"]);
+});

+ 56 - 0
tests/Feature/API/AuthenticationTest.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace Tests\Feature\API;
+
+use App\Models\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Illuminate\Foundation\Testing\WithFaker;
+use Illuminate\Support\Facades\Hash;
+use Laravel\Sanctum\Sanctum;
+use Tests\TestCase;
+
+class AuthenticationTest extends TestCase
+{
+    use RefreshDatabase, WithFaker;
+
+    public function test_user_can_login(): void
+    {
+        $password = 'lpc..pwd';
+        $user = User::factory()->create([
+            'password' => Hash::make($password),
+            'username' => 'lpc',
+        ]);
+
+        $fields = ['username', 'email'];
+
+        foreach($fields as $field) {
+            $response = $this->post('/api/login', [
+                'username' => $user->$field,
+                'password' => $password,
+            ]);
+
+            $response->assertStatus(200);
+
+            $response->assertJsonIsObject("data");
+
+            $response->assertJson([
+                'data' => [
+                    'token' => true,
+                ]
+            ]);
+        }
+    }
+
+    public function test_user_can_logout(): void
+    {
+        $user = User::factory()->create([
+            'username' => 'lpc'
+        ]);
+
+        Sanctum::actingAs($user);
+
+        $response = $this->post('/api/logout');
+
+        $response->assertStatus(204);
+    }
+}

Some files were not shown because too many files changed in this diff