*/ private const LOOKUP_PREFIXES = ['api.reasons.', 'api.', 'admin.', 'sso.', 'jackpot.', 'wallet.']; public static function locale(?Request $request = null): string { $request ??= request(); if ($request instanceof Request && $request->attributes->has('lottery_locale')) { return (string) $request->attributes->get('lottery_locale'); } return LotteryLocale::resolve($request instanceof Request ? $request : null); } /** * @param array $replace */ public static function get(?Request $request, string $key, array $replace = []): string { $key = trim($key); if ($key === '') { return self::translateKey($request, 'api.client_error', $replace); } if (preg_match('/\p{Han}/u', $key) === 1) { return $key; } $resolved = self::translateKey($request, $key, $replace); if ($resolved !== null) { return $resolved; } return $key; } /** * RuntimeException / 业务 reason 机器码 → 用户可见文案。 * * @param array $replace */ public static function reason(?Request $request, string $reasonKey, array $replace = []): string { $reasonKey = trim($reasonKey); if ($reasonKey === '') { return self::translateKey($request, 'api.client_error', $replace) ?? trans('api.client_error', [], self::locale($request)); } if (preg_match('/\p{Han}/u', $reasonKey) === 1) { return $reasonKey; } $resolved = self::translateKey($request, $reasonKey, $replace); if ($resolved !== null) { return $resolved; } $resolved = self::translateBusinessKey($request, $reasonKey, $replace); if ($resolved !== null) { return $resolved; } return $reasonKey; } /** * abort() / HttpException 的 message 转为用户可见文案。 */ public static function httpExceptionMessage(?Request $request, int $status, string $rawMessage): string { $trimmed = trim($rawMessage); if ($trimmed !== '') { if (preg_match('/\p{Han}/u', $trimmed) === 1) { return $trimmed; } if (str_starts_with($trimmed, 'admin.')) { return self::get($request, $trimmed); } $resolved = self::translateKey($request, $trimmed); if ($resolved !== null) { return $resolved; } $resolved = self::translateBusinessKey($request, $trimmed); if ($resolved !== null) { return $resolved; } if (str_contains($trimmed, ' ')) { return $trimmed; } return $trimmed; } return match ($status) { 401 => self::get($request, 'admin.unauthenticated'), 403 => self::get($request, 'admin.permission_denied'), 404 => self::get($request, 'api.not_found'), 422 => self::get($request, 'api.validation_failed'), 429 => self::get($request, 'api.too_many_requests'), default => $status >= 500 ? self::get($request, 'api.server_error') : self::get($request, 'api.client_error'), }; } public static function successMessage(?Request $request = null): string { return self::get($request, 'success.ok'); } /** * @param array $replace */ public static function errorResponse( ?Request $request, string $messageKey, int $code, mixed $data = null, int $httpStatus = 400, array $replace = [], ): JsonResponse { return ApiResponse::error( self::get($request, $messageKey, $replace), $code, $data, $httpStatus, ); } public static function runtimeErrorResponse( ?Request $request, \RuntimeException $exception, int $code = 0, int $httpStatus = 409, ): JsonResponse { $reason = trim($exception->getMessage()); $resolvedCode = $code !== 0 ? $code : ErrorCode::ClientHttpError->value; return ApiResponse::error( self::reason($request, $reason), $resolvedCode, ['reason' => $reason], $httpStatus, ); } /** * @param array $replace */ private static function translateKey(?Request $request, string $key, array $replace = []): ?string { $locale = self::locale($request); $fallback = (string) config('lottery.locales.fallback', 'en'); foreach ([$locale, $fallback] as $tryLocale) { foreach (self::candidateKeys($key) as $fullKey) { $msg = trans($fullKey, $replace, $tryLocale); if ($msg !== $fullKey && $msg !== '') { return $msg; } } } return null; } /** * @param array $replace */ private static function translateBusinessKey(?Request $request, string $key, array $replace = []): ?string { $locale = self::locale($request); $fallback = (string) config('lottery.locales.fallback', 'en'); $candidates = array_values(array_unique([ $key, str_contains($key, '.') ? substr($key, strrpos($key, '.') + 1) : $key, ])); foreach ([$locale, $fallback] as $tryLocale) { foreach ($candidates as $candidate) { if ($candidate === '') { continue; } $businessKey = 'validation.business.'.$candidate; $msg = trans($businessKey, $replace, $tryLocale); if ($msg !== $businessKey) { return $msg; } } } return null; } /** * @return list */ private static function candidateKeys(string $key): array { $key = trim($key); if ($key === '') { return ['api.client_error']; } if (str_contains($key, '.')) { return array_values(array_unique([ $key, 'api.'.$key, 'admin.'.$key, 'sso.'.$key, 'jackpot.'.$key, 'wallet.'.$key, 'api.reasons.'.$key, ])); } $keys = []; foreach (self::LOOKUP_PREFIXES as $prefix) { $keys[] = $prefix.$key; } return $keys; } }