Files
thebet365/packages/shared/src/api-errors.js
2026-06-13 17:38:25 +08:00

1003 lines
39 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const DEFAULT_LOCALE = 'zh-CN';
const SUPPORTED_LOCALES = ['zh-CN', 'ms-MY', 'en-US'];
/** 后端错误码 → 三语文案({param} 占位) */
export const API_ERROR_MESSAGES = {
INTERNAL_SERVER_ERROR: {
'zh-CN': '服务器内部错误',
'en-US': 'Internal server error',
'ms-MY': 'Ralat pelayan dalaman',
},
INVALID_CREDENTIALS: {
'zh-CN': '用户名或密码错误',
'en-US': 'Invalid username or password',
'ms-MY': 'Nama pengguna atau kata laluan tidak sah',
},
ACCOUNT_DISABLED: {
'zh-CN': '账号已停用',
'en-US': 'Account disabled',
'ms-MY': 'Akaun telah dinyahaktifkan',
},
ACCOUNT_SUSPENDED: {
'zh-CN': '账号已冻结',
'en-US': 'Account suspended',
'ms-MY': 'Akaun digantung',
},
AGENT_ACCOUNT_SUSPENDED: {
'zh-CN': '代理账号已停用',
'en-US': 'Agent account suspended',
'ms-MY': 'Akaun ejen digantung',
},
PARENT_AGENT_SUSPENDED: {
'zh-CN': '上级代理已停用,暂无法登录',
'en-US': 'Parent agent is suspended; login unavailable',
'ms-MY': 'Ejen induk digantung; log masuk tidak tersedia',
},
ACCOUNT_LOCKED: {
'zh-CN': '账号已锁定,请稍后再试',
'en-US': 'Account locked, try again later',
'ms-MY': 'Akaun dikunci, cuba lagi nanti',
},
USER_NOT_FOUND: {
'zh-CN': '用户不存在',
'en-US': 'User not found',
'ms-MY': 'Pengguna tidak dijumpai',
},
PASSWORD_CHANGE_DISABLED: {
'zh-CN': '当前平台未开放玩家自行修改密码',
'en-US': 'Password change is disabled for players',
'ms-MY': 'Pertukaran kata laluan pemain tidak dibenarkan',
},
INVALID_OLD_PASSWORD: {
'zh-CN': '当前密码不正确',
'en-US': 'Current password is incorrect',
'ms-MY': 'Kata laluan semasa tidak betul',
},
ADMIN_ACCESS_REQUIRED: {
'zh-CN': '需要管理员权限',
'en-US': 'Admin access required',
'ms-MY': 'Akses pentadbir diperlukan',
},
INSUFFICIENT_PERMISSIONS: {
'zh-CN': '权限不足',
'en-US': 'Insufficient permissions',
'ms-MY': 'Kebenaran tidak mencukupi',
},
ACCESS_DENIED_PORTAL: {
'zh-CN': '无权访问该门户',
'en-US': 'Access denied for this portal',
'ms-MY': 'Akses portal ditolak',
},
PLAYER_ACCESS_ONLY: {
'zh-CN': '仅玩家可访问',
'en-US': 'Player access only',
'ms-MY': 'Akses pemain sahaja',
},
ADMIN_ACCESS_ONLY: {
'zh-CN': '仅管理员可访问',
'en-US': 'Admin access only',
'ms-MY': 'Akses pentadbir sahaja',
},
AGENT_ACCESS_ONLY: {
'zh-CN': '仅代理可访问',
'en-US': 'Agent access only',
'ms-MY': 'Akses ejen sahaja',
},
LEAGUE_NAME_REQUIRED: {
'zh-CN': '请填写联赛名称(中文、英文或马来文至少一项)',
'en-US': 'League name required (Chinese, English or Malay)',
'ms-MY': 'Nama liga diperlukan (Cina, Inggeris atau Melayu)',
},
LEAGUE_NOT_FOUND: {
'zh-CN': '联赛不存在',
'en-US': 'League not found',
'ms-MY': 'Liga tidak dijumpai',
},
LEAGUE_UNPUBLISH_SETTLED: {
'zh-CN': '联赛冠军盘已结算,不可下架',
'en-US': 'Cannot unpublish league after outright market is settled',
'ms-MY': 'Liga tidak boleh ditarik selepas pasaran juara diselesaikan',
},
MATCH_UNPUBLISH_FORBIDDEN: {
'zh-CN': '当前状态不可下架',
'en-US': 'Match cannot be unpublished in current status',
'ms-MY': 'Perlawanan tidak boleh ditarik dalam status semasa',
},
TEAM_CODE_REQUIRED: {
'zh-CN': '请填写球队代码',
'en-US': 'Team code is required',
'ms-MY': 'Kod pasukan diperlukan',
},
TEAMS_NAME_REQUIRED: {
'zh-CN': '请填写主客队名称(中文、英文或马来文至少一项)',
'en-US': 'Home and away team names required',
'ms-MY': 'Nama pasukan home/away diperlukan',
},
TEAMS_SAME: {
'zh-CN': '主客队不能相同,请填写不同的队名',
'en-US': 'Home and away teams must be different',
'ms-MY': 'Pasukan home dan away mesti berbeza',
},
MATCH_NOT_FOUND: {
'zh-CN': '赛事不存在',
'en-US': 'Match not found',
'ms-MY': 'Perlawanan tidak dijumpai',
},
OUTRIGHT_EDIT_VIA_MARKETS: {
'zh-CN': '冠军盘请通过盘口管理维护',
'en-US': 'Edit outright markets in the markets page',
'ms-MY': 'Urus outright melalui halaman pasaran',
},
MATCH_NOT_EDITABLE: {
'zh-CN': '当前状态不可编辑',
'en-US': 'Match cannot be edited in current status',
'ms-MY': 'Perlawanan tidak boleh diedit dalam status semasa',
},
MATCH_NOT_REOPENABLE: {
'zh-CN': '当前状态不可解除封盘',
'en-US': 'Match cannot be reopened in current status',
'ms-MY': 'Perlawanan tidak boleh dibuka semula dalam status semasa',
},
MATCH_NOT_SETTLEABLE: {
'zh-CN': '当前状态不可确认结算',
'en-US': 'Match cannot be settled in current status',
'ms-MY': 'Perlawanan tidak boleh diselesaikan dalam status semasa',
},
MATCH_MUST_CLOSE_FOR_SETTLEMENT: {
'zh-CN': '请先封盘后再结算',
'en-US': 'Close betting before settlement',
'ms-MY': 'Tutup pertaruhan sebelum penyelesaian',
},
MATCH_REOPEN_KICKOFF_REQUIRED: {
'zh-CN': '开赛时间已过,请设置新的未来开赛时间',
'en-US': 'Kickoff has passed; set a new future start time',
'ms-MY': 'Masa mula telah berlalu; tetapkan masa mula baharu pada masa hadapan',
},
MATCH_START_TIME_INVALID: {
'zh-CN': '开赛时间格式无效',
'en-US': 'Invalid kickoff time',
'ms-MY': 'Format masa mula tidak sah',
},
OUTRIGHT_DELETE_FORBIDDEN: {
'zh-CN': '冠军盘不可删除',
'en-US': 'Outright events cannot be deleted',
'ms-MY': 'Acara outright tidak boleh dipadam',
},
MATCH_DELETE_DRAFT_ONLY: {
'zh-CN': '仅草稿状态可删除',
'en-US': 'Only draft matches can be deleted',
'ms-MY': 'Hanya draf boleh dipadam',
},
MATCH_HAS_BETS: {
'zh-CN': '该赛事已有注单关联,无法删除',
'en-US': 'Match has bets and cannot be deleted',
'ms-MY': 'Perlawanan mempunyai pertaruhan dan tidak boleh dipadam',
},
ARCHIVE_BLOCKED: {
'zh-CN': '存在未结注单或未结算状态,需确认强制删除',
'en-US': 'Unsettled bets or match state require forced archive',
'ms-MY': 'Pertaruhan belum selesai atau status perlawanan memerlukan arkib paksa',
},
LEAGUE_ARCHIVE_NOT_READY: {
'zh-CN': '联赛下仍有未结算赛事或未结注单,无法删除',
'en-US': 'League still has unsettled fixtures or pending bets',
'ms-MY': 'Liga masih mempunyai perlawanan belum selesai atau pertaruhan tertunda',
},
ALREADY_ARCHIVED: {
'zh-CN': '已删除或已隐藏',
'en-US': 'Already archived',
'ms-MY': 'Sudah diarkibkan',
},
MATCHES_ARRAY_REQUIRED: {
'zh-CN': '请提供 matches 数组',
'en-US': 'matches array is required',
'ms-MY': 'Tatasusunan matches diperlukan',
},
SELECTION_NOT_FOUND: {
'zh-CN': '投注选项不存在',
'en-US': 'Selection not found',
'ms-MY': 'Pilihan tidak dijumpai',
},
SELECTION_CLOSED: {
'zh-CN': '投注选项已关闭',
'en-US': 'Selection closed',
'ms-MY': 'Pilihan ditutup',
},
MARKET_CLOSED: {
'zh-CN': '盘口已关闭',
'en-US': 'Market closed',
'ms-MY': 'Pasaran ditutup',
},
MATCH_NOT_BETTING: {
'zh-CN': '该赛事暂不可投注',
'en-US': 'Match not available for betting',
'ms-MY': 'Perlawanan tidak tersedia untuk pertaruhan',
},
FOOTBALL_ONLY: {
'zh-CN': '仅支持足球投注',
'en-US': 'Only football betting is supported',
'ms-MY': 'Hanya pertaruhan bola sepak disokong',
},
PRE_MATCH_ONLY: {
'zh-CN': '仅支持赛前投注,比赛已开始',
'en-US': 'Pre-match betting only; match has started',
'ms-MY': 'Pertaruhan pra-perlawanan sahaja; perlawanan telah bermula',
},
ODDS_CHANGED: {
'zh-CN': '赔率已变更,请重新确认',
'en-US': 'Odds changed, please confirm again',
'ms-MY': 'Odds berubah, sila sahkan semula',
},
INVALID_STAKE: {
'zh-CN': '投注金额无效',
'en-US': 'Invalid stake',
'ms-MY': 'Stake tidak sah',
},
PARLAY_LEG_COUNT_INVALID: {
'zh-CN': '串关场次须在 {min}{max} 场之间',
'en-US': 'Parlay must have {min}{max} legs',
'ms-MY': 'Parlay mesti {min}{max} pilihan',
},
PARLAY_OUTRIGHT_FORBIDDEN: {
'zh-CN': '冠军盘不可加入串关',
'en-US': 'Outright cannot be in parlay',
'ms-MY': 'Outright tidak boleh dalam parlay',
},
PARLAY_QUARTER_LINE_FORBIDDEN: {
'zh-CN': '四分之一盘口不可加入串关',
'en-US': 'Quarter line markets cannot be in parlay',
'ms-MY': 'Pasaran suku baris tidak boleh dalam parlay',
},
PARLAY_MARKET_NOT_ALLOWED: {
'zh-CN': '该盘口不可加入串关',
'en-US': 'Market not allowed in parlay',
'ms-MY': 'Pasaran tidak dibenarkan dalam parlay',
},
SINGLE_MARKET_NOT_ALLOWED: {
'zh-CN': '该盘口不可单关投注',
'en-US': 'Market not allowed for single bets',
'ms-MY': 'Pasaran ini tidak dibenarkan untuk taruhan tunggal',
},
PARLAY_SAME_MATCH_FORBIDDEN: {
'zh-CN': '同一场比赛不能串关',
'en-US': 'Cannot parlay selections from the same match',
'ms-MY': 'Perlawanan sama tidak boleh berganda',
},
WALLET_FROZEN_INSUFFICIENT: {
'zh-CN': '注单冻结金额不足,无法结算',
'en-US': 'Frozen bet funds are insufficient for settlement',
'ms-MY': 'Dana beku pertaruhan tidak mencukupi untuk penyelesaian',
},
BET_NOT_FOUND: {
'zh-CN': '注单不存在',
'en-US': 'Bet not found',
'ms-MY': 'Pertaruhan tidak dijumpai',
},
MIN_STAKE: {
'zh-CN': '最低投注额为 {minStake}',
'en-US': 'Minimum stake is {minStake}',
'ms-MY': 'Stake minimum ialah {minStake}',
},
MAX_STAKE: {
'zh-CN': '最高投注额为 {maxStake}',
'en-US': 'Maximum stake is {maxStake}',
'ms-MY': 'Stake maksimum ialah {maxStake}',
},
MAX_PAYOUT: {
'zh-CN': '潜在派彩超过限额 {maxPayout}',
'en-US': 'Potential return exceeds limit of {maxPayout}',
'ms-MY': 'Bayaran melebihi had {maxPayout}',
},
DAILY_STAKE_LIMIT: {
'zh-CN': '已超过日投注限额 {limit}',
'en-US': 'Daily stake limit of {limit} exceeded',
'ms-MY': 'Had stake harian {limit} telah melebihi',
},
WALLET_NOT_FOUND: {
'zh-CN': '钱包不存在',
'en-US': 'Wallet not found',
'ms-MY': 'Dompet tidak dijumpai',
},
AMOUNT_MUST_BE_POSITIVE: {
'zh-CN': '金额须大于 0',
'en-US': 'Amount must be positive',
'ms-MY': 'Jumlah mesti positif',
},
INSUFFICIENT_BALANCE: {
'zh-CN': '余额不足',
'en-US': 'Insufficient balance',
'ms-MY': 'Baki tidak mencukupi',
},
AGENT_PROFILE_NOT_FOUND: {
'zh-CN': '代理资料不存在',
'en-US': 'Agent profile not found',
'ms-MY': 'Profil ejen tidak dijumpai',
},
AGENT_NOT_FOUND: {
'zh-CN': '代理不存在',
'en-US': 'Agent not found',
'ms-MY': 'Ejen tidak dijumpai',
},
CREDIT_LIMIT_NEGATIVE: {
'zh-CN': '授信额度不能为负',
'en-US': 'Credit limit cannot be negative',
'ms-MY': 'Had kredit tidak boleh negatif',
},
PLAYER_NOT_FOUND: {
'zh-CN': '玩家不存在',
'en-US': 'Player not found',
'ms-MY': 'Pemain tidak dijumpai',
},
NOT_PLAYER: {
'zh-CN': '该用户不是玩家',
'en-US': 'User is not a player',
'ms-MY': 'Pengguna bukan pemain',
},
PLAYER_HAS_PENDING_BETS: {
'zh-CN': '玩家仍有未结算注单,无法删除',
'en-US': 'Player has pending bets and cannot be deleted',
'ms-MY': 'Pemain masih ada pertaruhan belum selesai dan tidak boleh dipadam',
},
PLAYER_HAS_BALANCE: {
'zh-CN': '玩家钱包仍有余额,无法删除',
'en-US': 'Player wallet still has balance and cannot be deleted',
'ms-MY': 'Dompet pemain masih ada baki dan tidak boleh dipadam',
},
MANAGE_DIRECT_PLAYERS_ONLY: {
'zh-CN': '仅可管理直属玩家',
'en-US': 'Can only manage direct players',
'ms-MY': 'Hanya pemain terus boleh diurus',
},
PARENT_AGENT_NOT_FOUND: {
'zh-CN': '上级代理不存在',
'en-US': 'Parent agent not found',
'ms-MY': 'Ejen induk tidak dijumpai',
},
CREDIT_EXCEEDS_PARENT: {
'zh-CN': '下级代理授信不能超过上级授信额度',
'en-US': 'Sub-agent credit cannot exceed parent limit',
'ms-MY': 'Kredit sub-ejen tidak boleh melebihi induk',
},
CASHBACK_RATE_NEGATIVE: {
'zh-CN': '回水比例不能为负',
'en-US': 'Cashback rate cannot be negative',
'ms-MY': 'Kadar rebat tidak boleh negatif',
},
CASHBACK_RATE_EXCEEDS_PARENT: {
'zh-CN': '下级代理回水比例不能超过上级',
'en-US': 'Sub-agent cashback rate cannot exceed parent',
'ms-MY': 'Kadar rebat sub-ejen tidak boleh melebihi induk',
},
BET_LIMIT_EXCEEDS_PARENT: {
'zh-CN': '下级代理单笔限额不能超过上级',
'en-US': 'Sub-agent bet limit cannot exceed parent',
'ms-MY': 'Had pertaruhan sub-ejen tidak boleh melebihi induk',
},
BET_LIMIT_NEGATIVE: {
'zh-CN': '单笔限额不能为负',
'en-US': 'Bet limit cannot be negative',
'ms-MY': 'Had pertaruhan tidak boleh negatif',
},
DAILY_LIMIT_EXCEEDS_PARENT: {
'zh-CN': '下级代理日限额不能超过上级',
'en-US': 'Sub-agent daily limit cannot exceed parent',
'ms-MY': 'Had harian sub-ejen tidak boleh melebihi induk',
},
DAILY_LIMIT_NEGATIVE: {
'zh-CN': '日限额不能为负',
'en-US': 'Daily limit cannot be negative',
'ms-MY': 'Had harian tidak boleh negatif',
},
CREDIT_TOPUP_EXCEEDED: {
'zh-CN': '超过玩家上级代理可用授信,无法上分',
'en-US': 'Exceeds parent agent available credit',
'ms-MY': 'Melebihi kredit ejen induk yang tersedia',
},
AGENT_SINGLE_TOPUP_LIMIT: {
'zh-CN': '超过代理单笔上分限额',
'en-US': 'Exceeds agent single top-up limit',
'ms-MY': 'Melebihi had top-up tunggal ejen',
},
AGENT_DAILY_TOPUP_LIMIT: {
'zh-CN': '超过代理日上分限额',
'en-US': 'Exceeds agent daily top-up limit',
'ms-MY': 'Melebihi had top-up harian ejen',
},
INSUFFICIENT_AGENT_CREDIT: {
'zh-CN': '代理可用授信不足',
'en-US': 'Insufficient agent credit',
'ms-MY': 'Kredit ejen tidak mencukupi',
},
INVALID_STATUS: {
'zh-CN': '无效状态',
'en-US': 'Invalid status',
'ms-MY': 'Status tidak sah',
},
USERNAME_REQUIRED: {
'zh-CN': '账号名称不能为空',
'en-US': 'Username is required',
'ms-MY': 'Nama pengguna diperlukan',
},
USERNAME_TAKEN: {
'zh-CN': '账号名称已被占用',
'en-US': 'Username already taken',
'ms-MY': 'Nama pengguna sudah digunakan',
},
INVITE_CODE_REQUIRED: {
'zh-CN': '请填写邀请码',
'en-US': 'Invitation code is required',
'ms-MY': 'Kod jemputan diperlukan',
},
INVITE_CODE_INVALID: {
'zh-CN': '邀请码无效或已失效',
'en-US': 'Invalid or inactive invitation code',
'ms-MY': 'Kod jemputan tidak sah atau tidak aktif',
},
INVITE_CODE_NOT_AVAILABLE: {
'zh-CN': '该邀请码暂不可用于注册',
'en-US': 'This invitation code is not available for registration',
'ms-MY': 'Kod jemputan ini tidak tersedia untuk pendaftaran',
},
INVITE_NOT_FOUND: {
'zh-CN': '邀请码记录不存在',
'en-US': 'Invitation record not found',
'ms-MY': 'Rekod jemputan tidak dijumpai',
},
INVITE_MUST_REVOKE_FIRST: {
'zh-CN': '请先作废该邀请码后再删除',
'en-US': 'Revoke the invitation code before deleting',
'ms-MY': 'Batalkan kod jemputan dahulu sebelum padam',
},
INVITE_CODE_ALREADY_USED: {
'zh-CN': '该邀请码已被使用,每个邀请码仅可注册一名玩家',
'en-US': 'This invitation code has already been used; each code allows one registration only',
'ms-MY': 'Kod jemputan ini telah digunakan; setiap kod hanya untuk satu pendaftaran',
},
INVITE_CANNOT_DELETE_USED: {
'zh-CN': '已使用的邀请码不可删除',
'en-US': 'Used invitation codes cannot be deleted',
'ms-MY': 'Kod jemputan yang telah digunakan tidak boleh dipadam',
},
INVITE_CASHBACK_RATE_INVALID: {
'zh-CN': '返水比例无效,请输入非负数',
'en-US': 'Invalid cashback rate; must be a non-negative number',
'ms-MY': 'Kadar rebat tidak sah; mesti nombor bukan negatif',
},
USERNAME_FORMAT_INVALID: {
'zh-CN': '玩家用户名仅可使用英文字母和数字732 位),不可含中文或特殊符号',
'en-US': 'Username must be 732 letters or digits only',
'ms-MY': 'Nama pengguna mesti 732 huruf atau digit sahaja',
},
PASSWORD_MIN_LENGTH: {
'zh-CN': '密码至少 8 位',
'en-US': 'Password must be at least 8 characters',
'ms-MY': 'Kata laluan sekurang-kurangnya 8 aksara',
},
AUTH_INFO_MISSING: {
'zh-CN': '账号认证信息缺失',
'en-US': 'Account auth info missing',
'ms-MY': 'Maklumat auth akaun tiada',
},
USERNAME_CHANGE_DISABLED: {
'zh-CN': '当前平台未开放玩家自行修改账号名称',
'en-US': 'Username change is disabled for players',
'ms-MY': 'Pertukaran nama pengguna pemain tidak dibenarkan',
},
INVALID_AVATAR: {
'zh-CN': '无效头像',
'en-US': 'Invalid avatar',
'ms-MY': 'Avatar tidak sah',
},
UNSUPPORTED_LOCALE: {
'zh-CN': '不支持的语言',
'en-US': 'Unsupported locale',
'ms-MY': 'Locale tidak disokong',
},
NOT_SUB_AGENT: {
'zh-CN': '非您的下级代理',
'en-US': 'Not your sub-agent',
'ms-MY': 'Bukan sub-ejen anda',
},
PROMOTE_PLAYER_ONLY: {
'zh-CN': '仅玩家账号可设为代理',
'en-US': 'Only player accounts can be promoted to agent',
'ms-MY': 'Hanya pemain boleh dinaikkan ke ejen',
},
ALREADY_AGENT: {
'zh-CN': '该用户已是代理',
'en-US': 'User is already an agent',
'ms-MY': 'Pengguna sudah menjadi ejen',
},
AGENT_LEVEL_INVALID: {
'zh-CN': '代理级别无效',
'en-US': 'Invalid agent level',
'ms-MY': 'Tahap ejen tidak sah',
},
AGENT_MAX_LEVEL_REACHED: {
'zh-CN': '已达到最大代理层级,无法继续创建下级',
'en-US': 'Maximum agent level reached; cannot create sub-agents',
'ms-MY': 'Tahap ejen maksimum dicapai; tidak boleh cipta sub-ejen',
},
AGENT_PARENT_LEVEL_MISMATCH: {
'zh-CN': '上级代理层级与目标层级不匹配',
'en-US': 'Parent agent level does not match target level',
'ms-MY': 'Tahap ejen induk tidak sepadan dengan tahap sasaran',
},
AGENT_LEVEL_ROOT_INVALID: {
'zh-CN': '一级代理不可指定上级',
'en-US': 'Root-level agents cannot have a parent',
'ms-MY': 'Ejen peringkat akar tidak boleh ada induk',
},
LEVEL2_REQUIRES_PARENT: {
'zh-CN': '二级代理必须指定上级代理',
'en-US': 'Level 2 agent requires parent',
'ms-MY': 'Ejen peringkat 2 memerlukan induk',
},
TIER1_NO_PARENT_PLAYER: {
'zh-CN': '一级代理不可设置上级玩家',
'en-US': 'Tier-1 agents cannot have a parent player',
'ms-MY': 'Ejen peringkat 1 tidak boleh ada pemain induk',
},
PROMOTE_USE_CREDIT_NOT_BALANCE: {
'zh-CN': '设为代理时请使用授信额度,勿填玩家初始余额',
'en-US': 'Use credit limit when promoting to agent, not initial balance',
'ms-MY': 'Guna had kredit apabila naik taraf ke ejen, bukan baki awal',
},
INITIAL_DEPOSIT_REMARK_REQUIRED: {
'zh-CN': '有初始余额时必须选择上分流水说明',
'en-US': 'Ledger note is required when initial balance > 0',
'ms-MY': 'Nota ledger diperlukan apabila baki awal > 0',
},
INITIAL_DEPOSIT_REMARK_CUSTOM_INVALID: {
'zh-CN': '自定义流水说明至少 2 个字符',
'en-US': 'Custom ledger note must be at least 2 characters',
'ms-MY': 'Nota ledger tersuai mesti sekurang-kurangnya 2 aksara',
},
TIER2_REQUIRES_PARENT_AGENT: {
'zh-CN': '二级代理必须指定上级代理',
'en-US': 'Tier-2 agent must specify parent agent',
'ms-MY': 'Ejen peringkat 2 mesti nyatakan ejen induk',
},
PARENT_MUST_BE_AGENT: {
'zh-CN': '上级必须为代理账号',
'en-US': 'Parent must be an agent account',
'ms-MY': 'Induk mesti akaun ejen',
},
CREATE_DIRECT_PLAYERS_ONLY: {
'zh-CN': '仅可创建直属玩家',
'en-US': 'Can only create direct players',
'ms-MY': 'Hanya pemain terus boleh dicipta',
},
DB_RESET_FORBIDDEN: {
'zh-CN': '生产环境禁止重置数据库(需设置 ALLOW_DB_RESET=true',
'en-US': 'Database reset forbidden in production (set ALLOW_DB_RESET=true)',
'ms-MY': 'Reset DB dilarang di produksi (set ALLOW_DB_RESET=true)',
},
SMOKE_TESTS_FORBIDDEN: {
'zh-CN': '生产环境已禁用自动化测试',
'en-US': 'Smoke tests are disabled in production',
'ms-MY': 'Ujian asap dilumpuhkan di produksi',
},
CASHBACK_DATE_RANGE_INVALID: {
'zh-CN': '开始日期不能晚于结束日期',
'en-US': 'Start date cannot be after end date',
'ms-MY': 'Tarikh mula tidak boleh selepas tarikh tamat',
},
CASHBACK_ALREADY_ISSUED: {
'zh-CN': '该统计周期已发放返水,不可重复生成预览',
'en-US': 'Cashback already issued for this period',
'ms-MY': 'Rebat telah dikeluarkan untuk tempoh ini',
},
CASHBACK_NO_ELIGIBLE_BETS: {
'zh-CN': '该周期内无符合条件的返水,无法生成预览',
'en-US': 'No eligible bets in period for cashback preview',
'ms-MY': 'Tiada pertaruhan layak untuk pratonton rebat',
},
CASHBACK_BETS_IN_OTHER_BATCH: {
'zh-CN': '该周期内的有效注单均已计入其他返水批次,无法生成预览',
'en-US': 'Eligible bets already in another cashback batch',
'ms-MY': 'Pertaruhan layak sudah dalam batch rebat lain',
},
CASHBACK_BATCH_NOT_FOUND: {
'zh-CN': '返水批次不存在',
'en-US': 'Cashback batch not found',
'ms-MY': 'Batch rebat tidak dijumpai',
},
CASHBACK_BATCH_NOT_ISSUABLE: {
'zh-CN': '该批次不可发放',
'en-US': 'Batch cannot be issued',
'ms-MY': 'Batch tidak boleh dikeluarkan',
},
CASHBACK_NO_AMOUNT: {
'zh-CN': '批次无有效返水金额',
'en-US': 'Batch has no valid cashback amount',
'ms-MY': 'Batch tiada jumlah rebat sah',
},
CASHBACK_PERIOD_ALREADY_ISSUED: {
'zh-CN': '该统计周期已发放返水',
'en-US': 'Cashback already issued for this period',
'ms-MY': 'Rebat tempoh ini telah dikeluarkan',
},
CASHBACK_BETS_ALREADY_PAID: {
'zh-CN': '部分注单已在其他批次发放返水,请作废本预览后重新生成',
'en-US': 'Some bets paid in another batch; void preview and regenerate',
'ms-MY': 'Sebahagian pertaruhan dibayar dalam batch lain; batalkan pratonton',
},
CASHBACK_PREVIEW_ONLY_VOID: {
'zh-CN': '只能作废待发放批次',
'en-US': 'Only preview batches can be voided',
'ms-MY': 'Hanya batch pratonton boleh dibatalkan',
},
UNKNOWN_MARKET_TYPE: {
'zh-CN': '未知盘口类型:{marketType}',
'en-US': 'Unknown market type: {marketType}',
'ms-MY': 'Jenis pasaran tidak diketahui: {marketType}',
},
ODDS_MIN: {
'zh-CN': '赔率须大于 1.00',
'en-US': 'Odds must be greater than 1.00',
'ms-MY': 'Odds mesti lebih daripada 1.00',
},
MARKET_NOT_FOUND: {
'zh-CN': '盘口不存在',
'en-US': 'Market not found',
'ms-MY': 'Pasaran tidak dijumpai',
},
MARKET_TEMPLATE_NOT_FOUND: {
'zh-CN': '盘口模板不存在',
'en-US': 'Market template not found',
'ms-MY': 'Templat pasaran tidak dijumpai',
},
MARKET_LINE_REQUIRED: {
'zh-CN': '该盘口类型必须设置盘口线',
'en-US': 'This market type requires a line value',
'ms-MY': 'Jenis pasaran ini memerlukan nilai garisan',
},
MARKET_LINE_NOT_ALLOWED: {
'zh-CN': '该盘口类型不允许设置盘口线',
'en-US': 'This market type does not allow a line value',
'ms-MY': 'Jenis pasaran ini tidak membenarkan nilai garisan',
},
OPERATOR_REQUIRED: {
'zh-CN': '修改赔率须指定操作员',
'en-US': 'Operator required for odds update',
'ms-MY': 'Operator diperlukan untuk kemas kini odds',
},
OUTRIGHT_SELECTION_EXISTS: {
'zh-CN': '该球队代码已存在选项',
'en-US': 'Selection already exists for this team code',
'ms-MY': 'Pilihan untuk kod pasukan ini sudah wujud',
},
OUTRIGHT_TEAMS_REQUIRED: {
'zh-CN': '至少添加一支球队',
'en-US': 'At least one team required',
'ms-MY': 'Sekurang-kurangnya satu pasukan diperlukan',
},
OUTRIGHT_SELECTION_INVALID: {
'zh-CN': '无效的夺冠选项',
'en-US': 'Invalid selection for this outright event',
'ms-MY': 'Pilihan tidak sah untuk outright ini',
},
OUTRIGHT_LEAGUE_FIXTURES_UNSETTLED: {
'zh-CN': '该联赛仍有未结算的单场赛事,请先完成单场结算后再结算冠军盘',
'en-US': 'This league still has unsettled fixture matches. Settle them before settling the outright market.',
'ms-MY': 'Liga ini masih ada perlawanan belum diselesaikan. Selesaikan dahulu sebelum juara.',
},
OUTRIGHT_EVENT_NOT_FOUND: {
'zh-CN': '冠军盘赛事不存在',
'en-US': 'Outright event not found',
'ms-MY': 'Acara outright tidak dijumpai',
},
SETTLEMENT_WINNER_REQUIRED: {
'zh-CN': '冠军盘结算需指定获胜球队',
'en-US': 'Outright settlement requires winner team',
'ms-MY': 'Penyelesaian outright memerlukan pasukan pemenang',
},
SETTLEMENT_WINNER_NOT_FOUND: {
'zh-CN': '获胜球队不存在',
'en-US': 'Winner team not found',
'ms-MY': 'Pasukan pemenang tidak dijumpai',
},
SETTLEMENT_WINNER_NOT_IN_MARKET: {
'zh-CN': '该球队不在本冠军盘选项中',
'en-US': 'Team is not in this outright market',
'ms-MY': 'Pasukan tiada dalam pasaran outright ini',
},
SCORE_NOT_RECORDED: {
'zh-CN': '尚未录入比分',
'en-US': 'Score not recorded',
'ms-MY': 'Skor belum direkod',
},
SETTLEMENT_FACTS_REQUIRED: {
'zh-CN': '缺少比赛统计事实:{fields}',
'en-US': 'Missing match settlement facts: {fields}',
'ms-MY': 'Fakta penyelesaian perlawanan belum lengkap: {fields}',
},
SETTLEMENT_BATCH_NOT_FOUND: {
'zh-CN': '结算批次不存在',
'en-US': 'Settlement batch not found',
'ms-MY': 'Batch penyelesaian tidak dijumpai',
},
SETTLEMENT_BATCH_NOT_PREVIEW: {
'zh-CN': '结算批次不在预览状态',
'en-US': 'Batch is not in preview status',
'ms-MY': 'Batch bukan dalam status pratonton',
},
SETTLEMENT_BATCH_ALREADY_CONFIRMED: {
'zh-CN': '结算批次已确认',
'en-US': 'Batch already confirmed',
'ms-MY': 'Batch sudah disahkan',
},
SCORE_NOT_FOUND: {
'zh-CN': '比分不存在',
'en-US': 'Score not found',
'ms-MY': 'Skor tidak dijumpai',
},
PARLAY_UNSETTLED_LEGS: {
'zh-CN': '串关注单 {betId} 仍有未结算场次',
'en-US': 'Parlay bet {betId} has unsettled legs',
'ms-MY': 'Parlay {betId} masih ada pilihan belum selesai',
},
RESETTLE_SETTLED_ONLY: {
'zh-CN': '仅已结算赛事可重结算',
'en-US': 'Only settled matches can be resettled',
'ms-MY': 'Hanya perlawanan selesai boleh diselesaikan semula',
},
RESETTLE_BATCH_ONLY: {
'zh-CN': '非重结算批次',
'en-US': 'Not a resettle batch',
'ms-MY': 'Bukan batch penyelesaian semula',
},
UPLOAD_CATEGORY_UNSUPPORTED: {
'zh-CN': '不支持的上传分类',
'en-US': 'Unsupported upload category',
'ms-MY': 'Kategori muat naik tidak disokong',
},
UPLOAD_IMAGE_REQUIRED: {
'zh-CN': '请选择图片文件',
'en-US': 'Image file is required',
'ms-MY': 'Fail imej diperlukan',
},
UPLOAD_IMAGE_TYPE_INVALID: {
'zh-CN': '仅支持 PNG、JPG、WEBP、GIF 或 SVG 图片',
'en-US': 'Only PNG, JPG, WEBP, GIF or SVG images are allowed',
'ms-MY': 'Hanya PNG, JPG, WEBP, GIF atau SVG dibenarkan',
},
UPLOAD_SVG_UNSAFE: {
'zh-CN': 'SVG 内容不安全,已拒绝',
'en-US': 'Unsafe SVG content is not allowed',
'ms-MY': 'Kandungan SVG tidak selamat',
},
DB_RESET_PHRASE_INVALID: {
'zh-CN': '确认短语不正确,请输入 RESET',
'en-US': 'Invalid confirmation phrase; type RESET',
'ms-MY': 'Frasa pengesahan salah; taip RESET',
},
IMPORT_MATCHES_REQUIRED: {
'zh-CN': '导入数据无效:需要 matches[]',
'en-US': 'Invalid import payload: matches[] required',
'ms-MY': 'Payload import tidak sah: matches[] diperlukan',
},
WC_OUTRIGHT_NOT_FOUND: {
'zh-CN': '未找到 WC2026 冠军盘,请先导入',
'en-US': 'WC2026 outright not found — run import',
'ms-MY': 'Outright WC2026 tidak dijumpai — jalankan import',
},
FILE_NOT_FOUND: {
'zh-CN': '文件不存在',
'en-US': 'File not found',
'ms-MY': 'Fail tidak dijumpai',
},
URL_REQUIRED: {
'zh-CN': '请提供 url 参数',
'en-US': 'url is required',
'ms-MY': 'url diperlukan',
},
CONTENT_TYPE_INVALID: {
'zh-CN': '无效内容类型:{type}',
'en-US': 'Invalid contentType: {type}',
'ms-MY': 'Jenis kandungan tidak sah: {type}',
},
CONTENT_STATUS_INVALID: {
'zh-CN': '无效状态:{status}',
'en-US': 'Invalid status: {status}',
'ms-MY': 'Status tidak sah: {status}',
},
CONTENT_END_BEFORE_START: {
'zh-CN': '结束时间须晚于开始时间',
'en-US': 'endTime must be after startTime',
'ms-MY': 'endTime mesti selepas startTime',
},
CONTENT_TRANSLATION_REQUIRED: {
'zh-CN': '至少填写一条翻译',
'en-US': 'At least one translation required',
'ms-MY': 'Sekurang-kurangnya satu terjemahan diperlukan',
},
CONTENT_LOCALE_REQUIRED: {
'zh-CN': '翻译语言不能为空',
'en-US': 'Translation locale required',
'ms-MY': 'Locale terjemahan diperlukan',
},
CONTENT_LOCALE_DUPLICATE: {
'zh-CN': '重复的语言:{locale}',
'en-US': 'Duplicate locale: {locale}',
'ms-MY': 'Locale pendua: {locale}',
},
CONTENT_LINK_TYPE_INVALID: {
'zh-CN': '无效链接类型:{linkType}',
'en-US': 'Invalid linkType: {linkType}',
'ms-MY': 'Jenis pautan tidak sah: {linkType}',
},
CONTENT_LINK_TARGET_REQUIRED: {
'zh-CN': '设置链接类型时须填写 linkTarget',
'en-US': 'linkTarget required when linkType is set',
'ms-MY': 'linkTarget diperlukan apabila linkType ditetapkan',
},
CONTENT_NOT_FOUND: {
'zh-CN': '内容不存在',
'en-US': 'Content not found',
'ms-MY': 'Kandungan tidak dijumpai',
},
CONTENT_ACTIVE_BANNER_INCOMPLETE: {
'zh-CN': '启用 Banner 须至少一种语言配置图片地址',
'en-US': 'ACTIVE banner requires imageUrl in at least one locale',
'ms-MY': 'Banner aktif memerlukan imageUrl sekurang-kurangnya satu locale',
},
CONTENT_ACTIVE_NOTICE_INCOMPLETE: {
'zh-CN': '启用公告须至少一种语言填写标题或正文',
'en-US': 'ACTIVE notice requires title or body in at least one locale',
'ms-MY': 'Notis aktif memerlukan tajuk atau kandungan',
},
CONTENT_ACTIVE_TICKER_INCOMPLETE: {
'zh-CN': '启用滚动公告须至少一种语言填写正文',
'en-US': 'ACTIVE ticker requires body in at least one locale',
'ms-MY': 'Ticker aktif memerlukan kandungan',
},
INVALID_METHOD_TYPE: {
'zh-CN': '无效的收款方式类型',
'en-US': 'Invalid payment method type',
'ms-MY': 'Jenis kaedah pembayaran tidak sah',
},
PAYMENT_METHOD_NOT_FOUND: {
'zh-CN': '收款方式不存在或已停用',
'en-US': 'Payment method not found or inactive',
'ms-MY': 'Kaedah pembayaran tidak dijumpai atau tidak aktif',
},
ORDER_NOT_FOUND: {
'zh-CN': '充值订单不存在',
'en-US': 'Deposit order not found',
'ms-MY': 'Pesanan deposit tidak dijumpai',
},
ORDER_NOT_PENDING: {
'zh-CN': '订单已被审核或不是待审核状态',
'en-US': 'Order is not in pending status',
'ms-MY': 'Pesanan bukan dalam status menunggu',
},
ORDER_ALREADY_PENDING: {
'zh-CN': '订单已是待审核状态',
'en-US': 'Order is already pending review',
'ms-MY': 'Pesanan sudah menunggu semakan',
},
ORDER_NOT_APPROVED: {
'zh-CN': '仅已通过的充值订单可撤销',
'en-US': 'Only approved deposit orders can be revoked',
'ms-MY': 'Hanya pesanan deposit yang diluluskan boleh dibatalkan',
},
DEPOSIT_REVOKE_WINDOW_EXPIRED: {
'zh-CN': '批准已超过 5 分钟,无法撤回',
'en-US': 'Approval was more than 5 minutes ago; revoke is no longer allowed',
'ms-MY': 'Kelulusan melebihi 5 minit; pembatalan tidak dibenarkan',
},
DEPOSIT_REVOKE_SETTLED_BETS: {
'zh-CN': '批准后已有投注,无法直接撤回;请走冲正流程',
'en-US': 'Bets were placed after approval; use an adjustment flow instead',
'ms-MY': 'Terdapat pertaruhan selepas kelulusan; sila gunakan aliran pelarasan',
},
DEPOSIT_ORDER_FUNDED_DELETE_FORBIDDEN: {
'zh-CN': '已入账充值订单不能删除,请走撤回或冲正流程',
'en-US': 'Funded deposit orders cannot be deleted; use revoke or adjustment flow',
'ms-MY': 'Pesanan deposit yang telah dikreditkan tidak boleh dipadam; gunakan pembatalan atau pelarasan',
},
REASON_REQUIRED: {
'zh-CN': '请填写拒绝原因',
'en-US': 'Rejection reason is required',
'ms-MY': 'Sebab penolakan diperlukan',
},
SCREENSHOT_REQUIRED: {
'zh-CN': '请上传转账截图',
'en-US': 'Screenshot is required',
'ms-MY': 'Screenshot diperlukan',
},
FILE_MUST_BE_IMAGE: {
'zh-CN': '请上传图片文件',
'en-US': 'File must be an image',
'ms-MY': 'Fail mesti imej',
},
INVALID_AMOUNT: {
'zh-CN': '金额无效',
'en-US': 'Invalid amount',
'ms-MY': 'Jumlah tidak sah',
},
PAYMENT_METHOD_REQUIRED: {
'zh-CN': '请选择收款方式',
'en-US': 'Payment method is required',
'ms-MY': 'Kaedah pembayaran diperlukan',
},
PHONE_REQUIRED: {
'zh-CN': '请填写手机号',
'en-US': 'Phone number is required',
'ms-MY': 'Nombor telefon diperlukan',
},
PHONE_INVALID: {
'zh-CN': '手机号格式无效',
'en-US': 'Invalid phone number',
'ms-MY': 'Nombor telefon tidak sah',
},
PHONE_TAKEN: {
'zh-CN': '该手机号已注册',
'en-US': 'This phone number is already registered',
'ms-MY': 'Nombor telefon ini sudah didaftarkan',
},
PHONE_NOT_REGISTERED: {
'zh-CN': '该手机号未注册',
'en-US': 'This phone number is not registered',
'ms-MY': 'Nombor telefon ini belum didaftarkan',
},
SMS_CODE_REQUIRED: {
'zh-CN': '请填写短信验证码',
'en-US': 'SMS verification code is required',
'ms-MY': 'Kod pengesahan SMS diperlukan',
},
SMS_CODE_INVALID: {
'zh-CN': '验证码错误',
'en-US': 'Incorrect verification code',
'ms-MY': 'Kod pengesahan salah',
},
SMS_CODE_EXPIRED: {
'zh-CN': '验证码已过期,请重新获取',
'en-US': 'Verification code expired, please request a new one',
'ms-MY': 'Kod pengesahan tamat tempoh, sila minta yang baharu',
},
SMS_RATE_LIMIT: {
'zh-CN': '发送太频繁请60秒后再试',
'en-US': 'Too many requests, please try again in 60 seconds',
'ms-MY': 'Terlalu kerap, sila cuba lagi dalam 60 saat',
},
SMS_SEND_FAILED: {
'zh-CN': '短信发送失败,请稍后重试',
'en-US': 'Failed to send SMS, please try again later',
'ms-MY': 'Gagal menghantar SMS, sila cuba lagi',
},
PHONE_COUNTRY_UNSUPPORTED: {
'zh-CN': '暂不支持该国家/地区',
'en-US': 'This country or region is not supported',
'ms-MY': 'Negara atau wilayah ini tidak disokong',
},
};
export function normalizeLocale(input) {
const raw = String(input ?? '').trim();
if (!raw)
return DEFAULT_LOCALE;
const lower = raw.toLowerCase();
if (lower.startsWith('zh'))
return 'zh-CN';
if (lower.startsWith('ms') || lower.startsWith('my'))
return 'ms-MY';
if (lower.startsWith('en'))
return 'en-US';
if (SUPPORTED_LOCALES.includes(raw))
return raw;
return DEFAULT_LOCALE;
}
export function formatApiErrorMessage(code, localeInput, params) {
const locale = normalizeLocale(localeInput);
const template = API_ERROR_MESSAGES[code]?.[locale] ??
API_ERROR_MESSAGES[code]?.[DEFAULT_LOCALE] ??
API_ERROR_MESSAGES.INTERNAL_SERVER_ERROR[locale];
if (!params)
return template;
return template.replace(/\{(\w+)\}/g, (_match, key) => String(params[key] ?? `{${key}}`));
}
export function isApiErrorCode(value) {
return typeof value === 'string' && value in API_ERROR_MESSAGES;
}