feat(i18n): 管理端与玩家端三语支持(中/英/马来语)
- 管理后台 adminT 文案库、结算与代理端页面、表单校验 - 玩家端 vue-i18n 补全首页/公告/串关与 ms 文案 - Element Plus ms 语言包与共享 locale 工具
This commit is contained in:
489
apps/admin/src/i18n/admin-messages.ts
Normal file
489
apps/admin/src/i18n/admin-messages.ts
Normal file
@@ -0,0 +1,489 @@
|
||||
import { adminPagesEn, adminPagesZh } from './admin-pages';
|
||||
import { adminPagesMs } from './admin-pages-ms';
|
||||
|
||||
/** 管理后台:中文 + 英文 + 马来语 */
|
||||
export type AdminLocale = 'zh-CN' | 'en-US' | 'ms-MY';
|
||||
|
||||
export const ADMIN_LOCALES: {
|
||||
code: AdminLocale;
|
||||
label: string;
|
||||
flag: string;
|
||||
}[] = [
|
||||
{ code: 'zh-CN', label: '中文', flag: '🇨🇳' },
|
||||
{ code: 'en-US', label: 'English', flag: '🇺🇸' },
|
||||
{ code: 'ms-MY', label: 'Bahasa Melayu', flag: '🇲🇾' },
|
||||
];
|
||||
|
||||
const zh: Record<string, string> = {
|
||||
'login.title': '管理后台',
|
||||
'login.username': '用户名',
|
||||
'login.password': '密码',
|
||||
'login.submit': '登录',
|
||||
'login.username_ph': '请输入用户名',
|
||||
'login.password_ph': '请输入密码',
|
||||
'login.err_captcha': '验证码错误,请重试',
|
||||
'login.err_failed': '登录失败,请检查账号与密码',
|
||||
'login.err_quick': '快速登录失败',
|
||||
'login.quick_label': '快速登录(调试)',
|
||||
'login.quick_admin': '管理员',
|
||||
'login.quick_agent': '一级代理',
|
||||
'login.captcha_ph': '验证码',
|
||||
'login.captcha_refresh': '点击刷新',
|
||||
|
||||
'nav.dashboard': '控制台',
|
||||
'nav.users': '玩家管理',
|
||||
'nav.agents': '代理管理',
|
||||
'nav.matches': '赛事管理',
|
||||
'nav.bets': '注单管理',
|
||||
'nav.cashback': '返水管理',
|
||||
'nav.audit': '操作日志',
|
||||
'nav.players': '直属玩家',
|
||||
'nav.subAgents': '下级代理',
|
||||
'nav.myBets': '注单查询',
|
||||
'role.admin': '系统管理员',
|
||||
'role.agent': '代理账号',
|
||||
'logout': '退出',
|
||||
'lang': '语言',
|
||||
'portal.admin': '平台后台',
|
||||
'portal.agent': '代理后台',
|
||||
|
||||
'common.all': '全部',
|
||||
'common.search': '查询',
|
||||
'common.reset': '重置',
|
||||
'common.edit': '编辑',
|
||||
'common.delete': '删除',
|
||||
'common.cancel': '取消',
|
||||
'common.confirm': '确定',
|
||||
'common.status': '状态',
|
||||
'common.type': '类型',
|
||||
'common.keyword': '关键词',
|
||||
'common.actions': '操作',
|
||||
'common.loading': '加载中…',
|
||||
'common.no_data': '暂无数据',
|
||||
'common.yesterday': '昨日',
|
||||
'common.vs_yesterday': '较昨日',
|
||||
'common.people': '人',
|
||||
'common.bets_unit': '单',
|
||||
'common.matches_unit': '场赛事',
|
||||
'common.frozen': '冻结',
|
||||
'common.used': '已用',
|
||||
'common.platform_direct': '平台直属',
|
||||
'common.updated_at': '更新于',
|
||||
|
||||
'dash.title': '控制台',
|
||||
'dash.desc': '平台整体运行概况',
|
||||
'dash.board_title': '整体概览',
|
||||
'dash.board_hint': '一屏查看经营趋势与平台分布',
|
||||
'dash.kpi_bet_count': '今日投注笔数',
|
||||
'dash.kpi_stake': '今日投注额',
|
||||
'dash.kpi_payout': '今日派彩',
|
||||
'dash.kpi_ggr': '今日毛利',
|
||||
'dash.kpi_users': '玩家 / 代理',
|
||||
'dash.kpi_new_players': '今日新增 {n} 人',
|
||||
'dash.kpi_pending': '待结算',
|
||||
'dash.kpi_pending_sub': '{bets} 单 · {matches} 场赛事',
|
||||
'dash.kpi_wallet': '玩家余额',
|
||||
'dash.kpi_credit': '代理授信',
|
||||
'dash.trend_caption': '近 7 日经营趋势(金额折线 + 注单柱)',
|
||||
'dash.chart_stake': '投注额',
|
||||
'dash.chart_payout': '派彩',
|
||||
'dash.chart_ggr': '毛利',
|
||||
'dash.chart_bet_count': '注单笔数',
|
||||
'dash.axis_amount': '金额',
|
||||
'dash.axis_count': '笔数',
|
||||
'dash.count_suffix': '笔',
|
||||
'dash.chart_tooltip': '{b}:{c}({d}%)',
|
||||
'dash.pie_empty': '暂无',
|
||||
'dash.pie_matches': '赛事',
|
||||
'dash.pie_bets': '今日注单',
|
||||
'dash.pie_users': '用户',
|
||||
'dash.match_draft': '草稿',
|
||||
'dash.match_published': '已发布',
|
||||
'dash.match_closed': '已封盘',
|
||||
'dash.match_pending_settle': '待结算',
|
||||
'dash.match_settled': '已结算',
|
||||
'dash.user_active': '正常玩家',
|
||||
'dash.user_suspended': '停用',
|
||||
'dash.user_direct': '直属',
|
||||
'dash.user_agents': '代理',
|
||||
|
||||
'page.users.title': '玩家管理',
|
||||
'page.users.desc': '创建玩家、查看余额与投注概况,支持上分与状态管理',
|
||||
'page.agents.title': '代理管理',
|
||||
'page.agents.desc': '创建一级代理、调整授信额度、查看直属玩家与额度占用',
|
||||
'page.matches.title': '赛事管理',
|
||||
'page.matches.desc': '草稿可编辑、删除;已发布可改开赛时间与热门',
|
||||
'page.bets.title': '注单管理',
|
||||
'page.bets.desc': '筛选、分页查看全平台注单,支持详情与投注项',
|
||||
'page.cashback.title': '返水管理',
|
||||
'page.cashback.desc': '按周期生成返水并发放',
|
||||
'page.audit.title': '操作日志',
|
||||
'page.audit.desc': '记录所有管理员操作行为',
|
||||
'page.settlement.title': '赛事结算',
|
||||
'page.agent_dash.title': '代理概览',
|
||||
'page.agent_dash.desc': '实时数据总览',
|
||||
'page.agent_players.title': '直属玩家',
|
||||
'page.agent_players.desc': '管理你名下的直属玩家',
|
||||
'page.agent_sub.title': '下级代理',
|
||||
'page.agent_sub.desc': '仅一级代理可见',
|
||||
'page.agent_bets.title': '注单查询',
|
||||
'page.agent_bets.desc': '下级玩家的全部投注记录',
|
||||
|
||||
'agent.credit_limit': '授信额度',
|
||||
'agent.credit_total': '总额度',
|
||||
'agent.credit_used': '已用额度',
|
||||
'agent.credit_occupied': '已占用',
|
||||
'agent.direct_players': '直属玩家',
|
||||
'agent.player_count': '玩家数',
|
||||
'agent.today_stake': '今日投注',
|
||||
'agent.currency_cny': '人民币',
|
||||
|
||||
'bet.status.PENDING': '待结算',
|
||||
'bet.status.WON': '已赢',
|
||||
'bet.status.LOST': '已输',
|
||||
'bet.status.VOID': '作废',
|
||||
'bet.status.REFUNDED': '已退款',
|
||||
'bet.type.SINGLE': '单关',
|
||||
'bet.type.PARLAY': '串关',
|
||||
'bet.settlement.PENDING': '待结算',
|
||||
'bet.settlement.SETTLED': '已结算',
|
||||
'bet.settlement.VOID': '已作废',
|
||||
'bet.result.WON': '赢',
|
||||
'bet.result.LOST': '输',
|
||||
'bet.result.VOID': '走水',
|
||||
'bet.result.PUSH': '走盘',
|
||||
'bet.result.HALF_WON': '半赢',
|
||||
'bet.result.HALF_LOST': '半输',
|
||||
|
||||
'match.status.DRAFT': '草稿',
|
||||
'match.status.PUBLISHED': '已发布',
|
||||
'match.status.CLOSED': '已封盘',
|
||||
'match.status.SETTLED': '已结算',
|
||||
...adminPagesZh,
|
||||
};
|
||||
|
||||
const en: Record<string, string> = {
|
||||
'login.title': 'Admin Console',
|
||||
'login.username': 'Username',
|
||||
'login.password': 'Password',
|
||||
'login.submit': 'Sign in',
|
||||
'login.username_ph': 'Enter username',
|
||||
'login.password_ph': 'Enter password',
|
||||
'login.err_captcha': 'Captcha incorrect, try again',
|
||||
'login.err_failed': 'Sign-in failed. Check username and password',
|
||||
'login.err_quick': 'Quick sign-in failed',
|
||||
'login.quick_label': 'Quick sign-in (debug)',
|
||||
'login.quick_admin': 'Admin',
|
||||
'login.quick_agent': 'Tier-1 agent',
|
||||
'login.captcha_ph': 'Captcha',
|
||||
'login.captcha_refresh': 'Click to refresh',
|
||||
|
||||
'nav.dashboard': 'Dashboard',
|
||||
'nav.users': 'Players',
|
||||
'nav.agents': 'Agents',
|
||||
'nav.matches': 'Matches',
|
||||
'nav.bets': 'Bets',
|
||||
'nav.cashback': 'Cashback',
|
||||
'nav.audit': 'Audit Log',
|
||||
'nav.players': 'My Players',
|
||||
'nav.subAgents': 'Sub-Agents',
|
||||
'nav.myBets': 'Bet Search',
|
||||
'role.admin': 'Administrator',
|
||||
'role.agent': 'Agent',
|
||||
'logout': 'Logout',
|
||||
'lang': 'Language',
|
||||
'portal.admin': 'Platform Admin',
|
||||
'portal.agent': 'Agent Portal',
|
||||
|
||||
'common.all': 'All',
|
||||
'common.search': 'Search',
|
||||
'common.reset': 'Reset',
|
||||
'common.edit': 'Edit',
|
||||
'common.delete': 'Delete',
|
||||
'common.cancel': 'Cancel',
|
||||
'common.confirm': 'OK',
|
||||
'common.status': 'Status',
|
||||
'common.type': 'Type',
|
||||
'common.keyword': 'Keyword',
|
||||
'common.actions': 'Actions',
|
||||
'common.loading': 'Loading…',
|
||||
'common.no_data': 'No data',
|
||||
'common.yesterday': 'Yesterday',
|
||||
'common.vs_yesterday': 'vs yesterday',
|
||||
'common.people': 'users',
|
||||
'common.bets_unit': 'bets',
|
||||
'common.matches_unit': 'matches',
|
||||
'common.frozen': 'Frozen',
|
||||
'common.used': 'Used',
|
||||
'common.platform_direct': 'Platform direct',
|
||||
'common.updated_at': 'Updated',
|
||||
|
||||
'dash.title': 'Dashboard',
|
||||
'dash.desc': 'Platform overview',
|
||||
'dash.board_title': 'Overview',
|
||||
'dash.board_hint': 'Trends and distribution at a glance',
|
||||
'dash.kpi_bet_count': "Today's bet count",
|
||||
'dash.kpi_stake': "Today's stake",
|
||||
'dash.kpi_payout': "Today's payout",
|
||||
'dash.kpi_ggr': "Today's GGR",
|
||||
'dash.kpi_users': 'Players / Agents',
|
||||
'dash.kpi_new_players': '{n} new today',
|
||||
'dash.kpi_pending': 'Pending settlement',
|
||||
'dash.kpi_pending_sub': '{bets} bets · {matches} matches',
|
||||
'dash.kpi_wallet': 'Player balance',
|
||||
'dash.kpi_credit': 'Agent credit',
|
||||
'dash.trend_caption': 'Last 7 days (amount lines + bet bars)',
|
||||
'dash.chart_stake': 'Stake',
|
||||
'dash.chart_payout': 'Payout',
|
||||
'dash.chart_ggr': 'GGR',
|
||||
'dash.chart_bet_count': 'Bet count',
|
||||
'dash.axis_amount': 'Amount',
|
||||
'dash.axis_count': 'Count',
|
||||
'dash.count_suffix': 'bets',
|
||||
'dash.chart_tooltip': '{b}: {c} ({d}%)',
|
||||
'dash.pie_empty': 'N/A',
|
||||
'dash.pie_matches': 'Matches',
|
||||
'dash.pie_bets': "Today's bets",
|
||||
'dash.pie_users': 'Users',
|
||||
'dash.match_draft': 'Draft',
|
||||
'dash.match_published': 'Published',
|
||||
'dash.match_closed': 'Closed',
|
||||
'dash.match_pending_settle': 'Pending settlement',
|
||||
'dash.match_settled': 'Settled',
|
||||
'dash.user_active': 'Active players',
|
||||
'dash.user_suspended': 'Suspended',
|
||||
'dash.user_direct': 'Direct',
|
||||
'dash.user_agents': 'Agents',
|
||||
|
||||
'page.users.title': 'Players',
|
||||
'page.users.desc': 'Create players, balances, stakes, top-ups, and status',
|
||||
'page.agents.title': 'Agents',
|
||||
'page.agents.desc': 'Tier-1 agents, credit limits, players, and usage',
|
||||
'page.matches.title': 'Matches',
|
||||
'page.matches.desc': 'Edit/delete drafts; adjust kickoff and featured when published',
|
||||
'page.bets.title': 'Bets',
|
||||
'page.bets.desc': 'Filter and paginate all bets with leg details',
|
||||
'page.cashback.title': 'Cashback',
|
||||
'page.cashback.desc': 'Generate and issue cashback by period',
|
||||
'page.audit.title': 'Audit Log',
|
||||
'page.audit.desc': 'Administrator action history',
|
||||
'page.settlement.title': 'Settlement',
|
||||
'page.agent_dash.title': 'Agent overview',
|
||||
'page.agent_dash.desc': 'Live summary',
|
||||
'page.agent_players.title': 'My players',
|
||||
'page.agent_players.desc': 'Players under your account',
|
||||
'page.agent_sub.title': 'Sub-agents',
|
||||
'page.agent_sub.desc': 'Tier-1 agents only',
|
||||
'page.agent_bets.title': 'Bet search',
|
||||
'page.agent_bets.desc': 'All bets from downstream players',
|
||||
|
||||
'agent.credit_limit': 'Credit limit',
|
||||
'agent.credit_total': 'Total limit',
|
||||
'agent.credit_used': 'Used credit',
|
||||
'agent.credit_occupied': 'In use',
|
||||
'agent.direct_players': 'Direct players',
|
||||
'agent.player_count': 'Players',
|
||||
'agent.today_stake': "Today's stake",
|
||||
'agent.currency_cny': 'CNY',
|
||||
|
||||
'bet.status.PENDING': 'Pending',
|
||||
'bet.status.WON': 'Won',
|
||||
'bet.status.LOST': 'Lost',
|
||||
'bet.status.VOID': 'Void',
|
||||
'bet.status.REFUNDED': 'Refunded',
|
||||
'bet.type.SINGLE': 'Single',
|
||||
'bet.type.PARLAY': 'Parlay',
|
||||
'bet.settlement.PENDING': 'Pending',
|
||||
'bet.settlement.SETTLED': 'Settled',
|
||||
'bet.settlement.VOID': 'Void',
|
||||
'bet.result.WON': 'Won',
|
||||
'bet.result.LOST': 'Lost',
|
||||
'bet.result.VOID': 'Void',
|
||||
'bet.result.PUSH': 'Push',
|
||||
'bet.result.HALF_WON': 'Half won',
|
||||
'bet.result.HALF_LOST': 'Half lost',
|
||||
|
||||
'match.status.DRAFT': 'Draft',
|
||||
'match.status.PUBLISHED': 'Published',
|
||||
'match.status.CLOSED': 'Closed',
|
||||
'match.status.SETTLED': 'Settled',
|
||||
...adminPagesEn,
|
||||
};
|
||||
|
||||
const ms: Record<string, string> = {
|
||||
'login.title': 'Konsol Admin',
|
||||
'login.username': 'Nama pengguna',
|
||||
'login.password': 'Kata laluan',
|
||||
'login.submit': 'Log masuk',
|
||||
'login.username_ph': 'Masukkan nama pengguna',
|
||||
'login.password_ph': 'Masukkan kata laluan',
|
||||
'login.err_captcha': 'Captcha salah, cuba lagi',
|
||||
'login.err_failed': 'Log masuk gagal. Semak nama pengguna dan kata laluan',
|
||||
'login.err_quick': 'Log masuk pantas gagal',
|
||||
'login.quick_label': 'Log masuk pantas (debug)',
|
||||
'login.quick_admin': 'Admin',
|
||||
'login.quick_agent': 'Ejen peringkat 1',
|
||||
'login.captcha_ph': 'Captcha',
|
||||
'login.captcha_refresh': 'Klik untuk muat semula',
|
||||
|
||||
'nav.dashboard': 'Papan pemuka',
|
||||
'nav.users': 'Pemain',
|
||||
'nav.agents': 'Ejen',
|
||||
'nav.matches': 'Perlawanan',
|
||||
'nav.bets': 'Pertaruhan',
|
||||
'nav.cashback': 'Rebat',
|
||||
'nav.audit': 'Log audit',
|
||||
'nav.players': 'Pemain saya',
|
||||
'nav.subAgents': 'Sub-ejen',
|
||||
'nav.myBets': 'Carian pertaruhan',
|
||||
'role.admin': 'Pentadbir',
|
||||
'role.agent': 'Ejen',
|
||||
'logout': 'Log keluar',
|
||||
'lang': 'Bahasa',
|
||||
'portal.admin': 'Admin Platform',
|
||||
'portal.agent': 'Portal Ejen',
|
||||
|
||||
'common.all': 'Semua',
|
||||
'common.search': 'Cari',
|
||||
'common.reset': 'Set semula',
|
||||
'common.edit': 'Edit',
|
||||
'common.delete': 'Padam',
|
||||
'common.cancel': 'Batal',
|
||||
'common.confirm': 'OK',
|
||||
'common.status': 'Status',
|
||||
'common.type': 'Jenis',
|
||||
'common.keyword': 'Kata kunci',
|
||||
'common.actions': 'Tindakan',
|
||||
'common.loading': 'Memuatkan…',
|
||||
'common.no_data': 'Tiada data',
|
||||
'common.yesterday': 'Semalam',
|
||||
'common.vs_yesterday': 'berbanding semalam',
|
||||
'common.people': 'pengguna',
|
||||
'common.bets_unit': 'pertaruhan',
|
||||
'common.matches_unit': 'perlawanan',
|
||||
'common.frozen': 'Dibekukan',
|
||||
'common.used': 'Digunakan',
|
||||
'common.platform_direct': 'Terus platform',
|
||||
'common.updated_at': 'Dikemas kini',
|
||||
|
||||
'dash.title': 'Papan pemuka',
|
||||
'dash.desc': 'Gambaran keseluruhan platform',
|
||||
'dash.board_title': 'Gambaran',
|
||||
'dash.board_hint': 'Trend dan taburan sepintas lalu',
|
||||
'dash.kpi_bet_count': 'Bilangan pertaruhan hari ini',
|
||||
'dash.kpi_stake': 'Stake hari ini',
|
||||
'dash.kpi_payout': 'Bayaran hari ini',
|
||||
'dash.kpi_ggr': 'GGR hari ini',
|
||||
'dash.kpi_users': 'Pemain / Ejen',
|
||||
'dash.kpi_new_players': '{n} baharu hari ini',
|
||||
'dash.kpi_pending': 'Menunggu penyelesaian',
|
||||
'dash.kpi_pending_sub': '{bets} pertaruhan · {matches} perlawanan',
|
||||
'dash.kpi_wallet': 'Baki pemain',
|
||||
'dash.kpi_credit': 'Kredit ejen',
|
||||
'dash.trend_caption': '7 hari lepas (garis jumlah + palang pertaruhan)',
|
||||
'dash.chart_stake': 'Stake',
|
||||
'dash.chart_payout': 'Bayaran',
|
||||
'dash.chart_ggr': 'GGR',
|
||||
'dash.chart_bet_count': 'Bilangan pertaruhan',
|
||||
'dash.axis_amount': 'Jumlah',
|
||||
'dash.axis_count': 'Kiraan',
|
||||
'dash.count_suffix': 'pertaruhan',
|
||||
'dash.chart_tooltip': '{b}: {c} ({d}%)',
|
||||
'dash.pie_empty': 'Tiada',
|
||||
'dash.pie_matches': 'Perlawanan',
|
||||
'dash.pie_bets': 'Pertaruhan hari ini',
|
||||
'dash.pie_users': 'Pengguna',
|
||||
'dash.match_draft': 'Draf',
|
||||
'dash.match_published': 'Diterbitkan',
|
||||
'dash.match_closed': 'Ditutup',
|
||||
'dash.match_pending_settle': 'Menunggu penyelesaian',
|
||||
'dash.match_settled': 'Diselesaikan',
|
||||
'dash.user_active': 'Pemain aktif',
|
||||
'dash.user_suspended': 'Digantung',
|
||||
'dash.user_direct': 'Terus',
|
||||
'dash.user_agents': 'Ejen',
|
||||
|
||||
'page.users.title': 'Pemain',
|
||||
'page.users.desc': 'Cipta pemain, baki, stake, tambah baki dan status',
|
||||
'page.agents.title': 'Ejen',
|
||||
'page.agents.desc': 'Ejen peringkat 1, had kredit, pemain dan penggunaan',
|
||||
'page.matches.title': 'Perlawanan',
|
||||
'page.matches.desc': 'Edit/padam draf; laraskan masa mula dan pilihan utama bila diterbitkan',
|
||||
'page.bets.title': 'Pertaruhan',
|
||||
'page.bets.desc': 'Tapis dan halaman semua pertaruhan dengan butiran pilihan',
|
||||
'page.cashback.title': 'Rebat',
|
||||
'page.cashback.desc': 'Jana dan keluarkan rebat mengikut tempoh',
|
||||
'page.audit.title': 'Log audit',
|
||||
'page.audit.desc': 'Sejarah tindakan pentadbir',
|
||||
'page.settlement.title': 'Penyelesaian',
|
||||
'page.agent_dash.title': 'Gambaran ejen',
|
||||
'page.agent_dash.desc': 'Ringkasan langsung',
|
||||
'page.agent_players.title': 'Pemain saya',
|
||||
'page.agent_players.desc': 'Pemain di bawah akaun anda',
|
||||
'page.agent_sub.title': 'Sub-ejen',
|
||||
'page.agent_sub.desc': 'Ejen peringkat 1 sahaja',
|
||||
'page.agent_bets.title': 'Carian pertaruhan',
|
||||
'page.agent_bets.desc': 'Semua pertaruhan pemain hiliran',
|
||||
|
||||
'agent.credit_limit': 'Had kredit',
|
||||
'agent.credit_total': 'Jumlah had',
|
||||
'agent.credit_used': 'Kredit digunakan',
|
||||
'agent.credit_occupied': 'Sedang digunakan',
|
||||
'agent.direct_players': 'Pemain terus',
|
||||
'agent.player_count': 'Pemain',
|
||||
'agent.today_stake': 'Stake hari ini',
|
||||
'agent.currency_cny': 'CNY',
|
||||
|
||||
'bet.status.PENDING': 'Menunggu',
|
||||
'bet.status.WON': 'Menang',
|
||||
'bet.status.LOST': 'Kalah',
|
||||
'bet.status.VOID': 'Batal',
|
||||
'bet.status.REFUNDED': 'Dibayar balik',
|
||||
'bet.type.SINGLE': 'Tunggal',
|
||||
'bet.type.PARLAY': 'Berganda',
|
||||
'bet.settlement.PENDING': 'Menunggu',
|
||||
'bet.settlement.SETTLED': 'Diselesaikan',
|
||||
'bet.settlement.VOID': 'Batal',
|
||||
'bet.result.WON': 'Menang',
|
||||
'bet.result.LOST': 'Kalah',
|
||||
'bet.result.VOID': 'Batal',
|
||||
'bet.result.PUSH': 'Seri',
|
||||
'bet.result.HALF_WON': 'Separuh menang',
|
||||
'bet.result.HALF_LOST': 'Separuh kalah',
|
||||
|
||||
'match.status.DRAFT': 'Draf',
|
||||
'match.status.PUBLISHED': 'Diterbitkan',
|
||||
'match.status.CLOSED': 'Ditutup',
|
||||
'match.status.SETTLED': 'Diselesaikan',
|
||||
...adminPagesMs,
|
||||
};
|
||||
|
||||
const messages: Record<AdminLocale, Record<string, string>> = {
|
||||
'zh-CN': zh,
|
||||
'en-US': en,
|
||||
'ms-MY': ms,
|
||||
};
|
||||
|
||||
export function adminT(locale: AdminLocale, key: string, params?: Record<string, string | number>): string {
|
||||
const chain = [locale, 'en-US', 'zh-CN'];
|
||||
const seen = new Set<string>();
|
||||
let raw = key;
|
||||
for (const loc of chain) {
|
||||
if (seen.has(loc)) continue;
|
||||
seen.add(loc);
|
||||
const v = messages[loc as AdminLocale]?.[key];
|
||||
if (v) {
|
||||
raw = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!params) return raw;
|
||||
return Object.entries(params).reduce(
|
||||
(s, [k, v]) => s.replace(new RegExp(`\\{${k}\\}`, 'g'), String(v)),
|
||||
raw,
|
||||
);
|
||||
}
|
||||
|
||||
export function adminLocaleTag(locale: AdminLocale): string {
|
||||
return locale;
|
||||
}
|
||||
239
apps/admin/src/i18n/admin-pages-ms.ts
Normal file
239
apps/admin/src/i18n/admin-pages-ms.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
/** 管理后台列表 / 弹窗 — Bahasa Melayu */
|
||||
export const adminPagesMs: Record<string, string> = {
|
||||
'common.detail': 'Butiran',
|
||||
'common.create': 'Cipta',
|
||||
'common.save': 'Simpan',
|
||||
'common.close': 'Tutup',
|
||||
'common.import': 'Import',
|
||||
'common.publish': 'Terbitkan',
|
||||
'common.topup': 'Tambah baki',
|
||||
'common.adjust_credit': 'Laraskan kredit',
|
||||
'common.freeze': 'Bekukan',
|
||||
'common.unfreeze': 'Nyahbeku',
|
||||
'common.settle': 'Selesaikan',
|
||||
'common.close_betting': 'Tutup pertaruhan',
|
||||
'common.never_login': 'Belum pernah log masuk',
|
||||
'common.optional': 'Pilihan',
|
||||
'common.to': 'Hingga',
|
||||
'common.module': 'Modul',
|
||||
'common.col_id': 'ID',
|
||||
'common.times': 'kali',
|
||||
'common.bets_count_unit': 'pertaruhan',
|
||||
|
||||
'user.create_btn': '+ Pemain baharu',
|
||||
'user.filter.username_ph': 'Nama pengguna',
|
||||
'user.filter.agent': 'Ejen',
|
||||
'user.filter.agent_ph': 'Semua',
|
||||
'user.col.username': 'Nama pengguna',
|
||||
'user.col.agent': 'Ejen',
|
||||
'user.col.balance': 'Tersedia / Dibekukan',
|
||||
'user.col.bets': 'Pertaruhan',
|
||||
'user.col.stake_payout': 'Stake / Bayaran',
|
||||
'user.col.last_login': 'Log masuk terakhir',
|
||||
'user.col.created': 'Daftar',
|
||||
'user.status.ACTIVE': 'Aktif',
|
||||
'user.status.SUSPENDED': 'Digantung',
|
||||
'user.dialog.create': 'Pemain baharu',
|
||||
'user.dialog.edit': 'Edit pemain',
|
||||
'user.dialog.deposit': 'Tambah baki pemain',
|
||||
'user.dialog.detail': 'Butiran pemain',
|
||||
'user.field.password': 'Kata laluan',
|
||||
'user.field.confirm_password': 'Sahkan kata laluan',
|
||||
'user.field.initial_balance': 'Baki permulaan',
|
||||
'user.field.deposit_remark': 'Nota tambah baki',
|
||||
'user.field.amount': 'Jumlah',
|
||||
'user.field.remark': 'Nota',
|
||||
'user.field.account_status': 'Status akaun',
|
||||
'user.field.available': 'Baki tersedia',
|
||||
'user.field.frozen_balance': 'Baki dibekukan',
|
||||
'user.field.bets_summary': 'Pertaruhan / stake',
|
||||
'user.field.total_payout': 'Jumlah bayaran',
|
||||
'user.field.login_fail': 'Log masuk gagal',
|
||||
'user.field.phone': 'Telefon',
|
||||
'user.field.email': 'E-mel',
|
||||
'user.ph.username_unique': 'Nama log masuk unik',
|
||||
'user.ph.no_agent': 'Tiada (terus platform)',
|
||||
'user.hint.no_agent': 'Biarkan kosong untuk pemain diurus platform',
|
||||
'user.hint.initial_balance': 'Auto tambah baki semasa cipta; 0 = tiada bonus',
|
||||
'user.hint.deposit_remark': 'Ditulis ke lejar jika baki permulaan > 0',
|
||||
'user.hint.freeze_in_list': 'Beku/nyahbeku dari lajur tindakan senarai',
|
||||
'user.hint.agent_change': 'Kosong = terus platform; perubahan dikira semula kredit ejen',
|
||||
'user.btn.create': 'Cipta',
|
||||
'user.btn.save_profile': 'Simpan',
|
||||
'user.btn.confirm_deposit': 'Sahkan tambah baki',
|
||||
'user.deposit_remark_default': 'Tambah baki admin',
|
||||
|
||||
'agent.create_btn': '+ Ejen peringkat 1 baharu',
|
||||
'agent.filter.username_ph': 'Nama pengguna',
|
||||
'agent.col.level': 'Peringkat',
|
||||
'agent.col.credit': 'Had / Digunakan / Tersedia',
|
||||
'agent.col.direct_players': 'Pemain terus',
|
||||
'agent.col.cashback': 'Kadar rebat',
|
||||
'agent.col.phone': 'Telefon',
|
||||
'agent.col.created': 'Dicipta',
|
||||
'agent.dialog.create': 'Ejen peringkat 1 baharu',
|
||||
'agent.dialog.edit': 'Edit ejen',
|
||||
'agent.dialog.credit': 'Laraskan had kredit',
|
||||
'agent.field.agent_id': 'ID ejen',
|
||||
'agent.dialog.detail': 'Butiran ejen',
|
||||
'agent.field.credit_limit': 'Had kredit',
|
||||
'agent.field.cashback_rate': 'Kadar rebat',
|
||||
'agent.field.adjust_amount': 'Pelarasan',
|
||||
'agent.field.used_credit': 'Kredit digunakan',
|
||||
'agent.field.available_credit': 'Kredit tersedia',
|
||||
'agent.field.player_liability': 'Liabiliti pemain',
|
||||
'agent.field.sub_agent_exposure': 'Pendedahan sub-ejen',
|
||||
'agent.hint.credit_limit': 'Had maksimum tambah baki untuk pemain terus',
|
||||
'agent.hint.cashback_example': 'cth. 0.01 = 1%',
|
||||
'agent.hint.credit_adjust': 'Positif menambah, negatif mengurangkan',
|
||||
'agent.hint.credit_remark': 'Pilihan, ditulis ke lejar kredit',
|
||||
'agent.section.credit_log': 'Perubahan kredit terkini',
|
||||
'agent.credit.increase': 'Tambah',
|
||||
'agent.credit.decrease': 'Kurang',
|
||||
'agent.col.credit_type': 'Jenis',
|
||||
'agent.col.credit_change': 'Perubahan',
|
||||
'agent.col.credit_after': 'Selepas',
|
||||
'agent.col.no_records': 'Tiada rekod',
|
||||
'agent.btn.confirm_adjust': 'Sahkan',
|
||||
|
||||
'match.create_btn': '+ Perlawanan baharu',
|
||||
'match.filter.keyword_ph': 'Nama perlawanan / kod pasukan',
|
||||
'match.col.matchup': 'Perlawanan',
|
||||
'match.col.kickoff': 'Masa mula',
|
||||
'match.dialog.create': 'Perlawanan baharu',
|
||||
'match.dialog.edit': 'Edit perlawanan',
|
||||
'match.dialog.import': 'Import perlawanan',
|
||||
'match.field.league_en': 'Liga (EN)',
|
||||
'match.field.league_zh': 'Liga (ZH)',
|
||||
'match.field.kickoff': 'Masa mula',
|
||||
'match.field.home_en': 'Tuan rumah (EN)',
|
||||
'match.field.home_zh': 'Tuan rumah (ZH)',
|
||||
'match.field.away_en': 'Pelawat (EN)',
|
||||
'match.field.away_zh': 'Pelawat (ZH)',
|
||||
'match.field.featured': 'Pilihan utama',
|
||||
'match.hint.create_draft': 'Disimpan sebagai draf; klik Terbitkan dalam senarai untuk buka pasaran.',
|
||||
'match.hint.edit_published': 'Diterbitkan: edit masa mula, pilihan utama, nama paparan; tertutup/selesai dikunci.',
|
||||
|
||||
'bet.filter.keyword_ph': 'No. pertaruhan / nama pengguna',
|
||||
'bet.filter.date_from': 'Tarikh pertaruhan dari',
|
||||
'bet.filter.date_start_ph': 'Mula',
|
||||
'bet.filter.date_end_ph': 'Tamat',
|
||||
'bet.col.serial': 'No.',
|
||||
'bet.col.bet_no': 'No. pertaruhan',
|
||||
'bet.col.player': 'Pemain',
|
||||
'bet.col.agent': 'Ejen',
|
||||
'bet.col.selection': 'Pilihan',
|
||||
'bet.col.stake': 'Stake',
|
||||
'bet.col.odds': 'Odds',
|
||||
'bet.col.payout': 'Bayaran',
|
||||
'bet.col.placed_at': 'Masa pertaruhan',
|
||||
'bet.dialog.detail': 'Butiran pertaruhan',
|
||||
'bet.field.total_odds': 'Jumlah odds',
|
||||
'bet.field.currency': 'Mata wang',
|
||||
'bet.field.potential_win': 'Menang berpotensi',
|
||||
'bet.field.actual_payout': 'Bayaran sebenar',
|
||||
'bet.field.bet_status': 'Status pertaruhan',
|
||||
'bet.field.settlement_status': 'Penyelesaian',
|
||||
'bet.field.settled_at': 'Diselesaikan pada',
|
||||
'bet.field.request_id': 'ID permintaan',
|
||||
'bet.selections_title': 'Pilihan ({n})',
|
||||
'bet.col.market': 'Pasaran',
|
||||
'bet.col.period': 'Tempoh',
|
||||
'bet.col.line': 'Garisan',
|
||||
'bet.col.result': 'Keputusan',
|
||||
|
||||
'audit.module_ph': 'cth. USERS, AGENTS',
|
||||
'audit.col.action': 'Tindakan',
|
||||
'audit.col.module': 'Modul',
|
||||
'audit.col.target_id': 'ID sasaran',
|
||||
'audit.col.time': 'Masa',
|
||||
|
||||
'cashback.start_date': 'Tarikh mula',
|
||||
'cashback.end_date': 'Tarikh tamat',
|
||||
'cashback.preview_btn': 'Pratonton',
|
||||
'cashback.preview_title': 'Pratonton rebat',
|
||||
'cashback.stat.players': 'Pemain',
|
||||
'cashback.stat.total': 'Jumlah rebat',
|
||||
'cashback.confirm_issue': 'Sahkan bayaran',
|
||||
|
||||
'user.field.player_id': 'ID pemain',
|
||||
'user.field.bet_count': 'Bilangan pertaruhan',
|
||||
'user.field.total_stake': 'Jumlah stake',
|
||||
'user.field.registered_at': 'Daftar',
|
||||
'user.ph.remark_initial': 'Nota lejar apabila baki permulaan > 0',
|
||||
'user.bets_edit_value': '{n} pertaruhan / {stake}',
|
||||
'user.login_fail_value': '{n} kali',
|
||||
|
||||
'match.import_hint': 'Tampal JSON dengan array matches. Import sebagai draf; terbitkan dari senarai.',
|
||||
'match.import_start': 'Import',
|
||||
'match.import_json_ph': '{"matches":[...]}',
|
||||
'match.delete_confirm_title': 'Padam perlawanan',
|
||||
'match.delete_confirm_body': 'Padam "{title}"? Hanya draf tanpa pertaruhan.',
|
||||
'match.ph.league_en': 'FIFA World Cup 2026',
|
||||
'match.ph.league_zh': 'Piala Dunia 2026',
|
||||
'match.ph.kickoff': '2026-06-11T19:00:00Z',
|
||||
'match.ph.home_en': 'Mexico',
|
||||
'match.ph.home_zh': 'Mexico',
|
||||
'match.ph.away_en': 'South Africa',
|
||||
'match.ph.away_zh': 'Afrika Selatan',
|
||||
|
||||
'err.username_required': 'Sila isi nama pengguna',
|
||||
'err.password_min': 'Kata laluan sekurang-kurangnya 8 aksara',
|
||||
'err.password_mismatch': 'Kata laluan tidak sepadan',
|
||||
'err.credit_negative': 'Had kredit tidak boleh negatif',
|
||||
'err.kickoff_required': 'Sila isi masa mula',
|
||||
'err.teams_required': 'Isi nama pasukan tuan rumah dan pelawat (ZH atau EN)',
|
||||
'err.league_required': 'Sila isi nama liga',
|
||||
|
||||
'settlement.ht_score': 'Skor separuh masa',
|
||||
'settlement.ft_score': 'Skor penuh masa',
|
||||
'settlement.record_score': 'Simpan skor',
|
||||
'settlement.preview_btn': 'Pratonton penyelesaian',
|
||||
'settlement.preview_title': 'Pratonton penyelesaian',
|
||||
'settlement.single_count': 'Pertaruhan tunggal',
|
||||
'settlement.est_payout': 'Anggaran bayaran',
|
||||
'settlement.refund_amount': 'Jumlah bayaran balik',
|
||||
'settlement.confirm_btn': 'Sahkan penyelesaian',
|
||||
'msg.score_recorded': 'Skor disimpan',
|
||||
'msg.settlement_confirmed': 'Penyelesaian disahkan',
|
||||
|
||||
'agent_portal.create_player_section': 'Cipta pemain',
|
||||
'agent_portal.deposit_section': 'Tambah baki',
|
||||
'agent_portal.create_player_btn': '+ Pemain baharu',
|
||||
'agent_portal.create_tier2_btn': '+ Sub-ejen baharu',
|
||||
'agent_portal.username_ph': 'Masukkan nama pengguna',
|
||||
'agent_portal.agent_username_ph': 'Nama pengguna ejen',
|
||||
'agent_portal.player_id_ph': 'ID pemain',
|
||||
'agent_portal.withdraw_btn': 'Keluarkan {amount}',
|
||||
'msg.agent_sub_created': 'Sub-ejen dicipta',
|
||||
'msg.withdraw_ok': 'Pengeluaran berjaya',
|
||||
|
||||
'msg.form_invalid': 'Sila semak borang',
|
||||
'msg.player_created': 'Pemain dicipta',
|
||||
'msg.agent_created': 'Ejen dicipta',
|
||||
'msg.create_failed': 'Gagal mencipta',
|
||||
'msg.saved': 'Disimpan',
|
||||
'msg.save_failed': 'Gagal menyimpan',
|
||||
'msg.deleted': 'Dipadam',
|
||||
'msg.delete_failed': 'Gagal memadam',
|
||||
'msg.match_created_draft': 'Perlawanan dicipta (draf)',
|
||||
'msg.published': 'Diterbitkan dengan pasaran',
|
||||
'msg.closed': 'Pertaruhan ditutup',
|
||||
'msg.invalid_json': 'JSON tidak sah',
|
||||
'msg.import_failed': 'Import gagal',
|
||||
'msg.import_done': 'Import: {imported} ok, {skipped} dilangkau, {failed} gagal / {total} jumlah',
|
||||
'msg.topup_ok': 'Tambah baki berjaya',
|
||||
'msg.topup_failed': 'Tambah baki gagal',
|
||||
'msg.amount_gt_zero': 'Jumlah mesti lebih daripada 0',
|
||||
'msg.credit_zero': 'Pelarasan tidak boleh 0',
|
||||
'msg.credit_adjusted': 'Kredit dikemas kini',
|
||||
'msg.credit_adjust_failed': 'Pelarasan gagal',
|
||||
'msg.outright_no_edit': 'Outright tidak boleh diedit di sini',
|
||||
'msg.load_matches_failed': 'Gagal memuatkan perlawanan',
|
||||
'msg.cashback_issued': 'Rebat telah dikeluarkan',
|
||||
'msg.freeze_confirm_title': '{action} akaun',
|
||||
'msg.freeze_confirm_body': '{action} pemain "{name}"?{extra}',
|
||||
'msg.freeze_extra': ' Mereka tidak akan dapat log masuk.',
|
||||
'msg.freeze_done': '{action} selesai',
|
||||
'msg.freeze_failed': '{action} gagal',
|
||||
};
|
||||
478
apps/admin/src/i18n/admin-pages.ts
Normal file
478
apps/admin/src/i18n/admin-pages.ts
Normal file
@@ -0,0 +1,478 @@
|
||||
/** 列表页 / 弹窗文案(并入 admin-messages) */
|
||||
export const adminPagesZh: Record<string, string> = {
|
||||
'common.detail': '详情',
|
||||
'common.create': '创建',
|
||||
'common.save': '保存',
|
||||
'common.close': '关闭',
|
||||
'common.import': '导入',
|
||||
'common.publish': '发布',
|
||||
'common.topup': '上分',
|
||||
'common.adjust_credit': '调额',
|
||||
'common.freeze': '冻结',
|
||||
'common.unfreeze': '解冻',
|
||||
'common.settle': '结算',
|
||||
'common.close_betting': '封盘',
|
||||
'common.never_login': '从未登录',
|
||||
'common.optional': '选填',
|
||||
'common.to': '止',
|
||||
'common.module': '模块',
|
||||
'common.col_id': 'ID',
|
||||
'common.times': '次',
|
||||
'common.bets_count_unit': '笔',
|
||||
|
||||
'user.create_btn': '+ 新建玩家',
|
||||
'user.filter.username_ph': '用户名',
|
||||
'user.filter.agent': '所属代理',
|
||||
'user.filter.agent_ph': '全部',
|
||||
'user.col.username': '用户名',
|
||||
'user.col.agent': '所属代理',
|
||||
'user.col.balance': '可用 / 冻结',
|
||||
'user.col.bets': '注单',
|
||||
'user.col.stake_payout': '投注 / 派彩',
|
||||
'user.col.last_login': '最后登录',
|
||||
'user.col.created': '注册时间',
|
||||
'user.status.ACTIVE': '正常',
|
||||
'user.status.SUSPENDED': '停用',
|
||||
'user.dialog.create': '新建玩家',
|
||||
'user.dialog.edit': '编辑玩家',
|
||||
'user.dialog.deposit': '玩家上分',
|
||||
'user.dialog.detail': '玩家详情',
|
||||
'user.field.password': '登录密码',
|
||||
'user.field.confirm_password': '确认密码',
|
||||
'user.field.initial_balance': '初始余额',
|
||||
'user.field.deposit_remark': '上分备注',
|
||||
'user.field.amount': '金额',
|
||||
'user.field.remark': '备注',
|
||||
'user.field.account_status': '账号状态',
|
||||
'user.field.available': '可用余额',
|
||||
'user.field.frozen_balance': '冻结余额',
|
||||
'user.field.bets_summary': '注单 / 投注',
|
||||
'user.field.total_payout': '累计派彩',
|
||||
'user.field.login_fail': '登录失败',
|
||||
'user.field.phone': '手机',
|
||||
'user.field.email': '邮箱',
|
||||
'user.ph.username_unique': '登录用户名,唯一',
|
||||
'user.ph.no_agent': '不设置(平台直属玩家)',
|
||||
'user.hint.no_agent': '留空表示不挂靠代理,由平台直接管理',
|
||||
'user.hint.initial_balance': '创建后自动上分,0 表示不开户赠金',
|
||||
'user.hint.deposit_remark': '有初始余额时写入流水备注',
|
||||
'user.hint.freeze_in_list': '冻结/解冻请在列表操作列进行',
|
||||
'user.hint.agent_change': '留空表示平台直属;变更后会重算相关代理已用授信',
|
||||
'user.btn.create': '创建',
|
||||
'user.btn.save_profile': '保存资料',
|
||||
'user.btn.confirm_deposit': '确认上分',
|
||||
'user.deposit_remark_default': '管理员上分',
|
||||
|
||||
'agent.create_btn': '+ 新建一级代理',
|
||||
'agent.filter.username_ph': '用户名',
|
||||
'agent.col.level': '层级',
|
||||
'agent.col.credit': '授信 / 已用 / 可用',
|
||||
'agent.col.direct_players': '直属玩家',
|
||||
'agent.col.cashback': '返水率',
|
||||
'agent.col.phone': '手机',
|
||||
'agent.col.created': '创建时间',
|
||||
'agent.dialog.create': '新建一级代理',
|
||||
'agent.dialog.edit': '编辑代理',
|
||||
'agent.dialog.credit': '调整授信额度',
|
||||
'agent.field.agent_id': '代理 ID',
|
||||
'agent.dialog.detail': '代理详情',
|
||||
'agent.field.credit_limit': '授信额度',
|
||||
'agent.field.cashback_rate': '返水比例',
|
||||
'agent.field.adjust_amount': '调整金额',
|
||||
'agent.field.used_credit': '已用额度',
|
||||
'agent.field.available_credit': '可用授信',
|
||||
'agent.field.player_liability': '玩家负债',
|
||||
'agent.field.sub_agent_exposure': '下级代理敞口',
|
||||
'agent.hint.credit_limit': '代理可向直属玩家上分的总额度上限',
|
||||
'agent.hint.cashback_example': '例如 0.01 表示 1%',
|
||||
'agent.hint.credit_adjust': '正数为增加授信,负数为减少',
|
||||
'agent.hint.credit_remark': '选填,写入额度流水',
|
||||
'agent.section.credit_log': '最近额度变动',
|
||||
'agent.credit.increase': '增加',
|
||||
'agent.credit.decrease': '减少',
|
||||
'agent.col.credit_type': '类型',
|
||||
'agent.col.credit_change': '变动',
|
||||
'agent.col.credit_after': '变动后',
|
||||
'agent.col.no_records': '暂无记录',
|
||||
'agent.btn.confirm_adjust': '确认调整',
|
||||
|
||||
'match.create_btn': '+ 新增赛事',
|
||||
'match.filter.keyword_ph': '赛事名 / 球队代码',
|
||||
'match.col.matchup': '对阵',
|
||||
'match.col.kickoff': '开赛时间',
|
||||
'match.dialog.create': '新增赛事',
|
||||
'match.dialog.edit': '编辑赛事',
|
||||
'match.dialog.import': '导入赛事',
|
||||
'match.field.league_en': '联赛(英)',
|
||||
'match.field.league_zh': '联赛(中)',
|
||||
'match.field.kickoff': '开赛时间',
|
||||
'match.field.home_en': '主队(英)',
|
||||
'match.field.home_zh': '主队(中)',
|
||||
'match.field.away_en': '客队(英)',
|
||||
'match.field.away_zh': '客队(中)',
|
||||
'match.field.featured': '热门',
|
||||
'match.hint.create_draft': '创建后为草稿,请在列表点击「发布」并生成盘口。',
|
||||
'match.hint.edit_published': '已发布:可修改开赛时间、热门及显示名称;封盘/已结算后不可编辑。',
|
||||
|
||||
'bet.filter.keyword_ph': '流水编号 / 玩家用户名',
|
||||
'bet.filter.date_from': '投注日起',
|
||||
'bet.filter.date_start_ph': '开始',
|
||||
'bet.filter.date_end_ph': '结束',
|
||||
'bet.col.serial': '单号',
|
||||
'bet.col.bet_no': '流水编号',
|
||||
'bet.col.player': '玩家',
|
||||
'bet.col.agent': '所属代理',
|
||||
'bet.col.selection': '选项',
|
||||
'bet.col.stake': '投注额',
|
||||
'bet.col.odds': '赔率',
|
||||
'bet.col.payout': '派彩',
|
||||
'bet.col.placed_at': '投注时间',
|
||||
'bet.dialog.detail': '注单详情',
|
||||
'bet.field.total_odds': '总赔率',
|
||||
'bet.field.currency': '币种',
|
||||
'bet.field.potential_win': '可赢额',
|
||||
'bet.field.actual_payout': '实际派彩',
|
||||
'bet.field.bet_status': '注单状态',
|
||||
'bet.field.settlement_status': '结算状态',
|
||||
'bet.field.settled_at': '结算时间',
|
||||
'bet.field.request_id': '请求 ID',
|
||||
'bet.selections_title': '投注项({n})',
|
||||
'bet.col.market': '玩法',
|
||||
'bet.col.period': '时段',
|
||||
'bet.col.line': '盘口',
|
||||
'bet.col.result': '赛果',
|
||||
|
||||
'audit.module_ph': '如 USERS、AGENTS',
|
||||
'audit.col.action': '操作',
|
||||
'audit.col.module': '模块',
|
||||
'audit.col.target_id': '目标 ID',
|
||||
'audit.col.time': '时间',
|
||||
|
||||
'cashback.start_date': '开始日期',
|
||||
'cashback.end_date': '结束日期',
|
||||
'cashback.preview_btn': '生成预览',
|
||||
'cashback.preview_title': '返水预览',
|
||||
'cashback.stat.players': '涉及玩家数',
|
||||
'cashback.stat.total': '返水总金额',
|
||||
'cashback.confirm_issue': '确认发放',
|
||||
|
||||
'user.field.player_id': '玩家 ID',
|
||||
'user.field.bet_count': '注单数',
|
||||
'user.field.total_stake': '累计投注',
|
||||
'user.field.registered_at': '注册时间',
|
||||
'user.ph.remark_initial': '有初始余额时写入流水备注',
|
||||
'user.bets_edit_value': '{n} 笔 / {stake}',
|
||||
'user.login_fail_value': '{n} 次',
|
||||
|
||||
'match.import_hint': '粘贴含 matches 的 JSON,导入后为草稿,需在列表发布。',
|
||||
'match.import_start': '开始导入',
|
||||
'match.import_json_ph': '{"matches":[...]}',
|
||||
'match.delete_confirm_title': '删除确认',
|
||||
'match.delete_confirm_body': '确定删除赛事「{title}」?仅草稿且无注单时可删除。',
|
||||
'match.ph.league_en': 'FIFA World Cup 2026',
|
||||
'match.ph.league_zh': '2026 世界杯',
|
||||
'match.ph.kickoff': '2026-06-11T19:00:00Z',
|
||||
'match.ph.home_en': 'Mexico',
|
||||
'match.ph.home_zh': '墨西哥',
|
||||
'match.ph.away_en': 'South Africa',
|
||||
'match.ph.away_zh': '南非',
|
||||
|
||||
'err.username_required': '请填写用户名',
|
||||
'err.password_min': '密码至少 8 位',
|
||||
'err.password_mismatch': '两次密码不一致',
|
||||
'err.credit_negative': '授信额度不能为负',
|
||||
'err.kickoff_required': '请填写开赛时间',
|
||||
'err.teams_required': '请填写主客队名称(中文或英文至少一项)',
|
||||
'err.league_required': '请填写联赛名称',
|
||||
|
||||
'settlement.ht_score': '半场比分',
|
||||
'settlement.ft_score': '全场比分',
|
||||
'settlement.record_score': '录入比分',
|
||||
'settlement.preview_btn': '生成结算预览',
|
||||
'settlement.preview_title': '结算预览',
|
||||
'settlement.single_count': '单关注单数',
|
||||
'settlement.est_payout': '预计派彩',
|
||||
'settlement.refund_amount': '退款金额',
|
||||
'settlement.confirm_btn': '确认结算',
|
||||
'msg.score_recorded': '比分已录入',
|
||||
'msg.settlement_confirmed': '结算已确认',
|
||||
|
||||
'agent_portal.create_player_section': '创建玩家',
|
||||
'agent_portal.deposit_section': '上分操作',
|
||||
'agent_portal.create_player_btn': '+ 创建玩家',
|
||||
'agent_portal.create_tier2_btn': '+ 创建二级代理',
|
||||
'agent_portal.username_ph': '输入用户名',
|
||||
'agent_portal.agent_username_ph': '代理用户名',
|
||||
'agent_portal.player_id_ph': '玩家 ID',
|
||||
'agent_portal.withdraw_btn': '下分 {amount}',
|
||||
'msg.agent_sub_created': '下级代理已创建',
|
||||
'msg.withdraw_ok': '下分成功',
|
||||
|
||||
'msg.form_invalid': '请检查表单',
|
||||
'msg.player_created': '玩家已创建',
|
||||
'msg.agent_created': '一级代理已创建',
|
||||
'msg.create_failed': '创建失败',
|
||||
'msg.saved': '已保存',
|
||||
'msg.save_failed': '保存失败',
|
||||
'msg.deleted': '已删除',
|
||||
'msg.delete_failed': '删除失败',
|
||||
'msg.match_created_draft': '赛事已创建(草稿)',
|
||||
'msg.published': '已发布并生成盘口',
|
||||
'msg.closed': '已封盘',
|
||||
'msg.invalid_json': 'JSON 格式无效',
|
||||
'msg.import_failed': '导入失败',
|
||||
'msg.import_done': '导入完成:成功 {imported},跳过 {skipped},失败 {failed} / 共 {total}',
|
||||
'msg.topup_ok': '上分成功',
|
||||
'msg.topup_failed': '上分失败',
|
||||
'msg.amount_gt_zero': '金额须大于 0',
|
||||
'msg.credit_zero': '调整金额不能为 0',
|
||||
'msg.credit_adjusted': '授信已调整',
|
||||
'msg.credit_adjust_failed': '调整失败',
|
||||
'msg.outright_no_edit': '冠军盘不支持在此编辑',
|
||||
'msg.load_matches_failed': '加载赛事失败',
|
||||
'msg.cashback_issued': '返水已发放',
|
||||
'msg.freeze_confirm_title': '{action}账号',
|
||||
'msg.freeze_confirm_body': '确定要{action}玩家「{name}」吗?{extra}',
|
||||
'msg.freeze_extra': '冻结后该账号将无法登录。',
|
||||
'msg.freeze_done': '已{action}',
|
||||
'msg.freeze_failed': '{action}失败',
|
||||
};
|
||||
|
||||
export const adminPagesEn: Record<string, string> = {
|
||||
'common.detail': 'Details',
|
||||
'common.create': 'Create',
|
||||
'common.save': 'Save',
|
||||
'common.close': 'Close',
|
||||
'common.import': 'Import',
|
||||
'common.publish': 'Publish',
|
||||
'common.topup': 'Top up',
|
||||
'common.adjust_credit': 'Adjust credit',
|
||||
'common.freeze': 'Freeze',
|
||||
'common.unfreeze': 'Unfreeze',
|
||||
'common.settle': 'Settle',
|
||||
'common.close_betting': 'Close',
|
||||
'common.never_login': 'Never signed in',
|
||||
'common.optional': 'Optional',
|
||||
'common.to': 'To',
|
||||
'common.module': 'Module',
|
||||
'common.col_id': 'ID',
|
||||
'common.times': 'times',
|
||||
'common.bets_count_unit': 'bets',
|
||||
|
||||
'user.create_btn': '+ New player',
|
||||
'user.filter.username_ph': 'Username',
|
||||
'user.filter.agent': 'Agent',
|
||||
'user.filter.agent_ph': 'All',
|
||||
'user.col.username': 'Username',
|
||||
'user.col.agent': 'Agent',
|
||||
'user.col.balance': 'Available / Frozen',
|
||||
'user.col.bets': 'Bets',
|
||||
'user.col.stake_payout': 'Stake / Payout',
|
||||
'user.col.last_login': 'Last login',
|
||||
'user.col.created': 'Registered',
|
||||
'user.status.ACTIVE': 'Active',
|
||||
'user.status.SUSPENDED': 'Suspended',
|
||||
'user.dialog.create': 'New player',
|
||||
'user.dialog.edit': 'Edit player',
|
||||
'user.dialog.deposit': 'Top up player',
|
||||
'user.dialog.detail': 'Player details',
|
||||
'user.field.password': 'Password',
|
||||
'user.field.confirm_password': 'Confirm password',
|
||||
'user.field.initial_balance': 'Initial balance',
|
||||
'user.field.deposit_remark': 'Top-up note',
|
||||
'user.field.amount': 'Amount',
|
||||
'user.field.remark': 'Note',
|
||||
'user.field.account_status': 'Account status',
|
||||
'user.field.available': 'Available balance',
|
||||
'user.field.frozen_balance': 'Frozen balance',
|
||||
'user.field.bets_summary': 'Bets / stake',
|
||||
'user.field.total_payout': 'Total payout',
|
||||
'user.field.login_fail': 'Failed logins',
|
||||
'user.field.phone': 'Phone',
|
||||
'user.field.email': 'Email',
|
||||
'user.ph.username_unique': 'Unique login username',
|
||||
'user.ph.no_agent': 'None (platform direct)',
|
||||
'user.hint.no_agent': 'Leave empty for platform-managed player',
|
||||
'user.hint.initial_balance': 'Auto top-up on create; 0 = no bonus',
|
||||
'user.hint.deposit_remark': 'Written to ledger when initial balance > 0',
|
||||
'user.hint.freeze_in_list': 'Freeze/unfreeze from the list actions',
|
||||
'user.hint.agent_change': 'Empty = platform direct; changes recalc agent credit',
|
||||
'user.btn.create': 'Create',
|
||||
'user.btn.save_profile': 'Save',
|
||||
'user.btn.confirm_deposit': 'Confirm top-up',
|
||||
'user.deposit_remark_default': 'Admin top-up',
|
||||
|
||||
'agent.create_btn': '+ New tier-1 agent',
|
||||
'agent.filter.username_ph': 'Username',
|
||||
'agent.col.level': 'Level',
|
||||
'agent.col.credit': 'Limit / Used / Available',
|
||||
'agent.col.direct_players': 'Direct players',
|
||||
'agent.col.cashback': 'Cashback rate',
|
||||
'agent.col.phone': 'Phone',
|
||||
'agent.col.created': 'Created',
|
||||
'agent.dialog.create': 'New tier-1 agent',
|
||||
'agent.dialog.edit': 'Edit agent',
|
||||
'agent.dialog.credit': 'Adjust credit limit',
|
||||
'agent.field.agent_id': 'Agent ID',
|
||||
'agent.dialog.detail': 'Agent details',
|
||||
'agent.field.credit_limit': 'Credit limit',
|
||||
'agent.field.cashback_rate': 'Cashback rate',
|
||||
'agent.field.adjust_amount': 'Adjustment',
|
||||
'agent.field.used_credit': 'Used credit',
|
||||
'agent.field.available_credit': 'Available credit',
|
||||
'agent.field.player_liability': 'Player liability',
|
||||
'agent.field.sub_agent_exposure': 'Sub-agent exposure',
|
||||
'agent.hint.credit_limit': 'Max total top-up capacity for direct players',
|
||||
'agent.hint.cashback_example': 'e.g. 0.01 = 1%',
|
||||
'agent.hint.credit_adjust': 'Positive increases, negative decreases',
|
||||
'agent.hint.credit_remark': 'Optional, written to credit ledger',
|
||||
'agent.section.credit_log': 'Recent credit changes',
|
||||
'agent.credit.increase': 'Increase',
|
||||
'agent.credit.decrease': 'Decrease',
|
||||
'agent.col.credit_type': 'Type',
|
||||
'agent.col.credit_change': 'Change',
|
||||
'agent.col.credit_after': 'After',
|
||||
'agent.col.no_records': 'No records',
|
||||
'agent.btn.confirm_adjust': 'Confirm',
|
||||
|
||||
'match.create_btn': '+ New match',
|
||||
'match.filter.keyword_ph': 'Match name / team code',
|
||||
'match.col.matchup': 'Matchup',
|
||||
'match.col.kickoff': 'Kickoff',
|
||||
'match.dialog.create': 'New match',
|
||||
'match.dialog.edit': 'Edit match',
|
||||
'match.dialog.import': 'Import matches',
|
||||
'match.field.league_en': 'League (EN)',
|
||||
'match.field.league_zh': 'League (ZH)',
|
||||
'match.field.kickoff': 'Kickoff time',
|
||||
'match.field.home_en': 'Home (EN)',
|
||||
'match.field.home_zh': 'Home (ZH)',
|
||||
'match.field.away_en': 'Away (EN)',
|
||||
'match.field.away_zh': 'Away (ZH)',
|
||||
'match.field.featured': 'Featured',
|
||||
'match.hint.create_draft': 'Saved as draft; click Publish in the list to open markets.',
|
||||
'match.hint.edit_published': 'Published: edit kickoff, featured, display names; closed/settled are locked.',
|
||||
|
||||
'bet.filter.keyword_ph': 'Bet no. / username',
|
||||
'bet.filter.date_from': 'Placed from',
|
||||
'bet.filter.date_start_ph': 'Start',
|
||||
'bet.filter.date_end_ph': 'End',
|
||||
'bet.col.serial': 'No.',
|
||||
'bet.col.bet_no': 'Bet no.',
|
||||
'bet.col.player': 'Player',
|
||||
'bet.col.agent': 'Agent',
|
||||
'bet.col.selection': 'Pick',
|
||||
'bet.col.stake': 'Stake',
|
||||
'bet.col.odds': 'Odds',
|
||||
'bet.col.payout': 'Payout',
|
||||
'bet.col.placed_at': 'Placed at',
|
||||
'bet.dialog.detail': 'Bet details',
|
||||
'bet.field.total_odds': 'Total odds',
|
||||
'bet.field.currency': 'Currency',
|
||||
'bet.field.potential_win': 'Potential win',
|
||||
'bet.field.actual_payout': 'Actual payout',
|
||||
'bet.field.bet_status': 'Bet status',
|
||||
'bet.field.settlement_status': 'Settlement',
|
||||
'bet.field.settled_at': 'Settled at',
|
||||
'bet.field.request_id': 'Request ID',
|
||||
'bet.selections_title': 'Selections ({n})',
|
||||
'bet.col.market': 'Market',
|
||||
'bet.col.period': 'Period',
|
||||
'bet.col.line': 'Line',
|
||||
'bet.col.result': 'Result',
|
||||
|
||||
'audit.module_ph': 'e.g. USERS, AGENTS',
|
||||
'audit.col.action': 'Action',
|
||||
'audit.col.module': 'Module',
|
||||
'audit.col.target_id': 'Target ID',
|
||||
'audit.col.time': 'Time',
|
||||
|
||||
'cashback.start_date': 'Start date',
|
||||
'cashback.end_date': 'End date',
|
||||
'cashback.preview_btn': 'Preview',
|
||||
'cashback.preview_title': 'Cashback preview',
|
||||
'cashback.stat.players': 'Players',
|
||||
'cashback.stat.total': 'Total cashback',
|
||||
'cashback.confirm_issue': 'Confirm payout',
|
||||
|
||||
'user.field.player_id': 'Player ID',
|
||||
'user.field.bet_count': 'Bet count',
|
||||
'user.field.total_stake': 'Total stake',
|
||||
'user.field.registered_at': 'Registered',
|
||||
'user.ph.remark_initial': 'Ledger note when initial balance > 0',
|
||||
'user.bets_edit_value': '{n} bets / {stake}',
|
||||
'user.login_fail_value': '{n} times',
|
||||
|
||||
'match.import_hint': 'Paste JSON with matches array. Imports as draft; publish from the list.',
|
||||
'match.import_start': 'Import',
|
||||
'match.import_json_ph': '{"matches":[...]}',
|
||||
'match.delete_confirm_title': 'Delete match',
|
||||
'match.delete_confirm_body': 'Delete "{title}"? Draft with no bets only.',
|
||||
'match.ph.league_en': 'FIFA World Cup 2026',
|
||||
'match.ph.league_zh': '2026 World Cup',
|
||||
'match.ph.kickoff': '2026-06-11T19:00:00Z',
|
||||
'match.ph.home_en': 'Mexico',
|
||||
'match.ph.home_zh': 'Mexico',
|
||||
'match.ph.away_en': 'South Africa',
|
||||
'match.ph.away_zh': 'South Africa',
|
||||
|
||||
'err.username_required': 'Username is required',
|
||||
'err.password_min': 'Password must be at least 8 characters',
|
||||
'err.password_mismatch': 'Passwords do not match',
|
||||
'err.credit_negative': 'Credit limit cannot be negative',
|
||||
'err.kickoff_required': 'Kickoff time is required',
|
||||
'err.teams_required': 'Enter home and away team names (ZH or EN)',
|
||||
'err.league_required': 'League name is required',
|
||||
|
||||
'settlement.ht_score': 'Half-time score',
|
||||
'settlement.ft_score': 'Full-time score',
|
||||
'settlement.record_score': 'Save score',
|
||||
'settlement.preview_btn': 'Preview settlement',
|
||||
'settlement.preview_title': 'Settlement preview',
|
||||
'settlement.single_count': 'Single bets',
|
||||
'settlement.est_payout': 'Est. payout',
|
||||
'settlement.refund_amount': 'Refund amount',
|
||||
'settlement.confirm_btn': 'Confirm settlement',
|
||||
'msg.score_recorded': 'Score saved',
|
||||
'msg.settlement_confirmed': 'Settlement confirmed',
|
||||
|
||||
'agent_portal.create_player_section': 'Create player',
|
||||
'agent_portal.deposit_section': 'Top up',
|
||||
'agent_portal.create_player_btn': '+ New player',
|
||||
'agent_portal.create_tier2_btn': '+ New tier-2 agent',
|
||||
'agent_portal.username_ph': 'Enter username',
|
||||
'agent_portal.agent_username_ph': 'Agent username',
|
||||
'agent_portal.player_id_ph': 'Player ID',
|
||||
'agent_portal.withdraw_btn': 'Withdraw {amount}',
|
||||
'msg.agent_sub_created': 'Sub-agent created',
|
||||
'msg.withdraw_ok': 'Withdrawal successful',
|
||||
|
||||
'msg.form_invalid': 'Please check the form',
|
||||
'msg.player_created': 'Player created',
|
||||
'msg.agent_created': 'Agent created',
|
||||
'msg.create_failed': 'Create failed',
|
||||
'msg.saved': 'Saved',
|
||||
'msg.save_failed': 'Save failed',
|
||||
'msg.deleted': 'Deleted',
|
||||
'msg.delete_failed': 'Delete failed',
|
||||
'msg.match_created_draft': 'Match created (draft)',
|
||||
'msg.published': 'Published with markets',
|
||||
'msg.closed': 'Betting closed',
|
||||
'msg.invalid_json': 'Invalid JSON',
|
||||
'msg.import_failed': 'Import failed',
|
||||
'msg.import_done': 'Import: {imported} ok, {skipped} skipped, {failed} failed / {total} total',
|
||||
'msg.topup_ok': 'Top-up successful',
|
||||
'msg.topup_failed': 'Top-up failed',
|
||||
'msg.amount_gt_zero': 'Amount must be greater than 0',
|
||||
'msg.credit_zero': 'Adjustment cannot be 0',
|
||||
'msg.credit_adjusted': 'Credit updated',
|
||||
'msg.credit_adjust_failed': 'Adjustment failed',
|
||||
'msg.outright_no_edit': 'Outright cannot be edited here',
|
||||
'msg.load_matches_failed': 'Failed to load matches',
|
||||
'msg.cashback_issued': 'Cashback issued',
|
||||
'msg.freeze_confirm_title': '{action} account',
|
||||
'msg.freeze_confirm_body': '{action} player "{name}"?{extra}',
|
||||
'msg.freeze_extra': ' They will not be able to sign in.',
|
||||
'msg.freeze_done': '{action} completed',
|
||||
'msg.freeze_failed': '{action} failed',
|
||||
};
|
||||
16
apps/admin/src/i18n/form-validation.ts
Normal file
16
apps/admin/src/i18n/form-validation.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/** 表单校验错误(key 对应 admin-messages / admin-pages) */
|
||||
export class FormValidationError extends Error {
|
||||
readonly key: string;
|
||||
|
||||
constructor(key: string) {
|
||||
super(key);
|
||||
this.name = 'FormValidationError';
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveFormError(e: unknown, t: (key: string) => string): string {
|
||||
if (e instanceof FormValidationError) return t(e.key);
|
||||
if (e instanceof Error && e.message.startsWith('err.')) return t(e.message);
|
||||
return t('msg.form_invalid');
|
||||
}
|
||||
Reference in New Issue
Block a user