From eb8123c7b3bfeeddac59e6adbc8bc3c3b07ce095 Mon Sep 17 00:00:00 2001 From: zhenhui <1276357500@qq.com> Date: Tue, 17 Mar 2026 16:31:31 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=90=8E=E5=8F=B0=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E4=B8=AD=E8=8B=B1=E6=96=87=E5=AF=B9=E7=85=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- saiadmin-artd/src/utils/http/index.ts | 9 ++ server/app/api/lang/en.php | 99 +++++++++++++ server/app/api/logic/PlayStartLogic.php | 3 +- server/app/api/util/ApiLang.php | 66 ++++++--- .../app/dice/logic/reward/DiceRewardLogic.php | 7 +- .../saiadmin/exception/ApiException.php | 5 +- server/resource/translations/api/en.php | 136 ++++++++++++++++++ 7 files changed, 295 insertions(+), 30 deletions(-) create mode 100644 server/resource/translations/api/en.php diff --git a/saiadmin-artd/src/utils/http/index.ts b/saiadmin-artd/src/utils/http/index.ts index 305164a..ab2cda3 100644 --- a/saiadmin-artd/src/utils/http/index.ts +++ b/saiadmin-artd/src/utils/http/index.ts @@ -16,11 +16,19 @@ import axios, { AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios' import { useUserStore } from '@/store/modules/user' +import i18n from '@/locales' import { ApiStatus } from './status' import { HttpError, handleError, showError, showSuccess } from './error' import { $t } from '@/locales' import { BaseResponse } from '@/types' +/** 当前语言(zh/en),供请求头 lang 使用,无 header 时后端按后台语言回包 */ +function getRequestLang(): string { + const locale = i18n.global.locale as string | { value: string } + const lang = typeof locale === 'string' ? locale : (locale && 'value' in locale ? locale.value : 'zh') + return lang === 'en' ? 'en' : 'zh' +} + /** 请求配置常量(超时时间 30s) */ const REQUEST_TIMEOUT = 30000 const LOGOUT_DELAY = 500 @@ -66,6 +74,7 @@ axiosInstance.interceptors.request.use( (request: InternalAxiosRequestConfig) => { const { accessToken } = useUserStore() if (accessToken) request.headers.set('Authorization', `Bearer ` + accessToken) + request.headers.set('lang', getRequestLang()) if (request.data && !(request.data instanceof FormData) && !request.headers['Content-Type']) { request.headers.set('Content-Type', 'application/json') diff --git a/server/app/api/lang/en.php b/server/app/api/lang/en.php index b0b851f..4c02220 100644 --- a/server/app/api/lang/en.php +++ b/server/app/api/lang/en.php @@ -36,7 +36,53 @@ return [ // PlayStartLogic / GameLogic '抽奖券不足' => 'Insufficient lottery tickets', '奖池配置不存在' => 'Lottery config not found', + '配置ID %s 不存在或档位为空' => 'Config ID %s not found or tier is empty', '该方向下暂无可用路径配置' => 'No path config available for this direction', + // Dice / pool config + '奖池配置不存在(需 type=0)' => 'Lottery pool config not found (type=0 required)', + '暂无可用奖励配置' => 'No available reward config', + '未找到 type=0 的奖池配置,请先创建' => 'No type=0 pool config found, please create one first', + // Dice / wallet & tickets + '参数错误:需要有效的 player_id 和 type(3=加点,4=扣点)' => 'Invalid params: player_id and type are required (3=add, 4=deduct)', + '平台币变动必须大于 0' => 'Coin change must be greater than 0', + '玩家不存在' => 'Player not found', + '扣点数量不能大于当前余额' => 'Deduct amount cannot exceed current balance', + // Dice / reward config record + '测试记录不存在' => 'Test record not found', + '付费奖池配置不存在' => 'Paid pool config not found', + '免费奖池配置不存在' => 'Free pool config not found', + '各抽奖次数仅支持 0、100、500、1000、5000' => 'Counts only support 0, 100, 500, 1000, 5000', + '付费或免费至少一种方向次数之和大于 0' => 'Sum of paid/free direction counts must be greater than 0', + '付费未选择奖池配置时,请填写付费自定义档位概率(T1~T5)' => 'When paid pool is not selected, please fill paid custom tier probabilities (T1–T5)', + '付费档位概率每档只能 0-100%' => 'Paid tier probability must be between 0 and 100%', + '付费档位概率 T1~T5 之和不能超过 100%' => 'Paid tier probabilities (T1–T5) sum cannot exceed 100%', + '免费未选择奖池配置时,请填写免费自定义档位概率(T1~T5)' => 'When free pool is not selected, please fill free custom tier probabilities (T1–T5)', + '免费档位概率每档只能 0-100%' => 'Free tier probability must be between 0 and 100%', + '免费档位概率 T1~T5 之和不能超过 100%' => 'Free tier probabilities (T1–T5) sum cannot exceed 100%', + // Dice / reward + '存在无效的配置ID' => 'Invalid config ID exists', + '存在无效的 DiceReward id' => 'Invalid DiceReward id exists', + '奖励配置为空,请先维护 dice_reward_config' => 'Reward config is empty, please maintain dice_reward_config first', + // Dice / reward_config + '测试次数仅支持 100、500、1000、5000、10000' => 'Test count only supports 100, 500, 1000, 5000, 10000', + // SaiAdmin permissions & auth + '没有权限操作该部门数据' => 'No permission to operate department data', + '没有权限操作该角色数据' => 'No permission to operate role data', + '没有权限操作该数据' => 'No permission to operate this data', + '禁止批量删除操作' => 'Batch delete is not allowed', + '超级管理员禁止删除' => 'Super admin cannot be deleted', + '原密码错误' => 'Old password is incorrect', + '上级部门和当前部门不能相同' => 'Parent department cannot be the same as current department', + '不能将上级部门设置为当前部门的子部门' => 'Cannot set parent department to a child of current department', + '该部门下存在子部门,请先删除子部门' => 'This department has sub-departments, please delete them first', + '该部门下存在用户,请先删除或者转移用户' => 'This department has users, please delete or transfer them first', + '您的登录凭证错误或者已过期,请重新登录' => 'Your login credential is invalid or expired, please login again', + '登录凭证校验失败' => 'Login credential verification failed', + // Saipackage install + '插件的基础配置信息错误' => 'Plugin base config is invalid', + '插件已经存在' => 'Plugin already exists', + '该插件的安装目录已经被占用' => 'Plugin install directory is already occupied', + '文件不存在' => 'File not found', // UserLogic '手机号格式错误,仅支持 +60 开头的马来西亚号码(如 +60123456789)' => 'Invalid phone format, only +60 Malaysia numbers supported (e.g. +60123456789)', // TokenMiddleware / Auth (api/user/*, api/game/*) @@ -50,4 +96,57 @@ return [ '请注册' => 'Please register', '请重新登录' => 'Please login again', '请重新登录(当前账号已在其他处登录)' => 'Please login again (account logged in elsewhere)', + // DiceRewardLogic 动态文案(占位符) + '奖励配置需覆盖 26 个格位(id 0-25 或 1-26),当前仅 %s 条,无法完整生成 5-30 共26个点数、顺时针与逆时针的奖励对照' => 'Reward config must cover 26 cells (id 0-25 or 1-26), currently only %s, cannot generate full 5-30 points and clockwise/counterclockwise mapping', + // SystemUserLogic / BaseController 等(validate 等动态 message 无 key,保留原文) + // CheckLogin + // BaseLogic / Crontab / Menu / Post / Role / Dict / Config / Category / Attachment / Database + '数据不存在' => 'Data not found', + '不能设置父级为自身' => 'Cannot set parent to self', + '该菜单下存在子菜单,请先删除子菜单' => 'This menu has sub-menus, please delete them first', + '导入文件错误,请上传正确的文件格式xlsx' => 'Import file error, please upload correct xlsx file', + '不能操作比当前账户职级高的角色' => 'Cannot operate roles with higher level than current account', + '该字典标识已存在' => 'This dict code already exists', + '修改数据异常,请检查' => 'Update data error, please check', + '删除数据异常,请检查' => 'Delete data error, please check', + '字典类型不存在' => 'Dict type not found', + '配置数据未找到' => 'Config data not found', + '系统默认分组,无法删除' => 'System default group cannot be deleted', + '配置组未找到' => 'Config group not found', + '上级分类和当前分类不能相同' => 'Parent category cannot be the same as current', + '不能将上级分类设置为当前分类的子分类' => 'Cannot set parent category as child of current', + '该部门下存在子分类,请先删除子分类' => 'This category has sub-categories, please delete them first', + '目标分类不存在' => 'Target category not found', + '获取文件资源失败' => 'Failed to get file resource', + '创建图片资源失败' => 'Failed to create image resource', + '文件格式错误' => 'Invalid file format', + '文件保存失败' => 'Failed to save file', + '当前表不支持回收站功能' => 'Current table does not support recycle bin', + '模板不存在' => 'Template not found', + '任务类型异常' => 'Invalid task type', + '数据库配置读取失败' => 'Failed to read database config', + '应用类型必须为plugin或者app' => 'App type must be plugin or app', + '请先设置应用名称' => 'Please set app name first', + '请选择要生成的表' => 'Please select tables to generate', + '非调试模式下,不允许生成文件' => 'File generation not allowed in non-debug mode', + '登录凭获取失败,请检查' => 'Failed to get login credential, please check', + '文件大小超过限制' => 'File size exceeds limit', + '不支持该格式的文件上传' => 'File format not supported for upload', + '该上传模式不存在' => 'Upload mode not found', + '切片上传服务必须在 HTTP 请求环境下调用' => 'Chunk upload must be called in HTTP request context', + '切片文件查找失败,请重新上传' => 'Chunk file not found, please upload again', + '未设置邮件配置' => 'Mail config not set', + '请执行 composer require phpmailer/phpmailer 并重启' => 'Please run composer require phpmailer/phpmailer and restart', + '仅超级管理员能够操作' => 'Only super admin can perform this action', + '等待依赖安装' => 'Waiting for dependencies to be installed', + '插件目录不存在' => 'Plugin directory not found', + '该插件的基础配置信息不完善' => 'Plugin base config is incomplete', + '参数错误' => 'Invalid parameters', + '不能设置父级为自身' => 'Cannot set parent to self', + '该分类下存在子分类,请先删除子分类' => 'This category has sub-categories, please delete them first', + '无法打开文件,或者文件创建失败' => 'Cannot open file or create file failed', + '系统生成文件错误' => 'System file generation error', + '模板目录不存在!' => 'Template directory not found', + '文件类型异常,无法生成指定文件!' => 'Invalid file type, cannot generate file', + '前端目录查找失败,必须与后端目录为同级目录!' => 'Frontend directory not found, must be same level as backend', ]; diff --git a/server/app/api/logic/PlayStartLogic.php b/server/app/api/logic/PlayStartLogic.php index 2ff4037..c03f4ef 100644 --- a/server/app/api/logic/PlayStartLogic.php +++ b/server/app/api/logic/PlayStartLogic.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace app\api\logic; use app\api\cache\UserCache; +use app\api\util\ApiLang; use app\api\service\LotteryService; use app\dice\model\lottery_pool_config\DiceLotteryPoolConfig; use app\dice\model\play_record\DicePlayRecord; @@ -59,7 +60,7 @@ class PlayStartLogic $minCoin = abs($minEv + self::MIN_COIN_EXTRA); $coin = (float) $player->coin; if ($coin < $minCoin) { - throw new ApiException('当前玩家余额'.$coin.'小于'.$minCoin.'无法继续游戏'); + throw new ApiException(ApiLang::translateParams('当前玩家余额%s小于%s无法继续游戏', [$coin, $minCoin])); } $paid = (int) ($player->paid_ticket_count ?? 0); diff --git a/server/app/api/util/ApiLang.php b/server/app/api/util/ApiLang.php index e688162..1ee3463 100644 --- a/server/app/api/util/ApiLang.php +++ b/server/app/api/util/ApiLang.php @@ -6,7 +6,9 @@ namespace app\api\util; use support\Request; /** - * API 多语言:根据请求头 lang(en=英文,zh=中文)翻译返回文案 + * API 多语言(兼容 Webman 多语言配置) + * 根据请求头 lang(zh=中文,en=英文)返回对应文案; + * 无 lang 请求头时使用 config('translation.locale') 推断(zh_CN/zh→中文,en→英文) */ class ApiLang { @@ -14,35 +16,34 @@ class ApiLang private const LANG_EN = 'en'; private const LANG_ZH = 'zh'; - /** @var array|null */ - private static ?array $enMap = null; + /** @var array> locale => [ 中文 => 译文 ] */ + private static array $messages = []; /** - * 从请求中获取语言:lang 请求头 en=英文,zh=中文,默认 zh + * 从请求中获取语言:优先读 header lang,否则按 Webman config('translation.locale') 推断 */ public static function getLang(?Request $request = null): string { $request = $request ?? (function_exists('request') ? request() : null); - if ($request === null) { - return self::LANG_ZH; - } - $lang = $request->header(self::LANG_HEADER); - if ($lang !== null && $lang !== '') { - $lang = strtolower(trim((string) $lang)); - if ($lang === self::LANG_EN) { - return self::LANG_EN; - } - if ($lang === self::LANG_ZH || $lang === 'chs') { - return self::LANG_ZH; + if ($request !== null) { + $lang = $request->header(self::LANG_HEADER); + if ($lang !== null && $lang !== '') { + $lang = strtolower(trim((string) $lang)); + if ($lang === self::LANG_EN) { + return self::LANG_EN; + } + if ($lang === self::LANG_ZH || $lang === 'chs') { + return self::LANG_ZH; + } } } - return self::LANG_ZH; + $locale = (string) (function_exists('config') ? config('translation.locale', 'zh_CN') : 'zh_CN'); + return stripos($locale, 'en') !== false ? self::LANG_EN : self::LANG_ZH; } /** - * 翻译文案:当前请求语言为 en 时返回英文,否则返回原文(中文) - * @param string $message 中文或原文 - * @param Request|null $request 当前请求,不传则自动取 request() + * 翻译文案:lang=zh 返回原文(中文),lang=en 返回英文映射 + * 语言文件优先从 Webman resource/translations/api/{locale}.php 加载,否则从 app/api/lang 加载 */ public static function translate(string $message, ?Request $request = null): string { @@ -50,11 +51,30 @@ class ApiLang if ($lang !== self::LANG_EN) { return $message; } - if (self::$enMap === null) { - $path = dirname(__DIR__) . '/lang/en.php'; - self::$enMap = is_file($path) ? (require $path) : []; + $map = self::loadMessages(self::LANG_EN); + return $map[$message] ?? $message; + } + + /** + * 加载某语言的 API 文案(key=中文,value=译文) + */ + private static function loadMessages(string $locale): array + { + if (isset(self::$messages[$locale])) { + return self::$messages[$locale]; } - return self::$enMap[$message] ?? $message; + $path = null; + if (function_exists('config')) { + $base = rtrim((string) config('translation.path', ''), DIRECTORY_SEPARATOR); + if ($base !== '') { + $path = $base . DIRECTORY_SEPARATOR . 'api' . DIRECTORY_SEPARATOR . $locale . '.php'; + } + } + if (($path === null || !is_file($path)) && $locale === self::LANG_EN) { + $path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . 'en.php'; + } + self::$messages[$locale] = ($path !== null && is_file($path)) ? (require $path) : []; + return self::$messages[$locale]; } /** diff --git a/server/app/dice/logic/reward/DiceRewardLogic.php b/server/app/dice/logic/reward/DiceRewardLogic.php index 6bb9e3e..047c93c 100644 --- a/server/app/dice/logic/reward/DiceRewardLogic.php +++ b/server/app/dice/logic/reward/DiceRewardLogic.php @@ -92,7 +92,7 @@ class DiceRewardLogic $tier = DiceRewardConfig::where('id', $id)->value('tier'); if ($tier === null || $tier === '') { - throw new ApiException('配置ID ' . $id . ' 不存在或档位为空'); + throw new ApiException(\app\api\util\ApiLang::translateParams('配置ID %s 不存在或档位为空', [$id])); } $tier = (string) $tier; @@ -318,7 +318,10 @@ class DiceRewardLogic $configCount = count($list); if ($configCount < self::BOARD_SIZE) { throw new ApiException( - '奖励配置需覆盖 26 个格位(id 0-25 或 1-26),当前仅 ' . $configCount . ' 条,无法完整生成 5-30 共26个点数、顺时针与逆时针的奖励对照' + \app\api\util\ApiLang::translateParams( + '奖励配置需覆盖 26 个格位(id 0-25 或 1-26),当前仅 %s 条,无法完整生成 5-30 共26个点数、顺时针与逆时针的奖励对照', + [$configCount] + ) ); } diff --git a/server/plugin/saiadmin/exception/ApiException.php b/server/plugin/saiadmin/exception/ApiException.php index 7d8a4b7..4b2191e 100644 --- a/server/plugin/saiadmin/exception/ApiException.php +++ b/server/plugin/saiadmin/exception/ApiException.php @@ -18,10 +18,7 @@ class ApiException extends BusinessException public function render(Request $request): ?Response { $message = $this->getMessage(); - $path = $request->path(); - if (str_contains($path, 'api/')) { - $message = \app\api\util\ApiLang::translate($message, $request); - } + $message = \app\api\util\ApiLang::translate($message, $request); return json(['code' => $this->getCode() ?: 500, 'message' => $message]); } } \ No newline at end of file diff --git a/server/resource/translations/api/en.php b/server/resource/translations/api/en.php new file mode 100644 index 0000000..fc153f4 --- /dev/null +++ b/server/resource/translations/api/en.php @@ -0,0 +1,136 @@ + 'Success', + 'fail' => 'Fail', + 'username、password 不能为空' => 'username and password are required', + '请携带 token' => 'Please provide token', + 'token 无效' => 'Invalid or expired token', + '已退出登录' => 'Logged out successfully', + '用户不存在' => 'User not found', + 'username 不能为空' => 'username is required', + '密码错误' => 'Wrong password', + '账号已被禁用,无法登录' => 'Account is disabled and cannot log in', + '购买抽奖券错误' => 'Invalid lottery ticket purchase', + '平台币不足' => 'Insufficient balance', + 'direction 必须为 0 或 1' => 'direction must be 0 or 1', + '当前玩家余额%s小于%s无法继续游戏' => 'Balance %s is less than %s, cannot continue', + '服务超时,' => 'Service timeout: ', + '没有原因' => 'Unknown reason', + '缺少参数:agent_id、secret、time、signature 不能为空' => 'Missing parameters: agent_id, secret, time, signature are required', + '服务端未配置 API_AUTH_TOKEN_SECRET' => 'API_AUTH_TOKEN_SECRET is not configured', + '密钥错误' => 'Invalid secret', + '时间戳已过期或无效,请同步时间' => 'Timestamp expired or invalid, please sync time', + '签名验证失败' => 'Signature verification failed', + '生成 token 失败' => 'Failed to generate token', + 'coin 不能为空' => 'coin is required', + 'coin 不能为 0' => 'coin cannot be 0', + '余额不足,无法转出' => 'Insufficient balance to transfer', + '操作失败:' => 'Operation failed: ', + '服务超时,没有原因' => 'Service timeout: Unknown reason', + '抽奖券不足' => 'Insufficient lottery tickets', + '奖池配置不存在' => 'Lottery config not found', + '配置ID %s 不存在或档位为空' => 'Config ID %s not found or tier is empty', + '该方向下暂无可用路径配置' => 'No path config available for this direction', + '奖池配置不存在(需 type=0)' => 'Lottery pool config not found (type=0 required)', + '暂无可用奖励配置' => 'No available reward config', + '未找到 type=0 的奖池配置,请先创建' => 'No type=0 pool config found, please create one first', + '参数错误:需要有效的 player_id 和 type(3=加点,4=扣点)' => 'Invalid params: player_id and type are required (3=add, 4=deduct)', + '平台币变动必须大于 0' => 'Coin change must be greater than 0', + '玩家不存在' => 'Player not found', + '扣点数量不能大于当前余额' => 'Deduct amount cannot exceed current balance', + '测试记录不存在' => 'Test record not found', + '付费奖池配置不存在' => 'Paid pool config not found', + '免费奖池配置不存在' => 'Free pool config not found', + '各抽奖次数仅支持 0、100、500、1000、5000' => 'Counts only support 0, 100, 500, 1000, 5000', + '付费或免费至少一种方向次数之和大于 0' => 'Sum of paid/free direction counts must be greater than 0', + '付费未选择奖池配置时,请填写付费自定义档位概率(T1~T5)' => 'When paid pool is not selected, please fill paid custom tier probabilities (T1–T5)', + '付费档位概率每档只能 0-100%' => 'Paid tier probability must be between 0 and 100%', + '付费档位概率 T1~T5 之和不能超过 100%' => 'Paid tier probabilities (T1–T5) sum cannot exceed 100%', + '免费未选择奖池配置时,请填写免费自定义档位概率(T1~T5)' => 'When free pool is not selected, please fill free custom tier probabilities (T1–T5)', + '免费档位概率每档只能 0-100%' => 'Free tier probability must be between 0 and 100%', + '免费档位概率 T1~T5 之和不能超过 100%' => 'Free tier probabilities (T1–T5) sum cannot exceed 100%', + '存在无效的配置ID' => 'Invalid config ID exists', + '存在无效的 DiceReward id' => 'Invalid DiceReward id exists', + '奖励配置为空,请先维护 dice_reward_config' => 'Reward config is empty, please maintain dice_reward_config first', + '奖励配置需覆盖 26 个格位(id 0-25 或 1-26),当前仅 %s 条,无法完整生成 5-30 共26个点数、顺时针与逆时针的奖励对照' => 'Reward config must cover 26 cells (id 0-25 or 1-26), currently only %s, cannot generate full 5-30 points and clockwise/counterclockwise mapping', + '测试次数仅支持 100、500、1000、5000、10000' => 'Test count only supports 100, 500, 1000, 5000, 10000', + '没有权限操作该部门数据' => 'No permission to operate department data', + '没有权限操作该角色数据' => 'No permission to operate role data', + '没有权限操作该数据' => 'No permission to operate this data', + '禁止批量删除操作' => 'Batch delete is not allowed', + '超级管理员禁止删除' => 'Super admin cannot be deleted', + '原密码错误' => 'Old password is incorrect', + '上级部门和当前部门不能相同' => 'Parent department cannot be the same as current department', + '不能将上级部门设置为当前部门的子部门' => 'Cannot set parent department to a child of current department', + '该部门下存在子部门,请先删除子部门' => 'This department has sub-departments, please delete them first', + '该部门下存在用户,请先删除或者转移用户' => 'This department has users, please delete or transfer them first', + '您的登录凭证错误或者已过期,请重新登录' => 'Your login credential is invalid or expired, please login again', + '登录凭证校验失败' => 'Login credential verification failed', + '插件的基础配置信息错误' => 'Plugin base config is invalid', + '插件已经存在' => 'Plugin already exists', + '该插件的安装目录已经被占用' => 'Plugin install directory is already occupied', + '文件不存在' => 'File not found', + '手机号格式错误,仅支持 +60 开头的马来西亚号码(如 +60123456789)' => 'Invalid phone format, only +60 Malaysia numbers supported (e.g. +60123456789)', + '请携带 auth-token' => 'Please provide auth-token', + 'auth-token 已过期' => 'auth-token expired', + 'auth-token 无效' => 'auth-token invalid', + 'auth-token 格式无效' => 'auth-token format invalid', + 'auth-token 无效或已失效' => 'auth-token invalid or expired', + 'token 已过期,请重新登录' => 'Token expired, please login again', + 'token 格式无效' => 'Token format invalid', + '请注册' => 'Please register', + '请重新登录' => 'Please login again', + '请重新登录(当前账号已在其他处登录)' => 'Please login again (account logged in elsewhere)', + '数据不存在' => 'Data not found', + '不能设置父级为自身' => 'Cannot set parent to self', + '该菜单下存在子菜单,请先删除子菜单' => 'This menu has sub-menus, please delete them first', + '导入文件错误,请上传正确的文件格式xlsx' => 'Import file error, please upload correct xlsx file', + '不能操作比当前账户职级高的角色' => 'Cannot operate roles with higher level than current account', + '该字典标识已存在' => 'This dict code already exists', + '修改数据异常,请检查' => 'Update data error, please check', + '删除数据异常,请检查' => 'Delete data error, please check', + '字典类型不存在' => 'Dict type not found', + '配置数据未找到' => 'Config data not found', + '系统默认分组,无法删除' => 'System default group cannot be deleted', + '配置组未找到' => 'Config group not found', + '上级分类和当前分类不能相同' => 'Parent category cannot be the same as current', + '不能将上级分类设置为当前分类的子分类' => 'Cannot set parent category as child of current', + '该部门下存在子分类,请先删除子分类' => 'This category has sub-categories, please delete them first', + '目标分类不存在' => 'Target category not found', + '获取文件资源失败' => 'Failed to get file resource', + '创建图片资源失败' => 'Failed to create image resource', + '文件格式错误' => 'Invalid file format', + '文件保存失败' => 'Failed to save file', + '当前表不支持回收站功能' => 'Current table does not support recycle bin', + '模板不存在' => 'Template not found', + '任务类型异常' => 'Invalid task type', + '数据库配置读取失败' => 'Failed to read database config', + '应用类型必须为plugin或者app' => 'App type must be plugin or app', + '请先设置应用名称' => 'Please set app name first', + '请选择要生成的表' => 'Please select tables to generate', + '非调试模式下,不允许生成文件' => 'File generation not allowed in non-debug mode', + '登录凭获取失败,请检查' => 'Failed to get login credential, please check', + '文件大小超过限制' => 'File size exceeds limit', + '不支持该格式的文件上传' => 'File format not supported for upload', + '该上传模式不存在' => 'Upload mode not found', + '切片上传服务必须在 HTTP 请求环境下调用' => 'Chunk upload must be called in HTTP request context', + '切片文件查找失败,请重新上传' => 'Chunk file not found, please upload again', + '未设置邮件配置' => 'Mail config not set', + '请执行 composer require phpmailer/phpmailer 并重启' => 'Please run composer require phpmailer/phpmailer and restart', + '仅超级管理员能够操作' => 'Only super admin can perform this action', + '等待依赖安装' => 'Waiting for dependencies to be installed', + '插件目录不存在' => 'Plugin directory not found', + '该插件的基础配置信息不完善' => 'Plugin base config is incomplete', + '参数错误' => 'Invalid parameters', + '无法打开文件,或者文件创建失败' => 'Cannot open file or create file failed', + '系统生成文件错误' => 'System file generation error', + '模板目录不存在!' => 'Template directory not found', + '文件类型异常,无法生成指定文件!' => 'Invalid file type, cannot generate file', + '前端目录查找失败,必须与后端目录为同级目录!' => 'Frontend directory not found, must be same level as backend', +];