From 275f94f96d30fcf1f1a818cef4d08d3e0d7018bd Mon Sep 17 00:00:00 2001 From: zhenhui <1276357500@qq.com> Date: Tue, 10 Mar 2026 11:42:39 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=8E=A5=E5=8F=A3lang?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=A4=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/app/api/controller/BaseController.php | 36 ++++++++++ server/app/api/controller/GameController.php | 8 ++- server/app/api/controller/UserController.php | 4 +- .../api/controller/v1/AuthTokenController.php | 4 +- .../app/api/controller/v1/GameController.php | 4 +- server/app/api/lang/en.php | 53 ++++++++++++++ server/app/api/util/ApiLang.php | 72 +++++++++++++++++++ .../saiadmin/exception/ApiException.php | 7 +- 8 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 server/app/api/controller/BaseController.php create mode 100644 server/app/api/lang/en.php create mode 100644 server/app/api/util/ApiLang.php diff --git a/server/app/api/controller/BaseController.php b/server/app/api/controller/BaseController.php new file mode 100644 index 0000000..8ae74a3 --- /dev/null +++ b/server/app/api/controller/BaseController.php @@ -0,0 +1,36 @@ +coin; if ($coin < $minCoin) { - return $this->success([], '当前玩家余额'.$coin.'小于'.$minCoin.'无法继续游戏'); + $msg = ApiLang::translateParams('当前玩家余额%s小于%s无法继续游戏', [$coin, $minCoin], $request); + return $this->success([], $msg); } try { diff --git a/server/app/api/controller/UserController.php b/server/app/api/controller/UserController.php index b72a9e9..93ee6f4 100644 --- a/server/app/api/controller/UserController.php +++ b/server/app/api/controller/UserController.php @@ -10,13 +10,13 @@ use app\api\logic\UserLogic; use app\api\util\ReturnCode; use app\dice\model\play_record\DicePlayRecord; use app\dice\model\player_wallet_record\DicePlayerWalletRecord; -use plugin\saiadmin\basic\OpenController; +use app\api\controller\BaseController; /** * API 用户登录等 * 登录接口 /api/user/Login 无需 token;其余接口需在请求头携带 token(base64(username.-.time)),由 TokenMiddleware 鉴权并注入 request->player_id / request->player */ -class UserController extends OpenController +class UserController extends BaseController { /** * 登录(form-data 参数) diff --git a/server/app/api/controller/v1/AuthTokenController.php b/server/app/api/controller/v1/AuthTokenController.php index 2f4e204..8a346e8 100644 --- a/server/app/api/controller/v1/AuthTokenController.php +++ b/server/app/api/controller/v1/AuthTokenController.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace app\api\controller\v1; use app\api\cache\AuthTokenCache; +use app\api\controller\BaseController; use app\api\util\ReturnCode; -use plugin\saiadmin\basic\OpenController; use support\Request; use support\Response; use Tinywan\Jwt\JwtToken; @@ -16,7 +16,7 @@ use Tinywan\Jwt\JwtToken; * GET 参数:signature, secret, time, agent_id * 签名:signature = md5(agent_id.secret.time) */ -class AuthTokenController extends OpenController +class AuthTokenController extends BaseController { /** * 获取 auth-token diff --git a/server/app/api/controller/v1/GameController.php b/server/app/api/controller/v1/GameController.php index c59ce81..e5259af 100644 --- a/server/app/api/controller/v1/GameController.php +++ b/server/app/api/controller/v1/GameController.php @@ -10,7 +10,7 @@ use app\dice\model\play_record\DicePlayRecord; use app\dice\model\player_wallet_record\DicePlayerWalletRecord; use app\dice\model\player_ticket_record\DicePlayerTicketRecord; use support\think\Db; -use plugin\saiadmin\basic\OpenController; +use app\api\controller\BaseController; use support\Request; use support\Response; @@ -18,7 +18,7 @@ use support\Response; * 平台 v1 游戏接口 * 请求头:auth-token */ -class GameController extends OpenController +class GameController extends BaseController { /** * 获取游戏地址 diff --git a/server/app/api/lang/en.php b/server/app/api/lang/en.php new file mode 100644 index 0000000..b0b851f --- /dev/null +++ b/server/app/api/lang/en.php @@ -0,0 +1,53 @@ + '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', + // PlayStartLogic / GameLogic + '抽奖券不足' => 'Insufficient lottery tickets', + '奖池配置不存在' => 'Lottery config not found', + '该方向下暂无可用路径配置' => 'No path config available for this direction', + // UserLogic + '手机号格式错误,仅支持 +60 开头的马来西亚号码(如 +60123456789)' => 'Invalid phone format, only +60 Malaysia numbers supported (e.g. +60123456789)', + // TokenMiddleware / Auth (api/user/*, api/game/*) + '请携带 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)', +]; diff --git a/server/app/api/util/ApiLang.php b/server/app/api/util/ApiLang.php new file mode 100644 index 0000000..e688162 --- /dev/null +++ b/server/app/api/util/ApiLang.php @@ -0,0 +1,72 @@ +|null */ + private static ?array $enMap = null; + + /** + * 从请求中获取语言:lang 请求头 en=英文,zh=中文,默认 zh + */ + 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; + } + } + return self::LANG_ZH; + } + + /** + * 翻译文案:当前请求语言为 en 时返回英文,否则返回原文(中文) + * @param string $message 中文或原文 + * @param Request|null $request 当前请求,不传则自动取 request() + */ + public static function translate(string $message, ?Request $request = null): string + { + $lang = self::getLang($request); + if ($lang !== self::LANG_EN) { + return $message; + } + if (self::$enMap === null) { + $path = dirname(__DIR__) . '/lang/en.php'; + self::$enMap = is_file($path) ? (require $path) : []; + } + return self::$enMap[$message] ?? $message; + } + + /** + * 带占位符的翻译,如 translateParams('当前玩家余额%s小于%s无法继续游戏', [$coin, $minCoin]) + * 先翻译再替换(en 文案使用 %s 占位) + */ + public static function translateParams(string $message, array $params = [], ?Request $request = null): string + { + $translated = self::translate($message, $request); + if ($params !== []) { + $translated = sprintf($translated, ...$params); + } + return $translated; + } +} diff --git a/server/plugin/saiadmin/exception/ApiException.php b/server/plugin/saiadmin/exception/ApiException.php index a9040e8..7d8a4b7 100644 --- a/server/plugin/saiadmin/exception/ApiException.php +++ b/server/plugin/saiadmin/exception/ApiException.php @@ -17,6 +17,11 @@ class ApiException extends BusinessException { public function render(Request $request): ?Response { - return json(['code' => $this->getCode() ?: 500, 'message' => $this->getMessage()]); + $message = $this->getMessage(); + $path = $request->path(); + if (str_contains($path, 'api/')) { + $message = \app\api\util\ApiLang::translate($message, $request); + } + return json(['code' => $this->getCode() ?: 500, 'message' => $message]); } } \ No newline at end of file