diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php
index e2c2ac56..3c56fcc2 100644
--- a/app/Console/Commands/Test.php
+++ b/app/Console/Commands/Test.php
@@ -45,7 +45,11 @@ class Test extends Command
public function handle()
{
$rep = new ExamRepository();
- $r = $rep->addProgress(15, 1, 250, 1);
- dd($r);
+// $r = $rep->assignToUser(1, 1);
+// $r = $rep->addProgress(27, 4, 1025, 1);
+// dd($r);
+// $rep->assignCronjob();
+ $rep->cronjobCheckout();
}
+
}
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index 567176cc..d8320799 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -63,5 +63,6 @@ class Kernel extends HttpKernel
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'permission' => \App\Http\Middleware\Permission::class,
+ 'locale' => \App\Http\Middleware\Locale::class,
];
}
diff --git a/app/Http/Middleware/Locale.php b/app/Http/Middleware/Locale.php
new file mode 100644
index 00000000..8eaa5381
--- /dev/null
+++ b/app/Http/Middleware/Locale.php
@@ -0,0 +1,32 @@
+ 'en',
+ 'chs' => 'zh_CN',
+ 'cht' => 'zh_TW',
+ ];
+
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @return mixed
+ */
+ public function handle(Request $request, Closure $next)
+ {
+ $language = $request->user()->language;
+ $locale = self::$languageMaps[$language->site_lang_folder] ?? 'en';
+ do_log("set locale: " . $locale);
+ App::setLocale($locale);
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/Permission.php b/app/Http/Middleware/Permission.php
index 1f4043cc..3131dfa8 100644
--- a/app/Http/Middleware/Permission.php
+++ b/app/Http/Middleware/Permission.php
@@ -25,7 +25,7 @@ class Permission
do_log("$log, denied!");
throw new UnauthorizedException('Unauthorized!');
}
- do_log("$log, pass!");
+ do_log("$log, allow!");
return $next($request);
}
}
diff --git a/app/Http/Resources/ExamUserResource.php b/app/Http/Resources/ExamUserResource.php
index ce1989f7..48c484f1 100644
--- a/app/Http/Resources/ExamUserResource.php
+++ b/app/Http/Resources/ExamUserResource.php
@@ -23,11 +23,11 @@ class ExamUserResource extends JsonResource
'id' => $this->id,
'status' => $this->status,
'status_text' => $this->statusText,
- 'created_at' => formatDatetime($this->created_at),
+ 'created_at' => format_datetime($this->created_at),
'progress' => $this->when($this->progress, $this->progress),
'progress_formatted' => $this->when($this->progress_formatted, $this->progress_formatted),
- 'begin' => formatDatetime($this->begin),
- 'end' => formatDatetime($this->end),
+ 'begin' => format_datetime($this->begin),
+ 'end' => format_datetime($this->end),
'uid' => $this->uid,
'exam_id' => $this->exam_id,
'user' => new UserResource($this->whenLoaded('user')),
diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php
index 27a4932d..cf7c9835 100644
--- a/app/Http/Resources/UserResource.php
+++ b/app/Http/Resources/UserResource.php
@@ -19,7 +19,7 @@ class UserResource extends JsonResource
'email' => $this->email,
'username' => $this->username,
'status' => $this->status,
- 'added' => formatDatetime($this->added),
+ 'added' => format_datetime($this->added),
'class' => $this->class,
'class_text' => $this->class_text,
'avatar' => $this->avatar,
diff --git a/app/Models/ExamUser.php b/app/Models/ExamUser.php
index b9b41375..5ba1855c 100644
--- a/app/Models/ExamUser.php
+++ b/app/Models/ExamUser.php
@@ -4,7 +4,7 @@ namespace App\Models;
class ExamUser extends NexusModel
{
- protected $fillable = ['exam_id', 'uid', 'status', 'progress', 'begin', 'end'];
+ protected $fillable = ['exam_id', 'uid', 'status', 'progress', 'begin', 'end', 'is_done'];
public $timestamps = true;
@@ -16,6 +16,10 @@ class ExamUser extends NexusModel
self::STATUS_FINISHED => ['text' => 'Finished'],
];
+ const IS_DONE_YES = 1;
+ const IS_DONE_NO = 0;
+
+
protected $casts = [
'progress' => 'json'
];
diff --git a/app/Models/Language.php b/app/Models/Language.php
new file mode 100644
index 00000000..8b0deb76
--- /dev/null
+++ b/app/Models/Language.php
@@ -0,0 +1,12 @@
+hasMany(ExamUser::class, 'uid');
}
+ public function language()
+ {
+ return $this->belongsTo(Language::class, 'lang');
+ }
+
}
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 0426947e..faa701f6 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -2,6 +2,7 @@
namespace App\Providers;
+use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
use Illuminate\Http\Resources\Json\JsonResource;
diff --git a/app/Repositories/ExamRepository.php b/app/Repositories/ExamRepository.php
index 9901935e..5670bff1 100644
--- a/app/Repositories/ExamRepository.php
+++ b/app/Repositories/ExamRepository.php
@@ -5,10 +5,12 @@ use App\Exceptions\NexusException;
use App\Models\Exam;
use App\Models\ExamProgress;
use App\Models\ExamUser;
+use App\Models\Message;
use App\Models\Setting;
use App\Models\Torrent;
use App\Models\User;
use Carbon\Carbon;
+use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
@@ -25,6 +27,10 @@ class ExamRepository extends BaseRepository
public function store(array $params)
{
$this->checkIndexes($params);
+ $valid = $this->listValid();
+ if ($valid->isNotEmpty()) {
+ throw new NexusException("Valid exam already exists.");
+ }
$exam = Exam::query()->create($params);
return $exam;
}
@@ -32,6 +38,10 @@ class ExamRepository extends BaseRepository
public function update(array $params, $id)
{
$this->checkIndexes($params);
+ $valid = $this->listValid($id);
+ if ($valid->isNotEmpty()) {
+ throw new NexusException("Valid exam already exists.");
+ }
$exam = Exam::query()->findOrFail($id);
$exam->update($params);
return $exam;
@@ -78,16 +88,21 @@ class ExamRepository extends BaseRepository
/**
* list valid exams
*
+ * @param null $excludeId
* @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
*/
- public function listValid()
+ public function listValid($excludeId = null)
{
$now = Carbon::now();
- return Exam::query()
+ $query = Exam::query()
->where('begin', '<=', $now)
->where('end', '>=', $now)
->where('status', Exam::STATUS_ENABLED)
- ->get();
+ ->orderBy('id', 'desc');
+ if ($excludeId) {
+ $query->whereNotIn('id', Arr::wrap($excludeId));
+ }
+ return $query->get();
}
/**
@@ -104,48 +119,55 @@ class ExamRepository extends BaseRepository
do_log("$logPrefix, no valid exam.");
return $exams;
}
- $user = User::query()->findOrFail($uid, ['id', 'username', 'added', 'class']);
- $filtered = $exams->filter(function (Exam $exam) use ($user, $logPrefix) {
- $filters = $exam->filters;
- if (empty($filters->classes)) {
- do_log("$logPrefix, exam: {$exam->id} no class");
- return false;
- }
- if (!in_array($user->class, $filters->classes)) {
- do_log("$logPrefix, user class: {$user->class} not in: " . json_encode($filters));
- return false;
- }
- if (!$user->added) {
- do_log("$logPrefix, user no added time", 'warning');
- return false;
- }
-
- $added = $user->added->toDateTimeString();
- $registerTimeBegin = $filters->register_time_range[0] ? Carbon::parse($filters->register_time_range[0])->toDateString() : '';
- $registerTimeEnd = $filters->register_time_range[1] ? Carbon::parse($filters->register_time_range[1])->toDateString() : '';
- if (empty($registerTimeBegin)) {
- do_log("$logPrefix, exam: {$exam->id} no register_time_begin");
- return false;
- }
- if ($added < $registerTimeBegin) {
- do_log("$logPrefix, added: $added not after: " . $registerTimeBegin);
- return false;
- }
-
- if (empty($registerTimeEnd)) {
- do_log("$logPrefix, exam: {$exam->id} no register_time_end");
- return false;
- }
- if ($added > $registerTimeEnd) {
- do_log("$logPrefix, added: $added not before: " . $registerTimeEnd);
- return false;
- }
-
- return true;
+ $matched = $exams->filter(function (Exam $exam) use ($uid, $logPrefix) {
+ return $this->isExamMatchUser($exam, $uid);
});
- return $filtered;
+ return $matched;
+ }
+
+ private function isExamMatchUser(Exam $exam, $user)
+ {
+ if (!$user instanceof User) {
+ $user = User::query()->findOrFail(intval($user), ['id', 'username', 'added', 'class']);
+ }
+ $logPrefix = sprintf('exam: %s, user: %s', $exam->id, $user->id);
+ $filters = $exam->filters;
+ if (empty($filters->classes)) {
+ do_log("$logPrefix, exam: {$exam->id} no class");
+ return false;
+ }
+ if (!in_array($user->class, $filters->classes)) {
+ do_log("$logPrefix, user class: {$user->class} not in: " . json_encode($filters));
+ return false;
+ }
+ if (!$user->added) {
+ do_log("$logPrefix, user no added time", 'warning');
+ return false;
+ }
+
+ $added = $user->added->toDateTimeString();
+ $registerTimeBegin = $filters->register_time_range[0] ? Carbon::parse($filters->register_time_range[0])->toDateString() : '';
+ $registerTimeEnd = $filters->register_time_range[1] ? Carbon::parse($filters->register_time_range[1])->toDateString() : '';
+ if (empty($registerTimeBegin)) {
+ do_log("$logPrefix, exam: {$exam->id} no register_time_begin");
+ return false;
+ }
+ if ($added < $registerTimeBegin) {
+ do_log("$logPrefix, added: $added not after: " . $registerTimeBegin);
+ return false;
+ }
+
+ if (empty($registerTimeEnd)) {
+ do_log("$logPrefix, exam: {$exam->id} no register_time_end");
+ return false;
+ }
+ if ($added > $registerTimeEnd) {
+ do_log("$logPrefix, added: $added not before: " . $registerTimeEnd);
+ return false;
+ }
+ return true;
}
@@ -244,8 +266,16 @@ class ExamRepository extends BaseRepository
do_log("$logPrefix [addProgress] " . nexus_json_encode($data));
$newProgress = $examUser->progresses()->create($data);
$examProgress = $this->calculateProgress($examUser);
- do_log("$logPrefix [updateProgress] " . nexus_json_encode($examProgress));
- $examUser->update(['progress' => $examProgress]);
+ $examProgressFormatted = $this->getProgressFormatted($exam, $examProgress);
+ $examNotPassed = array_filter($examProgressFormatted, function ($item) {
+ return !$item['passed'];
+ });
+ $update = [
+ 'progress' => $examProgress,
+ 'is_done' => count($examNotPassed) ? ExamUser::IS_DONE_NO : ExamUser::IS_DONE_YES,
+ ];
+ do_log("$logPrefix [updateProgress] " . nexus_json_encode($update));
+ $examUser->update($update);
return $newProgress;
}
@@ -253,7 +283,7 @@ class ExamRepository extends BaseRepository
{
$logPrefix = "uid: $uid";
$query = ExamUser::query()->where('uid', $uid)->orderBy('exam_id', 'desc');
- if ($status) {
+ if (!is_null($status)) {
$query->where('status', $status);
}
if (!empty($with)) {
@@ -310,7 +340,7 @@ class ExamRepository extends BaseRepository
->groupBy(['index'])
->get();
- do_log("$logPrefix, query: " . last_query() . ", progressSum: " . $progressSum->toJson());
+ do_log("$logPrefix, " . last_query() . ", progressSum: " . $progressSum->toJson());
return $progressSum->pluck('sum', 'index')->toArray();
@@ -339,7 +369,8 @@ class ExamRepository extends BaseRepository
$currentValueFormatted = $currentValue;
$requireValueAtomic = $requireValue;
}
- $index['require_value_formatted'] = "$requireValue {$index['unit']}";
+ $index['index_formatted'] = nexus_trans('exam.index_text_' . $index['index']);
+ $index['require_value_formatted'] = "$requireValue " . ($index['unit'] ?? '');
$index['current_value'] = $currentValue;
$index['current_value_formatted'] = $currentValueFormatted;
$index['passed'] = $currentValue >= $requireValueAtomic;
@@ -359,6 +390,119 @@ class ExamRepository extends BaseRepository
return $result;
}
+ public function cronjonAssign()
+ {
+ $exams = $this->listValid();
+ if ($exams->isEmpty()) {
+ do_log("No valid exam.");
+ return true;
+ }
+ if ($exams->count() > 1) {
+ do_log("Valid exam more than 1.", "warning");
+ }
+ /** @var Exam $exam */
+ $exam = $exams->first();
+ $userTable = (new User())->getTable();
+ $examUserTable = (new ExamUser())->getTable();
+ User::query()
+ ->leftJoin($examUserTable, function (JoinClause $join) use ($examUserTable, $userTable) {
+ $join->on("$userTable.id", "=", "$examUserTable.uid")
+ ->on("$examUserTable.status", "=", DB::raw(ExamUser::STATUS_NORMAL));
+ })
+ ->whereRaw("$examUserTable.id is null")
+ ->selectRaw("$userTable.*")
+ ->chunk(100, function ($users) use ($exam) {
+ do_log("user count: " . $users->count() . last_query());
+ $insert = [];
+ $now = Carbon::now()->toDateTimeString();
+ foreach ($users as $user) {
+ $logPrefix = sprintf('[assignCronjob] user: %s, exam: %s', $user->id, $exam->id);
+ if (!$this->isExamMatchUser($exam, $user)) {
+ do_log("$logPrefix, exam not match user.");
+ continue;
+ }
+ $insert[] = [
+ 'uid' => $user->id,
+ 'exam_id' => $exam->id,
+ 'created_at' => $now,
+ 'updated_at' => $now,
+ ];
+ do_log("$logPrefix, exam assign to user.");
+ }
+ ExamUser::query()->insert($insert);
+ });
+ return true;
+ }
+
+ public function cronjobCheckout()
+ {
+ $now = Carbon::now()->toDateTimeString();
+ $examUserTable = (new ExamUser())->getTable();
+ $examTable = (new Exam())->getTable();
+ $perPage = 100;
+ $page = 1;
+ $query = ExamUser::query()
+ ->join($examTable, "$examUserTable.exam_id", "=", "$examTable.id")
+ ->where("$examUserTable.status", ExamUser::STATUS_NORMAL)
+ ->whereRaw("if($examUserTable.begin is not null, $examUserTable.begin <= '$now', $examTable.begin <= '$now')")
+ ->whereRaw("if($examUserTable.end is not null, $examUserTable.end >= '$now', $examTable.end >= '$now')")
+ ->selectRaw("$examUserTable.*")
+ ->with(['exam', 'user', 'user.language'])
+ ->orderBy("$examUserTable.id", "asc");
+
+ while (true) {
+ $logPrefix = sprintf('[%s], page: %s', __FUNCTION__, $page);
+ $examUsers = $query->forPage($page, $perPage)->get("$examUserTable.*");
+ if ($examUsers->isEmpty()) {
+ do_log("$logPrefix, no more data..." . last_query());
+ break;
+ } else {
+ do_log("$logPrefix, fetch exam users: {$examUsers->count()}, " . last_query());
+ }
+ $now = Carbon::now()->toDateTimeString();
+ $idArr = $uidToDisable = $messageToSend = [];
+ foreach ($examUsers as $examUser) {
+ $idArr[] = $examUser->id;
+ $uid = $examUser->uid;
+ $currentLogPrefix = sprintf("$logPrefix, user: %s, exam: %s, examUser: %s", $uid, $examUser->exam_id, $examUser->id);
+ if ($examUser->is_done) {
+ do_log("$currentLogPrefix, [is_done]");
+ $messageToSend[] = [
+ 'receiver' => $uid,
+ 'added' => $now,
+ 'subject' => 'Exam passed!',
+ 'msg' => sprintf(
+ 'Congratulations! You have complete the exam: %s in time(%s ~ %s)!',
+ $examUser->exam->name, $examUser->begin ?? $examUser->exam->begin, $examUser->end ?? $examUser->exam->end
+ ),
+ ];
+ } else {
+ do_log("$currentLogPrefix, [will be banned]");
+ //ban user
+ $uidToDisable[] = $uid;
+ $messageToSend[] = [
+ 'receiver' => $uid,
+ 'added' => $now,
+ 'subject' => 'Exam not passed! And your account is banned!',
+ 'msg' => sprintf(
+ 'You did not complete the exam: %s in time(%s ~ %s), so your account has been banned!',
+ $examUser->exam->name, $examUser->begin ?? $examUser->exam->begin, $examUser->end ?? $examUser->exam->end
+ ),
+ ];
+ }
+ }
+ DB::transaction(function () use ($uidToDisable, $messageToSend, $idArr) {
+ ExamUser::query()->whereIn('id', $idArr)->update(['status' => ExamUser::STATUS_FINISHED]);
+ Message::query()->insert($messageToSend);
+ if (!empty($uidToDisable)) {
+ User::query()->whereIn('id', $uidToDisable)->update(['enabled' => User::ENABLED_NO]);
+ }
+ });
+ $page++;
+ }
+ return true;
+ }
+
diff --git a/app/Repositories/MessageRepository.php b/app/Repositories/MessageRepository.php
new file mode 100644
index 00000000..853fc26e
--- /dev/null
+++ b/app/Repositories/MessageRepository.php
@@ -0,0 +1,43 @@
+getSortFieldAndType($params);
+ $query->orderBy($sortField, $sortType);
+ return $query->paginate();
+ }
+
+ public function store(array $params)
+ {
+ $model = Message::query()->create($params);
+ return $model;
+ }
+
+ public function update(array $params, $id)
+ {
+ $model = Message::query()->findOrFail($id);
+ $model->update($params);
+ return $model;
+ }
+
+ public function getDetail($id)
+ {
+ $model = Message::query()->findOrFail($id);
+ return $model;
+ }
+
+ public function delete($id)
+ {
+ $model = Message::query()->findOrFail($id);
+ $result = $model->delete();
+ return $result;
+ }
+}
diff --git a/composer.json b/composer.json
index 4d651252..abbec6f7 100644
--- a/composer.json
+++ b/composer.json
@@ -31,6 +31,7 @@
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "~6.0",
"imdbphp/imdbphp": "^6.4",
+ "laravel-lang/lang": "~7.0",
"laravel/framework": "^8.12",
"laravel/sanctum": "^2.10",
"laravel/tinker": "^2.5",
diff --git a/composer.lock b/composer.lock
index 37eb7556..b552a1ad 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "1e34544d001a98aace85e50adb9ef74e",
+ "content-hash": "b775c2bbc9ad07b2efffe1afb30de056",
"packages": [
{
"name": "asm89/stack-cors",
@@ -934,6 +934,57 @@
"description": "Library for retrieving film and tv information from IMDb",
"time": "2020-11-08T18:20:39+00:00"
},
+ {
+ "name": "laravel-lang/lang",
+ "version": "7.0.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Laravel-Lang/lang.git",
+ "reference": "679a65755db37b35acd36a5e0ca51e055815a00a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Laravel-Lang/lang/zipball/679a65755db37b35acd36a5e0ca51e055815a00a",
+ "reference": "679a65755db37b35acd36a5e0ca51e055815a00a",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*"
+ },
+ "suggest": {
+ "andrey-helldar/laravel-lang-publisher": "Easy installation and update of translation files for your project",
+ "arcanedev/laravel-lang": "Translations manager and checker for Laravel 5",
+ "overtrue/laravel-lang": "Command to add languages in your project"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Laravel-Lang Team"
+ }
+ ],
+ "description": "Languages for Laravel",
+ "keywords": [
+ "lang",
+ "languages",
+ "laravel",
+ "lpm"
+ ],
+ "support": {
+ "issues": "https://github.com/Laravel-Lang/lang/issues",
+ "source": "https://github.com/Laravel-Lang/lang/tree/7.0.9"
+ },
+ "time": "2020-11-30T20:35:41+00:00"
+ },
{
"name": "laravel/framework",
"version": "v8.35.1",
diff --git a/database/migrations/2021_04_19_062743_create_exam_progress_table.php b/database/migrations/2021_04_19_062743_create_exam_progress_table.php
index a7321b41..dd091205 100644
--- a/database/migrations/2021_04_19_062743_create_exam_progress_table.php
+++ b/database/migrations/2021_04_19_062743_create_exam_progress_table.php
@@ -20,7 +20,7 @@ class CreateExamProgressTable extends Migration
$table->integer('uid')->index();
$table->integer('torrent_id');
$table->integer('index');
- $table->integer('value');
+ $table->bigInteger('value');
$table->timestamps();
$table->index('created_at');
});
diff --git a/database/migrations/2021_04_24_084104_create_exam_users_table.php b/database/migrations/2021_04_24_084104_create_exam_users_table.php
index 2baa9e9e..9e75b1c5 100644
--- a/database/migrations/2021_04_24_084104_create_exam_users_table.php
+++ b/database/migrations/2021_04_24_084104_create_exam_users_table.php
@@ -21,6 +21,7 @@ class CreateExamUsersTable extends Migration
$table->dateTime('begin')->nullable();
$table->dateTime('end')->nullable();
$table->text('progress')->nullable();
+ $table->tinyInteger('is_done')->default(0);
$table->timestamps();
});
}
diff --git a/include/functions.php b/include/functions.php
index 5b4b2f7a..f822dc8d 100644
--- a/include/functions.php
+++ b/include/functions.php
@@ -2012,7 +2012,7 @@ function mkprettytime($s) {
}
if ($t["day"])
- return $t["day"] . $lang_functions['text_day'] . sprintf("%02d:%02d:%02d", $t["hour"], $t["min"], $t["sec"]);
+ return $t["day"] . ($lang_functions['text_day'] ?? 'days') . sprintf("%02d:%02d:%02d", $t["hour"], $t["min"], $t["sec"]);
if ($t["hour"])
return sprintf("%d:%02d:%02d", $t["hour"], $t["min"], $t["sec"]);
// if ($t["min"])
diff --git a/include/globalfunctions.php b/include/globalfunctions.php
index 216ef75a..61b55cc2 100644
--- a/include/globalfunctions.php
+++ b/include/globalfunctions.php
@@ -523,7 +523,7 @@ function last_query($all = false)
return nexus_json_encode($query);
}
-function formatDatetime($datetime, $format = 'Y-m-d H:i:s')
+function format_datetime($datetime, $format = 'Y-m-d H:i:s')
{
if ($datetime instanceof \Carbon\Carbon) {
return $datetime->format($format);
@@ -534,9 +534,23 @@ function formatDatetime($datetime, $format = 'Y-m-d H:i:s')
return $datetime;
}
-function formatTtl($seconds)
+function nexus_trans($key)
{
- if ($seconds < 60) {
- return $seconds . "seconds";
+ if (!IN_NEXUS) {
+ return trans($key);
}
+ static $translations;
+ if (is_null($translations)) {
+ $lang = get_langfolder_cookie();
+ $lang = \App\Http\Middleware\Locale::$languageMaps[$lang] ?? 'en';
+ $dir = ROOT_PATH . 'resources/lang/' . $lang;
+ $files = glob($dir . '/*.php');
+ foreach ($files as $file) {
+ $basename = basename($file);
+ $values = require $file;
+ $key = strstr($basename, '.php', true);
+ arr_set($translations, $key, $values);
+ }
+ }
+ return arr_get($translations, $key);
}
diff --git a/nexus/Exam/Exam.php b/nexus/Exam/Exam.php
index efc92ee8..76a3d4df 100644
--- a/nexus/Exam/Exam.php
+++ b/nexus/Exam/Exam.php
@@ -2,50 +2,34 @@
namespace Nexus\Exam;
+use App\Models\ExamUser;
use App\Repositories\ExamRepository;
-use App\Models\Exam as ExamModel;
class Exam
{
public function render($uid)
{
- global $lang_functions;
$examRep = new ExamRepository();
- $userExam = $examRep->getUserExamProgress($uid);
+ $userExam = $examRep->getUserExamProgress($uid, ExamUser::STATUS_NORMAL, ['exam']);
if (empty($userExam)) {
return '';
}
$exam = $userExam->exam;
$row = [];
- $row[] = sprintf('%s:%s', $lang_functions['exam_name'], $exam->name);
- $row[] = sprintf('%s:%s ~ %s', $lang_functions['exam_time_range'], $exam->begin, $exam->end);
- foreach ($exam->indexes as $key => $index) {
+ $row[] = sprintf('%s:%s', nexus_trans('exam.name'), $exam->name);
+ $row[] = sprintf('%s:%s ~ %s', nexus_trans('exam.time_range'), $userExam->begin, $userExam->end);
+ foreach ($userExam->progress_formatted as $key => $index) {
if (isset($index['checked']) && $index['checked']) {
- $requireValue = $index['require_value'];
- $currentValue = $userExam->progress[$index['index']] ?? 0;
- $unit = ExamModel::$indexes[$index['index']]['unit'] ?? '';
$row[] = sprintf(
- '%s:%s, %s:%s %s, %s:%s, %s:%s',
- $lang_functions['exam_index'] . ($key + 1), $lang_functions['exam_index_' . $index['index']] ?? '',
- $lang_functions['exam_require'], $requireValue, $unit,
- $lang_functions['exam_progress_current'], $this->formatCurrentValue($index['index'], $currentValue),
- $lang_functions['exam_progress_result'],
- $currentValue >= $requireValue ? $lang_functions['exam_progress_result_pass_yes'] : $lang_functions['exam_progress_result_pass_no']
+ '%s:%s, %s:%s, %s:%s, %s:%s',
+ nexus_trans('exam.index') . ($key + 1), nexus_trans('exam.index_text_' . $index['index']),
+ nexus_trans('exam.require_value'), $index['require_value_formatted'],
+ nexus_trans('exam.current_value'), $index['current_value_formatted'],
+ nexus_trans('exam.result'),
+ $index['passed'] ? nexus_trans('exam.result_pass') : nexus_trans('exam.result_not_pass')
);
}
}
return nl2br(implode("\n", $row));
}
-
- private function formatCurrentValue($indexId, $value)
- {
- if ($indexId == ExamModel::INDEX_DOWNLOADED || $indexId == ExamModel::INDEX_UPLOADED) {
- return mksize($value);
- }
- if ($indexId == ExamModel::INDEX_SEED_TIME_AVERAGE) {
- return mkprettytime($value);
- }
- return $value;
-
- }
}
diff --git a/resources/lang/en/exam.php b/resources/lang/en/exam.php
new file mode 100644
index 00000000..bfbdbbcb
--- /dev/null
+++ b/resources/lang/en/exam.php
@@ -0,0 +1,16 @@
+ 'Exam name',
+ 'index' => 'Exam index',
+ 'time_range' => 'Exam time',
+ 'index_text_' . \App\Models\Exam::INDEX_UPLOADED => 'Uploaded',
+ 'index_text_' . \App\Models\Exam::INDEX_SEED_TIME_AVERAGE => 'Seed time average',
+ 'index_text_' . \App\Models\Exam::INDEX_DOWNLOADED => 'Downloaded',
+ 'index_text_' . \App\Models\Exam::INDEX_BONUS => 'Bonus',
+ 'require_value' => 'Require',
+ 'current_value' => 'Current',
+ 'result' => 'Result',
+ 'result_pass' => 'Pass!',
+ 'result_not_pass' => 'Not pass!',
+];
diff --git a/resources/lang/zh_CN/auth.php b/resources/lang/zh_CN/auth.php
new file mode 100644
index 00000000..c95c4fa1
--- /dev/null
+++ b/resources/lang/zh_CN/auth.php
@@ -0,0 +1,17 @@
+ '用户名或密码错误',
+ 'throttle' => '您尝试的登录次数过多,请 :seconds 秒后再试。',
+];
diff --git a/resources/lang/zh_CN/exam.php b/resources/lang/zh_CN/exam.php
new file mode 100644
index 00000000..13b90d17
--- /dev/null
+++ b/resources/lang/zh_CN/exam.php
@@ -0,0 +1,16 @@
+ '考核名称',
+ 'index' => '考核指标',
+ 'time_range' => '考核时间',
+ 'index_text_' . \App\Models\Exam::INDEX_UPLOADED => '上传量',
+ 'index_text_' . \App\Models\Exam::INDEX_SEED_TIME_AVERAGE => '平均做种时间',
+ 'index_text_' . \App\Models\Exam::INDEX_DOWNLOADED => '下载量',
+ 'index_text_' . \App\Models\Exam::INDEX_BONUS => '魔力',
+ 'require_value' => '要求',
+ 'current_value' => '当前',
+ 'result' => '结果',
+ 'result_pass' => '通过!',
+ 'result_not_pass' => '未通过!',
+];
diff --git a/resources/lang/zh_CN/pagination.php b/resources/lang/zh_CN/pagination.php
new file mode 100644
index 00000000..a34b4554
--- /dev/null
+++ b/resources/lang/zh_CN/pagination.php
@@ -0,0 +1,17 @@
+ '« 上一页',
+ 'next' => '下一页 »',
+];
diff --git a/resources/lang/zh_CN/passwords.php b/resources/lang/zh_CN/passwords.php
new file mode 100644
index 00000000..91c564cd
--- /dev/null
+++ b/resources/lang/zh_CN/passwords.php
@@ -0,0 +1,20 @@
+ '密码重置成功!',
+ 'sent' => '密码重置邮件已发送!',
+ 'throttled' => '请稍候再试。',
+ 'token' => '密码重置令牌无效。',
+ 'user' => '找不到该邮箱对应的用户。',
+];
diff --git a/resources/lang/zh_CN/validation-inline.php b/resources/lang/zh_CN/validation-inline.php
new file mode 100644
index 00000000..763e060a
--- /dev/null
+++ b/resources/lang/zh_CN/validation-inline.php
@@ -0,0 +1,182 @@
+ '您必须接受。',
+ 'active_url' => '不是一个有效的网址。',
+ 'after' => '必须要晚于 :date。',
+ 'after_or_equal' => '必须要等于 :date 或更晚。',
+ 'alpha' => '只能由字母组成。',
+ 'alpha_dash' => '只能由字母、数字、短划线(-)和下划线(_)组成。',
+ 'alpha_num' => '只能由字母和数字组成。',
+ 'array' => '必须是一个数组。',
+ 'before' => '必须要早于 :date。',
+ 'before_or_equal' => '必须要等于 :date 或更早。',
+ 'between' => [
+ 'numeric' => '必须介于 :min - :max 之间。',
+ 'file' => '必须介于 :min - :max KB 之间。',
+ 'string' => '必须介于 :min - :max 个字符之间。',
+ 'array' => '必须只有 :min - :max 个单元。',
+ ],
+ 'boolean' => '必须为布尔值。',
+ 'confirmed' => '两次输入不一致。',
+ 'date' => '不是一个有效的日期。',
+ 'date_equals' => '必须要等于 :date。',
+ 'date_format' => '格式必须为 :format。',
+ 'different' => '必须和 :other 不同。',
+ 'digits' => '必须是 :digits 位的数字。',
+ 'digits_between' => '必须是介于 :min 和 :max 位的数字。',
+ 'dimensions' => '图片尺寸不正确。',
+ 'distinct' => '已经存在。',
+ 'email' => '不是一个合法的邮箱。',
+ 'ends_with' => '必须以 :values 为结尾。',
+ 'exists' => '不存在。',
+ 'file' => '必须是文件。',
+ 'filled' => '不能为空。',
+ 'gt' => [
+ 'numeric' => '必须大于 :value。',
+ 'file' => '必须大于 :value KB。',
+ 'string' => '必须多于 :value 个字符。',
+ 'array' => '必须多于 :value 个元素。',
+ ],
+ 'gte' => [
+ 'numeric' => '必须大于或等于 :value。',
+ 'file' => '必须大于或等于 :value KB。',
+ 'string' => '必须多于或等于 :value 个字符。',
+ 'array' => '必须多于或等于 :value 个元素。',
+ ],
+ 'image' => '必须是图片。',
+ 'in' => '选项无效。',
+ 'in_array' => '必须在 :other 中。',
+ 'integer' => '必须是整数。',
+ 'ip' => '必须是有效的 IP 地址。',
+ 'ipv4' => '必须是有效的 IPv4 地址。',
+ 'ipv6' => '必须是有效的 IPv6 地址。',
+ 'json' => '必须是正确的 JSON 格式。',
+ 'lt' => [
+ 'numeric' => '必须小于 :value。',
+ 'file' => '必须小于 :value KB。',
+ 'string' => '必须少于 :value 个字符。',
+ 'array' => '必须少于 :value 个元素。',
+ ],
+ 'lte' => [
+ 'numeric' => '必须小于或等于 :value。',
+ 'file' => '必须小于或等于 :value KB。',
+ 'string' => '必须少于或等于 :value 个字符。',
+ 'array' => '必须少于或等于 :value 个元素。',
+ ],
+ 'max' => [
+ 'numeric' => '不能大于 :max。',
+ 'file' => '不能大于 :max KB。',
+ 'string' => '不能大于 :max 个字符。',
+ 'array' => '最多只有 :max 个单元。',
+ ],
+ 'mimes' => '必须是一个 :values 类型的文件。',
+ 'mimetypes' => '必须是一个 :values 类型的文件。',
+ 'min' => [
+ 'numeric' => '必须大于等于 :min。',
+ 'file' => '大小不能小于 :min KB。',
+ 'string' => '至少为 :min 个字符。',
+ 'array' => '至少有 :min 个单元。',
+ ],
+ 'multiple_of' => 'The value must be a multiple of :value',
+ 'not_in' => '选项无效。',
+ 'not_regex' => '格式错误。',
+ 'numeric' => '必须是一个数字。',
+ 'password' => '密码错误',
+ 'present' => '必须存在。',
+ 'regex' => '格式不正确。',
+ 'required' => '不能为空。',
+ 'required_if' => '当 :other 为 :value 时不能为空。',
+ 'required_unless' => '当 :other 不为 :values 时不能为空。',
+ 'required_with' => '当 :values 存在时不能为空。',
+ 'required_with_all' => '当 :values 存在时不能为空。',
+ 'required_without' => '当 :values 不存在时不能为空。',
+ 'required_without_all' => '当 :values 都不存在时不能为空。',
+ 'same' => '必须和 :other 相同。',
+ 'size' => [
+ 'numeric' => '大小必须为 :size。',
+ 'file' => '大小必须为 :size KB。',
+ 'string' => '必须是 :size 个字符。',
+ 'array' => '必须为 :size 个单元。',
+ ],
+ 'starts_with' => '必须以 :values 为开头。',
+ 'string' => '必须是一个字符串。',
+ 'timezone' => '必须是一个合法的时区值。',
+ 'unique' => '已经存在。',
+ 'uploaded' => '上传失败。',
+ 'url' => '格式不正确。',
+ 'uuid' => '必须是有效的 UUID。',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Language Lines
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify custom validation messages for attributes using the
+ | convention "attribute.rule" to name the lines. This makes it quick to
+ | specify a specific custom language line for a given attribute rule.
+ |
+ */
+
+ 'custom' => [
+ 'attribute-name' => [
+ 'rule-name' => 'custom-message',
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Attributes
+ |--------------------------------------------------------------------------
+ |
+ | The following language lines are used to swap our attribute placeholder
+ | with something more reader friendly such as "E-Mail Address" instead
+ | of "email". This simply helps us make our message more expressive.
+ |
+ */
+
+ 'attributes' => [
+ 'name' => '名称',
+ 'username' => '用户名',
+ 'email' => '邮箱',
+ 'first_name' => '名',
+ 'last_name' => '姓',
+ 'password' => '密码',
+ 'password_confirmation' => '确认密码',
+ 'city' => '城市',
+ 'country' => '国家',
+ 'address' => '地址',
+ 'phone' => '电话',
+ 'mobile' => '手机',
+ 'age' => '年龄',
+ 'sex' => '性别',
+ 'gender' => '性别',
+ 'day' => '天',
+ 'month' => '月',
+ 'year' => '年',
+ 'hour' => '时',
+ 'minute' => '分',
+ 'second' => '秒',
+ 'title' => '标题',
+ 'content' => '内容',
+ 'description' => '描述',
+ 'excerpt' => '摘要',
+ 'date' => '日期',
+ 'time' => '时间',
+ 'available' => '可用的',
+ 'size' => '大小',
+ ],
+
+];
diff --git a/resources/lang/zh_CN/validation.php b/resources/lang/zh_CN/validation.php
new file mode 100644
index 00000000..a5a27335
--- /dev/null
+++ b/resources/lang/zh_CN/validation.php
@@ -0,0 +1,180 @@
+ '您必须接受 :attribute。',
+ 'active_url' => ':attribute 不是一个有效的网址。',
+ 'after' => ':attribute 必须要晚于 :date。',
+ 'after_or_equal' => ':attribute 必须要等于 :date 或更晚。',
+ 'alpha' => ':attribute 只能由字母组成。',
+ 'alpha_dash' => ':attribute 只能由字母、数字、短划线(-)和下划线(_)组成。',
+ 'alpha_num' => ':attribute 只能由字母和数字组成。',
+ 'array' => ':attribute 必须是一个数组。',
+ 'before' => ':attribute 必须要早于 :date。',
+ 'before_or_equal' => ':attribute 必须要等于 :date 或更早。',
+ 'between' => [
+ 'numeric' => ':attribute 必须介于 :min - :max 之间。',
+ 'file' => ':attribute 必须介于 :min - :max KB 之间。',
+ 'string' => ':attribute 必须介于 :min - :max 个字符之间。',
+ 'array' => ':attribute 必须只有 :min - :max 个单元。',
+ ],
+ 'boolean' => ':attribute 必须为布尔值。',
+ 'confirmed' => ':attribute 两次输入不一致。',
+ 'date' => ':attribute 不是一个有效的日期。',
+ 'date_equals' => ':attribute 必须要等于 :date。',
+ 'date_format' => ':attribute 的格式必须为 :format。',
+ 'different' => ':attribute 和 :other 必须不同。',
+ 'digits' => ':attribute 必须是 :digits 位数字。',
+ 'digits_between' => ':attribute 必须是介于 :min 和 :max 位的数字。',
+ 'dimensions' => ':attribute 图片尺寸不正确。',
+ 'distinct' => ':attribute 已经存在。',
+ 'email' => ':attribute 不是一个合法的邮箱。',
+ 'ends_with' => ':attribute 必须以 :values 为结尾。',
+ 'exists' => ':attribute 不存在。',
+ 'file' => ':attribute 必须是文件。',
+ 'filled' => ':attribute 不能为空。',
+ 'gt' => [
+ 'numeric' => ':attribute 必须大于 :value。',
+ 'file' => ':attribute 必须大于 :value KB。',
+ 'string' => ':attribute 必须多于 :value 个字符。',
+ 'array' => ':attribute 必须多于 :value 个元素。',
+ ],
+ 'gte' => [
+ 'numeric' => ':attribute 必须大于或等于 :value。',
+ 'file' => ':attribute 必须大于或等于 :value KB。',
+ 'string' => ':attribute 必须多于或等于 :value 个字符。',
+ 'array' => ':attribute 必须多于或等于 :value 个元素。',
+ ],
+ 'image' => ':attribute 必须是图片。',
+ 'in' => '已选的属性 :attribute 无效。',
+ 'in_array' => ':attribute 必须在 :other 中。',
+ 'integer' => ':attribute 必须是整数。',
+ 'ip' => ':attribute 必须是有效的 IP 地址。',
+ 'ipv4' => ':attribute 必须是有效的 IPv4 地址。',
+ 'ipv6' => ':attribute 必须是有效的 IPv6 地址。',
+ 'json' => ':attribute 必须是正确的 JSON 格式。',
+ 'lt' => [
+ 'numeric' => ':attribute 必须小于 :value。',
+ 'file' => ':attribute 必须小于 :value KB。',
+ 'string' => ':attribute 必须少于 :value 个字符。',
+ 'array' => ':attribute 必须少于 :value 个元素。',
+ ],
+ 'lte' => [
+ 'numeric' => ':attribute 必须小于或等于 :value。',
+ 'file' => ':attribute 必须小于或等于 :value KB。',
+ 'string' => ':attribute 必须少于或等于 :value 个字符。',
+ 'array' => ':attribute 必须少于或等于 :value 个元素。',
+ ],
+ 'max' => [
+ 'numeric' => ':attribute 不能大于 :max。',
+ 'file' => ':attribute 不能大于 :max KB。',
+ 'string' => ':attribute 不能大于 :max 个字符。',
+ 'array' => ':attribute 最多只有 :max 个单元。',
+ ],
+ 'mimes' => ':attribute 必须是一个 :values 类型的文件。',
+ 'mimetypes' => ':attribute 必须是一个 :values 类型的文件。',
+ 'min' => [
+ 'numeric' => ':attribute 必须大于等于 :min。',
+ 'file' => ':attribute 大小不能小于 :min KB。',
+ 'string' => ':attribute 至少为 :min 个字符。',
+ 'array' => ':attribute 至少有 :min 个单元。',
+ ],
+ 'multiple_of' => 'The :attribute must be a multiple of :value',
+ 'not_in' => '已选的属性 :attribute 非法。',
+ 'not_regex' => ':attribute 的格式错误。',
+ 'numeric' => ':attribute 必须是一个数字。',
+ 'password' => '密码错误',
+ 'present' => ':attribute 必须存在。',
+ 'regex' => ':attribute 格式不正确。',
+ 'required' => ':attribute 不能为空。',
+ 'required_if' => '当 :other 为 :value 时 :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' => [
+ 'numeric' => ':attribute 大小必须为 :size。',
+ 'file' => ':attribute 大小必须为 :size KB。',
+ 'string' => ':attribute 必须是 :size 个字符。',
+ 'array' => ':attribute 必须为 :size 个单元。',
+ ],
+ 'starts_with' => ':attribute 必须以 :values 为开头。',
+ 'string' => ':attribute 必须是一个字符串。',
+ 'timezone' => ':attribute 必须是一个合法的时区值。',
+ 'unique' => ':attribute 已经存在。',
+ 'uploaded' => ':attribute 上传失败。',
+ 'url' => ':attribute 格式不正确。',
+ 'uuid' => ':attribute 必须是有效的 UUID。',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Language Lines
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify custom validation messages for attributes using the
+ | convention "attribute.rule" to name the lines. This makes it quick to
+ | specify a specific custom language line for a given attribute rule.
+ |
+ */
+
+ 'custom' => [
+ 'attribute-name' => [
+ 'rule-name' => 'custom-message',
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Attributes
+ |--------------------------------------------------------------------------
+ |
+ | The following language lines are used to swap our attribute placeholder
+ | with something more reader friendly such as "E-Mail Address" instead
+ | of "email". This simply helps us make our message more expressive.
+ |
+ */
+
+ 'attributes' => [
+ 'name' => '名称',
+ 'username' => '用户名',
+ 'email' => '邮箱',
+ 'first_name' => '名',
+ 'last_name' => '姓',
+ 'password' => '密码',
+ 'password_confirmation' => '确认密码',
+ 'city' => '城市',
+ 'country' => '国家',
+ 'address' => '地址',
+ 'phone' => '电话',
+ 'mobile' => '手机',
+ 'age' => '年龄',
+ 'sex' => '性别',
+ 'gender' => '性别',
+ 'day' => '天',
+ 'month' => '月',
+ 'year' => '年',
+ 'hour' => '时',
+ 'minute' => '分',
+ 'second' => '秒',
+ 'title' => '标题',
+ 'content' => '内容',
+ 'description' => '描述',
+ 'excerpt' => '摘要',
+ 'date' => '日期',
+ 'time' => '时间',
+ 'available' => '可用的',
+ 'size' => '大小',
+ ],
+];
diff --git a/resources/lang/zh_TW/auth.php b/resources/lang/zh_TW/auth.php
new file mode 100644
index 00000000..9c8a37bf
--- /dev/null
+++ b/resources/lang/zh_TW/auth.php
@@ -0,0 +1,17 @@
+ '使用者名稱或密碼錯誤',
+ 'throttle' => '嘗試登入太多次,請在 :seconds 秒後再試。',
+];
diff --git a/resources/lang/zh_TW/exam.php b/resources/lang/zh_TW/exam.php
new file mode 100644
index 00000000..596c309a
--- /dev/null
+++ b/resources/lang/zh_TW/exam.php
@@ -0,0 +1,16 @@
+ '考核名稱',
+ 'index' => '考核指標',
+ 'time_range' => '考核時間',
+ 'index_text_' . \App\Models\Exam::INDEX_UPLOADED => '上傳量',
+ 'index_text_' . \App\Models\Exam::INDEX_SEED_TIME_AVERAGE => '平均做種時間',
+ 'index_text_' . \App\Models\Exam::INDEX_DOWNLOADED => '下載量',
+ 'index_text_' . \App\Models\Exam::INDEX_BONUS => '魔力',
+ 'require_value' => '要求',
+ 'current_value' => '當前',
+ 'result' => '結果',
+ 'result_pass' => '通過!',
+ 'result_not_pass' => '未通過!',
+];
diff --git a/resources/lang/zh_TW/pagination.php b/resources/lang/zh_TW/pagination.php
new file mode 100644
index 00000000..5b4d4046
--- /dev/null
+++ b/resources/lang/zh_TW/pagination.php
@@ -0,0 +1,17 @@
+ '« 上一頁',
+ 'next' => '下一頁 »',
+];
diff --git a/resources/lang/zh_TW/passwords.php b/resources/lang/zh_TW/passwords.php
new file mode 100644
index 00000000..aa5c23a6
--- /dev/null
+++ b/resources/lang/zh_TW/passwords.php
@@ -0,0 +1,20 @@
+ '密碼已成功重設!',
+ 'sent' => '密碼重設郵件已發送!',
+ 'throttled' => '請稍候再試。',
+ 'token' => '密碼重設碼無效。',
+ 'user' => '找不到該 E-mail 對應的使用者。',
+];
diff --git a/resources/lang/zh_TW/validation-inline.php b/resources/lang/zh_TW/validation-inline.php
new file mode 100644
index 00000000..f511fbfc
--- /dev/null
+++ b/resources/lang/zh_TW/validation-inline.php
@@ -0,0 +1,181 @@
+ '必須接受。',
+ 'active_url' => '不是有效的網址。',
+ 'after' => '必須要晚於 :date。',
+ 'after_or_equal' => '必須要等於 :date 或更晚。',
+ 'alpha' => '只能以字母組成。',
+ 'alpha_dash' => '只能以字母、數字、連接線(-)及底線(_)組成。',
+ 'alpha_num' => '只能以字母及數字組成。',
+ 'array' => '必須為陣列。',
+ 'before' => '必須要早於 :date。',
+ 'before_or_equal' => '必須要等於 :date 或更早。',
+ 'between' => [
+ 'numeric' => '必須介於 :min 至 :max 之間。',
+ 'file' => '必須介於 :min 至 :max KB 之間。 ',
+ 'string' => '必須介於 :min 至 :max 個字元之間。',
+ 'array' => '必須有 :min - :max 個元素。',
+ ],
+ 'boolean' => '必須為布林值。',
+ 'confirmed' => '確認欄位的輸入不一致。',
+ 'date' => '不是有效的日期。',
+ 'date_equals' => '必須等於 :date。',
+ 'date_format' => '不符合 :format 的格式。',
+ 'different' => '必須與 :other 不同。',
+ 'digits' => '必須是 :digits 位數字。',
+ 'digits_between' => '必須介於 :min 至 :max 位數字。',
+ 'dimensions' => '圖片尺寸不正確。',
+ 'distinct' => '已經存在。',
+ 'email' => '必須是有效的 E-mail。',
+ 'ends_with' => '結尾必須包含下列之一::values。',
+ 'exists' => '不存在。',
+ 'file' => '必須是有效的檔案。',
+ 'filled' => '不能留空。',
+ 'gt' => [
+ 'numeric' => '必須大於 :value。',
+ 'file' => '必須大於 :value KB。',
+ 'string' => '必須多於 :value 個字元。',
+ 'array' => '必須多於 :value 個元素。',
+ ],
+ 'gte' => [
+ 'numeric' => '必須大於或等於 :value。',
+ 'file' => '必須大於或等於 :value KB。',
+ 'string' => '必須多於或等於 :value 個字元。',
+ 'array' => '必須多於或等於 :value 個元素。',
+ ],
+ 'image' => '必須是一張圖片。',
+ 'in' => '選項無效。',
+ 'in_array' => '沒有在 :other 中。',
+ 'integer' => '必須是一個整數。',
+ 'ip' => '必須是一個有效的 IP 位址。',
+ 'ipv4' => '必須是一個有效的 IPv4 位址。',
+ 'ipv6' => '必須是一個有效的 IPv6 位址。',
+ 'json' => '必須是正確的 JSON 字串。',
+ 'lt' => [
+ 'numeric' => '必須小於 :value。',
+ 'file' => '必須小於 :value KB。',
+ 'string' => '必須少於 :value 個字元。',
+ 'array' => '必須少於 :value 個元素。',
+ ],
+ 'lte' => [
+ 'numeric' => '必須小於或等於 :value。',
+ 'file' => '必須小於或等於 :value KB。',
+ 'string' => '必須少於或等於 :value 個字元。',
+ 'array' => '必須少於或等於 :value 個元素。',
+ ],
+ 'max' => [
+ 'numeric' => '不能大於 :max。',
+ 'file' => '不能大於 :max KB。',
+ 'string' => '不能多於 :max 個字元。',
+ 'array' => '最多有 :max 個元素。',
+ ],
+ 'mimes' => '必須為 :values 的檔案。',
+ 'mimetypes' => '必須為 :values 的檔案。',
+ 'min' => [
+ 'numeric' => '不能小於 :min。',
+ 'file' => '不能小於 :min KB。',
+ 'string' => '不能小於 :min 個字元。',
+ 'array' => '至少有 :min 個元素。',
+ ],
+ 'multiple_of' => 'The value must be a multiple of :value',
+ 'not_in' => '選項無效。',
+ 'not_regex' => '格式錯誤。',
+ 'numeric' => '必須為一個數字。',
+ 'password' => '密碼錯誤',
+ 'present' => '必須存在。',
+ 'regex' => '格式錯誤。',
+ 'required' => '不能留空。',
+ 'required_if' => '當 :other 是 :value 時不能留空。',
+ 'required_unless' => '當 :other 不是 :values 時不能留空。',
+ 'required_with' => '當 :values 出現時不能留空。',
+ 'required_with_all' => '當 :values 出現時不能為空。',
+ 'required_without' => '當 :values 留空時field 不能留空。',
+ 'required_without_all' => '當 :values 都不出現時不能留空。',
+ 'same' => '必須與 :other 相同。',
+ 'size' => [
+ 'numeric' => '大小必須是 :size。',
+ 'file' => '大小必須是 :size KB。',
+ 'string' => '必須是 :size 個字元。',
+ 'array' => '必須是 :size 個元素。',
+ ],
+ 'starts_with' => '開頭必須包含下列之一::values。',
+ 'string' => '必須是一個字串。',
+ 'timezone' => '必須是一個正確的時區值。',
+ 'unique' => '已經存在。',
+ 'uploaded' => '上傳失敗。',
+ 'url' => '格式錯誤。',
+ 'uuid' => '必須是有效的 UUID。',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Language Lines
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify custom validation messages for attributes using the
+ | convention "attribute.rule" to name the lines. This makes it quick to
+ | specify a specific custom language line for a given attribute rule.
+ |
+ */
+
+ 'custom' => [
+ 'attribute-name' => [
+ 'rule-name' => 'custom-message',
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Attributes
+ |--------------------------------------------------------------------------
+ |
+ | The following language lines are used to swap our attribute placeholder
+ | with something more reader friendly such as "E-Mail Address" instead
+ | of "email". This simply helps us make our message more expressive.
+ |
+ */
+
+ 'attributes' => [
+ 'address' => '地址',
+ 'age' => '年齡',
+ 'available' => '可用的',
+ 'city' => '城市',
+ 'content' => '內容',
+ 'country' => '國家',
+ 'date' => '日期',
+ 'day' => '天',
+ 'description' => '描述',
+ 'email' => 'E-mail',
+ 'excerpt' => '摘要',
+ 'first_name' => '名',
+ 'gender' => '性別',
+ 'hour' => '時',
+ 'last_name' => '姓',
+ 'minute' => '分',
+ 'mobile' => '手機',
+ 'month' => '月',
+ 'name' => '名稱',
+ 'password' => '密碼',
+ 'password_confirmation' => '確認密碼',
+ 'phone' => '電話',
+ 'second' => '秒',
+ 'sex' => '性別',
+ 'size' => '大小',
+ 'time' => '時間',
+ 'title' => '標題',
+ 'username' => '使用者名稱',
+ 'year' => '年',
+ ],
+
+];
diff --git a/resources/lang/zh_TW/validation.php b/resources/lang/zh_TW/validation.php
new file mode 100644
index 00000000..70f75b2e
--- /dev/null
+++ b/resources/lang/zh_TW/validation.php
@@ -0,0 +1,180 @@
+ '必須接受 :attribute。',
+ 'active_url' => ':attribute 不是有效的網址。',
+ 'after' => ':attribute 必須要晚於 :date。',
+ 'after_or_equal' => ':attribute 必須要等於 :date 或更晚。',
+ 'alpha' => ':attribute 只能以字母組成。',
+ 'alpha_dash' => ':attribute 只能以字母、數字、連接線(-)及底線(_)組成。',
+ 'alpha_num' => ':attribute 只能以字母及數字組成。',
+ 'array' => ':attribute 必須為陣列。',
+ 'before' => ':attribute 必須要早於 :date。',
+ 'before_or_equal' => ':attribute 必須要等於 :date 或更早。',
+ 'between' => [
+ 'numeric' => ':attribute 必須介於 :min 至 :max 之間。',
+ 'file' => ':attribute 必須介於 :min 至 :max KB 之間。 ',
+ 'string' => ':attribute 必須介於 :min 至 :max 個字元之間。',
+ 'array' => ':attribute: 必須有 :min - :max 個元素。',
+ ],
+ 'boolean' => ':attribute 必須為布林值。',
+ 'confirmed' => ':attribute 確認欄位的輸入不一致。',
+ 'date' => ':attribute 不是有效的日期。',
+ 'date_equals' => ':attribute 必須等於 :date。',
+ 'date_format' => ':attribute 不符合 :format 的格式。',
+ 'different' => ':attribute 與 :other 必須不同。',
+ 'digits' => ':attribute 必須是 :digits 位數字。',
+ 'digits_between' => ':attribute 必須介於 :min 至 :max 位數字。',
+ 'dimensions' => ':attribute 圖片尺寸不正確。',
+ 'distinct' => ':attribute 已經存在。',
+ 'email' => ':attribute 必須是有效的 E-mail。',
+ 'ends_with' => ':attribute 結尾必須包含下列之一::values。',
+ 'exists' => ':attribute 不存在。',
+ 'file' => ':attribute 必須是有效的檔案。',
+ 'filled' => ':attribute 不能留空。',
+ 'gt' => [
+ 'numeric' => ':attribute 必須大於 :value。',
+ 'file' => ':attribute 必須大於 :value KB。',
+ 'string' => ':attribute 必須多於 :value 個字元。',
+ 'array' => ':attribute 必須多於 :value 個元素。',
+ ],
+ 'gte' => [
+ 'numeric' => ':attribute 必須大於或等於 :value。',
+ 'file' => ':attribute 必須大於或等於 :value KB。',
+ 'string' => ':attribute 必須多於或等於 :value 個字元。',
+ 'array' => ':attribute 必須多於或等於 :value 個元素。',
+ ],
+ 'image' => ':attribute 必須是一張圖片。',
+ 'in' => '所選擇的 :attribute 選項無效。',
+ 'in_array' => ':attribute 沒有在 :other 中。',
+ 'integer' => ':attribute 必須是一個整數。',
+ 'ip' => ':attribute 必須是一個有效的 IP 位址。',
+ 'ipv4' => ':attribute 必須是一個有效的 IPv4 位址。',
+ 'ipv6' => ':attribute 必須是一個有效的 IPv6 位址。',
+ 'json' => ':attribute 必須是正確的 JSON 字串。',
+ 'lt' => [
+ 'numeric' => ':attribute 必須小於 :value。',
+ 'file' => ':attribute 必須小於 :value KB。',
+ 'string' => ':attribute 必須少於 :value 個字元。',
+ 'array' => ':attribute 必須少於 :value 個元素。',
+ ],
+ 'lte' => [
+ 'numeric' => ':attribute 必須小於或等於 :value。',
+ 'file' => ':attribute 必須小於或等於 :value KB。',
+ 'string' => ':attribute 必須少於或等於 :value 個字元。',
+ 'array' => ':attribute 必須少於或等於 :value 個元素。',
+ ],
+ 'max' => [
+ 'numeric' => ':attribute 不能大於 :max。',
+ 'file' => ':attribute 不能大於 :max KB。',
+ 'string' => ':attribute 不能多於 :max 個字元。',
+ 'array' => ':attribute 最多有 :max 個元素。',
+ ],
+ 'mimes' => ':attribute 必須為 :values 的檔案。',
+ 'mimetypes' => ':attribute 必須為 :values 的檔案。',
+ 'min' => [
+ 'numeric' => ':attribute 不能小於 :min。',
+ 'file' => ':attribute 不能小於 :min KB。',
+ 'string' => ':attribute 不能小於 :min 個字元。',
+ 'array' => ':attribute 至少有 :min 個元素。',
+ ],
+ 'multiple_of' => 'The :attribute must be a multiple of :value',
+ 'not_in' => '所選擇的 :attribute 選項無效。',
+ 'not_regex' => ':attribute 的格式錯誤。',
+ 'numeric' => ':attribute 必須為一個數字。',
+ 'password' => '密碼錯誤',
+ 'present' => ':attribute 必須存在。',
+ 'regex' => ':attribute 的格式錯誤。',
+ 'required' => ':attribute 不能留空。',
+ 'required_if' => '當 :other 是 :value 時 :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' => [
+ 'numeric' => ':attribute 的大小必須是 :size。',
+ 'file' => ':attribute 的大小必須是 :size KB。',
+ 'string' => ':attribute 必須是 :size 個字元。',
+ 'array' => ':attribute 必須是 :size 個元素。',
+ ],
+ 'starts_with' => ':attribute 開頭必須包含下列之一::values。',
+ 'string' => ':attribute 必須是一個字串。',
+ 'timezone' => ':attribute 必須是一個正確的時區值。',
+ 'unique' => ':attribute 已經存在。',
+ 'uploaded' => ':attribute 上傳失敗。',
+ 'url' => ':attribute 的格式錯誤。',
+ 'uuid' => ':attribute 必須是有效的 UUID。',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Language Lines
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify custom validation messages for attributes using the
+ | convention "attribute.rule" to name the lines. This makes it quick to
+ | specify a specific custom language line for a given attribute rule.
+ |
+ */
+
+ 'custom' => [
+ 'attribute-name' => [
+ 'rule-name' => 'custom-message',
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Attributes
+ |--------------------------------------------------------------------------
+ |
+ | The following language lines are used to swap our attribute placeholder
+ | with something more reader friendly such as "E-Mail Address" instead
+ | of "email". This simply helps us make our message more expressive.
+ |
+ */
+
+ 'attributes' => [
+ 'address' => '地址',
+ 'age' => '年齡',
+ 'available' => '可用的',
+ 'city' => '城市',
+ 'content' => '內容',
+ 'country' => '國家',
+ 'date' => '日期',
+ 'day' => '天',
+ 'description' => '描述',
+ 'email' => 'E-mail',
+ 'excerpt' => '摘要',
+ 'first_name' => '名',
+ 'gender' => '性別',
+ 'hour' => '時',
+ 'last_name' => '姓',
+ 'minute' => '分',
+ 'mobile' => '手機',
+ 'month' => '月',
+ 'name' => '名稱',
+ 'password' => '密碼',
+ 'password_confirmation' => '確認密碼',
+ 'phone' => '電話',
+ 'second' => '秒',
+ 'sex' => '性別',
+ 'size' => '大小',
+ 'time' => '時間',
+ 'title' => '標題',
+ 'username' => '使用者名稱',
+ 'year' => '年',
+ ],
+];
diff --git a/routes/api.php b/routes/api.php
index df1a24f7..bfe92e1e 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -14,7 +14,7 @@ use Illuminate\Support\Facades\Route;
|
*/
-Route::group(['middleware' => ['auth:sanctum', 'permission']], function () {
+Route::group(['middleware' => ['auth:sanctum', 'permission', 'locale']], function () {
Route::post('logout', [\App\Http\Controllers\AuthenticateController::class, 'logout']);
Route::resource('agent-allow', \App\Http\Controllers\AgentAllowController::class);