feat(i18n): 管理端与玩家端三语支持(中/英/马来语)
- 管理后台 adminT 文案库、结算与代理端页面、表单校验 - 玩家端 vue-i18n 补全首页/公告/串关与 ms 文案 - Element Plus ms 语言包与共享 locale 工具
This commit is contained in:
64
packages/shared/src/betting-rules.ts
Normal file
64
packages/shared/src/betting-rules.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/** 第一版仅足球;字段预留其他 sportType */
|
||||
export const SPORT_TYPE_FOOTBALL = 'FOOTBALL';
|
||||
|
||||
/** 常规赛事发布时生成的赛前盘口(手动维护,不含冠军盘) */
|
||||
export const STANDARD_PREMATCH_MARKET_TYPES = [
|
||||
'FT_1X2',
|
||||
'FT_HANDICAP',
|
||||
'FT_OVER_UNDER',
|
||||
'FT_ODD_EVEN',
|
||||
'HT_1X2',
|
||||
'HT_HANDICAP',
|
||||
'HT_OVER_UNDER',
|
||||
'FT_CORRECT_SCORE',
|
||||
'HT_CORRECT_SCORE',
|
||||
'SH_CORRECT_SCORE',
|
||||
] as const;
|
||||
|
||||
export const HANDICAP_TOTAL_MARKET_TYPES = [
|
||||
'FT_HANDICAP',
|
||||
'HT_HANDICAP',
|
||||
'FT_OVER_UNDER',
|
||||
'HT_OVER_UNDER',
|
||||
] as const;
|
||||
|
||||
export type ParlayRejectReason = 'OUTRIGHT' | 'NOT_ALLOWED' | 'QUARTER_LINE';
|
||||
|
||||
export function isQuarterLine(line: number | null | undefined): boolean {
|
||||
if (line == null || Number.isNaN(line)) return false;
|
||||
const frac = Math.abs(line % 1);
|
||||
return Math.abs(frac - 0.25) < 0.001 || Math.abs(frac - 0.75) < 0.001;
|
||||
}
|
||||
|
||||
export function isQuarterHandicapOrTotal(line: number | null | undefined): boolean {
|
||||
return isQuarterLine(line);
|
||||
}
|
||||
|
||||
export function canSelectForParlay(params: {
|
||||
marketType: string;
|
||||
lineValue?: number | null;
|
||||
allowParlay?: boolean;
|
||||
isOutright?: boolean;
|
||||
}): { ok: true } | { ok: false; reason: ParlayRejectReason } {
|
||||
if (params.marketType === 'OUTRIGHT_WINNER' || params.isOutright) {
|
||||
return { ok: false, reason: 'OUTRIGHT' };
|
||||
}
|
||||
if (params.allowParlay === false) {
|
||||
return { ok: false, reason: 'NOT_ALLOWED' };
|
||||
}
|
||||
if (
|
||||
(HANDICAP_TOTAL_MARKET_TYPES as readonly string[]).includes(params.marketType) &&
|
||||
isQuarterHandicapOrTotal(params.lineValue ?? null)
|
||||
) {
|
||||
return { ok: false, reason: 'QUARTER_LINE' };
|
||||
}
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export function isPreMatchKickoff(startTime: Date | string): boolean {
|
||||
return new Date() < new Date(startTime);
|
||||
}
|
||||
|
||||
export function isSupportedSport(sportType: string | null | undefined): boolean {
|
||||
return (sportType ?? SPORT_TYPE_FOOTBALL) === SPORT_TYPE_FOOTBALL;
|
||||
}
|
||||
@@ -102,7 +102,7 @@ export enum WalletStatus {
|
||||
// Locale
|
||||
export const SUPPORTED_LOCALES = ['zh-CN', 'ms-MY', 'en-US'] as const;
|
||||
export type Locale = (typeof SUPPORTED_LOCALES)[number];
|
||||
export const DEFAULT_LOCALE: Locale = 'en-US';
|
||||
export const DEFAULT_LOCALE: Locale = 'zh-CN';
|
||||
|
||||
// Admin roles
|
||||
export enum AdminRole {
|
||||
@@ -115,6 +115,9 @@ export enum AdminRole {
|
||||
export const PARLAY_MIN_LEGS = 2;
|
||||
export const PARLAY_MAX_LEGS = 5;
|
||||
|
||||
export * from './betting-rules';
|
||||
export * from './locale';
|
||||
|
||||
export interface ApiResponse<T = unknown> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
|
||||
28
packages/shared/src/locale.ts
Normal file
28
packages/shared/src/locale.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/** 内容翻译 fallback:当前语言 → 英文 → 中文 */
|
||||
export function resolveTranslationFallback(
|
||||
map: Record<string, string | undefined | null>,
|
||||
locale: string,
|
||||
): string {
|
||||
const chain = [locale, 'en-US', 'zh-CN'];
|
||||
const seen = new Set<string>();
|
||||
for (const loc of chain) {
|
||||
if (seen.has(loc)) continue;
|
||||
seen.add(loc);
|
||||
const v = map[loc];
|
||||
if (v != null && String(v).trim() !== '') return String(v).trim();
|
||||
}
|
||||
for (const v of Object.values(map)) {
|
||||
if (v != null && String(v).trim() !== '') return String(v).trim();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/** 前台语言显示名(PRD 3.1) */
|
||||
export const LOCALE_UI_LABELS: Record<string, string> = {
|
||||
'zh-CN': '中文',
|
||||
'ms-MY': 'Bahasa Melayu',
|
||||
'en-US': 'English',
|
||||
};
|
||||
|
||||
/** vue-i18n 缺 key 时:en-US → zh-CN */
|
||||
export const VUE_I18N_FALLBACK_LOCALES = ['en-US', 'zh-CN'] as const;
|
||||
Reference in New Issue
Block a user