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',
    },
    CANNOT_DELETE_SELF: {
        'zh-CN': '不能删除自己',
        'en-US': 'Cannot delete yourself',
        'ms-MY': 'Tidak boleh memadam diri sendiri',
    },
    STAFF_NOT_FOUND: {
        'zh-CN': '管理员不存在',
        'en-US': 'Staff member not found',
        'ms-MY': 'Ahli kakitangan tidak dijumpai',
    },
    CANNOT_DELETE_LAST_SUPER_ADMIN: {
        'zh-CN': '不能删除唯一的超级管理员',
        'en-US': 'Cannot delete the last super admin',
        'ms-MY': 'Tidak boleh memadam pentadbir super terakhir',
    },
    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',
    },
    INVALID_ROLE: {
        'zh-CN': '无效角色',
        'en-US': 'Invalid role',
        'ms-MY': 'Peranan tidak sah',
    },
    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': '玩家用户名仅可使用英文字母和数字（7–32 位），不可含中文或特殊符号',
        'en-US': 'Username must be 7–32 letters or digits only',
        'ms-MY': 'Nama pengguna mesti 7–32 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',
    },
    SETTLEMENT_BATCH_STALE: {
        'zh-CN': '结算批次已过期，请使用最新预览批次',
        'en-US': 'Settlement batch is stale; use the latest preview batch',
        'ms-MY': 'Batch penyelesaian lapuk; guna batch pratonton terkini',
    },
    SETTLEMENT_BET_UPDATE_FAILED: {
        'zh-CN': '注单 {betNo} 结算状态更新失败，请重试',
        'en-US': 'Failed to update bet {betNo} for settlement',
        'ms-MY': 'Gagal mengemas kini pertaruhan {betNo} untuk penyelesaian',
    },
    SETTLEMENT_SCORE_INVALID: {
        'zh-CN': '比分无效：半场比分不能大于全场比分',
        'en-US': 'Invalid score: half-time cannot exceed full-time',
        'ms-MY': 'Skor tidak sah: separuh masa tidak boleh melebihi masa penuh',
    },
    SETTLEMENT_MARKET_UNSUPPORTED: {
        'zh-CN': '盘口 {marketType} 暂不支持结算',
        'en-US': 'Market type {marketType} is not supported for settlement',
        'ms-MY': 'Jenis pasaran {marketType} belum disokong untuk penyelesaian',
    },
    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',
    },
    MESSAGE_NOT_FOUND: {
        'zh-CN': '消息不存在',
        'en-US': 'Message not found',
        'ms-MY': 'Mesej tidak dijumpai',
    },
    BROADCAST_NOT_FOUND: {
        'zh-CN': '发送记录不存在',
        'en-US': 'Broadcast record not found',
        'ms-MY': 'Rekod siaran tidak dijumpai',
    },
    BROADCAST_TITLE_REQUIRED: {
        'zh-CN': '标题不能为空',
        'en-US': 'Title is required',
        'ms-MY': 'Tajuk diperlukan',
    },
    BROADCAST_BODY_REQUIRED: {
        'zh-CN': '正文不能为空',
        'en-US': 'Body is required',
        'ms-MY': 'Kandungan diperlukan',
    },
    BROADCAST_TITLE_TOO_LONG: {
        'zh-CN': '标题过长（最多 256 字符）',
        'en-US': 'Title is too long (max 256 characters)',
        'ms-MY': 'Tajuk terlalu panjang (maks 256 aksara)',
    },
    BROADCAST_TARGET_USER_REQUIRED: {
        'zh-CN': '请指定玩家账号',
        'en-US': 'Target player username is required',
        'ms-MY': 'Nama pengguna pemain sasaran diperlukan',
    },
    BROADCAST_NO_RECIPIENTS: {
        'zh-CN': '没有可发送的玩家',
        'en-US': 'No eligible recipients',
        'ms-MY': 'Tiada penerima yang layak',
    },
    BROADCAST_CONTENT_REQUIRED: {
        'zh-CN': '请至少填写一种语言的标题或正文',
        'en-US': 'Provide a title or body in at least one language',
        'ms-MY': 'Sila isi tajuk atau kandungan sekurang-kurangnya satu bahasa',
    },
    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_REJECTED: {
        'zh-CN': '仅已拒绝的充值订单可重新申请',
        'en-US': 'Only rejected deposit orders can be resubmitted',
        'ms-MY': 'Hanya pesanan deposit yang ditolak boleh dihantar semula',
    },
    DEPOSIT_PENDING_ORDER_EXISTS: {
        'zh-CN': '您已有待审核的充值订单，请等待审核完成后再提交',
        'en-US': 'You already have a pending deposit order. Wait for review before submitting another.',
        'ms-MY': 'Anda sudah ada pesanan deposit menunggu. Tunggu semakan selesai sebelum hantar lagi.',
    },
    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',
    },
    BEFORE_DATE_REQUIRED: {
        'zh-CN': '截止日期不能为空',
        'en-US': 'Before date is required',
        'ms-MY': 'Tarikh akhir diperlukan',
    },
    INVALID_BEFORE_DATE: {
        'zh-CN': '截止日期无效',
        'en-US': 'Invalid before date',
        'ms-MY': 'Tarikh akhir tidak sah',
    },
    BEFORE_DATE_CANNOT_BE_FUTURE: {
        'zh-CN': '截止日期不能是未来日期',
        'en-US': 'Before date cannot be in the future',
        'ms-MY': 'Tarikh akhir tidak boleh pada masa hadapan',
    },
};
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;
}
