path(), '/'); if (str_starts_with($path, 'api/') || str_starts_with($path, 'admin/')) { $this->loadLang($request); } return $handler($request); } protected function loadLang(Request $request): void { $path = trim($request->path(), '/'); $isApi = str_starts_with($path, 'api/'); $isAdmin = str_starts_with($path, 'admin/'); $allowLangList = config('lang.allow_lang_list', ['zh-cn', 'en']); $langSet = null; // 对外 API(PlayX、H5 等):优先 lang 请求头,默认中文 zh-cn,不跟随浏览器 Accept-Language if ($isApi) { $langHeader = $request->header('lang'); if (is_array($langHeader)) { $langHeader = $langHeader[0] ?? ''; } $langHeader = is_string($langHeader) ? trim($langHeader) : ''; if ($langHeader !== '') { $langSet = $this->normalizeLangHeader($langHeader, $allowLangList); } } // 与后台 Vue 一致的 think-lang(对外 API 在 lang 未设置时仍可生效) if ($langSet === null) { $headerLang = $request->header('think-lang'); if (is_array($headerLang)) { $headerLang = $headerLang[0] ?? ''; } $headerLang = is_string($headerLang) ? trim($headerLang) : ''; if ($headerLang !== '') { $normalized = str_replace('_', '-', strtolower($headerLang)); if (in_array($normalized, $allowLangList, true)) { $langSet = $normalized; } } } if ($langSet === null) { if ($isApi) { $langSet = 'zh-cn'; } elseif ($isAdmin) { $acceptLang = $request->header('accept-language', ''); if (is_array($acceptLang)) { $acceptLang = $acceptLang[0] ?? ''; } $acceptLang = is_string($acceptLang) ? $acceptLang : ''; if (preg_match('/^zh[-_]?cn|^zh/i', $acceptLang)) { $langSet = 'zh-cn'; } elseif (preg_match('/^en/i', $acceptLang)) { $langSet = 'en'; } else { $langSet = config('lang.default_lang', config('translation.locale', 'zh-cn')); } $langSet = str_replace('_', '-', strtolower((string) $langSet)); } else { $langSet = config('lang.default_lang', config('translation.locale', 'zh-cn')); $langSet = str_replace('_', '-', strtolower((string) $langSet)); } } // 设置当前请求的翻译语言,使 __() 和 trans() 使用正确的语言 if (function_exists('locale')) { locale($langSet); } $parts = explode('/', $path); $app = $parts[0] ?? 'api'; $appLangDir = base_path() . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR; if (!class_exists(\support\Translation::class)) { return; } $translator = \support\Translation::instance(); // 1. 加载根级语言包(zh-cn.php / en.php),供 common 翻译使用 $rootLangFile = $appLangDir . $langSet . '.php'; if (is_file($rootLangFile)) { $translator->addResource('phpfile', $rootLangFile, $langSet, 'messages'); } // 2. 加载控制器专用语言包(如 zh-cn/auth/group.php),供 get_route_remark 等使用 // 同时加载到 messages 域,使 __() 能正确翻译控制器内的文案(如安装页错误提示) $controllerPath = get_controller_path($request); if ($controllerPath) { $controllerPathForFile = str_replace('.', '/', $controllerPath); $controllerPathForFile = implode('/', array_map(function ($p) { return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $p)); }, explode('/', $controllerPathForFile))); $controllerLangFile = $appLangDir . $langSet . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $controllerPathForFile) . '.php'; if (is_file($controllerLangFile)) { $translator->addResource('phpfile', $controllerLangFile, $langSet, $controllerPath); $translator->addResource('phpfile', $controllerLangFile, $langSet, 'messages'); } } } /** * 将 lang 请求头取值映射为语言包标识(zh / zh-cn → zh-cn,en → en) */ private function normalizeLangHeader(string $raw, array $allowLangList): ?string { $s = str_replace('_', '-', strtolower(trim($raw))); if ($s === '') { return null; } if (in_array($s, $allowLangList, true)) { return $s; } if (str_starts_with($s, 'en')) { return in_array('en', $allowLangList, true) ? 'en' : null; } if ($s === 'zh' || str_starts_with($s, 'zh-')) { return in_array('zh-cn', $allowLangList, true) ? 'zh-cn' : null; } return null; } }