diff --git a/panel/src/i18n/resources/en-US/errors.json b/panel/src/i18n/resources/en-US/errors.json index e68a87b..8f51810 100644 --- a/panel/src/i18n/resources/en-US/errors.json +++ b/panel/src/i18n/resources/en-US/errors.json @@ -26,5 +26,42 @@ "passkey_already_bound": "This Passkey is already bound to another account.", "passkey_unavailable": "Passkey subsystem is not available right now.", "passkey_not_allowed": "The operation was cancelled by the user or browser.", - "passkey_aborted": "Passkey registration was aborted." + "passkey_aborted": "Passkey registration was aborted.", + "bad_name": "That server name is invalid — use 3–32 lowercase letters, digits or dashes, and avoid reserved names.", + "bad_subdomain": "That subdomain is invalid — use 3–32 lowercase letters, digits or dashes, and avoid reserved names.", + "at_capacity": "The cluster is at its running-server cap — try again once a server stops.", + "storage_immutable": "Storage size can't be changed online.", + "email_taken": "That email is already verified on another account — sign in with it or use another address.", + "mail_undeliverable": "The verification email could not be delivered — try again later, or ask the operator to check the mail relay.", + "bad_path": "That path is invalid — use a relative path inside the world directory.", + "too_large": "That is larger than the size limit.", + "files_timeout": "The file operation timed out — try again shortly.", + "files_unavailable": "File editing isn't available right now.", + "jobs_unavailable": "The background job service isn't available right now.", + "already_terminal": "This build already finished — there is nothing to cancel.", + "build_unavailable": "Image builds aren't available right now.", + "build_logs_unavailable": "Build logs aren't available right now.", + "already_reviewed": "This submission has already been reviewed.", + "submissions_unavailable": "Submissions aren't available right now.", + "uploads_unavailable": "Uploads aren't available right now.", + "backup_unavailable": "Backups aren't available right now.", + "account_retired": "This account has been retired — sign in with the account it was migrated to.", + "no_migration": "There is no migration in progress.", + "not_confirmed": "This migration hasn't been confirmed yet.", + "already_confirmed": "This migration has already been confirmed.", + "invalid_target": "The migration target must be a different account.", + "target_not_found": "No account matches that migration target.", + "target_unavailable": "That migration target isn't available.", + "no_passkey": "This account has no Passkey bound yet.", + "passkey_required": "Bind a Passkey before doing that.", + "no_step_up_factor": "This account has no verified email or Passkey to confirm with yet.", + "passkey_login_failed": "Passkey verification failed — try again.", + "passkey_login_invalid": "That Passkey sign-in request is invalid or expired — start it again.", + "too_many_challenges": "Too many verification attempts right now — try again shortly.", + "op_login_invalid": "This operator sign-in couldn't be completed — restart the sign-in.", + "op_login_not_found": "No pending operator sign-in with that id.", + "too_many_streams": "Too many live streams are open — close some pages and try again.", + "protected_admin": "That name belongs to a linked administrator and can't be reclaimed.", + "auth_unavailable": "The sign-in service isn't available right now.", + "setup_token_invalid": "That setup token is invalid or expired." } diff --git a/panel/src/i18n/resources/zh-CN/errors.json b/panel/src/i18n/resources/zh-CN/errors.json index 1e0a772..14f37b8 100644 --- a/panel/src/i18n/resources/zh-CN/errors.json +++ b/panel/src/i18n/resources/zh-CN/errors.json @@ -26,5 +26,42 @@ "passkey_already_bound": "此 Passkey 已被其他账户绑定。", "passkey_unavailable": "Passkey 功能当前不可用。", "passkey_not_allowed": "操作已被用户或浏览器取消。", - "passkey_aborted": "Passkey 注册已被取消。" + "passkey_aborted": "Passkey 注册已被取消。", + "bad_name": "服务器名称不合法:需为 3–32 位小写字母、数字或连字符,且不能使用保留名。", + "bad_subdomain": "子域名不合法:需为 3–32 位小写字母、数字或连字符,且不能使用保留名。", + "at_capacity": "集群的在线服务器已达上限——等一台服务器停止后再试。", + "storage_immutable": "存储容量不支持在线调整。", + "email_taken": "该邮箱已在其他账户上完成验证;请直接用该邮箱登录,或换一个地址。", + "mail_undeliverable": "验证码邮件发送失败——请稍后重试,或联系管理员检查邮件服务。", + "bad_path": "路径不合法——请使用世界目录内的相对路径。", + "too_large": "内容超出大小限制,无法处理。", + "files_timeout": "文件操作超时——请稍后重试。", + "files_unavailable": "文件功能当前不可用。", + "jobs_unavailable": "后台任务服务当前不可用。", + "already_terminal": "该构建已经结束,无法重复取消。", + "build_unavailable": "构建功能当前不可用。", + "build_logs_unavailable": "构建日志暂时不可用。", + "already_reviewed": "该提交已经审核过了。", + "submissions_unavailable": "提交流程当前不可用。", + "uploads_unavailable": "上传功能当前不可用。", + "backup_unavailable": "备份功能当前不可用。", + "account_retired": "该账户已退役——请使用迁移后的账户登录。", + "no_migration": "当前没有进行中的迁移。", + "not_confirmed": "该迁移尚未完成确认。", + "already_confirmed": "该迁移已经确认过了。", + "invalid_target": "迁移目标账户不能与来源账户相同。", + "target_not_found": "找不到迁移目标账户。", + "target_unavailable": "迁移目标账户当前不可用。", + "no_passkey": "该账户尚未绑定 Passkey。", + "passkey_required": "此操作需要先绑定 Passkey。", + "no_step_up_factor": "该账户还没有已验证的邮箱或 Passkey,无法完成此操作。", + "passkey_login_failed": "Passkey 验证失败——请重试。", + "passkey_login_invalid": "Passkey 登录请求无效或已过期——请重新发起。", + "too_many_challenges": "验证请求过于频繁——请稍后再试。", + "op_login_invalid": "本次管理员登录未能完成——请重新发起登录。", + "op_login_not_found": "找不到该管理员登录请求。", + "too_many_streams": "同时打开的实时连接过多——请关闭一些页面后再试。", + "protected_admin": "该名字属于已绑定的管理员账户,不能被认领。", + "auth_unavailable": "登录服务当前不可用。", + "setup_token_invalid": "初始化令牌无效或已过期。" } diff --git a/panel/src/lib/api.ts b/panel/src/lib/api.ts index 293bd70..92ca188 100644 --- a/panel/src/lib/api.ts +++ b/panel/src/lib/api.ts @@ -683,6 +683,92 @@ export function humanizeError(e: unknown): string { return t("no_world_volume"); case "restore_unavailable": return t("restore_unavailable"); + // Server create/edit (spec §22): the portability regex + reservation list are + // enforced server-side, and the create form's own checks are weaker, so these + // refusals reach the dialog as-is. + case "bad_name": + return t("bad_name"); + case "bad_subdomain": + return t("bad_subdomain"); + case "at_capacity": + return t("at_capacity"); + case "storage_immutable": + return t("storage_immutable"); + // Email identity: the verified-email uniqueness index (migration 0020) plus + // VerifyEmailOTP's guard make a second verified holder impossible; the OTP + // relay can also refuse to deliver at all. + case "email_taken": + return t("email_taken"); + case "mail_undeliverable": + return t("mail_undeliverable"); + // File editor (spec §7): path/size refusals from the sandboxed job, plus the + // subsystem being unwired. + case "bad_path": + return t("bad_path"); + case "too_large": + return t("too_large"); + case "files_timeout": + return t("files_timeout"); + case "files_unavailable": + return t("files_unavailable"); + case "jobs_unavailable": + return t("jobs_unavailable"); + // Builds, uploads and review: terminal-state conflicts and unwired subsystems. + case "already_terminal": + return t("already_terminal"); + case "build_unavailable": + return t("build_unavailable"); + case "build_logs_unavailable": + return t("build_logs_unavailable"); + case "already_reviewed": + return t("already_reviewed"); + case "submissions_unavailable": + return t("submissions_unavailable"); + case "uploads_unavailable": + return t("uploads_unavailable"); + case "backup_unavailable": + return t("backup_unavailable"); + // Account migration + the re-auth steps it depends on: every refusal below is + // an expected outcome of the Account → migrate flow, not a fault. + case "account_retired": + return t("account_retired"); + case "no_migration": + return t("no_migration"); + case "not_confirmed": + return t("not_confirmed"); + case "already_confirmed": + return t("already_confirmed"); + case "invalid_target": + return t("invalid_target"); + case "target_not_found": + return t("target_not_found"); + case "target_unavailable": + return t("target_unavailable"); + case "no_passkey": + return t("no_passkey"); + case "passkey_required": + return t("passkey_required"); + case "no_step_up_factor": + return t("no_step_up_factor"); + case "passkey_login_failed": + return t("passkey_login_failed"); + case "passkey_login_invalid": + return t("passkey_login_invalid"); + case "too_many_challenges": + return t("too_many_challenges"); + // Operator-login approvals, live streams, and the remaining auth doors. + case "op_login_invalid": + return t("op_login_invalid"); + case "op_login_not_found": + return t("op_login_not_found"); + case "too_many_streams": + return t("too_many_streams"); + case "protected_admin": + return t("protected_admin"); + case "auth_unavailable": + return t("auth_unavailable"); + case "setup_token_invalid": + return t("setup_token_invalid"); default: if (err.status === 401) return t("session_expired"); if (err.status === 403) return t("forbidden");