Unverified Commit c9af5481 authored by Lemon-miaow's avatar Lemon-miaow
Browse files

fix(panel): localize every user-reachable API error code (#60)

parent a2ff2a11
Loading
Loading
Loading
Loading
+38 −1
Changes for panel/src/i18n/resources/en-US/errors.json: 38 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -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."
}
+38 −1
Changes for panel/src/i18n/resources/zh-CN/errors.json: 38 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -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": "初始化令牌无效或已过期。"
}
+86 −0
Changes for panel/src/lib/api.ts: 86 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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");