feat(i18n): 管理端与玩家端三语支持(中/英/马来语)

- 管理后台 adminT 文案库、结算与代理端页面、表单校验
- 玩家端 vue-i18n 补全首页/公告/串关与 ms 文案
- Element Plus ms 语言包与共享 locale 工具
This commit is contained in:
2026-06-03 15:05:36 +08:00
parent 80adc0e928
commit cbfa18d1d3
63 changed files with 3081 additions and 1038 deletions

View File

@@ -1,4 +1,5 @@
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { resolveTranslationFallback } from '@thebet365/shared';
import { Cron, CronExpression } from '@nestjs/schedule';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../../shared/prisma/prisma.service';
@@ -487,7 +488,7 @@ export class MatchesService {
const map = Object.fromEntries(
translations.filter((t) => t.fieldName === 'name').map((t) => [t.locale, t.value]),
);
return map[locale] || map['en-US'] || map['zh-CN'] || Object.values(map)[0] || '';
return resolveTranslationFallback(map, locale);
}
async enrichMatch(match: Record<string, unknown>, locale: string) {
@@ -518,10 +519,13 @@ export class MatchesService {
}
async listPublished(locale = 'en-US', leagueId?: bigint) {
const now = new Date();
const matches = await this.prisma.match.findMany({
where: {
status: 'PUBLISHED',
isOutright: false,
sportType: 'FOOTBALL',
startTime: { gt: now },
...(leagueId ? { leagueId } : {}),
},
include: {
@@ -553,12 +557,15 @@ export class MatchesService {
},
});
if (!match) throw new NotFoundException('Match not found');
if (match.sportType !== 'FOOTBALL') {
throw new NotFoundException('Match not found');
}
return this.enrichMatch(match, locale);
}
async listOutrights(locale = 'en-US') {
const matches = await this.prisma.match.findMany({
where: { status: 'PUBLISHED', isOutright: true },
where: { status: 'PUBLISHED', isOutright: true, sportType: 'FOOTBALL' },
include: {
markets: {
where: { marketType: 'OUTRIGHT_WINNER', status: 'OPEN' },