Merge branch 'main' of https://git.ambigrat.com/shenyan/physical-expo
This commit is contained in:
137
app/profile.tsx
137
app/profile.tsx
@@ -17,7 +17,7 @@ import { ThemedText } from "@/components/themed-text";
|
|||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
import { useAppState } from "@/context/AppStateContext";
|
import { useAppState } from "@/context/AppStateContext";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
import { changeLanguage } from "@/i18n";
|
import { changeLanguage, SUPPORTED_LANGUAGES } from "@/i18n";
|
||||||
import { appleSignIn, fetchUserProfile, logout } from "@/lib/api";
|
import { appleSignIn, fetchUserProfile, logout } from "@/lib/api";
|
||||||
import { storage } from "@/lib/storage";
|
import { storage } from "@/lib/storage";
|
||||||
import type { UserProfile } from "@/types/api";
|
import type { UserProfile } from "@/types/api";
|
||||||
@@ -50,6 +50,7 @@ export default function ProfileScreen() {
|
|||||||
const [user, setUser] = React.useState<UserProfile | null>(null);
|
const [user, setUser] = React.useState<UserProfile | null>(null);
|
||||||
const [loginModalVisible, setLoginModalVisible] = React.useState(false);
|
const [loginModalVisible, setLoginModalVisible] = React.useState(false);
|
||||||
const [oddsModalVisible, setOddsModalVisible] = React.useState(false);
|
const [oddsModalVisible, setOddsModalVisible] = React.useState(false);
|
||||||
|
const [languageModalVisible, setLanguageModalVisible] = React.useState(false);
|
||||||
|
|
||||||
const currentLanguage = i18n.language;
|
const currentLanguage = i18n.language;
|
||||||
|
|
||||||
@@ -87,9 +88,16 @@ export default function ProfileScreen() {
|
|||||||
selectedBookmakers: next,
|
selectedBookmakers: next,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const toggleLanguage = () => {
|
const selectLanguage = (langCode: string) => {
|
||||||
const nextLang = currentLanguage.startsWith("en") ? "zh" : "en";
|
changeLanguage(langCode);
|
||||||
changeLanguage(nextLang);
|
setLanguageModalVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurrentLanguageName = () => {
|
||||||
|
const lang = SUPPORTED_LANGUAGES.find((l) =>
|
||||||
|
currentLanguage.startsWith(l.code)
|
||||||
|
);
|
||||||
|
return lang?.name || "English";
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAppleSignIn = async () => {
|
const handleAppleSignIn = async () => {
|
||||||
@@ -120,13 +128,13 @@ export default function ProfileScreen() {
|
|||||||
},
|
},
|
||||||
user:
|
user:
|
||||||
credential.fullName &&
|
credential.fullName &&
|
||||||
(credential.fullName.givenName || credential.fullName.familyName)
|
(credential.fullName.givenName || credential.fullName.familyName)
|
||||||
? {
|
? {
|
||||||
name: {
|
name: {
|
||||||
firstName: credential.fullName.givenName || undefined,
|
firstName: credential.fullName.givenName || undefined,
|
||||||
lastName: credential.fullName.familyName || undefined,
|
lastName: credential.fullName.familyName || undefined,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -204,6 +212,7 @@ export default function ProfileScreen() {
|
|||||||
options={{
|
options={{
|
||||||
title: t("profile.title"),
|
title: t("profile.title"),
|
||||||
headerShown: true,
|
headerShown: true,
|
||||||
|
headerBackTitle: t("settings.back"),
|
||||||
// Ensure header matches theme to avoid white flash
|
// Ensure header matches theme to avoid white flash
|
||||||
headerStyle: {
|
headerStyle: {
|
||||||
backgroundColor: isDark ? "#000" : "#f2f2f7",
|
backgroundColor: isDark ? "#000" : "#f2f2f7",
|
||||||
@@ -243,9 +252,9 @@ export default function ProfileScreen() {
|
|||||||
<ThemedText type="title">
|
<ThemedText type="title">
|
||||||
{user?.nickname || t("profile.name")}
|
{user?.nickname || t("profile.name")}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
<ThemedText style={{ color: subTextColor }}>
|
{/* <ThemedText style={{ color: subTextColor }}>
|
||||||
{user?.appleId || ""}
|
{user?.appleId || ""}
|
||||||
</ThemedText>
|
</ThemedText> */}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@@ -320,24 +329,30 @@ export default function ProfileScreen() {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<View style={styles.settingLabel}>
|
<TouchableOpacity
|
||||||
<IconSymbol
|
style={styles.settingItemContent}
|
||||||
name="globe"
|
onPress={() => setLanguageModalVisible(true)}
|
||||||
size={20}
|
>
|
||||||
color={iconColor}
|
<View style={styles.settingLabel}>
|
||||||
style={{ marginRight: 10 }}
|
<IconSymbol
|
||||||
/>
|
name="globe"
|
||||||
<ThemedText>{t("settings.language")}</ThemedText>
|
size={20}
|
||||||
</View>
|
color={iconColor}
|
||||||
<View style={styles.settingControl}>
|
style={{ marginRight: 10 }}
|
||||||
<TouchableOpacity onPress={toggleLanguage} style={styles.button}>
|
/>
|
||||||
<ThemedText>
|
<ThemedText>{t("settings.language")}</ThemedText>
|
||||||
{currentLanguage.startsWith("zh")
|
</View>
|
||||||
? t("settings.chinese")
|
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||||
: t("settings.english")}
|
<ThemedText style={{ color: subTextColor, marginRight: 4 }}>
|
||||||
|
{getCurrentLanguageName()}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
</TouchableOpacity>
|
<IconSymbol
|
||||||
</View>
|
name="chevron-forward"
|
||||||
|
size={16}
|
||||||
|
color={subTextColor}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@@ -587,7 +602,7 @@ export default function ProfileScreen() {
|
|||||||
onPress={handleAppleSignIn}
|
onPress={handleAppleSignIn}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<TouchableOpacity style={styles.googleButton} onPress={() => {}}>
|
<TouchableOpacity style={styles.googleButton} onPress={() => { }}>
|
||||||
<ThemedText>{t("settings.google_login")}</ThemedText>
|
<ThemedText>{t("settings.google_login")}</ThemedText>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@@ -662,6 +677,68 @@ export default function ProfileScreen() {
|
|||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
visible={languageModalVisible}
|
||||||
|
transparent
|
||||||
|
animationType="slide"
|
||||||
|
onRequestClose={() => setLanguageModalVisible(false)}
|
||||||
|
>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.modalMask}
|
||||||
|
activeOpacity={1}
|
||||||
|
onPress={() => setLanguageModalVisible(false)}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.modalCard,
|
||||||
|
{
|
||||||
|
backgroundColor: isDark ? "#1c1c1e" : "#fff",
|
||||||
|
maxHeight: "70%",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<ThemedText style={styles.modalTitle}>
|
||||||
|
{t("settings.language")}
|
||||||
|
</ThemedText>
|
||||||
|
<ScrollView style={{ marginVertical: 10 }}>
|
||||||
|
{SUPPORTED_LANGUAGES.map((lang) => {
|
||||||
|
const isSelected = currentLanguage.startsWith(lang.code);
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={lang.code}
|
||||||
|
style={[
|
||||||
|
styles.bookmakerItem,
|
||||||
|
{ borderColor: isDark ? "#38383a" : "#eee" },
|
||||||
|
]}
|
||||||
|
onPress={() => selectLanguage(lang.code)}
|
||||||
|
>
|
||||||
|
<ThemedText
|
||||||
|
style={{
|
||||||
|
color: isSelected ? "#FF9500" : textColor,
|
||||||
|
fontWeight: isSelected ? "bold" : "normal",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{lang.name}
|
||||||
|
</ThemedText>
|
||||||
|
{isSelected && (
|
||||||
|
<IconSymbol name="checkmark" size={18} color="#FF9500" />
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ScrollView>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.modalCancel}
|
||||||
|
onPress={() => setLanguageModalVisible(false)}
|
||||||
|
>
|
||||||
|
<ThemedText type="defaultSemiBold">
|
||||||
|
{t("settings.cancel")}
|
||||||
|
</ThemedText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
1
assets/down-light.svg
Normal file
1
assets/down-light.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1768876941545" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4855" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M346.453333 396.373333L512 561.92l165.546667-165.546667a42.496 42.496 0 1 1 60.16 60.16l-195.84 195.84a42.496 42.496 0 0 1-60.16 0L285.866667 456.533333a42.496 42.496 0 0 1 0-60.16c16.64-16.213333 43.946667-16.64 60.586666 0z" fill="#515151" p-id="4856"></path></svg>
|
||||||
|
After Width: | Height: | Size: 600 B |
1
assets/down.svg
Normal file
1
assets/down.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1768876662032" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4702" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M346.453333 396.373333L512 561.92l165.546667-165.546667a42.496 42.496 0 1 1 60.16 60.16l-195.84 195.84a42.496 42.496 0 0 1-60.16 0L285.866667 456.533333a42.496 42.496 0 0 1 0-60.16c16.64-16.213333 43.946667-16.64 60.586666 0z" fill="#959BA7" p-id="4703"></path></svg>
|
||||||
|
After Width: | Height: | Size: 600 B |
@@ -1,5 +1,6 @@
|
|||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||||
|
import { Colors } from "@/constants/theme";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
import { addFavorite, removeFavorite } from "@/lib/api";
|
import { addFavorite, removeFavorite } from "@/lib/api";
|
||||||
import { Match } from "@/types/api";
|
import { Match } from "@/types/api";
|
||||||
@@ -13,6 +14,14 @@ interface MatchCardLeagueProps {
|
|||||||
onFavoriteToggle?: (matchId: string, isFav: boolean) => void;
|
onFavoriteToggle?: (matchId: string, isFav: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const WINNER_BORDER = "#D4A84B";
|
||||||
|
const WINNER_BG_LIGHT = "#FFF8E7";
|
||||||
|
const WINNER_BG_DARK = "#3D3422";
|
||||||
|
const NEUTRAL_BG_LIGHT = "#F5F5F5";
|
||||||
|
const NEUTRAL_BG_DARK = "#2C2C2E";
|
||||||
|
const RED_CARD_COLOR = "#C53030";
|
||||||
|
const YELLOW_CARD_COLOR = "#F59E0B";
|
||||||
|
|
||||||
export function MatchCardLeague({
|
export function MatchCardLeague({
|
||||||
match,
|
match,
|
||||||
onPress,
|
onPress,
|
||||||
@@ -28,12 +37,14 @@ export function MatchCardLeague({
|
|||||||
}, [match.fav]);
|
}, [match.fav]);
|
||||||
|
|
||||||
const isDark = theme === "dark";
|
const isDark = theme === "dark";
|
||||||
// 截图中的卡片背景通常非常深,接近纯黑
|
const textColor = isDark ? Colors.dark.text : Colors.light.text;
|
||||||
const cardBg = isDark ? "#1C1C1E" : "#FFFFFF";
|
const secondaryText = isDark ? "#8E8E93" : "#6B7280";
|
||||||
const textColor = isDark ? "#FFFFFF" : "#000000";
|
const scoreBorder = isDark ? "rgba(255,255,255,0.18)" : "rgba(0,0,0,0.12)";
|
||||||
// 赢家的高亮颜色 (截图中的橙黄色)
|
const scoreBg = isDark ? "rgba(255,255,255,0.04)" : "rgba(255,255,255,0.6)";
|
||||||
const winnerColor = "#FF9500";
|
|
||||||
const loserColor = isDark ? "#FFFFFF" : "#000000";
|
const isLive = React.useMemo(() => {
|
||||||
|
return !!match.isLive;
|
||||||
|
}, [match.isLive]);
|
||||||
|
|
||||||
const handlePress = () => {
|
const handlePress = () => {
|
||||||
if (onPress) {
|
if (onPress) {
|
||||||
@@ -72,116 +83,117 @@ export function MatchCardLeague({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 数据解析与样式逻辑 ---
|
const scoreParts = React.useMemo(() => {
|
||||||
|
const s = (match.scoreText || "").trim();
|
||||||
|
const m = s.match(/(\d+)\s*[-:]\s*(\d+)/);
|
||||||
|
if (m) return { home: m[1], away: m[2], hasScore: true };
|
||||||
|
if (s && s !== "-" && s !== "0 - 0")
|
||||||
|
return { home: s, away: "", hasScore: true };
|
||||||
|
if (s === "0 - 0") return { home: "0", away: "0", hasScore: true };
|
||||||
|
return { home: "", away: "", hasScore: false };
|
||||||
|
}, [match.scoreText]);
|
||||||
|
|
||||||
// 假设 match 对象中有 homeScore 和 awayScore (数字或字符串)
|
const { homeRedCards, awayRedCards, homeYellowCards, awayYellowCards } = React.useMemo(() => {
|
||||||
// 如果 API 只有 "1 - 0" 这种 scoreText,你需要在这里拆分
|
let homeRed = (match as any).homeRedCards || 0;
|
||||||
// 这里为了演示,假设 match 对象已经扩展了这些字段,或者我们从 scoreText 简单解析
|
let awayRed = (match as any).awayRedCards || 0;
|
||||||
let homeScore = 0;
|
let homeYellow = (match as any).homeYellowCards || 0;
|
||||||
let awayScore = 0;
|
let awayYellow = (match as any).awayYellowCards || 0;
|
||||||
|
|
||||||
// 简单的解析逻辑 demo (根据你的实际数据结构调整)
|
const matchStats = (match as any).stats;
|
||||||
if (match.scoreText && match.scoreText.includes("-")) {
|
if (matchStats) {
|
||||||
const parts = match.scoreText.split("-");
|
try {
|
||||||
homeScore = parseInt(parts[0].trim()) || 0;
|
const stats = typeof matchStats === 'string' ? JSON.parse(matchStats) : matchStats;
|
||||||
awayScore = parseInt(parts[1].trim()) || 0;
|
const redCardsStat = stats.find((s: any) => s.type === "Red Cards");
|
||||||
}
|
const yellowCardsStat = stats.find((s: any) => s.type === "Yellow Cards");
|
||||||
// 如果 match 对象里直接有 match.homeScore 最好:
|
|
||||||
// homeScore = match.homeScore;
|
|
||||||
// awayScore = match.awayScore;
|
|
||||||
|
|
||||||
// 判断文字颜色和背景样式
|
if (redCardsStat) {
|
||||||
let homeColor = loserColor;
|
homeRed = parseInt(redCardsStat.home) || 0;
|
||||||
let awayColor = loserColor;
|
awayRed = parseInt(redCardsStat.away) || 0;
|
||||||
let homeScoreBg = "#2C2C2E"; // 默认深灰背景
|
}
|
||||||
let awayScoreBg = "#2C2C2E";
|
if (yellowCardsStat) {
|
||||||
let homeBorderColor = "transparent";
|
homeYellow = parseInt(yellowCardsStat.home) || 0;
|
||||||
let awayBorderColor = "transparent";
|
awayYellow = parseInt(yellowCardsStat.away) || 0;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Parse stats error:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (homeScore > awayScore) {
|
return { homeRedCards: homeRed, awayRedCards: awayRed, homeYellowCards: homeYellow, awayYellowCards: awayYellow };
|
||||||
homeColor = "#000000"; // 赢家黑色文字
|
}, [(match as any).stats, (match as any).homeRedCards, (match as any).awayRedCards]);
|
||||||
homeScoreBg = winnerColor; // 金色背景
|
|
||||||
homeBorderColor = winnerColor;
|
const cardBg = isDark ? "#1C1C1E" : "#F5F5F5";
|
||||||
} else if (awayScore > homeScore) {
|
|
||||||
awayColor = "#000000"; // 赢家黑色文字
|
|
||||||
awayScoreBg = winnerColor; // 金色背景
|
|
||||||
awayBorderColor = winnerColor;
|
|
||||||
}
|
|
||||||
// 如果相等,保持默认深灰背景
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={handlePress}
|
onPress={handlePress}
|
||||||
style={({ pressed }) => [
|
style={({ pressed }) => [
|
||||||
styles.card,
|
styles.card,
|
||||||
{ backgroundColor: cardBg, opacity: pressed ? 0.8 : 1 },
|
{
|
||||||
|
backgroundColor: cardBg,
|
||||||
|
opacity: pressed ? 0.7 : 1
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{/* 1. 左侧:时间/状态 */}
|
|
||||||
<View style={styles.leftColumn}>
|
<View style={styles.leftColumn}>
|
||||||
{/* 如果有状态字段 match.meta (如 'FT', 'AET'), 优先显示,否则显示时间 */}
|
<ThemedText style={[styles.statusText, { color: secondaryText }]}>
|
||||||
<ThemedText style={styles.statusText}>
|
|
||||||
{(match.time || "").toUpperCase()}
|
{(match.time || "").toUpperCase()}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 2. 中间:球队信息 (上下排布) */}
|
|
||||||
<View style={styles.teamsColumn}>
|
<View style={styles.teamsColumn}>
|
||||||
{/* 主队行 */}
|
|
||||||
<View style={styles.teamRow}>
|
<View style={styles.teamRow}>
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: (match as any).homeLogo || match.homeTeamLogo || "https://placehold.co/24x24/png" }}
|
source={{ uri: (match as any).homeLogo || match.homeTeamLogo || "https://placehold.co/24x24/png" }}
|
||||||
style={styles.teamLogo}
|
style={styles.teamLogo}
|
||||||
/>
|
/>
|
||||||
<ThemedText style={[styles.teamName, { color: textColor }]} numberOfLines={1}>
|
<View style={styles.teamNameContainer}>
|
||||||
{match.home || match.homeTeamName}
|
<ThemedText style={[styles.teamName, { color: textColor }]} numberOfLines={1}>
|
||||||
</ThemedText>
|
{match.home || match.homeTeamName}
|
||||||
|
</ThemedText>
|
||||||
|
{homeYellowCards > 0 && <View style={[styles.cardBadge, styles.yellowCard]} />}
|
||||||
|
{homeRedCards > 0 && <View style={[styles.cardBadge, styles.redCard]} />}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 客队行 */}
|
<View style={[styles.teamRow, { marginTop: 10 }]}>
|
||||||
<View style={[styles.teamRow, { marginTop: 6 }]}>
|
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: (match as any).awayLogo || match.awayTeamLogo || "https://placehold.co/24x24/png" }}
|
source={{ uri: (match as any).awayLogo || match.awayTeamLogo || "https://placehold.co/24x24/png" }}
|
||||||
style={styles.teamLogo}
|
style={styles.teamLogo}
|
||||||
/>
|
/>
|
||||||
<ThemedText style={[styles.teamName, { color: textColor }]} numberOfLines={1}>
|
<View style={styles.teamNameContainer}>
|
||||||
{match.away || match.awayTeamName}
|
<ThemedText style={[styles.teamName, { color: textColor }]} numberOfLines={1}>
|
||||||
</ThemedText>
|
{match.away || match.awayTeamName}
|
||||||
|
</ThemedText>
|
||||||
|
{awayYellowCards > 0 && <View style={[styles.cardBadge, styles.yellowCard]} />}
|
||||||
|
{awayRedCards > 0 && <View style={[styles.cardBadge, styles.redCard]} />}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 3. 右侧:比分与铃铛 */}
|
|
||||||
<View style={styles.rightWrapper}>
|
<View style={styles.rightWrapper}>
|
||||||
{/* 比分列 (上下排布) */}
|
{scoreParts.hasScore ? (
|
||||||
<View style={styles.scoresColumn}>
|
<View
|
||||||
<View style={[
|
style={[
|
||||||
styles.scoreBox,
|
styles.scoreBox,
|
||||||
{
|
{
|
||||||
backgroundColor: homeScoreBg,
|
borderColor: isLive ? "#FF9500" : scoreBorder,
|
||||||
borderColor: homeBorderColor,
|
backgroundColor: scoreBg,
|
||||||
borderWidth: homeBorderColor !== "transparent" ? 1.5 : 0,
|
},
|
||||||
}
|
]}
|
||||||
]}>
|
>
|
||||||
<ThemedText style={[styles.scoreText, { color: homeColor }]}>
|
<ThemedText style={styles.scoreBoxText} numberOfLines={1}>
|
||||||
{homeScore}
|
{scoreParts.home}
|
||||||
|
</ThemedText>
|
||||||
|
<View style={[styles.scoreDivider, { backgroundColor: isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.06)" }]} />
|
||||||
|
<ThemedText style={styles.scoreBoxText} numberOfLines={1}>
|
||||||
|
{scoreParts.away}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
</View>
|
</View>
|
||||||
<View style={[
|
) : (
|
||||||
styles.scoreBox,
|
<View style={styles.scoreBoxPlaceholder} />
|
||||||
{
|
)}
|
||||||
backgroundColor: awayScoreBg,
|
|
||||||
borderColor: awayBorderColor,
|
|
||||||
borderWidth: awayBorderColor !== "transparent" ? 1.5 : 0,
|
|
||||||
marginTop: 6,
|
|
||||||
}
|
|
||||||
]}>
|
|
||||||
<ThemedText style={[styles.scoreText, { color: awayColor }]}>
|
|
||||||
{awayScore}
|
|
||||||
</ThemedText>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 收藏按钮 */}
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={(e) => {
|
onPress={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -194,7 +206,7 @@ export function MatchCardLeague({
|
|||||||
<IconSymbol
|
<IconSymbol
|
||||||
name={isFav ? "star" : "star-outline"}
|
name={isFav ? "star" : "star-outline"}
|
||||||
size={20}
|
size={20}
|
||||||
color={isFav ? "#FFD700" : "#545458"}
|
color={isFav ? "#FFD700" : (isDark ? "#545458" : "#9CA3AF")}
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
@@ -205,76 +217,87 @@ export function MatchCardLeague({
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
card: {
|
card: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
paddingVertical: 14,
|
paddingVertical: 16,
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
marginHorizontal: 0,
|
|
||||||
marginBottom: 0,
|
|
||||||
borderRadius: 12,
|
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
minHeight: 68,
|
borderRadius: 12,
|
||||||
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
// 左侧时间列
|
|
||||||
leftColumn: {
|
leftColumn: {
|
||||||
width: 52,
|
width: 48,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "flex-start",
|
alignItems: "center",
|
||||||
marginRight: 12,
|
marginRight: 8,
|
||||||
},
|
},
|
||||||
statusText: {
|
statusText: {
|
||||||
fontSize: 12,
|
fontSize: 14,
|
||||||
color: "#8E8E93", // 次要文本颜色 (Grey)
|
|
||||||
fontWeight: "600",
|
fontWeight: "600",
|
||||||
},
|
},
|
||||||
// 中间球队列
|
|
||||||
teamsColumn: {
|
teamsColumn: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
paddingRight: 8,
|
paddingRight: 12,
|
||||||
},
|
},
|
||||||
teamRow: {
|
teamRow: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
teamLogo: {
|
teamLogo: {
|
||||||
width: 22,
|
width: 24,
|
||||||
height: 22,
|
height: 24,
|
||||||
borderRadius: 11, // 圆形图标
|
borderRadius: 14,
|
||||||
marginRight: 10,
|
marginRight: 12,
|
||||||
backgroundColor: "#3A3A3C", // 图片加载占位
|
backgroundColor: "#E5E5E5",
|
||||||
},
|
},
|
||||||
teamName: {
|
teamNameContainer: {
|
||||||
fontSize: 16,
|
flexDirection: "row",
|
||||||
fontWeight: "500",
|
alignItems: "center",
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
// 右侧整体包装
|
teamName: {
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: "500",
|
||||||
|
},
|
||||||
|
cardBadge: {
|
||||||
|
width: 14,
|
||||||
|
height: 18,
|
||||||
|
borderRadius: 2,
|
||||||
|
marginLeft: 8,
|
||||||
|
},
|
||||||
|
redCard: {
|
||||||
|
backgroundColor: RED_CARD_COLOR,
|
||||||
|
},
|
||||||
|
yellowCard: {
|
||||||
|
backgroundColor: YELLOW_CARD_COLOR,
|
||||||
|
},
|
||||||
rightWrapper: {
|
rightWrapper: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
gap: 6,
|
||||||
// 比分列
|
|
||||||
scoresColumn: {
|
|
||||||
alignItems: "flex-end", // 数字右对齐
|
|
||||||
justifyContent: "center",
|
|
||||||
marginRight: 12,
|
|
||||||
},
|
},
|
||||||
scoreBox: {
|
scoreBox: {
|
||||||
minWidth: 32,
|
width: 36,
|
||||||
height: 28,
|
height: 54,
|
||||||
borderRadius: 6,
|
borderRadius: 8,
|
||||||
justifyContent: "center",
|
borderWidth: 1.5,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
paddingHorizontal: 8,
|
justifyContent: "center",
|
||||||
},
|
},
|
||||||
scoreText: {
|
scoreBoxText: {
|
||||||
fontSize: 16,
|
fontSize: 20,
|
||||||
fontWeight: "700",
|
fontWeight: "900",
|
||||||
lineHeight: 20,
|
},
|
||||||
|
scoreDivider: {
|
||||||
|
width: "60%",
|
||||||
|
height: 1,
|
||||||
|
marginVertical: 1,
|
||||||
|
},
|
||||||
|
scoreBoxPlaceholder: {
|
||||||
|
width: 36,
|
||||||
|
height: 54,
|
||||||
},
|
},
|
||||||
// 收藏按钮
|
|
||||||
favoriteButton: {
|
favoriteButton: {
|
||||||
paddingHorizontal: 4,
|
padding: 4,
|
||||||
paddingVertical: 4,
|
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { MatchCardLeague } from "@/components/match-card-league";
|
import { MatchCardLeague } from "@/components/match-card-league";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
|
import { Colors } from "@/constants/theme";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
import { fetchLeagues, fetchTodayMatches } from "@/lib/api";
|
import { fetchLeagues, fetchTodayMatches } from "@/lib/api";
|
||||||
import { League, Match } from "@/types/api";
|
import { League, Match } from "@/types/api";
|
||||||
|
import { Image } from "expo-image";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Image,
|
ActivityIndicator,
|
||||||
LayoutAnimation,
|
LayoutAnimation,
|
||||||
Platform,
|
Platform,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
@@ -14,6 +16,11 @@ import {
|
|||||||
UIManager,
|
UIManager,
|
||||||
View,
|
View,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
|
import Animated, {
|
||||||
|
useAnimatedStyle,
|
||||||
|
useSharedValue,
|
||||||
|
withTiming,
|
||||||
|
} from "react-native-reanimated";
|
||||||
|
|
||||||
// 开启 Android 上的 LayoutAnimation
|
// 开启 Android 上的 LayoutAnimation
|
||||||
if (
|
if (
|
||||||
@@ -41,6 +48,34 @@ export function MatchesByLeague({
|
|||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const isDark = theme === "dark";
|
const isDark = theme === "dark";
|
||||||
|
|
||||||
|
function ChevronIcon({ isCollapsed, isDark }: { isCollapsed: boolean; isDark: boolean }) {
|
||||||
|
const rotation = useSharedValue(isCollapsed ? 0 : 180);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
rotation.value = withTiming(isCollapsed ? 0 : 180, { duration: 200 });
|
||||||
|
}, [isCollapsed, rotation]);
|
||||||
|
|
||||||
|
const animatedStyle = useAnimatedStyle(() => ({
|
||||||
|
transform: [{ rotate: `${rotation.value}deg` }],
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.leagueHeaderRight}>
|
||||||
|
<Animated.View style={[styles.chevronContainer, animatedStyle]}>
|
||||||
|
<Image
|
||||||
|
source={
|
||||||
|
isDark
|
||||||
|
? require("@/assets/down.svg")
|
||||||
|
: require("@/assets/down-light.svg")
|
||||||
|
}
|
||||||
|
style={styles.chevronIcon}
|
||||||
|
contentFit="contain"
|
||||||
|
/>
|
||||||
|
</Animated.View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const [leagues, setLeagues] = useState<League[]>([]);
|
const [leagues, setLeagues] = useState<League[]>([]);
|
||||||
const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});
|
const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});
|
||||||
const [matchesByLeagueKey, setMatchesByLeagueKey] = useState<
|
const [matchesByLeagueKey, setMatchesByLeagueKey] = useState<
|
||||||
@@ -49,6 +84,7 @@ export function MatchesByLeague({
|
|||||||
const [loadingLeagueKey, setLoadingLeagueKey] = useState<
|
const [loadingLeagueKey, setLoadingLeagueKey] = useState<
|
||||||
Record<string, boolean>
|
Record<string, boolean>
|
||||||
>({});
|
>({});
|
||||||
|
const [loadingLeagues, setLoadingLeagues] = useState(true);
|
||||||
|
|
||||||
const dateStr = React.useMemo(() => {
|
const dateStr = React.useMemo(() => {
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
@@ -59,6 +95,7 @@ export function MatchesByLeague({
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
let mounted = true;
|
let mounted = true;
|
||||||
|
setLoadingLeagues(true);
|
||||||
fetchLeagues({
|
fetchLeagues({
|
||||||
sportId,
|
sportId,
|
||||||
date: dateStr,
|
date: dateStr,
|
||||||
@@ -79,8 +116,12 @@ export function MatchesByLeague({
|
|||||||
});
|
});
|
||||||
setMatchesByLeagueKey({});
|
setMatchesByLeagueKey({});
|
||||||
setLoadingLeagueKey({});
|
setLoadingLeagueKey({});
|
||||||
|
setLoadingLeagues(false);
|
||||||
})
|
})
|
||||||
.catch(() => { });
|
.catch(() => {
|
||||||
|
if (!mounted) return;
|
||||||
|
setLoadingLeagues(false);
|
||||||
|
});
|
||||||
return () => {
|
return () => {
|
||||||
mounted = false;
|
mounted = false;
|
||||||
};
|
};
|
||||||
@@ -105,12 +146,25 @@ export function MatchesByLeague({
|
|||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 50,
|
pageSize: 50,
|
||||||
});
|
});
|
||||||
|
console.log("choose", res);
|
||||||
setMatchesByLeagueKey((prev) => ({ ...prev, [leagueKey]: res.list }));
|
setMatchesByLeagueKey((prev) => ({ ...prev, [leagueKey]: res.list }));
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingLeagueKey((prev) => ({ ...prev, [leagueKey]: false }));
|
setLoadingLeagueKey((prev) => ({ ...prev, [leagueKey]: false }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (loadingLeagues) {
|
||||||
|
return (
|
||||||
|
<View style={styles.emptyContainer}>
|
||||||
|
<ActivityIndicator
|
||||||
|
size="large"
|
||||||
|
color={isDark ? Colors.dark.tint : Colors.light.tint}
|
||||||
|
/>
|
||||||
|
<ThemedText style={{ marginTop: 10 }}>加载中...</ThemedText>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (leagues.length === 0) {
|
if (leagues.length === 0) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.emptyContainer}>
|
<View style={styles.emptyContainer}>
|
||||||
@@ -119,13 +173,18 @@ export function MatchesByLeague({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cardBg = isDark ? "#1C1C1E" : "#FFFFFF";
|
||||||
|
const dividerColor = isDark ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.06)";
|
||||||
|
const skeletonBg = isDark ? "#2C2C2E" : "#E5E5E5";
|
||||||
|
const headerBg = isDark ? "#1C1C1E" : "#FBFBFB";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView
|
<ScrollView
|
||||||
style={[
|
style={[
|
||||||
styles.container,
|
styles.container,
|
||||||
{ backgroundColor: isDark ? "#000000" : "#F2F2F7" }
|
{ backgroundColor: isDark ? Colors.dark.background : "#FFFFFF" },
|
||||||
]}
|
]}
|
||||||
contentContainerStyle={{ paddingBottom: 40 }}
|
contentContainerStyle={{ paddingBottom: 40, paddingTop: 8 }}
|
||||||
>
|
>
|
||||||
{leagues.map((league) => {
|
{leagues.map((league) => {
|
||||||
const isCollapsed = collapsed[league.key] !== false;
|
const isCollapsed = collapsed[league.key] !== false;
|
||||||
@@ -141,103 +200,81 @@ export function MatchesByLeague({
|
|||||||
toggleCollapse(league.key);
|
toggleCollapse(league.key);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={styles.leagueHeaderWrapper}
|
style={[styles.leagueHeaderWrapper, { backgroundColor: headerBg }]}
|
||||||
>
|
>
|
||||||
<View style={styles.leagueHeaderLeft}>
|
<View style={styles.leagueHeaderLeft}>
|
||||||
<Image
|
<Image
|
||||||
source={{
|
source={{
|
||||||
uri: league.logo || "https://placehold.co/40x40/png",
|
uri: league.logo || "https://placehold.co/40x40/png",
|
||||||
}}
|
}}
|
||||||
style={styles.leagueLogo}
|
style={[styles.leagueLogo, { backgroundColor: isDark ? "#3A3A3C" : "#E5E5E5" }]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={styles.leagueInfoText}>
|
<View style={styles.leagueInfoText}>
|
||||||
<ThemedText style={styles.leagueTitle}>{league.name}</ThemedText>
|
<ThemedText
|
||||||
|
style={[
|
||||||
|
styles.leagueTitle,
|
||||||
|
{ color: isDark ? Colors.dark.text : Colors.light.text },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{league.name}
|
||||||
|
</ThemedText>
|
||||||
|
|
||||||
<View style={styles.countryRow}>
|
<View style={styles.countryRow}>
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: league.countryLogo || "https://placehold.co/20x20/png" }}
|
source={{ uri: league.countryLogo || "https://placehold.co/20x20/png" }}
|
||||||
style={styles.countryFlag}
|
style={styles.countryFlag}
|
||||||
/>
|
/>
|
||||||
<ThemedText style={styles.countryName}>
|
<ThemedText style={[styles.countryName, { color: isDark ? Colors.dark.text : Colors.light.text }]}>
|
||||||
{league.countryName || "International"}
|
{league.countryName || "International"}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.leagueHeaderRight}>
|
{enableCollapsible && league.matchCount > 0 && (
|
||||||
<ThemedText style={styles.matchCount}>
|
<ChevronIcon isCollapsed={isCollapsed} isDark={isDark} />
|
||||||
{league.matchCount}
|
)}
|
||||||
</ThemedText>
|
|
||||||
|
|
||||||
{enableCollapsible && league.matchCount > 0 && (
|
|
||||||
<ThemedText style={styles.chevron}>
|
|
||||||
{isCollapsed ? "⌄" : "⌃"}
|
|
||||||
</ThemedText>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
{!isCollapsed && (
|
{!isCollapsed && (
|
||||||
<View style={styles.matchListContainer}>
|
<View
|
||||||
|
style={[
|
||||||
|
styles.matchListContainer,
|
||||||
|
{ backgroundColor: cardBg, marginTop: 8 },
|
||||||
|
]}
|
||||||
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<View style={styles.matchCardWrapper}>
|
||||||
{[0, 1, 2].map((i) => (
|
<View style={styles.skeletonRow}>
|
||||||
<View
|
<View style={styles.leftColumn}>
|
||||||
key={i}
|
<View style={[styles.skeletonLine, { width: 30, backgroundColor: skeletonBg }]} />
|
||||||
style={[
|
</View>
|
||||||
styles.matchCardWrapper,
|
<View style={styles.teamsColumn}>
|
||||||
i < 2 && styles.matchCardDivider,
|
<View style={styles.skeletonTeamRow}>
|
||||||
]}
|
<View style={[styles.skeletonAvatar, { backgroundColor: skeletonBg }]} />
|
||||||
>
|
<View style={[styles.skeletonLine, { flex: 1, backgroundColor: skeletonBg }]} />
|
||||||
<View style={styles.skeletonRow}>
|
</View>
|
||||||
<View style={styles.leftColumn}>
|
<View style={[styles.skeletonTeamRow, { marginTop: 10 }]}>
|
||||||
<View
|
<View style={[styles.skeletonAvatar, { backgroundColor: skeletonBg }]} />
|
||||||
style={[styles.skeletonLine, { width: 30, marginBottom: 6 }]}
|
<View style={[styles.skeletonLine, { flex: 1, backgroundColor: skeletonBg }]} />
|
||||||
/>
|
|
||||||
<View
|
|
||||||
style={[styles.skeletonLine, { width: 24 }]}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.teamsColumn}>
|
|
||||||
<View style={styles.skeletonTeamRow}>
|
|
||||||
<View style={styles.skeletonAvatar} />
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
styles.skeletonLine,
|
|
||||||
{ flex: 1 },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={[styles.skeletonTeamRow, { marginTop: 8 }]}>
|
|
||||||
<View style={styles.skeletonAvatar} />
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
styles.skeletonLine,
|
|
||||||
{ flex: 1 },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View style={styles.rightWrapper}>
|
|
||||||
<View style={styles.skeletonScoreBox} />
|
|
||||||
<View style={styles.favoriteButton}>
|
|
||||||
<View style={styles.skeletonCircle} />
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
))}
|
<View style={styles.rightWrapper}>
|
||||||
</>
|
<View style={[styles.skeletonScoreBox, { backgroundColor: skeletonBg }]} />
|
||||||
|
<View style={styles.favoriteButton}>
|
||||||
|
<View style={[styles.skeletonCircle, { backgroundColor: skeletonBg }]} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
) : (
|
) : (
|
||||||
leagueMatches.map((match, index) => (
|
leagueMatches.map((match, index) => (
|
||||||
<View
|
<View
|
||||||
key={match.id}
|
key={match.id}
|
||||||
style={[
|
style={[
|
||||||
styles.matchCardWrapper,
|
styles.matchCardWrapper,
|
||||||
index < leagueMatches.length - 1 &&
|
index < leagueMatches.length - 1 && [styles.matchCardDivider, { borderBottomColor: dividerColor }],
|
||||||
styles.matchCardDivider,
|
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<MatchCardLeague
|
<MatchCardLeague
|
||||||
@@ -261,15 +298,16 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
leagueSection: {
|
leagueSection: {
|
||||||
marginBottom: 16,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
leagueHeaderWrapper: {
|
leagueHeaderWrapper: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
paddingVertical: 12,
|
paddingVertical: 14,
|
||||||
backgroundColor: "transparent",
|
borderRadius: 12,
|
||||||
|
marginHorizontal: 16,
|
||||||
},
|
},
|
||||||
leagueHeaderLeft: {
|
leagueHeaderLeft: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
@@ -277,81 +315,76 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
leagueLogo: {
|
leagueLogo: {
|
||||||
width: 36,
|
width: 24,
|
||||||
height: 36,
|
height: 24,
|
||||||
borderRadius: 6,
|
borderRadius: 8,
|
||||||
marginRight: 12,
|
marginRight: 12,
|
||||||
backgroundColor: "#3A3A3C",
|
marginLeft: 14,
|
||||||
},
|
},
|
||||||
leagueInfoText: {
|
leagueInfoText: {
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
},
|
},
|
||||||
leagueTitle: {
|
leagueTitle: {
|
||||||
fontSize: 17,
|
fontSize: 14,
|
||||||
fontWeight: "600",
|
fontWeight: "700",
|
||||||
color: "#FFFFFF",
|
marginBottom: 4,
|
||||||
marginBottom: 3,
|
lineHeight: 16,
|
||||||
},
|
},
|
||||||
countryRow: {
|
countryRow: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
countryFlag: {
|
countryFlag: {
|
||||||
width: 16,
|
width: 14,
|
||||||
height: 12,
|
height: 14,
|
||||||
marginRight: 5,
|
marginRight: 6,
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
},
|
},
|
||||||
countryName: {
|
countryName: {
|
||||||
fontSize: 13,
|
fontSize: 12,
|
||||||
color: "#8E8E93",
|
|
||||||
fontWeight: "500",
|
fontWeight: "500",
|
||||||
|
lineHeight: 12,
|
||||||
},
|
},
|
||||||
leagueHeaderRight: {
|
leagueHeaderRight: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 8,
|
|
||||||
},
|
},
|
||||||
matchCount: {
|
chevronContainer: {
|
||||||
fontSize: 15,
|
width: 16,
|
||||||
color: "#8E8E93",
|
height: 16,
|
||||||
fontWeight: "600",
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
chevron: {
|
chevronIcon: {
|
||||||
fontSize: 16,
|
width: 16,
|
||||||
color: "#8E8E93",
|
height: 16,
|
||||||
fontWeight: '600',
|
|
||||||
},
|
},
|
||||||
matchListContainer: {
|
matchListContainer: {
|
||||||
backgroundColor: "#1C1C1E",
|
borderRadius: 16,
|
||||||
borderRadius: 12,
|
|
||||||
marginHorizontal: 16,
|
marginHorizontal: 16,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
},
|
},
|
||||||
// 布局与 MatchCardLeague 保持一致,便于骨架对齐
|
|
||||||
leftColumn: {
|
leftColumn: {
|
||||||
width: 50,
|
width: 48,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "flex-start",
|
alignItems: "center",
|
||||||
marginRight: 8,
|
marginRight: 8,
|
||||||
},
|
},
|
||||||
teamsColumn: {
|
teamsColumn: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
paddingRight: 8,
|
paddingRight: 12,
|
||||||
},
|
},
|
||||||
rightWrapper: {
|
rightWrapper: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
matchCardWrapper: {
|
matchCardWrapper: {},
|
||||||
},
|
|
||||||
matchCardDivider: {
|
matchCardDivider: {
|
||||||
borderBottomWidth: 0.5,
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||||
borderBottomColor: "#3A3A3C",
|
|
||||||
},
|
},
|
||||||
favoriteButton: {
|
favoriteButton: {
|
||||||
paddingLeft: 12,
|
paddingLeft: 16,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
@@ -359,35 +392,31 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
paddingVertical: 14,
|
paddingVertical: 16,
|
||||||
},
|
},
|
||||||
skeletonLine: {
|
skeletonLine: {
|
||||||
height: 8,
|
height: 10,
|
||||||
borderRadius: 4,
|
borderRadius: 5,
|
||||||
backgroundColor: "#2C2C2E",
|
|
||||||
},
|
},
|
||||||
skeletonAvatar: {
|
skeletonAvatar: {
|
||||||
width: 20,
|
width: 28,
|
||||||
height: 20,
|
height: 28,
|
||||||
borderRadius: 10,
|
borderRadius: 14,
|
||||||
backgroundColor: "#2C2C2E",
|
marginRight: 12,
|
||||||
marginRight: 10,
|
|
||||||
},
|
},
|
||||||
skeletonTeamRow: {
|
skeletonTeamRow: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
skeletonScoreBox: {
|
skeletonScoreBox: {
|
||||||
width: 24,
|
width: 40,
|
||||||
height: 32,
|
height: 64,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
backgroundColor: "#2C2C2E",
|
|
||||||
},
|
},
|
||||||
skeletonCircle: {
|
skeletonCircle: {
|
||||||
width: 16,
|
width: 22,
|
||||||
height: 16,
|
height: 22,
|
||||||
borderRadius: 8,
|
borderRadius: 11,
|
||||||
backgroundColor: "#2C2C2E",
|
|
||||||
},
|
},
|
||||||
emptyContainer: {
|
emptyContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|||||||
@@ -4,13 +4,33 @@ import i18n from "i18next";
|
|||||||
import { initReactI18next } from "react-i18next";
|
import { initReactI18next } from "react-i18next";
|
||||||
|
|
||||||
import en from "./locales/en.json";
|
import en from "./locales/en.json";
|
||||||
|
import hi from "./locales/hi.json";
|
||||||
|
import id from "./locales/id.json";
|
||||||
|
import ms from "./locales/ms.json";
|
||||||
|
import th from "./locales/th.json";
|
||||||
|
import vi from "./locales/vi.json";
|
||||||
import zh from "./locales/zh.json";
|
import zh from "./locales/zh.json";
|
||||||
|
|
||||||
const RESOURCES = {
|
const RESOURCES = {
|
||||||
en: { translation: en },
|
en: { translation: en },
|
||||||
zh: { translation: zh },
|
zh: { translation: zh },
|
||||||
|
hi: { translation: hi },
|
||||||
|
id: { translation: id },
|
||||||
|
ms: { translation: ms },
|
||||||
|
th: { translation: th },
|
||||||
|
vi: { translation: vi },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SUPPORTED_LANGUAGES = [
|
||||||
|
{ code: "en", name: "English" },
|
||||||
|
{ code: "zh", name: "中文" },
|
||||||
|
{ code: "hi", name: "हिन्दी" },
|
||||||
|
{ code: "id", name: "Bahasa Indonesia" },
|
||||||
|
{ code: "ms", name: "Bahasa Melayu" },
|
||||||
|
{ code: "th", name: "ไทย" },
|
||||||
|
{ code: "vi", name: "Tiếng Việt" },
|
||||||
|
];
|
||||||
|
|
||||||
const LANGUAGE_STORAGE_KEY = "user_language_preference";
|
const LANGUAGE_STORAGE_KEY = "user_language_preference";
|
||||||
|
|
||||||
const initI18n = async () => {
|
const initI18n = async () => {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Settings",
|
"title": "Settings",
|
||||||
|
"back": "Back",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"light": "Light",
|
"light": "Light",
|
||||||
@@ -40,6 +41,9 @@
|
|||||||
"corner_show": "Show Corners",
|
"corner_show": "Show Corners",
|
||||||
"corner_enabled": "On",
|
"corner_enabled": "On",
|
||||||
"corner_disabled": "Off",
|
"corner_disabled": "Off",
|
||||||
|
"cards_unselected": "Unselected",
|
||||||
|
"cards_modal_title": "Cards Settings",
|
||||||
|
"cards_confirm": "Confirm",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"click_to_login": "Click to login",
|
"click_to_login": "Click to login",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
|
|||||||
212
i18n/locales/hi.json
Normal file
212
i18n/locales/hi.json
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
"welcome": "स्वागत है!",
|
||||||
|
"tabs": {
|
||||||
|
"all": "सभी",
|
||||||
|
"live": "लाइव",
|
||||||
|
"upcoming": "आगामी",
|
||||||
|
"finished": "समाप्त",
|
||||||
|
"fav": "पसंदीदा"
|
||||||
|
},
|
||||||
|
"favorites": {
|
||||||
|
"filter_match": "मैच",
|
||||||
|
"filter_team": "टीमें",
|
||||||
|
"filter_player": "खिलाड़ी",
|
||||||
|
"filter_league": "लीग",
|
||||||
|
"no_data": "कोई पसंदीदा नहीं मिला",
|
||||||
|
"unknown": "अज्ञात"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "सेटिंग्स",
|
||||||
|
"back": "वापस",
|
||||||
|
"theme": "थीम",
|
||||||
|
"language": "भाषा",
|
||||||
|
"light": "हल्का",
|
||||||
|
"dark": "डार्क",
|
||||||
|
"system": "सिस्टम",
|
||||||
|
"english": "अंग्रेज़ी",
|
||||||
|
"chinese": "चीनी",
|
||||||
|
"odds_title": "ऑड्स सेटिंग्स",
|
||||||
|
"odds_show": "ऑड्स दिखाएँ",
|
||||||
|
"odds_select_company": "बुकमेकर चुनें (अधिकतम 2)",
|
||||||
|
"odds_enabled": "चालू",
|
||||||
|
"odds_disabled": "बंद",
|
||||||
|
"odds_unselected": "चयन नहीं किया गया",
|
||||||
|
"odds_modal_title": "बुकमेकर चुनें (अधिकतम 2)",
|
||||||
|
"odds_confirm": "पुष्टि करें",
|
||||||
|
"cards_title": "कार्ड सेटिंग्स",
|
||||||
|
"cards_show": "कार्ड दिखाएँ",
|
||||||
|
"cards_enabled": "चालू",
|
||||||
|
"cards_disabled": "बंद",
|
||||||
|
"cards_unselected": "चयन नहीं किया गया",
|
||||||
|
"cards_modal_title": "कार्ड सेटिंग्स",
|
||||||
|
"cards_confirm": "पुष्टि करें",
|
||||||
|
"login": "लॉगिन",
|
||||||
|
"click_to_login": "लॉगिन करने के लिए क्लिक करें",
|
||||||
|
"logout": "लॉगआउट",
|
||||||
|
"select_login_method": "लॉगिन विधि चुनें",
|
||||||
|
"cancel": "रद्द करें",
|
||||||
|
"google_login": "Google से लॉगिन करें"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"title": "ScoreNow",
|
||||||
|
"time": "समय",
|
||||||
|
"league": "लीग",
|
||||||
|
"select_sport": "खेल",
|
||||||
|
"select_league": "लीग चुनें",
|
||||||
|
"all_leagues": "सभी लीग",
|
||||||
|
"loading": "लोड हो रहा है...",
|
||||||
|
"no_matches": "कोई मैच नहीं मिला",
|
||||||
|
"no_leagues": "कोई लीग उपलब्ध नहीं है"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"title": "मेरा प्रोफ़ाइल",
|
||||||
|
"name": "उपयोगकर्ता नाम",
|
||||||
|
"settings": "सेटिंग्स",
|
||||||
|
"privacy": "गोपनीयता नीति"
|
||||||
|
},
|
||||||
|
"privacy": {
|
||||||
|
"title": "गोपनीयता नीति",
|
||||||
|
"page_title": "ScoreNow गोपनीयता नीति",
|
||||||
|
"section1_title": "1. जानकारी संग्रह",
|
||||||
|
"section1_text": "ScoreNow निम्न जानकारी एकत्र करता है: पंजीकरण के दौरान दी गई खाता जानकारी; आपके द्वारा फ़ॉलो की गई टीमों और लीग की प्राथमिकताएँ; और लाइव स्कोर फ़ीचर उपयोग करते समय डिवाइस पहचानकर्ता व नेटवर्क लॉग।",
|
||||||
|
"section2_title": "2. जानकारी का उपयोग",
|
||||||
|
"section2_text": "हम इस जानकारी का उपयोग आपकी रुचि वाले मैच रिमाइंडर भेजने, समाचार व जानकारी के प्रदर्शन क्रम को अनुकूलित करने और रियल-टाइम व सटीक मैच डेटा प्रदान करने के लिए करते हैं।",
|
||||||
|
"section3_title": "3. तृतीय-पक्ष सेवाएँ",
|
||||||
|
"section3_text": "सटीक मैच डेटा और विश्लेषण प्रदान करने के लिए हम तृतीय-पक्ष डेटा प्रदाताओं के साथ सहयोग कर सकते हैं। हम आपकी व्यक्तिगत पहचान संबंधी जानकारी साझा नहीं करते, केवल गुमनाम सांख्यिकीय डेटा का उपयोग किया जाता है।",
|
||||||
|
"section4_title": "4. अनुमतियाँ",
|
||||||
|
"section4_text": "हम गोल अलर्ट भेजने के लिए नोटिफिकेशन अनुमति और मैच से संबंधित चित्र व सामग्री कैश करने के लिए स्टोरेज अनुमति मांगते हैं, जिससे डेटा की खपत कम होती है।",
|
||||||
|
"section5_title": "5. डेटा सुरक्षा",
|
||||||
|
"section5_text": "ScoreNow उद्योग-मानक एन्क्रिप्शन तकनीक का उपयोग करता है ताकि आपकी जानकारी सुरक्षित रहे और अनधिकृत पहुँच को रोका जा सके।",
|
||||||
|
"section6_title": "6. आपके अधिकार",
|
||||||
|
"section6_text": "आप किसी भी समय सेटिंग्स में जाकर नोटिफिकेशन प्राथमिकताएँ प्रबंधित कर सकते हैं, खोज इतिहास साफ कर सकते हैं या अपना खाता हटा सकते हैं।",
|
||||||
|
"last_updated": "अंतिम अपडेट: जनवरी 2026"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"title": "मैच विवरण",
|
||||||
|
"pending": "प्रतीक्षारत",
|
||||||
|
"retry": "फिर से प्रयास करें",
|
||||||
|
"fetch_failed": "विवरण प्राप्त करने में विफल",
|
||||||
|
"not_found": "मैच डेटा नहीं मिला",
|
||||||
|
"tabs": {
|
||||||
|
"info": "विवरण",
|
||||||
|
"stats": "आँकड़े",
|
||||||
|
"odds": "ऑड्स",
|
||||||
|
"lineup": "लाइनअप",
|
||||||
|
"analysis": "विश्लेषण",
|
||||||
|
"h2h": "आमने-सामने",
|
||||||
|
"chat": "चैट"
|
||||||
|
},
|
||||||
|
"info_card": {
|
||||||
|
"title": "मैच जानकारी",
|
||||||
|
"country": "देश/क्षेत्र",
|
||||||
|
"league": "लीग",
|
||||||
|
"stage": "चरण",
|
||||||
|
"stadium": "स्टेडियम",
|
||||||
|
"referee": "रेफरी",
|
||||||
|
"date": "तारीख व समय"
|
||||||
|
},
|
||||||
|
"score_table": {
|
||||||
|
"team": "टीम",
|
||||||
|
"total": "पूरा समय",
|
||||||
|
"halftime": "हाफ टाइम",
|
||||||
|
"full_time": "90'",
|
||||||
|
"extra_time": "अतिरिक्त समय",
|
||||||
|
"penalty": "पेनल्टी"
|
||||||
|
},
|
||||||
|
"halftime": "हाफ टाइम: {{score}}",
|
||||||
|
"empty_stats": "कोई आँकड़े उपलब्ध नहीं",
|
||||||
|
"empty_odds": "कोई ऑड्स उपलब्ध नहीं",
|
||||||
|
"empty_h2h": "कोई आमने-सामने डेटा नहीं",
|
||||||
|
"empty_chat": "चैट उपलब्ध नहीं है",
|
||||||
|
"h2h": {
|
||||||
|
"h2h": "आमने-सामने",
|
||||||
|
"loading": "लोड हो रहा है...",
|
||||||
|
"error": "लोड करने में विफल",
|
||||||
|
"retry": "फिर से प्रयास करें",
|
||||||
|
"no_data": "कोई डेटा उपलब्ध नहीं"
|
||||||
|
},
|
||||||
|
"odds_card": {
|
||||||
|
"title": "मैच ऑड्स",
|
||||||
|
"disclaimer": ""
|
||||||
|
},
|
||||||
|
"stats_card": {
|
||||||
|
"title": "आँकड़े",
|
||||||
|
"possession": "कब्ज़ा",
|
||||||
|
"info_title": "मोमेंटम विवरण",
|
||||||
|
"info_desc": "मोमेंटम चार्ट मैच के विभिन्न समयों में दोनों टीमों के आक्रमण दबाव और खतरे के स्तर को दर्शाता है।",
|
||||||
|
"point1": "ऊपरी तरंगें घरेलू टीम और निचली तरंगें मेहमान टीम को दर्शाती हैं।",
|
||||||
|
"point2": "तरंगों की ऊँचाई आक्रमण की तीव्रता, गेंद पर कब्ज़ा और शॉट के खतरे पर निर्भर करती है।",
|
||||||
|
"point3": "रेखा जितनी अधिक उतार-चढ़ाव करती है, उस अवधि में आक्रमण की आवृत्ति उतनी अधिक होती है।",
|
||||||
|
"close": "बंद करें"
|
||||||
|
},
|
||||||
|
"other_info": {
|
||||||
|
"title": "अन्य जानकारी"
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"goals": "गोल",
|
||||||
|
"cards": "कार्ड",
|
||||||
|
"substitutes": "सब्स्टीट्यूट",
|
||||||
|
"lineups": "लाइनअप",
|
||||||
|
"red_card": "लाल कार्ड",
|
||||||
|
"yellow_card": "पीला कार्ड",
|
||||||
|
"start": "मैच शुरू",
|
||||||
|
"ht": "हाफ टाइम",
|
||||||
|
"goal": "गोल",
|
||||||
|
"penalty_goal": "पेनल्टी गोल",
|
||||||
|
"toggle_visibility": "टाइमलाइन दिखाएँ/छुपाएँ",
|
||||||
|
"lineups_goalkeepers": "गोलकीपर",
|
||||||
|
"lineups_defenders": "डिफेंडर",
|
||||||
|
"lineups_midfielders": "मिडफील्डर",
|
||||||
|
"lineups_forwards": "फॉरवर्ड",
|
||||||
|
"lineups_coach": "कोच",
|
||||||
|
"lineups_subs": "सब्स्टीट्यूट",
|
||||||
|
"lineups_missing": "अनुपस्थित खिलाड़ी"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selection": {
|
||||||
|
"selected": "चयनित",
|
||||||
|
"click_to_toggle": "बदलने के लिए टैप करें"
|
||||||
|
},
|
||||||
|
"sports": {
|
||||||
|
"football": "फ़ुटबॉल",
|
||||||
|
"basketball": "बास्केटबॉल",
|
||||||
|
"tennis": "टेनिस",
|
||||||
|
"cricket": "क्रिकेट",
|
||||||
|
"baseball": "बेसबॉल",
|
||||||
|
"badminton": "बैडमिंटन",
|
||||||
|
"snooker": "स्नूकर",
|
||||||
|
"volleyball": "वॉलीबॉल"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"title": "खोज",
|
||||||
|
"subtitle": "टीमें · खिलाड़ी · लीग",
|
||||||
|
"placeholder": "टीम, खिलाड़ी या लीग खोजें",
|
||||||
|
"recent": "हाल की खोजें",
|
||||||
|
"clear": "साफ़ करें",
|
||||||
|
"results": "परिणाम",
|
||||||
|
"found": "मिले",
|
||||||
|
"tap_to_open": "खोलने के लिए टैप करें",
|
||||||
|
"no_results": "कोई परिणाम नहीं",
|
||||||
|
"no_results_hint": "कोई और कीवर्ड आज़माएँ या श्रेणी बदलें",
|
||||||
|
"loading": "लोड हो रहा है",
|
||||||
|
"error": "त्रुटि",
|
||||||
|
"error_hint": "खोज विफल रही, कृपया पुनः प्रयास करें",
|
||||||
|
"start_searching": "खोज शुरू करें",
|
||||||
|
"start_searching_hint": "टीम, खिलाड़ी या लीग खोजने के लिए कीवर्ड दर्ज करें",
|
||||||
|
"type": {
|
||||||
|
"all": "सभी",
|
||||||
|
"team": "टीम",
|
||||||
|
"player": "खिलाड़ी",
|
||||||
|
"league": "लीग"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"country": "देश",
|
||||||
|
"league": "लीग",
|
||||||
|
"season": "सीज़न",
|
||||||
|
"team": "टीम",
|
||||||
|
"position": "पोज़िशन",
|
||||||
|
"open": "विवरण खोलें",
|
||||||
|
"save": "हाल में सहेजें"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
212
i18n/locales/id.json
Normal file
212
i18n/locales/id.json
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Selamat datang!",
|
||||||
|
"tabs": {
|
||||||
|
"all": "Semua",
|
||||||
|
"live": "Langsung",
|
||||||
|
"upcoming": "Akan Datang",
|
||||||
|
"finished": "Selesai",
|
||||||
|
"fav": "Favorit"
|
||||||
|
},
|
||||||
|
"favorites": {
|
||||||
|
"filter_match": "Pertandingan",
|
||||||
|
"filter_team": "Tim",
|
||||||
|
"filter_player": "Pemain",
|
||||||
|
"filter_league": "Liga",
|
||||||
|
"no_data": "Tidak ada favorit",
|
||||||
|
"unknown": "Tidak diketahui"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Pengaturan",
|
||||||
|
"back": "Kembali",
|
||||||
|
"theme": "Tema",
|
||||||
|
"language": "Bahasa",
|
||||||
|
"light": "Terang",
|
||||||
|
"dark": "Gelap",
|
||||||
|
"system": "Sistem",
|
||||||
|
"english": "Inggris",
|
||||||
|
"chinese": "Tiongkok",
|
||||||
|
"odds_title": "Pengaturan Odds",
|
||||||
|
"odds_show": "Tampilkan Odds",
|
||||||
|
"odds_select_company": "Pilih Bandar (Maks. 2)",
|
||||||
|
"odds_enabled": "Aktif",
|
||||||
|
"odds_disabled": "Nonaktif",
|
||||||
|
"odds_unselected": "Belum dipilih",
|
||||||
|
"odds_modal_title": "Pilih Bandar (Maks. 2)",
|
||||||
|
"odds_confirm": "Konfirmasi",
|
||||||
|
"cards_title": "Pengaturan Kartu",
|
||||||
|
"cards_show": "Tampilkan Kartu",
|
||||||
|
"cards_enabled": "Aktif",
|
||||||
|
"cards_disabled": "Nonaktif",
|
||||||
|
"cards_unselected": "Belum dipilih",
|
||||||
|
"cards_modal_title": "Pengaturan Kartu",
|
||||||
|
"cards_confirm": "Konfirmasi",
|
||||||
|
"login": "Masuk",
|
||||||
|
"click_to_login": "Klik untuk masuk",
|
||||||
|
"logout": "Keluar",
|
||||||
|
"select_login_method": "Pilih metode login",
|
||||||
|
"cancel": "Batal",
|
||||||
|
"google_login": "Masuk dengan Google"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"title": "ScoreNow",
|
||||||
|
"time": "Waktu",
|
||||||
|
"league": "Liga",
|
||||||
|
"select_sport": "Olahraga",
|
||||||
|
"select_league": "Pilih Liga",
|
||||||
|
"all_leagues": "Semua Liga",
|
||||||
|
"loading": "Memuat...",
|
||||||
|
"no_matches": "Tidak ada pertandingan",
|
||||||
|
"no_leagues": "Tidak ada liga tersedia"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"title": "Profil Saya",
|
||||||
|
"name": "Nama Pengguna",
|
||||||
|
"settings": "Pengaturan",
|
||||||
|
"privacy": "Kebijakan Privasi"
|
||||||
|
},
|
||||||
|
"privacy": {
|
||||||
|
"title": "Kebijakan Privasi",
|
||||||
|
"page_title": "Kebijakan Privasi ScoreNow",
|
||||||
|
"section1_title": "1. Pengumpulan Informasi",
|
||||||
|
"section1_text": "ScoreNow mengumpulkan informasi seperti data akun saat pendaftaran, preferensi tim dan liga yang Anda ikuti, serta identitas perangkat dan log jaringan saat menggunakan fitur skor langsung.",
|
||||||
|
"section2_title": "2. Penggunaan Informasi",
|
||||||
|
"section2_text": "Informasi digunakan untuk mengirim pengingat pertandingan yang Anda minati, mengoptimalkan urutan tampilan berita, serta memastikan distribusi data pertandingan secara real-time dan akurat.",
|
||||||
|
"section3_title": "3. Layanan Pihak Ketiga",
|
||||||
|
"section3_text": "Untuk menyediakan data dan analisis pertandingan yang akurat, kami dapat bekerja sama dengan penyedia data pihak ketiga. Kami tidak membagikan identitas pribadi Anda, hanya data statistik anonim.",
|
||||||
|
"section4_title": "4. Izin Aplikasi",
|
||||||
|
"section4_text": "Kami meminta izin notifikasi untuk mengirim peringatan gol, serta izin penyimpanan untuk menyimpan gambar dan konten pertandingan agar menghemat penggunaan data.",
|
||||||
|
"section5_title": "5. Keamanan Data",
|
||||||
|
"section5_text": "ScoreNow menggunakan teknologi enkripsi standar industri untuk melindungi data Anda dari akses yang tidak sah.",
|
||||||
|
"section6_title": "6. Hak Anda",
|
||||||
|
"section6_text": "Anda dapat mengatur preferensi notifikasi, menghapus riwayat pencarian, atau menghapus akun kapan saja melalui pengaturan.",
|
||||||
|
"last_updated": "Terakhir diperbarui: Januari 2026"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"title": "Detail Pertandingan",
|
||||||
|
"pending": "Menunggu",
|
||||||
|
"retry": "Coba lagi",
|
||||||
|
"fetch_failed": "Gagal mengambil data",
|
||||||
|
"not_found": "Data pertandingan tidak ditemukan",
|
||||||
|
"tabs": {
|
||||||
|
"info": "Detail",
|
||||||
|
"stats": "Statistik",
|
||||||
|
"odds": "Odds",
|
||||||
|
"lineup": "Susunan Pemain",
|
||||||
|
"analysis": "Analisis",
|
||||||
|
"h2h": "Head-to-Head",
|
||||||
|
"chat": "Chat"
|
||||||
|
},
|
||||||
|
"info_card": {
|
||||||
|
"title": "Info Pertandingan",
|
||||||
|
"country": "Negara/Wilayah",
|
||||||
|
"league": "Liga",
|
||||||
|
"stage": "Babak",
|
||||||
|
"stadium": "Stadion",
|
||||||
|
"referee": "Wasit",
|
||||||
|
"date": "Tanggal & Waktu"
|
||||||
|
},
|
||||||
|
"score_table": {
|
||||||
|
"team": "Tim",
|
||||||
|
"total": "Waktu Normal",
|
||||||
|
"halftime": "Babak Pertama",
|
||||||
|
"full_time": "90'",
|
||||||
|
"extra_time": "Perpanjangan",
|
||||||
|
"penalty": "Adu Penalti"
|
||||||
|
},
|
||||||
|
"halftime": "Babak Pertama: {{score}}",
|
||||||
|
"empty_stats": "Tidak ada data statistik",
|
||||||
|
"empty_odds": "Tidak ada data odds",
|
||||||
|
"empty_h2h": "Tidak ada data H2H",
|
||||||
|
"empty_chat": "Chat tidak tersedia",
|
||||||
|
"h2h": {
|
||||||
|
"h2h": "Head-to-Head",
|
||||||
|
"loading": "Memuat...",
|
||||||
|
"error": "Gagal memuat",
|
||||||
|
"retry": "Coba lagi",
|
||||||
|
"no_data": "Tidak ada data"
|
||||||
|
},
|
||||||
|
"odds_card": {
|
||||||
|
"title": "Odds Pertandingan",
|
||||||
|
"disclaimer": ""
|
||||||
|
},
|
||||||
|
"stats_card": {
|
||||||
|
"title": "Statistik",
|
||||||
|
"possession": "Penguasaan Bola",
|
||||||
|
"info_title": "Penjelasan Momentum",
|
||||||
|
"info_desc": "Grafik momentum menunjukkan tekanan serangan dan tingkat ancaman kedua tim pada periode pertandingan yang berbeda.",
|
||||||
|
"point1": "Gelombang atas mewakili tim tuan rumah, gelombang bawah mewakili tim tamu.",
|
||||||
|
"point2": "Tinggi gelombang tergantung pada intensitas serangan, penguasaan bola, dan ancaman tembakan.",
|
||||||
|
"point3": "Semakin besar fluktuasi garis, semakin tinggi frekuensi serangan pada periode tersebut.",
|
||||||
|
"close": "Tutup"
|
||||||
|
},
|
||||||
|
"other_info": {
|
||||||
|
"title": "Info Lainnya"
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"goals": "Gol",
|
||||||
|
"cards": "Kartu",
|
||||||
|
"substitutes": "Pergantian",
|
||||||
|
"lineups": "Lineup",
|
||||||
|
"red_card": "Kartu Merah",
|
||||||
|
"yellow_card": "Kartu Kuning",
|
||||||
|
"start": "Pertandingan Dimulai",
|
||||||
|
"ht": "HT",
|
||||||
|
"goal": "Gol",
|
||||||
|
"penalty_goal": "Gol Penalti",
|
||||||
|
"toggle_visibility": "Tampilkan/Sembunyikan Timeline",
|
||||||
|
"lineups_goalkeepers": "Penjaga Gawang",
|
||||||
|
"lineups_defenders": "Bek",
|
||||||
|
"lineups_midfielders": "Gelandang",
|
||||||
|
"lineups_forwards": "Penyerang",
|
||||||
|
"lineups_coach": "Pelatih",
|
||||||
|
"lineups_subs": "Cadangan",
|
||||||
|
"lineups_missing": "Pemain Absen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selection": {
|
||||||
|
"selected": "Dipilih",
|
||||||
|
"click_to_toggle": "Ketuk untuk mengganti"
|
||||||
|
},
|
||||||
|
"sports": {
|
||||||
|
"football": "Sepak Bola",
|
||||||
|
"basketball": "Bola Basket",
|
||||||
|
"tennis": "Tenis",
|
||||||
|
"cricket": "Kriket",
|
||||||
|
"baseball": "Bisbol",
|
||||||
|
"badminton": "Bulu Tangkis",
|
||||||
|
"snooker": "Snuker",
|
||||||
|
"volleyball": "Bola Voli"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"title": "Cari",
|
||||||
|
"subtitle": "Tim · Pemain · Liga",
|
||||||
|
"placeholder": "Cari tim, pemain, liga",
|
||||||
|
"recent": "Pencarian terbaru",
|
||||||
|
"clear": "Hapus",
|
||||||
|
"results": "Hasil",
|
||||||
|
"found": "ditemukan",
|
||||||
|
"tap_to_open": "Ketuk untuk membuka",
|
||||||
|
"no_results": "Tidak ada hasil",
|
||||||
|
"no_results_hint": "Coba kata kunci lain atau ganti kategori",
|
||||||
|
"loading": "Memuat",
|
||||||
|
"error": "Kesalahan",
|
||||||
|
"error_hint": "Pencarian gagal, silakan coba lagi",
|
||||||
|
"start_searching": "Mulai mencari",
|
||||||
|
"start_searching_hint": "Masukkan kata kunci untuk mencari tim, pemain, atau liga",
|
||||||
|
"type": {
|
||||||
|
"all": "Semua",
|
||||||
|
"team": "Tim",
|
||||||
|
"player": "Pemain",
|
||||||
|
"league": "Liga"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"country": "Negara",
|
||||||
|
"league": "Liga",
|
||||||
|
"season": "Musim",
|
||||||
|
"team": "Tim",
|
||||||
|
"position": "Posisi",
|
||||||
|
"open": "Buka detail",
|
||||||
|
"save": "Simpan ke terbaru"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
212
i18n/locales/ms.json
Normal file
212
i18n/locales/ms.json
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Selamat datang!",
|
||||||
|
"tabs": {
|
||||||
|
"all": "Semua",
|
||||||
|
"live": "Langsung",
|
||||||
|
"upcoming": "Akan Datang",
|
||||||
|
"finished": "Tamat",
|
||||||
|
"fav": "Kegemaran"
|
||||||
|
},
|
||||||
|
"favorites": {
|
||||||
|
"filter_match": "Perlawanan",
|
||||||
|
"filter_team": "Pasukan",
|
||||||
|
"filter_player": "Pemain",
|
||||||
|
"filter_league": "Liga",
|
||||||
|
"no_data": "Tiada kegemaran ditemui",
|
||||||
|
"unknown": "Tidak diketahui"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Tetapan",
|
||||||
|
"back": "Kembali",
|
||||||
|
"theme": "Tema",
|
||||||
|
"language": "Bahasa",
|
||||||
|
"light": "Cerah",
|
||||||
|
"dark": "Gelap",
|
||||||
|
"system": "Sistem",
|
||||||
|
"english": "Bahasa Inggeris",
|
||||||
|
"chinese": "Bahasa Cina",
|
||||||
|
"odds_title": "Tetapan Odds",
|
||||||
|
"odds_show": "Paparkan Odds",
|
||||||
|
"odds_select_company": "Pilih Bookmaker (Maks 2)",
|
||||||
|
"odds_enabled": "Hidup",
|
||||||
|
"odds_disabled": "Mati",
|
||||||
|
"odds_unselected": "Belum dipilih",
|
||||||
|
"odds_modal_title": "Pilih Bookmaker (Maks 2)",
|
||||||
|
"odds_confirm": "Sahkan",
|
||||||
|
"cards_title": "Tetapan Kad",
|
||||||
|
"cards_show": "Paparkan Kad",
|
||||||
|
"cards_enabled": "Hidup",
|
||||||
|
"cards_disabled": "Mati",
|
||||||
|
"cards_unselected": "Belum dipilih",
|
||||||
|
"cards_modal_title": "Tetapan Kad",
|
||||||
|
"cards_confirm": "Sahkan",
|
||||||
|
"login": "Log Masuk",
|
||||||
|
"click_to_login": "Klik untuk log masuk",
|
||||||
|
"logout": "Log Keluar",
|
||||||
|
"select_login_method": "Pilih kaedah log masuk",
|
||||||
|
"cancel": "Batal",
|
||||||
|
"google_login": "Log Masuk Google"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"title": "ScoreNow",
|
||||||
|
"time": "Masa",
|
||||||
|
"league": "Liga",
|
||||||
|
"select_sport": "Sukan",
|
||||||
|
"select_league": "Pilih Liga",
|
||||||
|
"all_leagues": "Semua Liga",
|
||||||
|
"loading": "Memuatkan...",
|
||||||
|
"no_matches": "Tiada perlawanan ditemui",
|
||||||
|
"no_leagues": "Tiada liga tersedia"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"title": "Profil Saya",
|
||||||
|
"name": "Nama Pengguna",
|
||||||
|
"settings": "Tetapan",
|
||||||
|
"privacy": "Dasar Privasi"
|
||||||
|
},
|
||||||
|
"privacy": {
|
||||||
|
"title": "Dasar Privasi",
|
||||||
|
"page_title": "Dasar Privasi ScoreNow",
|
||||||
|
"section1_title": "1. Pengumpulan Maklumat",
|
||||||
|
"section1_text": "ScoreNow mengumpul maklumat termasuk: maklumat akaun semasa pendaftaran; pilihan pasukan dan liga yang anda ikuti; serta pengecam peranti dan log rangkaian semasa menggunakan ciri skor langsung.",
|
||||||
|
"section2_title": "2. Penggunaan Maklumat",
|
||||||
|
"section2_text": "Maklumat ini digunakan untuk menghantar peringatan perlawanan yang anda minati, mengoptimumkan paparan berita, serta memastikan penghantaran data perlawanan yang tepat dan masa nyata.",
|
||||||
|
"section3_title": "3. Perkhidmatan Pihak Ketiga",
|
||||||
|
"section3_text": "Bagi menyediakan data dan analisis yang tepat, kami bekerjasama dengan penyedia data pihak ketiga. Kami tidak berkongsi maklumat identiti peribadi anda, hanya data statistik tanpa nama.",
|
||||||
|
"section4_title": "4. Kebenaran",
|
||||||
|
"section4_text": "Kami memohon kebenaran notifikasi untuk menghantar amaran gol; dan kebenaran storan untuk menyimpan imej serta kandungan perlawanan bagi menjimatkan penggunaan data anda.",
|
||||||
|
"section5_title": "5. Keselamatan Data",
|
||||||
|
"section5_text": "ScoreNow menggunakan teknologi penyulitan standard industri untuk melindungi data anda dan mencegah akses tanpa kebenaran terhadap pilihan tontonan dan sejarah akaun.",
|
||||||
|
"section6_title": "6. Hak Anda",
|
||||||
|
"section6_text": "Anda boleh mengurus tetapan notifikasi, memadam sejarah carian, atau memadam akaun anda pada bila-bila masa melalui tetapan.",
|
||||||
|
"last_updated": "Kemas kini terakhir: Januari 2026"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"title": "Butiran Perlawanan",
|
||||||
|
"pending": "Menunggu",
|
||||||
|
"retry": "Cuba semula",
|
||||||
|
"fetch_failed": "Gagal mendapatkan maklumat",
|
||||||
|
"not_found": "Data perlawanan tidak ditemui",
|
||||||
|
"tabs": {
|
||||||
|
"info": "Maklumat",
|
||||||
|
"stats": "Statistik",
|
||||||
|
"odds": "Odds",
|
||||||
|
"lineup": "Kesebelasan",
|
||||||
|
"analysis": "Analisis",
|
||||||
|
"h2h": "Rekod Pertemuan",
|
||||||
|
"chat": "Sembang"
|
||||||
|
},
|
||||||
|
"info_card": {
|
||||||
|
"title": "Maklumat Perlawanan",
|
||||||
|
"country": "Negara/Wilayah",
|
||||||
|
"league": "Liga",
|
||||||
|
"stage": "Peringkat",
|
||||||
|
"stadium": "Stadium",
|
||||||
|
"referee": "Pengadil",
|
||||||
|
"date": "Tarikh & Masa"
|
||||||
|
},
|
||||||
|
"score_table": {
|
||||||
|
"team": "Pasukan",
|
||||||
|
"total": "Keputusan",
|
||||||
|
"halftime": "Separuh Masa",
|
||||||
|
"full_time": "90'",
|
||||||
|
"extra_time": "Masa Tambahan",
|
||||||
|
"penalty": "Sepakan Penalti"
|
||||||
|
},
|
||||||
|
"halftime": "Separuh masa: {{score}}",
|
||||||
|
"empty_stats": "Tiada data statistik",
|
||||||
|
"empty_odds": "Tiada data odds",
|
||||||
|
"empty_h2h": "Tiada data pertemuan",
|
||||||
|
"empty_chat": "Sembang tidak tersedia",
|
||||||
|
"h2h": {
|
||||||
|
"h2h": "Rekod Pertemuan",
|
||||||
|
"loading": "Memuatkan...",
|
||||||
|
"error": "Gagal dimuatkan",
|
||||||
|
"retry": "Cuba semula",
|
||||||
|
"no_data": "Tiada data tersedia"
|
||||||
|
},
|
||||||
|
"odds_card": {
|
||||||
|
"title": "Odds Perlawanan",
|
||||||
|
"disclaimer": ""
|
||||||
|
},
|
||||||
|
"stats_card": {
|
||||||
|
"title": "Statistik",
|
||||||
|
"possession": "Penguasaan Bola",
|
||||||
|
"info_title": "Penjelasan Momentum",
|
||||||
|
"info_desc": "Carta momentum menunjukkan tekanan serangan dan tahap ancaman kedua-dua pasukan dalam tempoh perlawanan yang berbeza.",
|
||||||
|
"point1": "Gelombang atas mewakili pasukan tuan rumah, manakala gelombang bawah mewakili pasukan pelawat.",
|
||||||
|
"point2": "Ketinggian gelombang bergantung kepada intensiti serangan, masa penguasaan bola dan ancaman percubaan.",
|
||||||
|
"point3": "Semakin kuat turun naik garis, semakin tinggi kekerapan serangan pada tempoh tersebut.",
|
||||||
|
"close": "Tutup"
|
||||||
|
},
|
||||||
|
"other_info": {
|
||||||
|
"title": "Maklumat Lain"
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"goals": "Gol",
|
||||||
|
"cards": "Kad",
|
||||||
|
"substitutes": "Pertukaran",
|
||||||
|
"lineups": "Barisan Pemain",
|
||||||
|
"red_card": "KAD MERAH",
|
||||||
|
"yellow_card": "KAD KUNING",
|
||||||
|
"start": "Perlawanan Bermula",
|
||||||
|
"ht": "Separuh Masa",
|
||||||
|
"goal": "Gol",
|
||||||
|
"penalty_goal": "Gol Penalti",
|
||||||
|
"toggle_visibility": "Tunjuk/Sembunyi garis masa",
|
||||||
|
"lineups_goalkeepers": "Penjaga Gol",
|
||||||
|
"lineups_defenders": "Pertahanan",
|
||||||
|
"lineups_midfielders": "Tengah",
|
||||||
|
"lineups_forwards": "Penyerang",
|
||||||
|
"lineups_coach": "Jurulatih",
|
||||||
|
"lineups_subs": "Pemain Simpanan",
|
||||||
|
"lineups_missing": "Pemain Tidak Tersenarai"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selection": {
|
||||||
|
"selected": "Dipilih",
|
||||||
|
"click_to_toggle": "Ketik untuk tukar"
|
||||||
|
},
|
||||||
|
"sports": {
|
||||||
|
"football": "Bola Sepak",
|
||||||
|
"basketball": "Bola Keranjang",
|
||||||
|
"tennis": "Tenis",
|
||||||
|
"cricket": "Kriket",
|
||||||
|
"baseball": "Besbol",
|
||||||
|
"badminton": "Badminton",
|
||||||
|
"snooker": "Snuker",
|
||||||
|
"volleyball": "Bola Tampar"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"title": "Carian",
|
||||||
|
"subtitle": "Pasukan · Pemain · Liga",
|
||||||
|
"placeholder": "Cari pasukan, pemain atau liga",
|
||||||
|
"recent": "Carian terkini",
|
||||||
|
"clear": "Padam",
|
||||||
|
"results": "Keputusan",
|
||||||
|
"found": "ditemui",
|
||||||
|
"tap_to_open": "Ketik untuk buka",
|
||||||
|
"no_results": "Tiada keputusan",
|
||||||
|
"no_results_hint": "Cuba kata kunci lain atau tukar kategori",
|
||||||
|
"loading": "Memuatkan",
|
||||||
|
"error": "Ralat",
|
||||||
|
"error_hint": "Carian gagal, sila cuba lagi",
|
||||||
|
"start_searching": "Mulakan carian",
|
||||||
|
"start_searching_hint": "Masukkan kata kunci untuk mencari pasukan, pemain atau liga",
|
||||||
|
"type": {
|
||||||
|
"all": "Semua",
|
||||||
|
"team": "Pasukan",
|
||||||
|
"player": "Pemain",
|
||||||
|
"league": "Liga"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"country": "Negara",
|
||||||
|
"league": "Liga",
|
||||||
|
"season": "Musim",
|
||||||
|
"team": "Pasukan",
|
||||||
|
"position": "Posisi",
|
||||||
|
"open": "Buka butiran",
|
||||||
|
"save": "Simpan ke terkini"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
212
i18n/locales/th.json
Normal file
212
i18n/locales/th.json
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
"welcome": "ยินดีต้อนรับ!",
|
||||||
|
"tabs": {
|
||||||
|
"all": "ทั้งหมด",
|
||||||
|
"live": "ถ่ายทอดสด",
|
||||||
|
"upcoming": "กำลังจะเริ่ม",
|
||||||
|
"finished": "จบการแข่งขัน",
|
||||||
|
"fav": "รายการโปรด"
|
||||||
|
},
|
||||||
|
"favorites": {
|
||||||
|
"filter_match": "การแข่งขัน",
|
||||||
|
"filter_team": "ทีม",
|
||||||
|
"filter_player": "ผู้เล่น",
|
||||||
|
"filter_league": "ลีก",
|
||||||
|
"no_data": "ไม่พบรายการโปรด",
|
||||||
|
"unknown": "ไม่ทราบ"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "การตั้งค่า",
|
||||||
|
"back": "กลับ",
|
||||||
|
"theme": "ธีม",
|
||||||
|
"language": "ภาษา",
|
||||||
|
"light": "สว่าง",
|
||||||
|
"dark": "มืด",
|
||||||
|
"system": "ตามระบบ",
|
||||||
|
"english": "ภาษาอังกฤษ",
|
||||||
|
"chinese": "ภาษาจีน",
|
||||||
|
"odds_title": "การตั้งค่าอัตราต่อรอง",
|
||||||
|
"odds_show": "แสดงอัตราต่อรอง",
|
||||||
|
"odds_select_company": "เลือกบริษัทรับพนัน (สูงสุด 2 แห่ง)",
|
||||||
|
"odds_enabled": "เปิด",
|
||||||
|
"odds_disabled": "ปิด",
|
||||||
|
"odds_unselected": "ยังไม่เลือก",
|
||||||
|
"odds_modal_title": "เลือกบริษัทรับพนัน (สูงสุด 2 แห่ง)",
|
||||||
|
"odds_confirm": "ยืนยัน",
|
||||||
|
"cards_title": "การตั้งค่าบัตร",
|
||||||
|
"cards_show": "แสดงบัตร",
|
||||||
|
"cards_enabled": "เปิด",
|
||||||
|
"cards_disabled": "ปิด",
|
||||||
|
"cards_unselected": "ยังไม่เลือก",
|
||||||
|
"cards_modal_title": "การตั้งค่าบัตร",
|
||||||
|
"cards_confirm": "ยืนยัน",
|
||||||
|
"login": "เข้าสู่ระบบ",
|
||||||
|
"click_to_login": "คลิกเพื่อเข้าสู่ระบบ",
|
||||||
|
"logout": "ออกจากระบบ",
|
||||||
|
"select_login_method": "เลือกวิธีเข้าสู่ระบบ",
|
||||||
|
"cancel": "ยกเลิก",
|
||||||
|
"google_login": "เข้าสู่ระบบด้วย Google"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"title": "ScoreNow",
|
||||||
|
"time": "เวลา",
|
||||||
|
"league": "ลีก",
|
||||||
|
"select_sport": "กีฬา",
|
||||||
|
"select_league": "เลือกลีก",
|
||||||
|
"all_leagues": "ทุกลีก",
|
||||||
|
"loading": "กำลังโหลด...",
|
||||||
|
"no_matches": "ไม่พบการแข่งขัน",
|
||||||
|
"no_leagues": "ไม่มีลีกให้เลือก"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"title": "โปรไฟล์ของฉัน",
|
||||||
|
"name": "ชื่อผู้ใช้",
|
||||||
|
"settings": "การตั้งค่า",
|
||||||
|
"privacy": "นโยบายความเป็นส่วนตัว"
|
||||||
|
},
|
||||||
|
"privacy": {
|
||||||
|
"title": "นโยบายความเป็นส่วนตัว",
|
||||||
|
"page_title": "นโยบายความเป็นส่วนตัวของ ScoreNow",
|
||||||
|
"section1_title": "1. การเก็บรวบรวมข้อมูล",
|
||||||
|
"section1_text": "ScoreNow จะเก็บข้อมูล เช่น ข้อมูลบัญชีที่ให้ไว้ระหว่างการสมัคร ความสนใจเกี่ยวกับทีมและลีกที่คุณติดตาม รวมถึงตัวระบุอุปกรณ์และบันทึกเครือข่ายเมื่อใช้ฟีเจอร์คะแนนสด",
|
||||||
|
"section2_title": "2. การใช้ข้อมูล",
|
||||||
|
"section2_text": "เราใช้ข้อมูลเหล่านี้เพื่อส่งการแจ้งเตือนการแข่งขันที่คุณสนใจ ปรับลำดับการแสดงข่าวสาร และรับรองการแสดงผลข้อมูลการแข่งขันแบบเรียลไทม์อย่างถูกต้อง",
|
||||||
|
"section3_title": "3. บริการจากบุคคลที่สาม",
|
||||||
|
"section3_text": "เพื่อให้ข้อมูลการแข่งขันและการวิเคราะห์มีความแม่นยำ เราอาจร่วมมือกับผู้ให้บริการข้อมูลจากบุคคลที่สาม โดยจะไม่เปิดเผยข้อมูลระบุตัวตนส่วนบุคคลของคุณ มีเพียงข้อมูลเชิงสถิติที่ไม่สามารถระบุตัวตนได้เท่านั้น",
|
||||||
|
"section4_title": "4. การขออนุญาต",
|
||||||
|
"section4_text": "เราขอสิทธิ์การแจ้งเตือนเพื่อส่งการแจ้งเตือนประตู และขอสิทธิ์การจัดเก็บเพื่อแคชรูปภาพและเนื้อหาการแข่งขันเพื่อลดการใช้ดาต้า",
|
||||||
|
"section5_title": "5. ความปลอดภัยของข้อมูล",
|
||||||
|
"section5_text": "ScoreNow ใช้เทคโนโลยีการเข้ารหัสตามมาตรฐานอุตสาหกรรมเพื่อปกป้องข้อมูลของคุณ ป้องกันการเข้าถึงโดยไม่ได้รับอนุญาต",
|
||||||
|
"section6_title": "6. สิทธิของคุณ",
|
||||||
|
"section6_text": "คุณสามารถจัดการการแจ้งเตือน ล้างประวัติการค้นหา หรือ ลบบัญชีของคุณได้ตลอดเวลาในหน้าการตั้งค่า",
|
||||||
|
"last_updated": "อัปเดตล่าสุด: มกราคม 2026"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"title": "รายละเอียดการแข่งขัน",
|
||||||
|
"pending": "กำลังดำเนินการ",
|
||||||
|
"retry": "ลองอีกครั้ง",
|
||||||
|
"fetch_failed": "ไม่สามารถดึงข้อมูลได้",
|
||||||
|
"not_found": "ไม่พบข้อมูลการแข่งขัน",
|
||||||
|
"tabs": {
|
||||||
|
"info": "รายละเอียด",
|
||||||
|
"stats": "สถิติ",
|
||||||
|
"odds": "อัตราต่อรอง",
|
||||||
|
"lineup": "รายชื่อผู้เล่น",
|
||||||
|
"analysis": "วิเคราะห์",
|
||||||
|
"h2h": "เฮดทูเฮด",
|
||||||
|
"chat": "แชท"
|
||||||
|
},
|
||||||
|
"info_card": {
|
||||||
|
"title": "ข้อมูลการแข่งขัน",
|
||||||
|
"country": "ประเทศ/ภูมิภาค",
|
||||||
|
"league": "ลีก",
|
||||||
|
"stage": "รอบการแข่งขัน",
|
||||||
|
"stadium": "สนาม",
|
||||||
|
"referee": "ผู้ตัดสิน",
|
||||||
|
"date": "วันที่และเวลา"
|
||||||
|
},
|
||||||
|
"score_table": {
|
||||||
|
"team": "ทีม",
|
||||||
|
"total": "เต็มเวลา",
|
||||||
|
"halftime": "ครึ่งแรก",
|
||||||
|
"full_time": "90'",
|
||||||
|
"extra_time": "ต่อเวลา",
|
||||||
|
"penalty": "จุดโทษ"
|
||||||
|
},
|
||||||
|
"halftime": "ครึ่งแรก: {{score}}",
|
||||||
|
"empty_stats": "ไม่มีข้อมูลสถิติ",
|
||||||
|
"empty_odds": "ไม่มีข้อมูลอัตราต่อรอง",
|
||||||
|
"empty_h2h": "ไม่มีข้อมูลการพบกัน",
|
||||||
|
"empty_chat": "ไม่สามารถใช้งานแชทได้",
|
||||||
|
"h2h": {
|
||||||
|
"h2h": "สถิติการพบกัน",
|
||||||
|
"loading": "กำลังโหลด...",
|
||||||
|
"error": "โหลดไม่สำเร็จ",
|
||||||
|
"retry": "ลองใหม่",
|
||||||
|
"no_data": "ไม่มีข้อมูล"
|
||||||
|
},
|
||||||
|
"odds_card": {
|
||||||
|
"title": "อัตราต่อรองการแข่งขัน",
|
||||||
|
"disclaimer": ""
|
||||||
|
},
|
||||||
|
"stats_card": {
|
||||||
|
"title": "สถิติ",
|
||||||
|
"possession": "การครองบอล",
|
||||||
|
"info_title": "คำอธิบายโมเมนตัม",
|
||||||
|
"info_desc": "กราฟโมเมนตัมแสดงแรงกดดันในการบุกและระดับความอันตรายของทั้งสองทีมในช่วงเวลาต่าง ๆ ของการแข่งขัน",
|
||||||
|
"point1": "คลื่นด้านบนแทนทีมเหย้า และคลื่นด้านล่างแทนทีมเยือน",
|
||||||
|
"point2": "ความสูงของคลื่นขึ้นอยู่กับความเข้มข้นในการบุก เวลาในการครองบอล และโอกาสยิงประตู",
|
||||||
|
"point3": "ยิ่งเส้นกราฟเคลื่อนไหวรุนแรง ความถี่ในการบุกยิ่งสูงขึ้น",
|
||||||
|
"close": "ปิด"
|
||||||
|
},
|
||||||
|
"other_info": {
|
||||||
|
"title": "ข้อมูลอื่น ๆ"
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"goals": "ประตู",
|
||||||
|
"cards": "ใบ",
|
||||||
|
"substitutes": "เปลี่ยนตัว",
|
||||||
|
"lineups": "รายชื่อผู้เล่น",
|
||||||
|
"red_card": "ใบแดง",
|
||||||
|
"yellow_card": "ใบเหลือง",
|
||||||
|
"start": "เริ่มการแข่งขัน",
|
||||||
|
"ht": "พักครึ่ง",
|
||||||
|
"goal": "ทำประตู",
|
||||||
|
"penalty_goal": "ประตูจากจุดโทษ",
|
||||||
|
"toggle_visibility": "แสดง/ซ่อนไทม์ไลน์",
|
||||||
|
"lineups_goalkeepers": "ผู้รักษาประตู",
|
||||||
|
"lineups_defenders": "กองหลัง",
|
||||||
|
"lineups_midfielders": "กองกลาง",
|
||||||
|
"lineups_forwards": "กองหน้า",
|
||||||
|
"lineups_coach": "โค้ช",
|
||||||
|
"lineups_subs": "ตัวสำรอง",
|
||||||
|
"lineups_missing": "ผู้เล่นที่ขาดหาย"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selection": {
|
||||||
|
"selected": "เลือกแล้ว",
|
||||||
|
"click_to_toggle": "แตะเพื่อสลับ"
|
||||||
|
},
|
||||||
|
"sports": {
|
||||||
|
"football": "ฟุตบอล",
|
||||||
|
"basketball": "บาสเกตบอล",
|
||||||
|
"tennis": "เทนนิส",
|
||||||
|
"cricket": "คริกเก็ต",
|
||||||
|
"baseball": "เบสบอล",
|
||||||
|
"badminton": "แบดมินตัน",
|
||||||
|
"snooker": "สนุกเกอร์",
|
||||||
|
"volleyball": "วอลเลย์บอล"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"title": "ค้นหา",
|
||||||
|
"subtitle": "ทีม · ผู้เล่น · ลีก",
|
||||||
|
"placeholder": "ค้นหาทีม ผู้เล่น หรือ ลีก",
|
||||||
|
"recent": "การค้นหาล่าสุด",
|
||||||
|
"clear": "ล้าง",
|
||||||
|
"results": "ผลลัพธ์",
|
||||||
|
"found": "พบ",
|
||||||
|
"tap_to_open": "แตะเพื่อเปิด",
|
||||||
|
"no_results": "ไม่พบผลลัพธ์",
|
||||||
|
"no_results_hint": "ลองคำค้นอื่นหรือเปลี่ยนหมวดหมู่",
|
||||||
|
"loading": "กำลังโหลด",
|
||||||
|
"error": "เกิดข้อผิดพลาด",
|
||||||
|
"error_hint": "การค้นหาล้มเหลว กรุณาลองใหม่",
|
||||||
|
"start_searching": "เริ่มค้นหา",
|
||||||
|
"start_searching_hint": "ป้อนคำค้นเพื่อค้นหาทีม ผู้เล่น หรือ ลีก",
|
||||||
|
"type": {
|
||||||
|
"all": "ทั้งหมด",
|
||||||
|
"team": "ทีม",
|
||||||
|
"player": "ผู้เล่น",
|
||||||
|
"league": "ลีก"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"country": "ประเทศ",
|
||||||
|
"league": "ลีก",
|
||||||
|
"season": "ฤดูกาล",
|
||||||
|
"team": "ทีม",
|
||||||
|
"position": "ตำแหน่ง",
|
||||||
|
"open": "เปิดรายละเอียด",
|
||||||
|
"save": "บันทึกการค้นหา"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
212
i18n/locales/vi.json
Normal file
212
i18n/locales/vi.json
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Chào mừng!",
|
||||||
|
"tabs": {
|
||||||
|
"all": "Tất cả",
|
||||||
|
"live": "Trực tiếp",
|
||||||
|
"upcoming": "Sắp diễn ra",
|
||||||
|
"finished": "Đã kết thúc",
|
||||||
|
"fav": "Yêu thích"
|
||||||
|
},
|
||||||
|
"favorites": {
|
||||||
|
"filter_match": "Trận đấu",
|
||||||
|
"filter_team": "Đội",
|
||||||
|
"filter_player": "Cầu thủ",
|
||||||
|
"filter_league": "Giải đấu",
|
||||||
|
"no_data": "Không có mục yêu thích",
|
||||||
|
"unknown": "Không rõ"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Cài đặt",
|
||||||
|
"back": "Quay lại",
|
||||||
|
"theme": "Giao diện",
|
||||||
|
"language": "Ngôn ngữ",
|
||||||
|
"light": "Sáng",
|
||||||
|
"dark": "Tối",
|
||||||
|
"system": "Theo hệ thống",
|
||||||
|
"english": "Tiếng Anh",
|
||||||
|
"chinese": "Tiếng Trung",
|
||||||
|
"odds_title": "Cài đặt tỷ lệ cược",
|
||||||
|
"odds_show": "Hiển thị tỷ lệ cược",
|
||||||
|
"odds_select_company": "Chọn nhà cái (tối đa 2)",
|
||||||
|
"odds_enabled": "Bật",
|
||||||
|
"odds_disabled": "Tắt",
|
||||||
|
"odds_unselected": "Chưa chọn",
|
||||||
|
"odds_modal_title": "Chọn nhà cái (tối đa 2)",
|
||||||
|
"odds_confirm": "Xác nhận",
|
||||||
|
"cards_title": "Cài đặt thẻ",
|
||||||
|
"cards_show": "Hiển thị thẻ",
|
||||||
|
"cards_enabled": "Bật",
|
||||||
|
"cards_disabled": "Tắt",
|
||||||
|
"cards_unselected": "Chưa chọn",
|
||||||
|
"cards_modal_title": "Cài đặt thẻ",
|
||||||
|
"cards_confirm": "Xác nhận",
|
||||||
|
"login": "Đăng nhập",
|
||||||
|
"click_to_login": "Nhấn để đăng nhập",
|
||||||
|
"logout": "Đăng xuất",
|
||||||
|
"select_login_method": "Chọn phương thức đăng nhập",
|
||||||
|
"cancel": "Hủy",
|
||||||
|
"google_login": "Đăng nhập bằng Google"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"title": "ScoreNow",
|
||||||
|
"time": "Thời gian",
|
||||||
|
"league": "Giải đấu",
|
||||||
|
"select_sport": "Môn thể thao",
|
||||||
|
"select_league": "Chọn giải",
|
||||||
|
"all_leagues": "Tất cả giải",
|
||||||
|
"loading": "Đang tải...",
|
||||||
|
"no_matches": "Không tìm thấy trận đấu",
|
||||||
|
"no_leagues": "Không có giải đấu"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"title": "Hồ sơ của tôi",
|
||||||
|
"name": "Tên người dùng",
|
||||||
|
"settings": "Cài đặt",
|
||||||
|
"privacy": "Chính sách bảo mật"
|
||||||
|
},
|
||||||
|
"privacy": {
|
||||||
|
"title": "Chính sách bảo mật",
|
||||||
|
"page_title": "Chính sách bảo mật ScoreNow",
|
||||||
|
"section1_title": "1. Thu thập thông tin",
|
||||||
|
"section1_text": "ScoreNow thu thập thông tin bao gồm dữ liệu tài khoản khi đăng ký, đội và giải đấu bạn theo dõi, cũng như thông tin thiết bị và nhật ký mạng khi sử dụng tính năng tỷ số trực tiếp.",
|
||||||
|
"section2_title": "2. Sử dụng thông tin",
|
||||||
|
"section2_text": "Chúng tôi sử dụng thông tin này để gửi nhắc nhở trận đấu bạn quan tâm, tối ưu thứ tự hiển thị tin tức và đảm bảo dữ liệu trận đấu được cập nhật chính xác theo thời gian thực.",
|
||||||
|
"section3_title": "3. Dịch vụ bên thứ ba",
|
||||||
|
"section3_text": "Để cung cấp dữ liệu và phân tích chính xác, chúng tôi có thể hợp tác với các nhà cung cấp dữ liệu bên thứ ba. Chúng tôi không chia sẻ thông tin cá nhân, chỉ sử dụng dữ liệu thống kê ẩn danh.",
|
||||||
|
"section4_title": "4. Quyền truy cập",
|
||||||
|
"section4_text": "Chúng tôi yêu cầu quyền thông báo để gửi cảnh báo bàn thắng và quyền lưu trữ để lưu hình ảnh, nội dung trận đấu nhằm tiết kiệm dữ liệu.",
|
||||||
|
"section5_title": "5. Bảo mật dữ liệu",
|
||||||
|
"section5_text": "ScoreNow sử dụng công nghệ mã hóa tiêu chuẩn ngành để bảo vệ dữ liệu của bạn khỏi truy cập trái phép.",
|
||||||
|
"section6_title": "6. Quyền của bạn",
|
||||||
|
"section6_text": "Bạn có thể quản lý thông báo, xóa lịch sử tìm kiếm hoặc xóa tài khoản bất cứ lúc nào trong phần cài đặt.",
|
||||||
|
"last_updated": "Cập nhật lần cuối: Tháng 1 năm 2026"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"title": "Chi tiết trận đấu",
|
||||||
|
"pending": "Đang chờ",
|
||||||
|
"retry": "Thử lại",
|
||||||
|
"fetch_failed": "Không thể tải dữ liệu",
|
||||||
|
"not_found": "Không tìm thấy dữ liệu trận đấu",
|
||||||
|
"tabs": {
|
||||||
|
"info": "Thông tin",
|
||||||
|
"stats": "Thống kê",
|
||||||
|
"odds": "Tỷ lệ cược",
|
||||||
|
"lineup": "Đội hình",
|
||||||
|
"analysis": "Phân tích",
|
||||||
|
"h2h": "Đối đầu",
|
||||||
|
"chat": "Trò chuyện"
|
||||||
|
},
|
||||||
|
"info_card": {
|
||||||
|
"title": "Thông tin trận đấu",
|
||||||
|
"country": "Quốc gia/Khu vực",
|
||||||
|
"league": "Giải đấu",
|
||||||
|
"stage": "Vòng đấu",
|
||||||
|
"stadium": "Sân vận động",
|
||||||
|
"referee": "Trọng tài",
|
||||||
|
"date": "Ngày & Giờ"
|
||||||
|
},
|
||||||
|
"score_table": {
|
||||||
|
"team": "Đội",
|
||||||
|
"total": "Cả trận",
|
||||||
|
"halftime": "Hiệp 1",
|
||||||
|
"full_time": "90'",
|
||||||
|
"extra_time": "Hiệp phụ",
|
||||||
|
"penalty": "Luân lưu"
|
||||||
|
},
|
||||||
|
"halftime": "Hiệp 1: {{score}}",
|
||||||
|
"empty_stats": "Không có dữ liệu thống kê",
|
||||||
|
"empty_odds": "Không có dữ liệu tỷ lệ cược",
|
||||||
|
"empty_h2h": "Không có dữ liệu đối đầu",
|
||||||
|
"empty_chat": "Không khả dụng trò chuyện",
|
||||||
|
"h2h": {
|
||||||
|
"h2h": "Đối đầu",
|
||||||
|
"loading": "Đang tải...",
|
||||||
|
"error": "Tải thất bại",
|
||||||
|
"retry": "Thử lại",
|
||||||
|
"no_data": "Không có dữ liệu"
|
||||||
|
},
|
||||||
|
"odds_card": {
|
||||||
|
"title": "Tỷ lệ cược trận đấu",
|
||||||
|
"disclaimer": ""
|
||||||
|
},
|
||||||
|
"stats_card": {
|
||||||
|
"title": "Thống kê",
|
||||||
|
"possession": "Kiểm soát bóng",
|
||||||
|
"info_title": "Giải thích động lượng",
|
||||||
|
"info_desc": "Biểu đồ động lượng thể hiện áp lực tấn công và mức độ nguy hiểm của hai đội trong các giai đoạn khác nhau của trận đấu.",
|
||||||
|
"point1": "Sóng phía trên đại diện cho đội chủ nhà, sóng phía dưới đại diện cho đội khách.",
|
||||||
|
"point2": "Độ cao của sóng phụ thuộc vào cường độ tấn công, thời gian kiểm soát bóng và mức độ nguy hiểm của cú sút.",
|
||||||
|
"point3": "Đường biểu đồ dao động càng mạnh thì tần suất tấn công trong giai đoạn đó càng cao.",
|
||||||
|
"close": "Đóng"
|
||||||
|
},
|
||||||
|
"other_info": {
|
||||||
|
"title": "Thông tin khác"
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"goals": "Bàn thắng",
|
||||||
|
"cards": "Thẻ",
|
||||||
|
"substitutes": "Thay người",
|
||||||
|
"lineups": "Đội hình",
|
||||||
|
"red_card": "Thẻ đỏ",
|
||||||
|
"yellow_card": "Thẻ vàng",
|
||||||
|
"start": "Bắt đầu trận đấu",
|
||||||
|
"ht": "HT",
|
||||||
|
"goal": "Bàn thắng",
|
||||||
|
"penalty_goal": "Bàn thắng luân lưu",
|
||||||
|
"toggle_visibility": "Ẩn/hiện dòng thời gian",
|
||||||
|
"lineups_goalkeepers": "Thủ môn",
|
||||||
|
"lineups_defenders": "Hậu vệ",
|
||||||
|
"lineups_midfielders": "Tiền vệ",
|
||||||
|
"lineups_forwards": "Tiền đạo",
|
||||||
|
"lineups_coach": "Huấn luyện viên",
|
||||||
|
"lineups_subs": "Dự bị",
|
||||||
|
"lineups_missing": "Cầu thủ vắng mặt"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selection": {
|
||||||
|
"selected": "Đã chọn",
|
||||||
|
"click_to_toggle": "Nhấn để chuyển"
|
||||||
|
},
|
||||||
|
"sports": {
|
||||||
|
"football": "Bóng đá",
|
||||||
|
"basketball": "Bóng rổ",
|
||||||
|
"tennis": "Quần vợt",
|
||||||
|
"cricket": "Cricket",
|
||||||
|
"baseball": "Bóng chày",
|
||||||
|
"badminton": "Cầu lông",
|
||||||
|
"snooker": "Snooker",
|
||||||
|
"volleyball": "Bóng chuyền"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"title": "Tìm kiếm",
|
||||||
|
"subtitle": "Đội · Cầu thủ · Giải đấu",
|
||||||
|
"placeholder": "Tìm đội, cầu thủ, giải đấu",
|
||||||
|
"recent": "Tìm kiếm gần đây",
|
||||||
|
"clear": "Xóa",
|
||||||
|
"results": "Kết quả",
|
||||||
|
"found": "tìm thấy",
|
||||||
|
"tap_to_open": "Nhấn để mở",
|
||||||
|
"no_results": "Không có kết quả",
|
||||||
|
"no_results_hint": "Thử từ khóa khác hoặc đổi danh mục",
|
||||||
|
"loading": "Đang tải",
|
||||||
|
"error": "Lỗi",
|
||||||
|
"error_hint": "Tìm kiếm thất bại, vui lòng thử lại",
|
||||||
|
"start_searching": "Bắt đầu tìm kiếm",
|
||||||
|
"start_searching_hint": "Nhập từ khóa để tìm đội, cầu thủ hoặc giải đấu",
|
||||||
|
"type": {
|
||||||
|
"all": "Tất cả",
|
||||||
|
"team": "Đội",
|
||||||
|
"player": "Cầu thủ",
|
||||||
|
"league": "Giải đấu"
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"country": "Quốc gia",
|
||||||
|
"league": "Giải đấu",
|
||||||
|
"season": "Mùa giải",
|
||||||
|
"team": "Đội",
|
||||||
|
"position": "Vị trí",
|
||||||
|
"open": "Mở chi tiết",
|
||||||
|
"save": "Lưu vào gần đây"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "设置",
|
"title": "设置",
|
||||||
|
"back": "返回",
|
||||||
"theme": "主题",
|
"theme": "主题",
|
||||||
"language": "语言",
|
"language": "语言",
|
||||||
"light": "浅色",
|
"light": "浅色",
|
||||||
@@ -40,6 +41,9 @@
|
|||||||
"corner_show": "显示角球",
|
"corner_show": "显示角球",
|
||||||
"corner_enabled": "开启",
|
"corner_enabled": "开启",
|
||||||
"corner_disabled": "关闭",
|
"corner_disabled": "关闭",
|
||||||
|
"cards_unselected": "未选择",
|
||||||
|
"cards_modal_title": "卡牌设置",
|
||||||
|
"cards_confirm": "确定",
|
||||||
"login": "登录",
|
"login": "登录",
|
||||||
"click_to_login": "点击登录",
|
"click_to_login": "点击登录",
|
||||||
"logout": "登出",
|
"logout": "登出",
|
||||||
|
|||||||
Reference in New Issue
Block a user