Feat: 粘贴剪贴板中不同语言的翻译值
This commit is contained in:
parent
8335970e2e
commit
d3f78f40d8
|
|
@ -3,7 +3,7 @@ import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMe
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { ArrowBigDownDash, ArrowBigUpDash, Brackets, CaseSensitive, Copy, Filter, Languages, LocateFixed, MoreVertical, PencilLine, Trash2 } from "lucide-react";
|
||||
import { ArrowBigDownDash, ArrowBigUpDash, Brackets, CaseSensitive, ClipboardPaste, Copy, Filter, Languages, LocateFixed, MoreVertical, PencilLine, Trash2 } from "lucide-react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import type React from "react";
|
||||
import { TableVirtuoso, type TableVirtuosoHandle } from "react-virtuoso";
|
||||
|
|
@ -145,6 +145,46 @@ export default function EditorTable({
|
|||
toast.success("复制成功");
|
||||
};
|
||||
|
||||
const handlePasteAllValues = async () => {
|
||||
if (!navigator.clipboard?.readText) {
|
||||
toast.error("当前环境不支持读取剪贴板");
|
||||
return;
|
||||
}
|
||||
|
||||
let parsed: unknown;
|
||||
try {
|
||||
const text = await navigator.clipboard.readText();
|
||||
parsed = JSON.parse(text);
|
||||
} catch {
|
||||
toast.error("读取或解析剪贴板失败");
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
||||
toast.error("剪贴板内容不是有效的语言值 JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
const valuesToPaste: Record<string, string> = {};
|
||||
for (const [lang, value] of Object.entries(parsed)) {
|
||||
if (languages.includes(lang) && typeof value === "string") {
|
||||
valuesToPaste[lang] = value;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const pastedCount = await inlineEdit.updateValuesForPath(entry.path, valuesToPaste);
|
||||
if (pastedCount === 0) {
|
||||
toast.error("剪贴板中没有可粘贴的语言值");
|
||||
return;
|
||||
}
|
||||
|
||||
toast.success(`已粘贴 ${pastedCount} 种语言的值`);
|
||||
} catch {
|
||||
toast.error("粘贴语言值失败");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<td className="sticky left-0 px-3 py-2 bg-white">
|
||||
|
|
@ -236,6 +276,10 @@ export default function EditorTable({
|
|||
<Copy />
|
||||
复制所有语言值
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => { void handlePasteAllValues(); }}>
|
||||
<ClipboardPaste />
|
||||
粘贴语言值
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onSelect={async () => {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,31 @@ export function useTranslationInlineEdit(opts: {
|
|||
return valuesByLang[lang]?.[path] ?? "";
|
||||
}
|
||||
|
||||
async function updateValuesForPath(path: string, values: Record<string, string>) {
|
||||
if (!projectId) return 0;
|
||||
const entries = Object.entries(values);
|
||||
if (entries.length === 0) return 0;
|
||||
|
||||
const updates: ValuesByLang = {};
|
||||
|
||||
try {
|
||||
await Promise.all(
|
||||
entries.map(async ([lang, value]) => {
|
||||
const prev = valuesByLang[lang] ?? {};
|
||||
const next = { ...prev, [path]: value };
|
||||
updates[lang] = next;
|
||||
await upsertLanguageTranslations(projectId, lang, next);
|
||||
})
|
||||
);
|
||||
setValuesByLang((old) => ({ ...old, ...updates }));
|
||||
cancelEdit();
|
||||
return entries.length;
|
||||
} catch (e) {
|
||||
onError?.((e as Error)?.message ?? "保存失败");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveEdit() {
|
||||
if (!projectId || !editingKey) return;
|
||||
if (savingKey === editingKey) return;
|
||||
|
|
@ -87,6 +112,7 @@ export function useTranslationInlineEdit(opts: {
|
|||
startEdit,
|
||||
cancelEdit,
|
||||
setEditingValue,
|
||||
updateValuesForPath,
|
||||
saveEdit,
|
||||
handleKeyDown,
|
||||
} as const;
|
||||
|
|
|
|||
Loading…
Reference in New Issue