搜索页面

This commit is contained in:
xianyi
2026-01-15 15:03:31 +08:00
parent e60b190dff
commit cff077c5f7
3 changed files with 371 additions and 260 deletions

View File

@@ -1,13 +1,19 @@
import { ThemedText } from "@/components/themed-text"; import { ThemedText } from "@/components/themed-text";
import { ThemedView } from "@/components/themed-view"; import { ThemedView } from "@/components/themed-view";
import { IconSymbol } from "@/components/ui/icon-symbol"; import { IconSymbol } from "@/components/ui/icon-symbol";
import { Colors } from "@/constants/theme";
import { useAppState } from "@/context/AppStateContext";
import { useTheme } from "@/context/ThemeContext"; import { useTheme } from "@/context/ThemeContext";
import { fetchSearch } from "@/lib/api";
import { SearchLeague, SearchPlayer, SearchTeam } from "@/types/api";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { BlurView } from "expo-blur"; import { BlurView } from "expo-blur";
import { LinearGradient } from "expo-linear-gradient"; import { LinearGradient } from "expo-linear-gradient";
import { Stack, useRouter } from "expo-router"; import { Stack, useRouter } from "expo-router";
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
ActivityIndicator,
Animated, Animated,
FlatList, FlatList,
ScrollView, ScrollView,
@@ -18,7 +24,7 @@ import {
} from "react-native"; } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
type SearchType = "team" | "player" | "league"; type SearchType = "all" | "team" | "player" | "league";
interface SearchEntity { interface SearchEntity {
id: string; id: string;
@@ -33,22 +39,44 @@ interface SearchEntity {
}; };
} }
// 模拟数据 // 将 API 数据转换为 SearchEntity
const ENTITIES: SearchEntity[] = [ function convertLeagueToEntity(league: SearchLeague): SearchEntity {
{ id: "t_rm", type: "team", name: "Real Madrid", meta: { country: "Spain", league: "LaLiga", season: "2025/26" } }, return {
{ id: "t_fcb", type: "team", name: "FC Barcelona", meta: { country: "Spain", league: "LaLiga", season: "2025/26" } }, id: `league_${league.ID}`,
{ id: "t_mci", type: "team", name: "Manchester City", meta: { country: "England", league: "Premier League", season: "2025/26" } }, type: "league",
{ id: "t_liv", type: "team", name: "Liverpool", meta: { country: "England", league: "Premier League", season: "2025/26" } }, name: league.name,
{ id: "t_ars", type: "team", name: "Arsenal", meta: { country: "England", league: "Premier League", season: "2025/26" } }, meta: {
{ id: "p_mbappe", type: "player", name: "Kylian Mbappé", meta: { country: "France", team: "Real Madrid", league: "LaLiga", pos: "FW", season: "2025/26" } }, country: league.countryName,
{ id: "p_haaland", type: "player", name: "Erling Haaland", meta: { country: "Norway", team: "Manchester City", league: "Premier League", pos: "FW", season: "2025/26" } }, season: "", // API 中没有 season 字段
{ id: "p_salah", type: "player", name: "Mohamed Salah", meta: { country: "Egypt", team: "Liverpool", league: "Premier League", pos: "FW", season: "2025/26" } }, },
{ id: "p_curry", type: "player", name: "Stephen Curry", meta: { country: "USA", team: "Golden State Warriors", league: "NBA", pos: "G", season: "2025/26" } }, };
{ id: "l_epl", type: "league", name: "Premier League", meta: { country: "England", season: "2025/26" } }, }
{ id: "l_laliga", type: "league", name: "LaLiga", meta: { country: "Spain", season: "2025/26" } },
{ id: "l_ucl", type: "league", name: "UEFA Champions League", meta: { country: "Europe", season: "2025/26" } }, function convertPlayerToEntity(player: SearchPlayer): SearchEntity {
{ id: "l_nba", type: "league", name: "NBA", meta: { country: "USA", season: "2025/26" } }, return {
]; id: `player_${player.ID}`,
type: "player",
name: player.name,
meta: {
country: player.countryName,
team: player.teamName,
league: player.leagueName,
pos: player.position,
},
};
}
function convertTeamToEntity(team: SearchTeam): SearchEntity {
return {
id: `team_${team.ID}`,
type: "team",
name: team.name,
meta: {
country: team.countryName,
league: team.leagueName,
},
};
}
// 生成首字母 // 生成首字母
function getInitials(name: string): string { function getInitials(name: string): string {
@@ -113,33 +141,66 @@ function getLogoGradient(name: string): { color1: string; color2: string } {
// 获取副标题文本 // 获取副标题文本
function getSubText(entity: SearchEntity): string { function getSubText(entity: SearchEntity): string {
const { meta } = entity; const { meta } = entity;
const parts: string[] = [];
if (entity.type === "league") { if (entity.type === "league") {
return `${meta.country || ""} · ${meta.season || ""}`; // 联赛:显示国家
if (meta.country) parts.push(meta.country);
} else if (entity.type === "player") {
// 球员:优先显示球队和位置,如果没有位置则显示联赛
if (meta.team) parts.push(meta.team);
if (meta.pos) {
parts.push(meta.pos);
} else if (meta.league) {
parts.push(meta.league);
}
} else if (entity.type === "team") {
// 球队:显示国家和联赛
if (meta.country) parts.push(meta.country);
if (meta.league) parts.push(meta.league);
} }
if (entity.type === "player") {
return `${meta.team || ""} · ${meta.league || ""}`; return parts.filter(Boolean).join(" · ") || "";
}
return `${meta.country || ""} · ${meta.league || ""}`;
} }
const RECENT_SEARCHES_KEY = "recent_searches";
export default function SearchScreen() { export default function SearchScreen() {
const router = useRouter(); const router = useRouter();
const { theme } = useTheme(); const { theme } = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const isDark = theme === "dark"; const isDark = theme === "dark";
const { state } = useAppState();
const [searchType, setSearchType] = useState<SearchType>("team"); const [searchType, setSearchType] = useState<SearchType>("all");
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const [recentSearches, setRecentSearches] = useState<string[]>([ const [recentSearches, setRecentSearches] = useState<string[]>([]);
"Real Madrid",
"Premier League",
"Curry",
]);
const [showDetailSheet, setShowDetailSheet] = useState(false); const [showDetailSheet, setShowDetailSheet] = useState(false);
const [selectedEntity, setSelectedEntity] = useState<SearchEntity | null>(null); const [selectedEntity, setSelectedEntity] = useState<SearchEntity | null>(null);
const [entities, setEntities] = useState<SearchEntity[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const sheetAnim = useRef(new Animated.Value(0)).current; const sheetAnim = useRef(new Animated.Value(0)).current;
const searchTimeoutRef = useRef<number | null>(null);
useEffect(() => {
const loadRecentSearches = async () => {
try {
const stored = await AsyncStorage.getItem(RECENT_SEARCHES_KEY);
if (stored) {
const parsed = JSON.parse(stored);
if (Array.isArray(parsed)) {
setRecentSearches(parsed);
}
}
} catch (err) {
console.error("Failed to load recent searches:", err);
}
};
loadRecentSearches();
}, []);
useEffect(() => { useEffect(() => {
if (showDetailSheet) { if (showDetailSheet) {
@@ -156,18 +217,70 @@ export default function SearchScreen() {
} }
}, [showDetailSheet]); }, [showDetailSheet]);
const filteredEntities = ENTITIES.filter((e) => { // 搜索 API 调用(带防抖)
useEffect(() => {
if (searchTimeoutRef.current) {
clearTimeout(searchTimeoutRef.current);
}
if (!query.trim()) {
setEntities([]);
setError(null);
return;
}
searchTimeoutRef.current = setTimeout(async () => {
try {
setLoading(true);
setError(null);
const result = await fetchSearch(
query.trim(),
state.selectedSportId || undefined
);
const allEntities: SearchEntity[] = [
...result.leagues.map(convertLeagueToEntity),
...result.players.map(convertPlayerToEntity),
...result.teams.map(convertTeamToEntity),
];
setEntities(allEntities);
if (allEntities.length > 0) {
const searchQuery = query.trim();
setRecentSearches((prev) => {
const filtered = prev.filter((x) => x !== searchQuery);
const newRecent = [searchQuery, ...filtered].slice(0, 8);
AsyncStorage.setItem(RECENT_SEARCHES_KEY, JSON.stringify(newRecent)).catch(
(err) => console.error("Failed to save recent searches:", err)
);
return newRecent;
});
}
} catch (err: any) {
console.error("Search error:", err);
setError(err?.message || t("search.error"));
setEntities([]);
} finally {
setLoading(false);
}
}, 500); // 500ms 防抖
return () => {
if (searchTimeoutRef.current) {
clearTimeout(searchTimeoutRef.current);
}
};
}, [query, state.selectedSportId]);
const filteredEntities = entities.filter((e) => {
if (searchType === "all") return true;
if (e.type !== searchType) return false; if (e.type !== searchType) return false;
if (!query.trim()) return true; return true;
const q = query.toLowerCase();
return (
e.name.toLowerCase().includes(q) || getSubText(e).toLowerCase().includes(q)
);
}); });
const handleSearchTypeChange = (type: SearchType) => { const handleSearchTypeChange = (type: SearchType) => {
setSearchType(type); setSearchType(type);
setQuery(""); // 不再清空输入框,保持搜索结果
}; };
const handleRecentClick = (text: string) => { const handleRecentClick = (text: string) => {
@@ -181,6 +294,9 @@ export default function SearchScreen() {
const handleClearRecent = () => { const handleClearRecent = () => {
setRecentSearches([]); setRecentSearches([]);
AsyncStorage.removeItem(RECENT_SEARCHES_KEY).catch(
(err) => console.error("Failed to clear recent searches:", err)
);
}; };
const handleSaveToRecent = () => { const handleSaveToRecent = () => {
@@ -188,8 +304,11 @@ export default function SearchScreen() {
const newRecent = [ const newRecent = [
selectedEntity.name, selectedEntity.name,
...recentSearches.filter((x) => x !== selectedEntity.name), ...recentSearches.filter((x) => x !== selectedEntity.name),
].slice(0, 10); ].slice(0, 8);
setRecentSearches(newRecent); setRecentSearches(newRecent);
AsyncStorage.setItem(RECENT_SEARCHES_KEY, JSON.stringify(newRecent)).catch(
(err) => console.error("Failed to save recent searches:", err)
);
} }
}; };
@@ -242,7 +361,7 @@ export default function SearchScreen() {
style={styles.chips} style={styles.chips}
contentContainerStyle={styles.chipsContent} contentContainerStyle={styles.chipsContent}
> >
{(["team", "player", "league"] as SearchType[]).map((type) => ( {(["all", "team", "player", "league"] as SearchType[]).map((type) => (
<TouchableOpacity <TouchableOpacity
key={type} key={type}
style={[ style={[
@@ -473,194 +592,168 @@ export default function SearchScreen() {
<View style={styles.kvContainer}> <View style={styles.kvContainer}>
{selectedEntity.type === "team" && ( {selectedEntity.type === "team" && (
<> <>
<View {selectedEntity.meta.country && (
style={[ <View
styles.kvItem, style={[
{ styles.kvItem,
backgroundColor: isDark {
? "rgba(255, 255, 255, 0.05)" backgroundColor: isDark
: "rgba(0, 0, 0, 0.04)", ? "rgba(255, 255, 255, 0.05)"
borderColor: isDark : "rgba(0, 0, 0, 0.04)",
? "rgba(255, 255, 255, 0.10)" borderColor: isDark
: "rgba(0, 0, 0, 0.08)", ? "rgba(255, 255, 255, 0.10)"
}, : "rgba(0, 0, 0, 0.08)",
]} },
> ]}
<ThemedText style={styles.kvLabel}> >
{t("search.detail.country")} <ThemedText style={styles.kvLabel}>
</ThemedText> {t("search.detail.country")}
<ThemedText style={styles.kvValue} numberOfLines={1}> </ThemedText>
{selectedEntity.meta.country || "-"} <ThemedText style={styles.kvValue} numberOfLines={1}>
</ThemedText> {selectedEntity.meta.country}
</View> </ThemedText>
<View </View>
style={[ )}
styles.kvItem, {selectedEntity.meta.league && (
{ <View
backgroundColor: isDark style={[
? "rgba(255, 255, 255, 0.05)" styles.kvItem,
: "rgba(0, 0, 0, 0.04)", {
borderColor: isDark backgroundColor: isDark
? "rgba(255, 255, 255, 0.10)" ? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.08)", : "rgba(0, 0, 0, 0.04)",
}, borderColor: isDark
]} ? "rgba(255, 255, 255, 0.10)"
> : "rgba(0, 0, 0, 0.08)",
<ThemedText style={styles.kvLabel}> },
{t("search.detail.league")} ]}
</ThemedText> >
<ThemedText style={styles.kvValue} numberOfLines={1}> <ThemedText style={styles.kvLabel}>
{selectedEntity.meta.league || "-"} {t("search.detail.league")}
</ThemedText> </ThemedText>
</View> <ThemedText style={styles.kvValue} numberOfLines={1}>
<View {selectedEntity.meta.league}
style={[ </ThemedText>
styles.kvItem, </View>
{ )}
backgroundColor: isDark
? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.04)",
borderColor: isDark
? "rgba(255, 255, 255, 0.10)"
: "rgba(0, 0, 0, 0.08)",
},
]}
>
<ThemedText style={styles.kvLabel}>
{t("search.detail.season")}
</ThemedText>
<ThemedText style={styles.kvValue} numberOfLines={1}>
{selectedEntity.meta.season || "-"}
</ThemedText>
</View>
</> </>
)} )}
{selectedEntity.type === "player" && ( {selectedEntity.type === "player" && (
<> <>
<View {selectedEntity.meta.team && (
style={[ <View
styles.kvItem, style={[
{ styles.kvItem,
backgroundColor: isDark {
? "rgba(255, 255, 255, 0.05)" backgroundColor: isDark
: "rgba(0, 0, 0, 0.04)", ? "rgba(255, 255, 255, 0.05)"
borderColor: isDark : "rgba(0, 0, 0, 0.04)",
? "rgba(255, 255, 255, 0.10)" borderColor: isDark
: "rgba(0, 0, 0, 0.08)", ? "rgba(255, 255, 255, 0.10)"
}, : "rgba(0, 0, 0, 0.08)",
]} },
> ]}
<ThemedText style={styles.kvLabel}> >
{t("search.detail.team")} <ThemedText style={styles.kvLabel}>
</ThemedText> {t("search.detail.team")}
<ThemedText style={styles.kvValue} numberOfLines={1}> </ThemedText>
{selectedEntity.meta.team || "-"} <ThemedText style={styles.kvValue} numberOfLines={1}>
</ThemedText> {selectedEntity.meta.team}
</View> </ThemedText>
<View </View>
style={[ )}
styles.kvItem, {selectedEntity.meta.pos && (
{ <View
backgroundColor: isDark style={[
? "rgba(255, 255, 255, 0.05)" styles.kvItem,
: "rgba(0, 0, 0, 0.04)", {
borderColor: isDark backgroundColor: isDark
? "rgba(255, 255, 255, 0.10)" ? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.08)", : "rgba(0, 0, 0, 0.04)",
}, borderColor: isDark
]} ? "rgba(255, 255, 255, 0.10)"
> : "rgba(0, 0, 0, 0.08)",
<ThemedText style={styles.kvLabel}> },
{t("search.detail.league")} ]}
</ThemedText> >
<ThemedText style={styles.kvValue} numberOfLines={1}> <ThemedText style={styles.kvLabel}>
{selectedEntity.meta.league || "-"} {t("search.detail.position")}
</ThemedText> </ThemedText>
</View> <ThemedText style={styles.kvValue} numberOfLines={1}>
<View {selectedEntity.meta.pos}
style={[ </ThemedText>
styles.kvItem, </View>
{ )}
backgroundColor: isDark {selectedEntity.meta.league && (
? "rgba(255, 255, 255, 0.05)" <View
: "rgba(0, 0, 0, 0.04)", style={[
borderColor: isDark styles.kvItem,
? "rgba(255, 255, 255, 0.10)" {
: "rgba(0, 0, 0, 0.08)", backgroundColor: isDark
}, ? "rgba(255, 255, 255, 0.05)"
]} : "rgba(0, 0, 0, 0.04)",
> borderColor: isDark
<ThemedText style={styles.kvLabel}> ? "rgba(255, 255, 255, 0.10)"
{t("search.detail.position")} : "rgba(0, 0, 0, 0.08)",
</ThemedText> },
<ThemedText style={styles.kvValue} numberOfLines={1}> ]}
{selectedEntity.meta.pos || "-"} >
</ThemedText> <ThemedText style={styles.kvLabel}>
</View> {t("search.detail.league")}
<View </ThemedText>
style={[ <ThemedText style={styles.kvValue} numberOfLines={1}>
styles.kvItem, {selectedEntity.meta.league}
{ </ThemedText>
backgroundColor: isDark </View>
? "rgba(255, 255, 255, 0.05)" )}
: "rgba(0, 0, 0, 0.04)", {selectedEntity.meta.country && (
borderColor: isDark <View
? "rgba(255, 255, 255, 0.10)" style={[
: "rgba(0, 0, 0, 0.08)", styles.kvItem,
}, {
]} backgroundColor: isDark
> ? "rgba(255, 255, 255, 0.05)"
<ThemedText style={styles.kvLabel}> : "rgba(0, 0, 0, 0.04)",
{t("search.detail.country")} borderColor: isDark
</ThemedText> ? "rgba(255, 255, 255, 0.10)"
<ThemedText style={styles.kvValue} numberOfLines={1}> : "rgba(0, 0, 0, 0.08)",
{selectedEntity.meta.country || "-"} },
</ThemedText> ]}
</View> >
<ThemedText style={styles.kvLabel}>
{t("search.detail.country")}
</ThemedText>
<ThemedText style={styles.kvValue} numberOfLines={1}>
{selectedEntity.meta.country}
</ThemedText>
</View>
)}
</> </>
)} )}
{selectedEntity.type === "league" && ( {selectedEntity.type === "league" && (
<> <>
<View {selectedEntity.meta.country && (
style={[ <View
styles.kvItem, style={[
{ styles.kvItem,
backgroundColor: isDark {
? "rgba(255, 255, 255, 0.05)" backgroundColor: isDark
: "rgba(0, 0, 0, 0.04)", ? "rgba(255, 255, 255, 0.05)"
borderColor: isDark : "rgba(0, 0, 0, 0.04)",
? "rgba(255, 255, 255, 0.10)" borderColor: isDark
: "rgba(0, 0, 0, 0.08)", ? "rgba(255, 255, 255, 0.10)"
}, : "rgba(0, 0, 0, 0.08)",
]} },
> ]}
<ThemedText style={styles.kvLabel}> >
{t("search.detail.country")} <ThemedText style={styles.kvLabel}>
</ThemedText> {t("search.detail.country")}
<ThemedText style={styles.kvValue} numberOfLines={1}> </ThemedText>
{selectedEntity.meta.country || "-"} <ThemedText style={styles.kvValue} numberOfLines={1}>
</ThemedText> {selectedEntity.meta.country}
</View> </ThemedText>
<View </View>
style={[ )}
styles.kvItem,
{
backgroundColor: isDark
? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.04)",
borderColor: isDark
? "rgba(255, 255, 255, 0.10)"
: "rgba(0, 0, 0, 0.08)",
},
]}
>
<ThemedText style={styles.kvLabel}>
{t("search.detail.season")}
</ThemedText>
<ThemedText style={styles.kvValue} numberOfLines={1}>
{selectedEntity.meta.season || "-"}
</ThemedText>
</View>
</> </>
)} )}
</View> </View>
@@ -684,7 +777,7 @@ export default function SearchScreen() {
{t("search.detail.open")} {t("search.detail.open")}
</ThemedText> </ThemedText>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity {/* <TouchableOpacity
style={[ style={[
styles.sheetBtn, styles.sheetBtn,
styles.sheetBtnPrimary, styles.sheetBtnPrimary,
@@ -707,7 +800,7 @@ export default function SearchScreen() {
> >
{t("search.detail.save")} {t("search.detail.save")}
</ThemedText> </ThemedText>
</TouchableOpacity> </TouchableOpacity> */}
</View> </View>
</Animated.View> </Animated.View>
</> </>
@@ -762,26 +855,6 @@ export default function SearchScreen() {
{t("search.subtitle")} {t("search.subtitle")}
</ThemedText> </ThemedText>
</View> </View>
<TouchableOpacity
style={[
styles.iconBtn,
{
backgroundColor: isDark
? "rgba(255, 255, 255, 0.06)"
: "rgba(0, 0, 0, 0.06)",
borderColor: isDark
? "rgba(255, 255, 255, 0.10)"
: "rgba(0, 0, 0, 0.10)",
},
]}
onPress={handleClearRecent}
>
<IconSymbol
name="refresh"
size={18}
color={isDark ? "rgba(255,255,255,0.9)" : "rgba(0,0,0,0.9)"}
/>
</TouchableOpacity>
</BlurView> </BlurView>
<View style={styles.content}> <View style={styles.content}>
@@ -790,29 +863,49 @@ export default function SearchScreen() {
{renderChips()} {renderChips()}
{renderRecentSearches()} {renderRecentSearches()}
<View style={styles.sectionHeader}> {query.trim() && (
<ThemedText style={styles.sectionTitle}> <View style={styles.sectionHeader}>
{t("search.results")} · {t(`search.type.${searchType}`)} <ThemedText style={styles.sectionTitle}>
</ThemedText> {t("search.results")}
<ThemedText style={styles.sectionAction}> {searchType !== "all" && ` · ${t(`search.type.${searchType}`)}`}
{query.trim() </ThemedText>
? `${filteredEntities.length} ${t("search.found")}` <ThemedText style={styles.sectionAction}>
: t("search.tap_to_open")} {loading
</ThemedText> ? t("search.loading")
</View> : error
? t("search.error")
: `${filteredEntities.length} ${t("search.found")}`}
</ThemedText>
</View>
)}
</View> </View>
<FlatList {query.trim() ? (
data={filteredEntities} loading ? (
keyExtractor={(item) => item.id} <ThemedView style={styles.center}>
renderItem={renderEntityItem} <ActivityIndicator size="large" color={Colors[theme].tint} />
ListEmptyComponent={() => renderEmptyState()} </ThemedView>
contentContainerStyle={[ ) : error ? (
styles.listContent, <View style={styles.emptyState}>
{ paddingBottom: insets.bottom + 20 }, <ThemedText style={styles.emptyTitle}>{error}</ThemedText>
]} <ThemedText style={styles.emptySub}>
showsVerticalScrollIndicator={false} {t("search.error_hint")}
/> </ThemedText>
</View>
) : (
<FlatList
data={filteredEntities}
keyExtractor={(item) => item.id}
renderItem={renderEntityItem}
ListEmptyComponent={() => renderEmptyState()}
contentContainerStyle={[
styles.listContent,
{ paddingBottom: insets.bottom + 20 },
]}
showsVerticalScrollIndicator={false}
/>
)
) : null}
</View> </View>
{showDetailSheet && renderDetailSheet()} {showDetailSheet && renderDetailSheet()}
@@ -984,6 +1077,12 @@ const styles = StyleSheet.create({
opacity: 0.52, opacity: 0.52,
fontWeight: "900", fontWeight: "900",
}, },
center: {
flex: 1,
justifyContent: "center",
alignItems: "center",
padding: 20,
},
emptyState: { emptyState: {
padding: 14, padding: 14,
borderRadius: 20, borderRadius: 20,

View File

@@ -133,7 +133,13 @@
"tap_to_open": "Tap to open", "tap_to_open": "Tap to open",
"no_results": "No results", "no_results": "No results",
"no_results_hint": "Try another keyword or switch category", "no_results_hint": "Try another keyword or switch category",
"loading": "Loading",
"error": "Error",
"error_hint": "Search failed, please try again",
"start_searching": "Start searching",
"start_searching_hint": "Enter keywords to search for teams, players or leagues",
"type": { "type": {
"all": "All",
"team": "Team", "team": "Team",
"player": "Player", "player": "Player",
"league": "League" "league": "League"

View File

@@ -133,7 +133,13 @@
"tap_to_open": "点击打开", "tap_to_open": "点击打开",
"no_results": "无结果", "no_results": "无结果",
"no_results_hint": "尝试其他关键词或切换分类", "no_results_hint": "尝试其他关键词或切换分类",
"loading": "加载中",
"error": "错误",
"error_hint": "搜索失败,请重试",
"start_searching": "开始搜索",
"start_searching_hint": "输入关键词搜索球队、球员或联赛",
"type": { "type": {
"all": "全部",
"team": "球队", "team": "球队",
"player": "球员", "player": "球员",
"league": "联赛" "league": "联赛"