I18n-Translate-It/src/components/biz/ai-translate-modal.tsx

113 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { memo, useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { requestTranslations } from "@/lib/ai";
type Props = {
open: boolean;
onOpenChange: (next: boolean) => void;
languages: string[];
path: string;
prompt: string | undefined;
onConfirm: (result: Record<string, string>) => Promise<void> | void;
};
function AiTranslateModalImpl({ open, onOpenChange, languages, path, prompt, onConfirm }: Props) {
const [text, setText] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const payload = text.trim();
if (!payload) {
setError("请输入待翻译文本");
return;
}
if (languages.length === 0) {
setError("当前项目暂无目标语言");
return;
}
setLoading(true);
setError(null);
try {
const result = await requestTranslations({ text: payload, languages, prompt });
// 二次校验 keys 完整性
for (const l of languages) {
if (!Object.prototype.hasOwnProperty.call(result, l)) {
throw new Error("AI 返回的 key 不完整");
}
}
await onConfirm(result);
setText("");
onOpenChange(false);
} catch (e) {
setError((e as Error)?.message ?? "AI 翻译失败");
} finally {
setLoading(false);
}
}
return (
<Dialog
open={open}
onOpenChange={(v) => {
if (!loading) {
onOpenChange(v);
if (!v) setError(null);
}
}}
>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>{`AI 翻译 — ${path || "条目"}`}</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-3">
<div>
<label className="text-sm text-muted-foreground"></label>
<Input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="请输入原文本"
aria-label="待翻译文本"
/>
</div>
<div className="text-xs text-muted-foreground">
{languages.join(", ") || "无"}
</div>
{error && (
<div className="text-sm text-red-600" role="alert">{error}</div>
)}
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => {
onOpenChange(false);
setError(null);
}}
disabled={loading}
>
</Button>
<Button type="submit" disabled={loading}>
{loading ? "翻译中..." : "生成翻译"}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}
export const AiTranslateModal = memo(AiTranslateModalImpl);