Skip to content

Commit 2c0f608

Browse files
feat(i18n): enhance i18n coverage (#582)
## 📝 Pull Request Template ### 1. Related Issue Closes # (issue number) ### 2. Type of Change (select one) Type of Change: New Feature ### 3. Description Please describe the changes made and why they are necessary. ### 4. Testing - [x] I have tested this locally. - [ ] I have updated or added relevant tests. ### 5. Checklist - [x] I have read the [Code of Conduct](./CODE_OF_CONDUCT.md) - [x] I have followed the [Contributing Guidelines](./CONTRIBUTING.md) - [x] My changes follow the project's coding style
1 parent f8c70c2 commit 2c0f608

15 files changed

Lines changed: 364 additions & 155 deletions

File tree

frontend/src/api/system.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
22
import { toast } from "sonner";
33
import { API_QUERY_KEYS, VALUECELL_BACKEND_URL } from "@/constants/api";
4+
import i18n from "@/i18n";
45
import { type ApiResponse, apiClient } from "@/lib/api-client";
56
import { useLanguage } from "@/store/settings-store";
67
import { useSystemStore } from "@/store/system-store";
@@ -111,7 +112,7 @@ export const usePublishStrategy = () => {
111112
);
112113
},
113114
onSuccess: () => {
114-
toast.success("Strategy published successfully");
115+
toast.success(i18n.t("strategy.toast.published"));
115116
queryClient.invalidateQueries({
116117
queryKey: API_QUERY_KEYS.SYSTEM.strategyList([]),
117118
});

frontend/src/app/agent/components/strategy-items/modals/create-strategy-modal.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useStore } from "@tanstack/react-form";
22
import { AlertCircleIcon } from "lucide-react";
33
import type { FC, RefObject } from "react";
4-
import { memo, useImperativeHandle, useState } from "react";
4+
import { memo, useImperativeHandle, useMemo, useState } from "react";
55
import { useTranslation } from "react-i18next";
66
import { useGetModelProviderDetail } from "@/api/setting";
77
import {
@@ -29,9 +29,9 @@ import { TradingStrategyForm } from "@/components/valuecell/form/trading-strateg
2929
import { StepIndicator } from "@/components/valuecell/step-indicator";
3030
import { TRADING_SYMBOLS } from "@/constants/agent";
3131
import {
32-
aiModelSchema,
33-
exchangeSchema,
34-
tradingStrategySchema,
32+
createAiModelSchema,
33+
createExchangeSchema,
34+
createTradingStrategySchema,
3535
} from "@/constants/schema";
3636
import { useAppForm } from "@/hooks/use-form";
3737
import { tracker } from "@/lib/tracker";
@@ -50,6 +50,12 @@ const CreateStrategyModal: FC<CreateStrategyModalProps> = ({
5050
children,
5151
}) => {
5252
const { t } = useTranslation();
53+
const aiModelSchema = useMemo(() => createAiModelSchema(t), [t]);
54+
const exchangeSchema = useMemo(() => createExchangeSchema(t), [t]);
55+
const tradingStrategySchema = useMemo(
56+
() => createTradingStrategySchema(t),
57+
[t],
58+
);
5359
const [open, setOpen] = useState(false);
5460
const [currentStep, setCurrentStep] = useState(1);
5561
const [error, setError] = useState<string | null>(null);

frontend/src/app/agent/components/strategy-items/modals/share-portfolio-modal.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
useRef,
1212
useState,
1313
} from "react";
14+
import { useTranslation } from "react-i18next";
1415
import { toast } from "sonner";
1516
import { Button } from "@/components/ui/button";
1617
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
@@ -38,6 +39,7 @@ export interface SharePortfolioCardRef {
3839
const SharePortfolioModal: FC<{
3940
ref?: RefObject<SharePortfolioCardRef | null>;
4041
}> = ({ ref }) => {
42+
const { t } = useTranslation();
4143
const cardRef = useRef<HTMLDivElement>(null);
4244
const [isDownloading, setIsDownloading] = useState(false);
4345

@@ -84,18 +86,21 @@ const SharePortfolioModal: FC<{
8486
await writeFile(path, new Uint8Array(arrayBuffer));
8587

8688
setOpen(false);
87-
toast.success("Image downloaded successfully", {
89+
toast.success(t("sharePortfolio.toast.downloaded"), {
8890
action: {
89-
label: "open file",
91+
label: t("sharePortfolio.toast.openFile"),
9092
onClick: async () => {
9193
return await openPath(path);
9294
},
9395
},
9496
});
9597
} catch (err) {
96-
toast.error(`Failed to download image: ${JSON.stringify(err)}`, {
97-
duration: 6 * 1000,
98-
});
98+
toast.error(
99+
t("sharePortfolio.toast.downloadFailed", {
100+
error: JSON.stringify(err),
101+
}),
102+
{ duration: 6 * 1000 },
103+
);
99104
} finally {
100105
setIsDownloading(false);
101106
}
@@ -116,7 +121,9 @@ const SharePortfolioModal: FC<{
116121
className="h-[600px] w-[434px] overflow-hidden border-none bg-transparent p-0 shadow-none"
117122
showCloseButton={false}
118123
>
119-
<DialogTitle className="sr-only">Share Portfolio</DialogTitle>
124+
<DialogTitle className="sr-only">
125+
{t("sharePortfolio.title")}
126+
</DialogTitle>
120127

121128
{/* Card to be captured */}
122129
<div
@@ -166,10 +173,10 @@ const SharePortfolioModal: FC<{
166173
{formatChange(data.total_pnl, "", 2)}
167174
</span>
168175

169-
<p>Model</p>
176+
<p>{t("sharePortfolio.fields.model")}</p>
170177
<span>{data.llm_model_id}</span>
171178

172-
<p>Exchange</p>
179+
<p>{t("sharePortfolio.fields.exchange")}</p>
173180
<span className="ml-auto flex items-center gap-1">
174181
<PngIcon
175182
src={
@@ -182,7 +189,7 @@ const SharePortfolioModal: FC<{
182189
{data.exchange_id}
183190
</span>
184191

185-
<p>Strategy</p>
192+
<p>{t("sharePortfolio.fields.strategy")}</p>
186193
<span>{data.strategy_type}</span>
187194
</div>
188195

@@ -219,7 +226,7 @@ const SharePortfolioModal: FC<{
219226
className="h-12 flex-1 rounded-xl border-border bg-card font-medium text-base hover:bg-muted"
220227
onClick={() => setOpen(false)}
221228
>
222-
Cancel
229+
{t("strategy.action.cancel")}
223230
</Button>
224231

225232
<Button
@@ -232,7 +239,7 @@ const SharePortfolioModal: FC<{
232239
) : (
233240
<Download className="mr-2 size-5" />
234241
)}
235-
Download
242+
{t("sharePortfolio.action.download")}
236243
</Button>
237244
</div>
238245
</DialogContent>

frontend/src/app/setting/general.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,18 @@ export default function GeneralPage() {
9696
<SelectValue />
9797
</SelectTrigger>
9898
<SelectContent>
99-
<SelectItem value="en">English (United States)</SelectItem>
100-
<SelectItem value="zh_CN">简体中文</SelectItem>
101-
<SelectItem value="zh_TW">繁體中文</SelectItem>
102-
<SelectItem value="ja">日本語</SelectItem>
99+
<SelectItem value="en">
100+
{t("general.language.options.en")}
101+
</SelectItem>
102+
<SelectItem value="zh_CN">
103+
{t("general.language.options.zh_CN")}
104+
</SelectItem>
105+
<SelectItem value="zh_TW">
106+
{t("general.language.options.zh_TW")}
107+
</SelectItem>
108+
<SelectItem value="ja">
109+
{t("general.language.options.ja")}
110+
</SelectItem>
103111
</SelectContent>
104112
</Select>
105113
</Field>

frontend/src/components/valuecell/app/app-sidebar.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
type ReactNode,
66
useMemo,
77
} from "react";
8+
import { useTranslation } from "react-i18next";
89
import { NavLink, useLocation } from "react-router";
910
import { useGetAgentList } from "@/api/agent";
1011
import {
@@ -126,6 +127,7 @@ const SidebarMenuItem: FC<SidebarItemProps> = ({
126127
};
127128

128129
const AppSidebar: FC = () => {
130+
const { t } = useTranslation();
129131
const pathArray = useLocation().pathname.split("/");
130132

131133
const prefix = useMemo(() => {
@@ -144,38 +146,38 @@ const AppSidebar: FC = () => {
144146
{
145147
id: "home",
146148
icon: Logo,
147-
label: "Home",
149+
label: t("nav.home"),
148150
to: "/home",
149151
},
150152
{
151153
id: "strategy",
152154
icon: StrategyAgent,
153-
label: "Strategy",
155+
label: t("nav.strategy"),
154156
to: "/agent/StrategyAgent",
155157
},
156158
{
157159
id: "ranking",
158160
icon: Ranking,
159-
label: "Ranking",
161+
label: t("nav.ranking"),
160162
to: "/ranking",
161163
},
162164
{
163165
id: "market",
164166
icon: Market,
165-
label: "Market",
167+
label: t("nav.market"),
166168
to: "/market",
167169
},
168170
],
169171
config: [
170172
{
171173
id: "setting",
172174
icon: Setting,
173-
label: "Setting",
175+
label: t("nav.setting"),
174176
to: "/setting",
175177
},
176178
],
177179
};
178-
}, []);
180+
}, [t]);
179181

180182
const { data: agentList } = useGetAgentList({ enabled_only: "true" });
181183
const agentItems = useMemo(() => {

frontend/src/components/valuecell/app/backend-health-check.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AnimatePresence, motion } from "framer-motion";
22
import type React from "react";
33
import { useEffect, useState } from "react";
4+
import { useTranslation } from "react-i18next";
45
import { useBackendHealth } from "@/api/system";
56
import ValuecellLogo from "@/assets/png/logo/valuecell-logo.webp";
67
import { Progress } from "@/components/ui/progress";
@@ -10,6 +11,7 @@ export function BackendHealthCheck({
1011
}: {
1112
children: React.ReactNode;
1213
}) {
14+
const { t } = useTranslation();
1315
const { isError, isSuccess } = useBackendHealth();
1416
const [showError, setShowError] = useState(false);
1517
const [progress, setProgress] = useState(0);
@@ -92,7 +94,7 @@ export function BackendHealthCheck({
9294
className="space-y-4"
9395
>
9496
<p className="text-lg text-muted-foreground leading-relaxed">
95-
Setting up environment...
97+
{t("common.settingUpEnvironment")}
9698
</p>
9799
</motion.div>
98100

@@ -106,7 +108,7 @@ export function BackendHealthCheck({
106108
<div className="max-w-lg space-y-2">
107109
<Progress value={progress} className="h-2 w-full bg-muted/50" />
108110
<div className="flex justify-between font-medium text-muted-foreground text-xs uppercase tracking-wider">
109-
<span>Loading</span>
111+
<span>{t("common.loading")}</span>
110112
<span>{Math.round(progress)}%</span>
111113
</div>
112114
</div>

frontend/src/components/valuecell/modal/copy-strategy-modal.tsx

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { useStore } from "@tanstack/react-form";
22
import { AlertCircleIcon } from "lucide-react";
33
import type { FC, RefObject } from "react";
4-
import { memo, useImperativeHandle, useState } from "react";
4+
import { memo, useImperativeHandle, useMemo, useState } from "react";
5+
import { useTranslation } from "react-i18next";
56
import { useGetModelProviderDetail } from "@/api/setting";
67
import {
78
useCreateStrategy,
@@ -27,9 +28,9 @@ import {
2728
import { StepIndicator } from "@/components/valuecell/step-indicator";
2829
import { TRADING_SYMBOLS } from "@/constants/agent";
2930
import {
30-
aiModelSchema,
31-
copyTradingStrategySchema,
32-
exchangeSchema,
31+
createAiModelSchema,
32+
createCopyTradingStrategySchema,
33+
createExchangeSchema,
3334
} from "@/constants/schema";
3435
import { useAppForm } from "@/hooks/use-form";
3536
import { tracker } from "@/lib/tracker";
@@ -45,22 +46,32 @@ interface CopyStrategyModalProps {
4546
callback?: () => void;
4647
}
4748

48-
const STEPS = [
49-
{ step: 1, title: "AI Models" },
50-
{ step: 2, title: "Exchanges" },
51-
{ step: 3, title: "Trading strategy" },
52-
];
53-
5449
const CopyStrategyModal: FC<CopyStrategyModalProps> = ({
5550
ref,
5651
children,
5752
callback,
5853
}) => {
54+
const { t } = useTranslation();
55+
const aiModelSchema = useMemo(() => createAiModelSchema(t), [t]);
56+
const exchangeSchema = useMemo(() => createExchangeSchema(t), [t]);
57+
const copyTradingStrategySchema = useMemo(
58+
() => createCopyTradingStrategySchema(t),
59+
[t],
60+
);
5961
const [open, setOpen] = useState(false);
6062
const [currentStep, setCurrentStep] = useState(1);
6163
const [defaultValues, setDefaultValues] = useState<CopyStrategy>();
6264
const [error, setError] = useState<string | null>(null);
6365

66+
const STEPS = useMemo(
67+
() => [
68+
{ step: 1, title: t("strategy.create.steps.aiModels") },
69+
{ step: 2, title: t("strategy.create.steps.exchanges") },
70+
{ step: 3, title: t("strategy.create.steps.tradingStrategy") },
71+
],
72+
[t],
73+
);
74+
6475
const { data: strategies = [] } = useGetStrategyList();
6576
const { mutateAsync: createStrategy, isPending: isCreatingStrategy } =
6677
useCreateStrategy();
@@ -201,7 +212,7 @@ const CopyStrategyModal: FC<CopyStrategyModalProps> = ({
201212
<DialogTitle className="flex flex-col gap-4 px-1">
202213
<div className="flex items-center justify-between">
203214
<h2 className="font-semibold text-lg">
204-
Duplicate trading strategy
215+
{t("strategy.copy.title")}
205216
</h2>
206217
<CloseButton onClick={resetAll} />
207218
</div>
@@ -230,7 +241,7 @@ const CopyStrategyModal: FC<CopyStrategyModalProps> = ({
230241
{error && (
231242
<Alert variant="destructive">
232243
<AlertCircleIcon />
233-
<AlertTitle>Error Duplicating Strategy</AlertTitle>
244+
<AlertTitle>{t("strategy.copy.error")}</AlertTitle>
234245
<AlertDescription>{error}</AlertDescription>
235246
</Alert>
236247
)}
@@ -241,7 +252,9 @@ const CopyStrategyModal: FC<CopyStrategyModalProps> = ({
241252
onClick={currentStep === 1 ? resetAll : handleBack}
242253
className="py-4 font-semibold text-base"
243254
>
244-
{currentStep === 1 ? "Cancel" : "Back"}
255+
{currentStep === 1
256+
? t("strategy.action.cancel")
257+
: t("strategy.action.back")}
245258
</Button>
246259
<Button
247260
type="button"
@@ -261,7 +274,9 @@ const CopyStrategyModal: FC<CopyStrategyModalProps> = ({
261274
className="relative py-4 font-semibold text-base"
262275
>
263276
{isCreatingStrategy && <Spinner className="absolute left-4" />}
264-
{currentStep === 3 ? "Confirm" : "Next"}
277+
{currentStep === 3
278+
? t("strategy.action.confirm")
279+
: t("strategy.action.next")}
265280
</Button>
266281
</div>
267282
</DialogFooter>

0 commit comments

Comments
 (0)