Commit 8c215ebd authored by BBeretta's avatar BBeretta

feat/language-translation (Filter, CardDetail)

parent f4da8169
......@@ -15,12 +15,14 @@ import {
import { CardEffectText, IconFont, ScrollableArea, YgoCard } from "@/ui/Shared";
import styles from "./CardDetail.module.scss";
import { useTranslation } from "react-i18next";
export const CardDetail: React.FC<{
code: number;
open: boolean;
onClose: () => void;
}> = ({ code, open, onClose }) => {
const { t: i18n } = useTranslation("CardDetails");
const [card, setCard] = useState<CardMeta>();
useEffect(() => {
setCard(fetchCard(code));
......@@ -44,20 +46,20 @@ export const CardDetail: React.FC<{
const result: DescriptionsProps["items"] = [];
if (card?.data.level) {
result.push({
label: "等级",
label: i18n("Level"),
children: card?.data.level,
});
}
result.push({
label: "类型",
label: i18n("Type"),
children: cardType,
span: 2,
});
if (card?.data.attribute) {
result.push({
label: "属性",
label: i18n("Attribute"),
children: fetchStrings(
Region.System,
Attribute2StringCodeMap.get(card?.data.attribute ?? 0) || 0,
......@@ -67,7 +69,7 @@ export const CardDetail: React.FC<{
if (card?.data.race) {
result.push({
label: "种族",
label: i18n("Race"),
children: fetchStrings(
Region.System,
Race2StringCodeMap.get(card?.data.race ?? 0) || 0,
......@@ -78,20 +80,20 @@ export const CardDetail: React.FC<{
if (isMonster(card?.data.type ?? 0)) {
result.push({
label: "攻击力",
label: i18n("Attack"),
children: card?.data.atk,
});
if (!isLinkMonster(card?.data.type ?? 0)) {
result.push({
label: "守备力",
label: i18n("Defence"),
children: card?.data.def,
});
}
if (card?.data.lscale) {
result.push({
label: "灵摆刻度",
label: i18n("PendulumScale"),
children: (
<>
{card.data.lscale} - {card.data.rscale}
......@@ -125,7 +127,7 @@ export const CardDetail: React.FC<{
size="small"
items={desc.filter(Boolean).map((d, i) => ({
label:
desc.length > 1 ? (i ? "怪兽效果" : "灵摆效果") : "卡片效果",
desc.length > 1 ? (i ? i18n("MonsterEffect") : i18n("PendulumEffect")) : i18n("CardEffect"),
span: 3,
children: <CardEffectText desc={d} />,
}))}
......
......@@ -17,6 +17,7 @@ import {
import { FtsConditions } from "@/middleware/sqlite/fts";
import styles from "./Filter.module.scss";
import { useTranslation } from "react-i18next";
const levels = Array.from({ length: 12 }, (_, index) => ({
value: index + 1,
......@@ -47,12 +48,15 @@ export const Filter: React.FC<{
value: key,
label: fetchStrings(Region.System, value),
}));
const { t: i18n } = useTranslation("Filter");
const T = [
[genOptions(Attribute2StringCodeMap), "属性", "attributes"],
[genOptions(Race2StringCodeMap), "种族", "races"],
[genOptions(Type2StringCodeMap), "类型", "types"],
[levels, "星级", "levels"],
[lscales, "灵摆刻度", "lscales"],
[genOptions(Attribute2StringCodeMap), i18n("Attribute"), "attributes"],
[genOptions(Race2StringCodeMap), i18n("Race"), "races"],
[genOptions(Type2StringCodeMap), i18n("Type"), "types"],
[levels, i18n("Level"), "levels"],
[lscales, i18n("PendulumScale"), "lscales"],
] as const;
const handleInputNumberChange =
......@@ -68,7 +72,7 @@ export const Filter: React.FC<{
return (
<>
<div className={styles.title}>卡片筛选</div>
<div className={styles.title}>{i18n("CardFilter")}</div>
<div className={styles.form}>
{T.map(([options, title, key]) => (
<Item title={title} key={key}>
......@@ -79,31 +83,31 @@ export const Filter: React.FC<{
/>
</Item>
))}
<Item title="攻击力" showTip>
<Item title={i18n("Attack")} showTip>
<div className={styles.number}>
<CustomInputNumber
placeholder="最小值"
placeholder={i18n("Minimum")}
onChange={handleInputNumberChange("atk", "min")}
value={newConditions.atk.min}
/>
<span className={styles.divider}>~</span>
<CustomInputNumber
placeholder="最大值"
placeholder={i18n("Maximum")}
onChange={handleInputNumberChange("atk", "max")}
value={newConditions.atk.max}
/>
</div>
</Item>
<Item title="守备力" showTip>
<Item title={i18n("Defense")} showTip>
<div className={styles.number}>
<CustomInputNumber
placeholder="最小值"
placeholder={i18n("Minimum")}
onChange={handleInputNumberChange("def", "min")}
value={newConditions.def.min}
/>
<span className={styles.divider}>~</span>
<CustomInputNumber
placeholder="最大值"
placeholder={i18n("Maximum")}
onChange={handleInputNumberChange("def", "max")}
value={newConditions.def.max}
/>
......@@ -117,10 +121,10 @@ export const Filter: React.FC<{
onConfirm(newConditions);
}}
>
确定
{i18n("Confirm")}
</Button>
<Button type="text" onClick={onCancel}>
&nbsp;
{i18n("Cancel")}
</Button>
</div>
</>
......@@ -151,12 +155,13 @@ const CustomSelect: React.FC<{
defaultValue: number[];
onChange: (values: number[]) => void;
}> = ({ options, defaultValue, onChange }) => {
const { t: i18n } = useTranslation("Filter");
return (
<Select
mode="multiple"
allowClear
style={{ width: "100%" }}
placeholder="请选择"
placeholder={i18n("Select")}
options={options}
defaultValue={defaultValue}
onChange={onChange}
......
......@@ -57,6 +57,7 @@ import {
editingDeckToIDeck,
iDeckToEditingDeck,
} from "./utils";
import { useTranslation } from "react-i18next";
export const loader: LoaderFunction = async () => {
// 必须先加载卡组,不然页面会崩溃
......@@ -240,11 +241,13 @@ export const DeckEditor: React.FC<{
event.preventDefault();
};
const { t: i18n } = useTranslation("BuildDeck");
return (
<div className={styles.container}>
<Space className={styles.title}>
<Input
placeholder="请输入卡组名字"
placeholder={i18n("EnterTheDeckName")}
bordered={false}
prefix={<EditOutlined />}
style={{ width: "8.8rem" }}
......@@ -258,7 +261,7 @@ export const DeckEditor: React.FC<{
icon={<SwapOutlined />}
onClick={onShuffle}
>
打乱
{i18n("Shuffle")}
</Button>
<Button
type="text"
......@@ -266,7 +269,7 @@ export const DeckEditor: React.FC<{
icon={<RetweetOutlined />}
onClick={onSort}
>
排序
{i18n("Sort")}
</Button>
<Button
type="text"
......@@ -274,7 +277,7 @@ export const DeckEditor: React.FC<{
icon={<DeleteOutlined />}
onClick={onClear}
>
清空
{i18n("Clear")}
</Button>
<Button
type="text"
......@@ -282,7 +285,7 @@ export const DeckEditor: React.FC<{
icon={<UndoOutlined />}
onClick={() => onReset()}
>
重置
{i18n("Reset")}
</Button>
<Button
type={snapEditDeck.edited ? "primary" : "text"}
......@@ -290,9 +293,9 @@ export const DeckEditor: React.FC<{
icon={<CheckOutlined />}
onClick={() => onSave()}
>
保存
{i18n("Save")}
</Button>
<Tooltip title="双击添加卡片,单击右键删除卡片,按下滑轮在主卡组和副卡组之间切换卡片">
<Tooltip title={i18n("QuestionCircleTooltip")}>
<QuestionCircleOutlined />
</Tooltip>
</Space>
......@@ -350,18 +353,20 @@ const Search: React.FC = () => {
);
};
const { t } = useTranslation("BuildDeck");
const dropdownOptions: MenuProps["items"] = (
[
["从新到旧", () => setSortRef((a, b) => b.id - a.id)],
["从旧到新", () => setSortRef((a, b) => a.id - b.id)],
["攻击力从高到低", genSort("atk", -1)],
["攻击力从低到高", genSort("atk")],
["守备力从高到低", genSort("def", -1)],
["守备力从低到高", genSort("def")],
["星/阶/刻/Link从高到低", genSort("level", -1)],
["星/阶/刻/Link从低到高", genSort("level")],
["灵摆刻度从高到低", genSort("lscale", -1)],
["灵摆刻度从低到高", genSort("lscale")],
[t("FromNewToOld"), () => setSortRef((a, b) => b.id - a.id)],
[t("FromOldToNew"), () => setSortRef((a, b) => a.id - b.id)],
[t("AttackPowerFromHighToLow"), genSort("atk", -1)],
[t("AttackPowerFromLowToHigh"), genSort("atk")],
[t("DefensePowerFromHighToLow"), genSort("def", -1)],
[t("DefensePowerFromLowToHigh"), genSort("def")],
[t("StarsRanksLevelsLinkFromHighToLow"), genSort("level", -1)],
[t("StarsRanksLevelsLinkFromLowToHigh"), genSort("level")],
[t("PendulumScaleFromHighToLow"), genSort("lscale", -1)],
[t("PendulumScaleFromLowToHigh"), genSort("lscale")],
] as const
).map(([label, onClick], key) => ({ key, label, onClick }));
......@@ -415,11 +420,13 @@ const Search: React.FC = () => {
if (viewport) viewport.scrollTop = 0;
}, []);
const { t: i18n } = useTranslation("BuildDeck");
return (
<div className={styles.container} ref={dropRef}>
<div className={styles.title}>
<Input
placeholder="关键词(空格分隔)"
placeholder={i18n("KeywordsPlaceholder")}
bordered={false}
suffix={
<Button
......@@ -445,7 +452,7 @@ const Search: React.FC = () => {
icon={<FilterOutlined />}
onClick={showFilterModal}
>
筛选
{i18n("Filter")}
</Button>
<Dropdown
menu={{ items: dropdownOptions }}
......@@ -459,7 +466,7 @@ const Search: React.FC = () => {
icon={<SortAscendingOutlined />}
>
<span>
排列
{i18n("SortBy")}
<span className={styles["search-count"]}>
({searchResult.length})
</span>
......@@ -477,7 +484,7 @@ const Search: React.FC = () => {
handleSearch(emptySearchConditions);
}}
>
重置
{i18n("Reset")}
</Button>
</div>
<ScrollableArea className={styles["search-cards-container"]} ref={ref}>
......
......@@ -24,8 +24,8 @@ export const I18NSelector: React.FC = () => {
{ value: "en", label: "English" },
{ value: "fr", label: "Français" },
{ value: "jp", label: "日本語" },
{ value: "br", label: "português do Brasil" },
{ value: "pt", label: "português" },
{ value: "br", label: "Português do Brasil" },
{ value: "pt", label: "Português" },
{ value: "es", label: "Castellano" },
]}
/>
......
......@@ -36,5 +36,58 @@
"ReplayDesc": "自由查看进行过的决斗,回味那些精彩的逆转瞬间。",
"WIPTitle": "开发中...",
"WIPDesc": "其他功能敬请期待。"
},
"BuildDeck": {
"EnterTheDeckName": "请输入卡组名字",
"Shuffle": "打乱",
"Sort": "排序",
"Clear": "清空",
"Reset": "重置",
"Save": "保存",
"QuestionCircleTooltip": "双击添加卡片,单击右键删除卡片,按下滑轮在主卡组和副卡组之间切换卡片",
"Filter": "筛选",
"SortBy": "排列 ",
"KeywordsPlaceholder": "关键词(空格分隔)",
"FromNewToOld": "从新到旧",
"FromOldToNew": "从旧到新",
"AttackPowerFromHighToLow": "攻击力从高到低",
"AttackPowerFromLowToHigh": "攻击力从低到高",
"DefensePowerFromHighToLow": "守备力从高到低",
"DefensePowerFromLowToHigh": "守备力从低到高",
"StarsRanksLevelsLinkFromHighToLow": "星/阶/刻/Link从高到低",
"StarsRanksLevelsLinkFromLowToHigh": "星/阶/刻/Link从低到高",
"PendulumScaleFromHighToLow": "灵摆刻度从高到低",
"PendulumScaleFromLowToHigh": "灵摆刻度从低到高"
},
"Filter": {
"CardFilter": "卡片筛选",
"Attribute": "属性",
"Race": "种族",
"Type": "类型",
"Level": "星级",
"PendulumScale": "灵摆刻度",
"Attack": "攻击力",
"Defense": "守备力",
"Select": "请选择",
"Minimum": "最小值",
"Maximum": "最大值",
"Confirm": "确 定",
"Cancel": "取 消"
},
"CardDetails": {
"Level": "等级",
"Type": "类型",
"Attribute": "类型",
"Race": "种族",
"Attack": "攻击力",
"Defence": "守备力",
"PendulumScale": "灵摆刻度",
"MonsterEffect": "怪兽效果",
"PendulumEffect": "灵摆效果",
"CardEffect": "卡片效果"
},
"Store": {
"CannotAddTokens": "不能添加衍生物",
"CardTypeDoesNotMatch": "卡片种类不符合"
}
}
\ No newline at end of file
......@@ -36,5 +36,58 @@
"ReplayDesc": "Freely watch past duels and relive those exciting moments of reversal.",
"WIPTitle": "Under development...",
"WIPDesc": "Stay tuned for other features."
},
"BuildDeck": {
"EnterTheDeckName": "Deck name",
"Shuffle": "Shuffle",
"Sort": "Sort",
"Clear": "Clear",
"Reset": "Reset",
"Save": "Save",
"QuestionCircleTooltip": "Double-click to add a card, right-click to remove a card, press the mouse wheel to switch cards between the main deck and the side deck.",
"Filter": "Filter",
"SortBy": "Sort By ",
"KeywordsPlaceholder": "Keywords (separated by spaces)",
"FromNewToOld": "From new to old",
"FromOldToNew": "From old to new",
"AttackPowerFromHighToLow": "Attack power from high to low",
"AttackPowerFromLowToHigh": "Attack power from low to high",
"DefensePowerFromHighToLow": "Defense power from high to low",
"DefensePowerFromLowToHigh": "Defense power from low to high",
"StarsRanksLevelsLinkFromHighToLow": "Stars/Ranks/Levels/Link from high to low",
"StarsRanksLevelsLinkFromLowToHigh": "Stars/Ranks/Levels/Link from low to high",
"PendulumScaleFromHighToLow": "Pendulum Scale from high to low",
"PendulumScaleFromLowToHigh": "Pendulum Scale from low to high"
},
"Filter": {
"CardFilter": "Card Filter",
"Attribute": "Attribute",
"Race": "Race",
"Type": "Type",
"Level": "Level",
"PendulumScale": "Pendulum Scale",
"Attack": "Attack",
"Defense": "Defense",
"Select": "-Select-",
"Minimum": "Min",
"Maximum": "Max",
"Confirm": "Confirm",
"Cancel": "Cancel"
},
"CardDetails": {
"Level": "Level",
"Type": "Type",
"Attribute": "Attribute",
"Race": "Race",
"Attack": "Attack",
"Defence": "Defence",
"PendulumScale": "Pendulum Scale",
"MonsterEffect": "Monster Effect",
"PendulumEffect": "Pendulum Effect",
"CardEffect": "Card Effect"
},
"Store": {
"CannotAddTokens": "Cannot add Tokens",
"CardTypeDoesNotMatch": "The card type does not match"
}
}
\ No newline at end of file
......@@ -18,11 +18,19 @@ const resources = {
Header: translationChinese.Header,
Start: translationChinese.Start,
Match: translationChinese.Match,
BuildDeck: translationChinese.BuildDeck,
Filter: translationChinese.Filter,
CardDetails: translationChinese.CardDetails,
Store: translationChinese.Store,
},
en: {
Header: translationEnglish.Header,
Start: translationEnglish.Start,
Match: translationEnglish.Match,
BuildDeck: translationEnglish.BuildDeck,
Filter: translationEnglish.Filter,
CardDetails: translationEnglish.CardDetails,
Store: translationChinese.Store,
},
es: {
Header: translationSpanish.Header,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment