Skip to content

Commit 53ea1c3

Browse files
committed
feat(i18n): 为隧道管理相关模态框添加多语言支持,替换硬编码文本为国际化字符串
1 parent e3492eb commit 53ea1c3

10 files changed

Lines changed: 456 additions & 150 deletions

File tree

web/src/components/tunnels/instance-tag-modal.tsx

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import { parseDate } from "@internationalized/date";
2020
import { addToast } from "@heroui/toast";
2121
import Editor from "@monaco-editor/react";
22+
import { useTranslation } from "react-i18next";
2223

2324
interface InstanceTagModalProps {
2425
isOpen: boolean;
@@ -70,6 +71,8 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
7071
currentTags = {},
7172
onSaved,
7273
}) => {
74+
const { t } = useTranslation("tunnels");
75+
7376
// 标准字段状态
7477
const [startDate, setStartDate] = useState<string>("");
7578
const [endDate, setEndDate] = useState<string>("");
@@ -184,7 +187,8 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
184187
}
185188

186189
// 检查是否为免费
187-
if (amount.toLowerCase().includes("免费") || amount.toLowerCase().includes("free")) {
190+
const freeText = t("instanceTagModal.amountType.free").toLowerCase();
191+
if (amount.toLowerCase().includes(freeText) || amount.toLowerCase().includes("free")) {
188192
setAmountType("free");
189193
setAmountValue("");
190194
return;
@@ -441,12 +445,12 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
441445
});
442446

443447
if (!response.ok) {
444-
throw new Error("保存标签失败");
448+
throw new Error(t("instanceTagModal.toast.saveFailedMessage"));
445449
}
446450

447451
addToast({
448-
title: "保存成功",
449-
description: "实例标签已更新",
452+
title: t("instanceTagModal.toast.saveSuccess"),
453+
description: t("instanceTagModal.toast.saveSuccessDesc"),
450454
color: "success",
451455
});
452456

