feat(player,shared): 内置球员头像名称支持三语显示
This commit is contained in:
@@ -118,6 +118,7 @@ export const PARLAY_MAX_LEGS = 5;
|
||||
export * from './betting-rules';
|
||||
export * from './locale';
|
||||
export * from './builtinPlayers';
|
||||
export * from './playerLocale';
|
||||
|
||||
export interface ApiResponse<T = unknown> {
|
||||
success: boolean;
|
||||
|
||||
350
packages/shared/src/playerLocale.ts
Normal file
350
packages/shared/src/playerLocale.ts
Normal file
@@ -0,0 +1,350 @@
|
||||
import { resolveTranslationFallback } from './locale';
|
||||
import type { BuiltinPlayer } from './builtinPlayers';
|
||||
|
||||
export type PlayerPositionCode = 'FW' | 'MF' | 'DF' | 'GK';
|
||||
|
||||
type LocaleText = Partial<Record<string, string>>;
|
||||
|
||||
export type PlayerLocaleMeta = {
|
||||
name: LocaleText;
|
||||
position: PlayerPositionCode;
|
||||
country: string;
|
||||
};
|
||||
|
||||
export const PLAYER_POSITION_LABELS: Record<PlayerPositionCode, LocaleText> = {
|
||||
FW: { 'zh-CN': '前锋', 'en-US': 'Forward', 'ms-MY': 'Penyerang' },
|
||||
MF: { 'zh-CN': '中场', 'en-US': 'Midfielder', 'ms-MY': 'Pemain Tengah' },
|
||||
DF: { 'zh-CN': '后卫', 'en-US': 'Defender', 'ms-MY': 'Pertahan' },
|
||||
GK: { 'zh-CN': '守门员', 'en-US': 'Goalkeeper', 'ms-MY': 'Penjaga Gol' },
|
||||
};
|
||||
|
||||
export const PLAYER_COUNTRY_LABELS: Record<string, LocaleText> = {
|
||||
ARG: { 'zh-CN': '阿根廷', 'en-US': 'Argentina', 'ms-MY': 'Argentina' },
|
||||
ESP: { 'zh-CN': '西班牙', 'en-US': 'Spain', 'ms-MY': 'Sepanyol' },
|
||||
CRO: { 'zh-CN': '克罗地亚', 'en-US': 'Croatia', 'ms-MY': 'Croatia' },
|
||||
URU: { 'zh-CN': '乌拉圭', 'en-US': 'Uruguay', 'ms-MY': 'Uruguay' },
|
||||
TUR: { 'zh-CN': '土耳其', 'en-US': 'Turkey', 'ms-MY': 'Turki' },
|
||||
USA: { 'zh-CN': '美国', 'en-US': 'USA', 'ms-MY': 'Amerika Syarikat' },
|
||||
BRA: { 'zh-CN': '巴西', 'en-US': 'Brazil', 'ms-MY': 'Brazil' },
|
||||
POR: { 'zh-CN': '葡萄牙', 'en-US': 'Portugal', 'ms-MY': 'Portugal' },
|
||||
ENG: { 'zh-CN': '英格兰', 'en-US': 'England', 'ms-MY': 'England' },
|
||||
FRA: { 'zh-CN': '法国', 'en-US': 'France', 'ms-MY': 'Perancis' },
|
||||
NED: { 'zh-CN': '荷兰', 'en-US': 'Netherlands', 'ms-MY': 'Belanda' },
|
||||
GER: { 'zh-CN': '德国', 'en-US': 'Germany', 'ms-MY': 'Jerman' },
|
||||
NOR: { 'zh-CN': '挪威', 'en-US': 'Norway', 'ms-MY': 'Norway' },
|
||||
MEX: { 'zh-CN': '墨西哥', 'en-US': 'Mexico', 'ms-MY': 'Mexico' },
|
||||
EGY: { 'zh-CN': '埃及', 'en-US': 'Egypt', 'ms-MY': 'Mesir' },
|
||||
SWE: { 'zh-CN': '瑞典', 'en-US': 'Sweden', 'ms-MY': 'Sweden' },
|
||||
COL: { 'zh-CN': '哥伦比亚', 'en-US': 'Colombia', 'ms-MY': 'Colombia' },
|
||||
MAR: { 'zh-CN': '摩洛哥', 'en-US': 'Morocco', 'ms-MY': 'Maghribi' },
|
||||
BEL: { 'zh-CN': '比利时', 'en-US': 'Belgium', 'ms-MY': 'Belgium' },
|
||||
};
|
||||
|
||||
/** key = BuiltinPlayer.id(文件名去掉 .jpg) */
|
||||
export const PLAYER_LOCALE_META: Record<string, PlayerLocaleMeta> = {
|
||||
'何塞·曼努埃尔·洛佩斯-前锋-阿根廷': {
|
||||
name: { 'zh-CN': '何塞·曼努埃尔·洛佩斯', 'en-US': 'José Manuel López', 'ms-MY': 'José Manuel López' },
|
||||
position: 'FW',
|
||||
country: 'ARG',
|
||||
},
|
||||
'佩德里-中场-西班牙': {
|
||||
name: { 'zh-CN': '佩德里', 'en-US': 'Pedri', 'ms-MY': 'Pedri' },
|
||||
position: 'MF',
|
||||
country: 'ESP',
|
||||
},
|
||||
'卢卡·莫德里奇-中场-克罗地亚': {
|
||||
name: { 'zh-CN': '卢卡·莫德里奇', 'en-US': 'Luka Modrić', 'ms-MY': 'Luka Modrić' },
|
||||
position: 'MF',
|
||||
country: 'CRO',
|
||||
},
|
||||
'华金·皮克雷斯-中场-乌拉圭': {
|
||||
name: { 'zh-CN': '华金·皮克雷斯', 'en-US': 'Joaquín Piquerez', 'ms-MY': 'Joaquín Piquerez' },
|
||||
position: 'MF',
|
||||
country: 'URU',
|
||||
},
|
||||
'乔治亚·德·阿拉斯凯塔-中场-乌拉圭': {
|
||||
name: { 'zh-CN': '乔治亚·德·阿拉斯凯塔', 'en-US': 'Giorgian de Arrascaeta', 'ms-MY': 'Giorgian de Arrascaeta' },
|
||||
position: 'MF',
|
||||
country: 'URU',
|
||||
},
|
||||
'乌古尔坎·卡基尔-守門員-土耳其': {
|
||||
name: { 'zh-CN': '乌古尔坎·卡基尔', 'en-US': 'Uğurcan Çakır', 'ms-MY': 'Uğurcan Çakır' },
|
||||
position: 'GK',
|
||||
country: 'TUR',
|
||||
},
|
||||
'亚历杭德罗·曾德哈斯-前锋-美国': {
|
||||
name: { 'zh-CN': '亚历杭德罗·曾德哈斯', 'en-US': 'Alejandro Zendejas', 'ms-MY': 'Alejandro Zendejas' },
|
||||
position: 'FW',
|
||||
country: 'USA',
|
||||
},
|
||||
'恩德里克-前锋-巴西': {
|
||||
name: { 'zh-CN': '恩德里克', 'en-US': 'Endrick', 'ms-MY': 'Endrick' },
|
||||
position: 'FW',
|
||||
country: 'BRA',
|
||||
},
|
||||
'克里斯蒂亚诺·罗纳尔多-前锋-葡萄牙': {
|
||||
name: { 'zh-CN': '克里斯蒂亚诺·罗纳尔多', 'en-US': 'Cristiano Ronaldo', 'ms-MY': 'Cristiano Ronaldo' },
|
||||
position: 'FW',
|
||||
country: 'POR',
|
||||
},
|
||||
'克里斯蒂安·罗梅罗-后卫-阿根廷': {
|
||||
name: { 'zh-CN': '克里斯蒂安·罗梅罗', 'en-US': 'Cristian Romero', 'ms-MY': 'Cristian Romero' },
|
||||
position: 'DF',
|
||||
country: 'ARG',
|
||||
},
|
||||
'内马尔-前锋-巴西': {
|
||||
name: { 'zh-CN': '内马尔', 'en-US': 'Neymar', 'ms-MY': 'Neymar' },
|
||||
position: 'FW',
|
||||
country: 'BRA',
|
||||
},
|
||||
'凯南·耶尔德兹-前锋-土耳其': {
|
||||
name: { 'zh-CN': '凯南·耶尔德兹', 'en-US': 'Kenan Yıldız', 'ms-MY': 'Kenan Yıldız' },
|
||||
position: 'FW',
|
||||
country: 'TUR',
|
||||
},
|
||||
'卡塞米罗-中场-巴西': {
|
||||
name: { 'zh-CN': '卡塞米罗', 'en-US': 'Casemiro', 'ms-MY': 'Casemiro' },
|
||||
position: 'MF',
|
||||
country: 'BRA',
|
||||
},
|
||||
'卢卡斯·帕奎塔-中场-巴西': {
|
||||
name: { 'zh-CN': '卢卡斯·帕奎塔', 'en-US': 'Lucas Paquetá', 'ms-MY': 'Lucas Paquetá' },
|
||||
position: 'MF',
|
||||
country: 'BRA',
|
||||
},
|
||||
'基利安·姆巴佩-前锋-法国': {
|
||||
name: { 'zh-CN': '基利安·姆巴佩', 'en-US': 'Kylian Mbappé', 'ms-MY': 'Kylian Mbappé' },
|
||||
position: 'FW',
|
||||
country: 'FRA',
|
||||
},
|
||||
'孟菲斯·德派-前锋-荷兰': {
|
||||
name: { 'zh-CN': '孟菲斯·德派', 'en-US': 'Memphis Depay', 'ms-MY': 'Memphis Depay' },
|
||||
position: 'FW',
|
||||
country: 'NED',
|
||||
},
|
||||
'奥斯曼·登贝莱-前锋-法国': {
|
||||
name: { 'zh-CN': '奥斯曼·登贝莱', 'en-US': 'Ousmane Dembélé', 'ms-MY': 'Ousmane Dembélé' },
|
||||
position: 'FW',
|
||||
country: 'FRA',
|
||||
},
|
||||
'布鲁诺·吉马良斯-中场-巴西': {
|
||||
name: { 'zh-CN': '布鲁诺·吉马良斯', 'en-US': 'Bruno Guimarães', 'ms-MY': 'Bruno Guimarães' },
|
||||
position: 'MF',
|
||||
country: 'BRA',
|
||||
},
|
||||
'布鲁诺·费尔南德斯-中场-葡萄牙': {
|
||||
name: { 'zh-CN': '布鲁诺·费尔南德斯', 'en-US': 'Bruno Fernandes', 'ms-MY': 'Bruno Fernandes' },
|
||||
position: 'MF',
|
||||
country: 'POR',
|
||||
},
|
||||
'布卡约·萨卡-前锋-英格兰': {
|
||||
name: { 'zh-CN': '布卡约·萨卡', 'en-US': 'Bukayo Saka', 'ms-MY': 'Bukayo Saka' },
|
||||
position: 'FW',
|
||||
country: 'ENG',
|
||||
},
|
||||
'德尼兹·居尔-前锋-土耳其': {
|
||||
name: { 'zh-CN': '德尼兹·居尔', 'en-US': 'Deniz Gül', 'ms-MY': 'Deniz Gül' },
|
||||
position: 'FW',
|
||||
country: 'TUR',
|
||||
},
|
||||
'德尼兹·温达夫-前锋-德国': {
|
||||
name: { 'zh-CN': '德尼兹·温达夫', 'en-US': 'Deniz Undav', 'ms-MY': 'Deniz Undav' },
|
||||
position: 'FW',
|
||||
country: 'GER',
|
||||
},
|
||||
'拉斐尔·迪亚斯·贝洛利-前锋-巴西': {
|
||||
name: { 'zh-CN': '拉斐尔·迪亚斯·贝洛利', 'en-US': 'Raphinha', 'ms-MY': 'Raphinha' },
|
||||
position: 'FW',
|
||||
country: 'BRA',
|
||||
},
|
||||
'拉明·亚马尔-前锋-西班牙': {
|
||||
name: { 'zh-CN': '拉明·亚马尔', 'en-US': 'Lamine Yamal', 'ms-MY': 'Lamine Yamal' },
|
||||
position: 'FW',
|
||||
country: 'ESP',
|
||||
},
|
||||
'朱利安·阿尔瓦雷斯-前锋-阿根廷': {
|
||||
name: { 'zh-CN': '朱利安·阿尔瓦雷斯', 'en-US': 'Julián Álvarez', 'ms-MY': 'Julián Álvarez' },
|
||||
position: 'FW',
|
||||
country: 'ARG',
|
||||
},
|
||||
'梅西-前锋-阿根廷': {
|
||||
name: { 'zh-CN': '梅西', 'en-US': 'Lionel Messi', 'ms-MY': 'Lionel Messi' },
|
||||
position: 'FW',
|
||||
country: 'ARG',
|
||||
},
|
||||
'迈克尔·奥利塞-前锋-法国': {
|
||||
name: { 'zh-CN': '迈克尔·奥利塞', 'en-US': 'Michael Olise', 'ms-MY': 'Michael Olise' },
|
||||
position: 'FW',
|
||||
country: 'FRA',
|
||||
},
|
||||
'穆罕默德·萨拉赫-前锋-埃及': {
|
||||
name: { 'zh-CN': '穆罕默德·萨拉赫', 'en-US': 'Mohamed Salah', 'ms-MY': 'Mohamed Salah' },
|
||||
position: 'FW',
|
||||
country: 'EGY',
|
||||
},
|
||||
'维尼修斯·儒尼奥尔-前锋-巴西': {
|
||||
name: { 'zh-CN': '维尼修斯·儒尼奥尔', 'en-US': 'Vinícius Júnior', 'ms-MY': 'Vinícius Júnior' },
|
||||
position: 'FW',
|
||||
country: 'BRA',
|
||||
},
|
||||
'维克托·哲凯赖什-前锋-瑞典': {
|
||||
name: { 'zh-CN': '维克托·哲凯赖什', 'en-US': 'Viktor Gyökeres', 'ms-MY': 'Viktor Gyökeres' },
|
||||
position: 'FW',
|
||||
country: 'SWE',
|
||||
},
|
||||
'圣地亚哥·吉梅内斯-前锋-墨西哥': {
|
||||
name: { 'zh-CN': '圣地亚哥·吉梅内斯', 'en-US': 'Santiago Giménez', 'ms-MY': 'Santiago Giménez' },
|
||||
position: 'FW',
|
||||
country: 'MEX',
|
||||
},
|
||||
'埃德森·阿尔瓦雷斯-后卫-墨西哥': {
|
||||
name: { 'zh-CN': '埃德森·阿尔瓦雷斯', 'en-US': 'Edson Álvarez', 'ms-MY': 'Edson Álvarez' },
|
||||
position: 'DF',
|
||||
country: 'MEX',
|
||||
},
|
||||
'埃贝雷奇·埃泽-中场-英格兰': {
|
||||
name: { 'zh-CN': '埃贝雷奇·埃泽', 'en-US': 'Eberechi Eze', 'ms-MY': 'Eberechi Eze' },
|
||||
position: 'MF',
|
||||
country: 'ENG',
|
||||
},
|
||||
'埃尔林·哈兰德-前锋-挪威': {
|
||||
name: { 'zh-CN': '埃尔林·哈兰德', 'en-US': 'Erling Haaland', 'ms-MY': 'Erling Haaland' },
|
||||
position: 'FW',
|
||||
country: 'NOR',
|
||||
},
|
||||
'蒂博·库尔图瓦-守門員-比利时': {
|
||||
name: { 'zh-CN': '蒂博·库尔图瓦', 'en-US': 'Thibaut Courtois', 'ms-MY': 'Thibaut Courtois' },
|
||||
position: 'GK',
|
||||
country: 'BEL',
|
||||
},
|
||||
'曼努埃尔·诺伊尔-守門員-德国': {
|
||||
name: { 'zh-CN': '曼努埃尔·诺伊尔', 'en-US': 'Manuel Neuer', 'ms-MY': 'Manuel Neuer' },
|
||||
position: 'GK',
|
||||
country: 'GER',
|
||||
},
|
||||
'祖德·贝林厄姆-中场-英格兰': {
|
||||
name: { 'zh-CN': '祖德·贝林厄姆', 'en-US': 'Jude Bellingham', 'ms-MY': 'Jude Bellingham' },
|
||||
position: 'MF',
|
||||
country: 'ENG',
|
||||
},
|
||||
'伦纳特·卡尔-中场-德国': {
|
||||
name: { 'zh-CN': '伦纳特·卡尔', 'en-US': 'Lennart Karl', 'ms-MY': 'Lennart Karl' },
|
||||
position: 'MF',
|
||||
country: 'GER',
|
||||
},
|
||||
'费德里科·巴尔韦德-中场-乌拉圭': {
|
||||
name: { 'zh-CN': '费德里科·巴尔韦德', 'en-US': 'Federico Valverde', 'ms-MY': 'Federico Valverde' },
|
||||
position: 'MF',
|
||||
country: 'URU',
|
||||
},
|
||||
'贾马尔·慕斯拉-中场-德国': {
|
||||
name: { 'zh-CN': '贾马尔·慕斯拉', 'en-US': 'Jamal Musiala', 'ms-MY': 'Jamal Musiala' },
|
||||
position: 'MF',
|
||||
country: 'GER',
|
||||
},
|
||||
'路易斯·迪亚斯-前锋-哥伦比亚': {
|
||||
name: { 'zh-CN': '路易斯·迪亚斯', 'en-US': 'Luis Díaz', 'ms-MY': 'Luis Díaz' },
|
||||
position: 'FW',
|
||||
country: 'COL',
|
||||
},
|
||||
'阿什拉夫·哈基米-后卫-摩洛哥': {
|
||||
name: { 'zh-CN': '阿什拉夫·哈基米', 'en-US': 'Achraf Hakimi', 'ms-MY': 'Achraf Hakimi' },
|
||||
position: 'DF',
|
||||
country: 'MAR',
|
||||
},
|
||||
'阿利松·贝克尔-守門員-巴西': {
|
||||
name: { 'zh-CN': '阿利松·贝克尔', 'en-US': 'Alisson Becker', 'ms-MY': 'Alisson Becker' },
|
||||
position: 'GK',
|
||||
country: 'BRA',
|
||||
},
|
||||
'阿尔达·居莱尔-前锋-土耳其': {
|
||||
name: { 'zh-CN': '阿尔达·居莱尔', 'en-US': 'Arda Güler', 'ms-MY': 'Arda Güler' },
|
||||
position: 'FW',
|
||||
country: 'TUR',
|
||||
},
|
||||
'马西斯·拉扬·切尔基-中场-法国': {
|
||||
name: { 'zh-CN': '马西斯·拉扬·切尔基', 'en-US': 'Rayan Cherki', 'ms-MY': 'Rayan Cherki' },
|
||||
position: 'MF',
|
||||
country: 'FRA',
|
||||
},
|
||||
'马库斯·拉什福德-前锋-英格兰': {
|
||||
name: { 'zh-CN': '马库斯·拉什福德', 'en-US': 'Marcus Rashford', 'ms-MY': 'Marcus Rashford' },
|
||||
position: 'FW',
|
||||
country: 'ENG',
|
||||
},
|
||||
'哈里·凯恩-前锋-英格兰': {
|
||||
name: { 'zh-CN': '哈里·凯恩', 'en-US': 'Harry Kane', 'ms-MY': 'Harry Kane' },
|
||||
position: 'FW',
|
||||
country: 'ENG',
|
||||
},
|
||||
'尼科·威廉斯-前锋-西班牙': {
|
||||
name: { 'zh-CN': '尼科·威廉斯', 'en-US': 'Nico Williams', 'ms-MY': 'Nico Williams' },
|
||||
position: 'FW',
|
||||
country: 'ESP',
|
||||
},
|
||||
'巴勃罗·加维-中场-西班牙': {
|
||||
name: { 'zh-CN': '巴勃罗·加维', 'en-US': 'Pablo Gavi', 'ms-MY': 'Pablo Gavi' },
|
||||
position: 'MF',
|
||||
country: 'ESP',
|
||||
},
|
||||
'吉列尔莫·奥乔亚-守門員-墨西哥': {
|
||||
name: { 'zh-CN': '吉列尔莫·奥乔亚', 'en-US': 'Guillermo Ochoa', 'ms-MY': 'Guillermo Ochoa' },
|
||||
position: 'GK',
|
||||
country: 'MEX',
|
||||
},
|
||||
};
|
||||
|
||||
function mapPositionFromZh(position: string): PlayerPositionCode {
|
||||
if (position.includes('守') || position.includes('门') || position.includes('門')) return 'GK';
|
||||
if (position.includes('后') || position.includes('後')) return 'DF';
|
||||
if (position.includes('中')) return 'MF';
|
||||
return 'FW';
|
||||
}
|
||||
|
||||
export function getPlayerLocaleMeta(player: BuiltinPlayer): PlayerLocaleMeta {
|
||||
const meta = PLAYER_LOCALE_META[player.id];
|
||||
if (meta) return meta;
|
||||
return {
|
||||
name: { 'zh-CN': player.name },
|
||||
position: mapPositionFromZh(player.position),
|
||||
country: '',
|
||||
};
|
||||
}
|
||||
|
||||
export function getPlayerDisplayName(player: BuiltinPlayer, locale: string): string {
|
||||
const meta = getPlayerLocaleMeta(player);
|
||||
return resolveTranslationFallback(meta.name, locale) || player.name;
|
||||
}
|
||||
|
||||
export function getPlayerPositionLabel(player: BuiltinPlayer, locale: string): string {
|
||||
const meta = getPlayerLocaleMeta(player);
|
||||
const labels = PLAYER_POSITION_LABELS[meta.position];
|
||||
return resolveTranslationFallback(labels, locale) || player.position;
|
||||
}
|
||||
|
||||
export function getPlayerCountryLabel(player: BuiltinPlayer, locale: string): string {
|
||||
const meta = getPlayerLocaleMeta(player);
|
||||
if (!meta.country) return player.country;
|
||||
const labels = PLAYER_COUNTRY_LABELS[meta.country];
|
||||
return resolveTranslationFallback(labels ?? {}, locale) || player.country;
|
||||
}
|
||||
|
||||
export function getPlayerSearchTokens(player: BuiltinPlayer): string[] {
|
||||
const meta = getPlayerLocaleMeta(player);
|
||||
const tokens = new Set<string>();
|
||||
for (const value of Object.values(meta.name)) {
|
||||
if (value) tokens.add(value.toLowerCase());
|
||||
}
|
||||
for (const loc of ['zh-CN', 'en-US', 'ms-MY']) {
|
||||
tokens.add(getPlayerPositionLabel(player, loc).toLowerCase());
|
||||
tokens.add(getPlayerCountryLabel(player, loc).toLowerCase());
|
||||
}
|
||||
tokens.add(player.name.toLowerCase());
|
||||
tokens.add(player.position.toLowerCase());
|
||||
tokens.add(player.country.toLowerCase());
|
||||
return [...tokens];
|
||||
}
|
||||
|
||||
export function formatPlayerMeta(player: BuiltinPlayer, locale: string): string {
|
||||
return `${getPlayerPositionLabel(player, locale)} · ${getPlayerCountryLabel(player, locale)}`;
|
||||
}
|
||||
Reference in New Issue
Block a user