'" * - 同时写入 resource/translations/api/{en,zh}.php:english=>english / english=>中文 * * 英文短句来源优先级: * 1) resource/translations/api/en.php 与 zh.php 中的 MSG_ 对照(中文=>英文) * 2) 内置常用中文短语翻译表 */ $root = dirname(__DIR__); $repoRoot = dirname($root); $enPath = $root . '/resource/translations/api/en.php'; $zhPath = $root . '/resource/translations/api/zh.php'; $enMap = is_file($enPath) ? (require $enPath) : []; $zhMap = is_file($zhPath) ? (require $zhPath) : []; /** @var array $cnToEn 基于 MSG_ 的中文=>英文 */ $cnToEn = []; foreach ($zhMap as $k => $cn) { if (!is_string($k) || !is_string($cn)) { continue; } if (strncmp($k, 'MSG_', 4) !== 0) { continue; } $en = $enMap[$k] ?? null; if (is_string($en) && $en !== '') { $cnToEn[$cn] = $en; } } /** 常用中文短语翻译(兜底) */ $fallbackCn = [ '未查找到信息' => 'not found', '记录不存在' => 'record not found', '数据不存在' => 'data not found', '添加失败' => 'add failed', '修改失败' => 'update failed', '删除失败' => 'delete failed', '请选择要删除的数据' => 'please select data to delete', '参数错误,请检查' => 'invalid parameters, please check', '参数错误,请检查参数' => 'invalid parameters, please check', '操作失败' => 'operation failed', '执行失败' => 'execution failed', '请先登录' => 'please login first', '未登录' => 'not logged in', '下载失败' => 'download failed', '请求过于频繁,请稍后再试' => 'too many requests, please try again later', '验证码错误' => 'captcha error', '请选择要删除的缓存' => 'please select cache to delete', '请选择要删除的数据' => 'please select data to delete', '请选择要生成的表' => 'please select tables to generate', '发送失败,请查看日志' => 'send failed, please check logs', '请输入邮箱' => 'please input email', '版本ID不能为空' => 'version id is required', '上传文件校验失败' => 'upload file validation failed', '文件大小不能超过5M' => 'file size cannot exceed 5M', '文件格式上传失败,请选择zip格式文件上传' => 'upload failed, please upload zip file', '登录已过期或用户信息无效,请重新登录' => 'login expired or invalid, please login again', '超级管理员不允许重置密码' => 'super admin cannot reset password', '未找到上传文件' => 'uploaded file not found', '参数 items 必须为数组' => 'parameter items must be an array', '缺少参数 id' => 'missing parameter id', '缺少参数 status' => 'missing parameter status', '缺少 player_id' => 'missing player_id', '缺少参数:agent_id、secret、time、signature 不能为空' => 'missing parameters: agent_id, secret, time, signature are required', '无权限查看该记录' => 'no permission to view this record', '无权限修改该记录' => 'no permission to update this record', '无权限删除所选数据' => 'no permission to delete selected data', '无权限操作该玩家' => 'no permission to operate this player', '请选择玩家' => 'please select player', '操作类型必须为 3=加点 或 4=扣点' => 'operation type must be 3 (add) or 4 (deduct)', '请指定测试记录' => 'please specify test record', '请传入 record_id' => 'please provide record_id', '请传入 direction(0=顺时针 1=逆时针)' => 'please provide direction (0=clockwise, 1=counterclockwise)', 'direction 必须为 0(顺时针)或 1(逆时针)' => 'direction must be 0 (clockwise) or 1 (counterclockwise)', '清空失败:' => 'clear failed: ', '管理后台已经安装!如需重新安装,请删除根目录env配置文件并重启' => 'admin already installed, to reinstall please delete env file and restart', '数据库用户名或密码错误' => 'database username or password is incorrect', 'Connection refused. 请确认数据库IP端口是否正确,数据库已经启动' => 'connection refused, please check database ip/port and ensure database is running', '数据库连接超时,请确认数据库IP端口是否正确,安全组及防火墙已经放行端口' => 'database connection timeout, please check ip/port and firewall/security group rules', '数据库已经安装,请勿重复安装' => 'database already installed, please do not install again', '数据库SQL文件不存在' => 'database SQL file not found', ]; $dump = static function (array $arr): string { ksort($arr); $out = " $v) { $k = str_replace("'", "\\'", (string) $k); $v = str_replace("'", "\\'", (string) $v); $out .= " '{$k}' => '{$v}',\n"; } $out .= "];\n"; return $out; }; $diff = shell_exec('git -C ' . escapeshellarg($repoRoot) . ' diff -U0 -- server'); if (!is_string($diff) || $diff === '') { echo "no diff\n"; exit(0); } /** @var array $hexToCn */ $hexToCn = []; $lines = preg_split("/\r\n|\n|\r/", $diff) ?: []; $prevCn = null; foreach ($lines as $line) { // - return $this->fail('未查找到信息'); if (preg_match("/^\\-.*'([^']*?)'.*$/u", $line, $m) === 1) { $maybeCn = $m[1]; if (preg_match('/[\x{4e00}-\x{9fff}]/u', $maybeCn) === 1) { $prevCn = $maybeCn; continue; } } // + return $this->fail('E_FC6490F8'); if ($prevCn !== null && preg_match("/^\\+.*'E_([0-9A-Fa-f]{8})'.*$/", $line, $m) === 1) { $hexToCn[strtoupper($m[1])] = $prevCn; $prevCn = null; continue; } // reset when encountering unrelated added/removed line if (strlen($line) > 0 && ($line[0] === '+' || $line[0] === '-')) { $prevCn = null; } } if ($hexToCn === []) { echo "no E_ mappings found in diff\n"; exit(0); } // 替换代码中的 E_XXXXXXXX $serverDir = $root; $files = []; $it = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($serverDir, FilesystemIterator::SKIP_DOTS) ); foreach ($it as $file) { /** @var SplFileInfo $file */ if (!$file->isFile()) { continue; } $path = $file->getPathname(); if (substr($path, -4) !== '.php') { continue; } $norm = str_replace('\\', '/', $path); if (str_contains($norm, '/resource/translations/')) { continue; } if (str_contains($norm, '/scripts/')) { continue; } $files[] = $path; } $touched = 0; $replaced = 0; $addedPairs = 0; foreach ($files as $path) { $content = file_get_contents($path); if (!is_string($content) || $content === '') { continue; } $new = preg_replace_callback( "/(['\"])E_([0-9A-Fa-f]{8})\\1/", function (array $m) use (&$enMap, &$zhMap, &$replaced, &$addedPairs, $hexToCn, $cnToEn, $fallbackCn) { $hex = strtoupper($m[2]); $cn = $hexToCn[$hex] ?? null; if (!is_string($cn) || $cn === '') { return $m[0]; } $en = $cnToEn[$cn] ?? ($fallbackCn[$cn] ?? null); if (!is_string($en) || $en === '') { return $m[0]; } if (!isset($enMap[$en])) { $enMap[$en] = $en; $addedPairs++; } if (!isset($zhMap[$en])) { $zhMap[$en] = $cn; $addedPairs++; } $replaced++; return "'" . str_replace("'", "\\'", $en) . "'"; }, $content ); if (is_string($new) && $new !== $content) { file_put_contents($path, $new); $touched++; } } file_put_contents($enPath, $dump($enMap)); file_put_contents($zhPath, $dump($zhMap)); echo "touched_files={$touched}\n"; echo "replaced={$replaced}\n"; echo "added_translation_pairs={$addedPairs}\n";