@@ -455,8 +459,8 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
455459
} catch (error) {
456460
console.error("保存标签失败:", error);
457461
addToast({
458-
title: "保存失败",
459-
description: error instanceof Error ? error.message : "未知错误",
462+
title: t("instanceTagModal.toast.saveFailed"),
463+
description: error instanceof Error ? error.message : t("instanceTagModal.toast.unknownError"),
460464
color: "danger",
461465
});
462466
} finally {
@@ -476,16 +480,16 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
476480
{(onClose) => (
477481
<>
478482
<ModalHeader className="flex flex-col gap-1 pb-0">
479-
<h2 className="text-xl font-semibold">设置标签</h2>
483+
<h2 className="text-xl font-semibold">{t("instanceTagModal.title")}</h2>
480484
</ModalHeader>
481485
<ModalBody>
482486
<Tabs
483487
fullWidth
484488
selectedKey={activeTab}
485489
onSelectionChange={handleTabChange}
486-
aria-label="编辑模式"
490+
aria-label={t("instanceTagModal.tabs.ariaLabel")}
487491
>
488-
<Tab key="json" title="JSON 编辑">
492+
<Tab key="json" title={t("instanceTagModal.tabs.json")}>
489493
<div className={`border rounded-lg overflow-hidden ${isJsonError ? 'border-danger' : 'border-default-200'}`}>
490494
<Editor
491495
defaultLanguage="json"
@@ -508,13 +512,13 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
508512
/>
509513
</div>
510514
</Tab>
511-
<Tab key="template" title="模板编辑">
515+
<Tab key="template" title={t("instanceTagModal.tabs.template")}>
512516
<div className="space-y-4 ">
513517
{/* 日期字段 */}
514518
<div className="grid grid-cols-2 gap-4">
515519
{/* 开始日期 */}
516520
<div className="flex flex-col gap-2">
517-
<label className="text-sm font-medium text-default-700">开始日期</label>
521+
<label className="text-sm font-medium text-default-700">{t("instanceTagModal.fields.startDate")}</label>
518522
<DatePicker
519523
value={startDate ? (parseDate(startDate.split('T')[0]) as any) : undefined}
520524
onChange={(date) => {
@@ -536,7 +540,7 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
536540
{/* 结束日期 */}
537541
<div className="flex flex-col gap-1.5">
538542
<div className="flex items-center justify-between">
539-
<label className="text-sm font-medium text-default-700">结束日期</label>
543+
<label className="text-sm font-medium text-default-700">{t("instanceTagModal.fields.endDate")}</label>
540544
<Checkbox
541545
isSelected={isUnlimited}
542546
onValueChange={(checked) => {
@@ -547,7 +551,7 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
547551
}}
548552
size="sm"
549553
>
550-
无限期
554+
{t("instanceTagModal.fields.unlimited")}
551555
</Checkbox>
552556
</div>
553557
<DatePicker
@@ -574,25 +578,25 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
574578
{/* 金额字段 */}
575579
<div className="flex flex-col gap-1.5">
576580
<div className="flex items-center justify-between">
577-
<label className="text-sm font-medium text-default-700">金额</label>
581+
<label className="text-sm font-medium text-default-700">{t("instanceTagModal.fields.amount")}</label>
578582
<RadioGroup
579583
value={amountType}
580584
onValueChange={setAmountType}
581585
orientation="horizontal"
582586
className="gap-2"
583587
size="sm"
584588
>
585-
<Radio value="none">无格式</Radio>
586-
<Radio value="prefix">前缀</Radio>
587-
<Radio value="suffix">后缀</Radio>
588-
<Radio value="free">免费</Radio>
589+
<Radio value="none">{t("instanceTagModal.amountType.none")}</Radio>
590+
<Radio value="prefix">{t("instanceTagModal.amountType.prefix")}</Radio>
591+
<Radio value="suffix">{t("instanceTagModal.amountType.suffix")}</Radio>
592+
<Radio value="free">{t("instanceTagModal.amountType.free")}</Radio>
589593
</RadioGroup>
590594
</div>
591595

592596
{/* 金额输入区域 */}
593597
{amountType === "none" && (
594598
<Input
595-
placeholder="输入金额"
599+
placeholder={t("instanceTagModal.fields.amountPlaceholder")}
596600
value={amountValue}
597601
onValueChange={setAmountValue}
598602
variant="bordered"
@@ -609,7 +613,7 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
609613
}}
610614
className="w-32"
611615
variant="bordered"
612-
aria-label="货币符号"
616+
aria-label={t("instanceTagModal.ariaLabels.currencySymbol")}
613617
>
614618
{CURRENCY_OPTIONS.map((currency) => (
615619
<SelectItem key={currency.key}>
@@ -618,7 +622,7 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
618622
))}
619623
</Select>
620624
<Input
621-
placeholder="输入金额"
625+
placeholder={t("instanceTagModal.fields.amountPlaceholder")}
622626
value={amountValue}
623627
onValueChange={setAmountValue}
624628
variant="bordered"
@@ -635,7 +639,7 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
635639
{amountType === "suffix" && (
636640
<div className="flex gap-2">
637641
<Input
638-
placeholder="输入金额"
642+
placeholder={t("instanceTagModal.fields.amountPlaceholder")}
639643
value={amountValue}
640644
onValueChange={setAmountValue}
641645
variant="bordered"
@@ -654,7 +658,7 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
654658
}}
655659
className="w-32"
656660
variant="bordered"
657-
aria-label="货币代码"
661+
aria-label={t("instanceTagModal.ariaLabels.currencyCode")}
658662
>
659663
{CURRENCY_CODES.map((currency) => (
660664
<SelectItem key={currency.key}>
@@ -679,9 +683,9 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
679683
<div className="grid grid-cols-2 gap-4">
680684
{/* 带宽 */}
681685
<div className="flex flex-col gap-1.5">
682-
<label className="text-sm font-medium text-default-700">带宽</label>
686+
<label className="text-sm font-medium text-default-700">{t("instanceTagModal.fields.bandwidth")}</label>
683687
<Input
684-
placeholder="输入带宽数值"
688+
placeholder={t("instanceTagModal.fields.bandwidthPlaceholder")}
685689
value={bandwidthValue}
686690
onValueChange={setBandwidthValue}
687691
variant="bordered"
@@ -703,9 +707,9 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
703707

704708
{/* 流量 */}
705709
<div className="flex flex-col gap-1.5">
706-
<label className="text-sm font-medium text-default-700">流量</label>
710+
<label className="text-sm font-medium text-default-700">{t("instanceTagModal.fields.traffic")}</label>
707711
<Input
708-
placeholder="输入流量数值"
712+
placeholder={t("instanceTagModal.fields.trafficPlaceholder")}
709713
value={trafficValue}
710714
onValueChange={setTrafficValue}
711715
variant="bordered"
@@ -730,9 +734,9 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
730734
<div className="grid grid-cols-2 gap-4">
731735
{/* 网络路由 */}
732736
<div className="flex flex-col gap-1.5">
733-
<label className="text-sm font-medium text-default-700">网络路由</label>
737+
<label className="text-sm font-medium text-default-700">{t("instanceTagModal.fields.networkRoute")}</label>
734738
<Input
735-
placeholder="例如:4837、9929、联通 等"
739+
placeholder={t("instanceTagModal.fields.networkRoutePlaceholder")}
736740
value={networkRoute}
737741
onValueChange={setNetworkRoute}
738742
variant="bordered"
@@ -741,9 +745,9 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
741745

742746
{/* 额外信息 */}
743747
<div className="flex flex-col gap-1.5">
744-
<label className="text-sm font-medium text-default-700">额外信息</label>
748+
<label className="text-sm font-medium text-default-700">{t("instanceTagModal.fields.extra")}</label>
745749
<Input
746-
placeholder="使用逗号分隔多个信息"
750+
placeholder={t("instanceTagModal.fields.extraPlaceholder")}
747751
value={extra}
748752
onValueChange={setExtra}
749753
variant="bordered"
@@ -756,15 +760,15 @@ const InstanceTagModal: React.FC<InstanceTagModalProps> = ({
756760
</ModalBody>
757761
<ModalFooter className="pt-0">
758762
<Button color="danger" variant="light" onPress={onClose}>
759-
取消
763+
{t("instanceTagModal.buttons.cancel")}
760764
</Button>
761765
<Button
762766
color="primary"
763767
onPress={handleSave}
764768
isLoading={isSaving}
765769
isDisabled={isJsonError}
766770
>
767-
保存
771+
{t("instanceTagModal.buttons.save")}
768772
</Button>
769773
</ModalFooter>
770774
</>

web/src/components/tunnels/simple-tag-modal.tsx

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
1414
import { faTag, faTimes } from "@fortawesome/free-solid-svg-icons";
1515
import { addToast } from "@heroui/toast";
16+
import { useTranslation } from "react-i18next";
1617

1718
import { buildApiUrl } from "@/lib/utils";
1819

@@ -37,6 +38,7 @@ export default function SimpleTagModal({
3738
currentTag,
3839
onSaved,
3940
}: SimpleTagModalProps) {
41+
const { t } = useTranslation("tunnels");
4042
const [tags, setTags] = useState<Tag[]>([]);
4143
const [loading, setLoading] = useState(false);
4244
const [saving, setSaving] = useState(false);
@@ -50,15 +52,15 @@ export default function SimpleTagModal({
5052
setLoading(true);
5153
const response = await fetch(buildApiUrl("/api/tags"));
5254

53-
if (!response.ok) throw new Error("获取标签列表失败");
55+
if (!response.ok) throw new Error(t("simpleTagModal.toast.fetchFailedMessage"));
5456
const data = await response.json();
5557

5658
setTags(data.tags || []);
5759
} catch (error) {
5860
console.error("获取标签列表失败:", error);
5961
addToast({
60-
title: "错误",
61-
description: "获取标签列表失败",
62+
title: t("simpleTagModal.toast.fetchFailed"),
63+
description: t("simpleTagModal.toast.fetchFailedDesc"),
6264
color: "danger",
6365
});
6466
} finally {
@@ -85,12 +87,12 @@ export default function SimpleTagModal({
8587
if (!response.ok) {
8688
const error = await response.json();
8789

88-
throw new Error(error.message || "设置标签失败");
90+
throw new Error(error.message || t("simpleTagModal.toast.setFailedMessage"));
8991
}
9092

9193
addToast({
92-
title: "成功",
93-
description: "标签设置成功",
94+
title: t("simpleTagModal.toast.setSuccess"),
95+
description: t("simpleTagModal.toast.setSuccessDesc"),
9496
color: "success",
9597
});
9698

@@ -99,8 +101,8 @@ export default function SimpleTagModal({
99101
} catch (error) {
100102
console.error("设置标签失败:", error);
101103
addToast({
102-
title: "错误",
103-
description: error instanceof Error ? error.message : "设置标签失败",
104+
title: t("simpleTagModal.toast.setFailed"),
105+
description: error instanceof Error ? error.message : t("simpleTagModal.toast.setFailedMessage"),
104106
color: "danger",
105107
});
106108
} finally {
@@ -127,12 +129,12 @@ export default function SimpleTagModal({
127129
if (!response.ok) {
128130
const error = await response.json();
129131

130-
throw new Error(error.message || "清除标签失败");
132+
throw new Error(error.message || t("simpleTagModal.toast.clearFailedMessage"));
131133
}
132134

133135
addToast({
134-
title: "成功",
135-
description: "标签已清除",
136+
title: t("simpleTagModal.toast.clearSuccess"),
137+
description: t("simpleTagModal.toast.clearSuccessDesc"),
136138
color: "success",
137139
});
138140

@@ -141,8 +143,8 @@ export default function SimpleTagModal({
141143
} catch (error) {
142144
console.error("清除标签失败:", error);
143145
addToast({
144-
title: "错误",
145-
description: error instanceof Error ? error.message : "清除标签失败",
146+
title: t("simpleTagModal.toast.clearFailed"),
147+
description: error instanceof Error ? error.message : t("simpleTagModal.toast.clearFailedMessage"),
146148
color: "danger",
147149
});
148150
} finally {
@@ -166,7 +168,7 @@ export default function SimpleTagModal({
166168
<ModalHeader className="flex flex-col gap-1">
167169
<div className="flex items-center gap-2">
168170
<FontAwesomeIcon className="text-primary" icon={faTag} />
169-
设置标签
171+
{t("simpleTagModal.title")}
170172
</div>
171173
</ModalHeader>
172174
<ModalBody>
@@ -177,7 +179,7 @@ export default function SimpleTagModal({
177179
) : (
178180
<div className="space-y-4">
179181
<p className="text-sm text-default-600">
180-
为当前实例选择一个标签,或选择"无标签"来清除现有标签
182+
{t("simpleTagModal.description")}
181183
</p>
182184

183185
{/* 无标签选项 */}
@@ -188,7 +190,7 @@ export default function SimpleTagModal({
188190
<div
189191
className={`w-4 h-4 rounded-full border-2 ${selectedTagId === null ? "border-primary bg-primary" : "border-default-300"}`}
190192
/>
191-
<span className="text-sm">无标签</span>
193+
<span className="text-sm">{t("simpleTagModal.noTag")}</span>
192194
</div>
193195

194196
{/* 标签列表 */}
@@ -214,9 +216,9 @@ export default function SimpleTagModal({
214216
className="text-xl text-default-400"
215217
icon={faTag}
216218
/>
217-
<p className="text-sm text-default-500">暂无可用标签</p>
219+
<p className="text-sm text-default-500">{t("simpleTagModal.empty.title")}</p>
218220
<p className="text-xs text-default-400">
219-
请先在标签管理中创建标签
221+
{t("simpleTagModal.empty.description")}
220222
</p>
221223
</div>
222224
</div>
@@ -231,7 +233,7 @@ export default function SimpleTagModal({
231233
variant="light"
232234
onPress={onClose}
233235
>
234-
取消
236+
{t("simpleTagModal.buttons.cancel")}
235237
</Button>
236238
{currentTag && (
237239
<Button
@@ -241,7 +243,7 @@ export default function SimpleTagModal({
241243
variant="light"
242244
onPress={handleClear}
243245
>
244-
清除标签
246+
{t("simpleTagModal.buttons.clear")}
245247
</Button>
246248
)}
247249
<Button
@@ -250,7 +252,7 @@ export default function SimpleTagModal({
250252
isLoading={saving}
251253
onPress={handleSave}
252254
>
253-
保存
255+
{t("simpleTagModal.buttons.save")}
254256
</Button>
255257
</ModalFooter>
256258
</>

0 commit comments

Comments
 (0)