I18n-Translate-It/src/components/biz/sync-from-files-button.tsx

107 lines
3.2 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 { useState } from "react";
import { Button } from "@/components/ui/button";
import { useFileConnections } from "@/store/file-connection";
import { toast } from "sonner";
import { upsertLanguageTranslations } from "@/lib/db";
import { flattenValues } from "@/lib/i18n-structure";
import { RefreshCw } from "lucide-react";
type Props = {
projectId: string;
onSynced: () => void | Promise<void>;
disabled?: boolean;
};
async function ensureReadPermission(handle: FileSystemFileHandle): Promise<boolean> {
try {
// @ts-expect-error - queryPermission/requestPermission exist in supporting browsers
const q = await handle.queryPermission?.({ mode: "read" });
if (q === "granted") return true;
// @ts-expect-error - requestPermission exist in supporting browsers
const r = await handle.requestPermission?.({ mode: "read" });
return r === "granted";
} catch {
try {
// Fallback: try to read once; if it throws, we treat as no permission
const f = await handle.getFile();
// Touch the file object so TS doesn't complain about unused variable
if (!f) return false;
return true;
} catch {
return false;
}
}
}
export function SyncFromFilesButton({ projectId, onSynced, disabled }: Props) {
const [syncing, setSyncing] = useState(false);
const connSnap = useFileConnections(projectId);
const hasConnections = Object.keys(connSnap.connections).length > 0;
async function handleSync() {
if (!projectId) return;
if (!hasConnections) {
toast.info("没有已连接的语言");
return;
}
setSyncing(true);
try {
const entries = Object.entries(connSnap.connections);
const results = await Promise.allSettled(
entries.map(async ([lang, conn]) => {
const canRead = await ensureReadPermission(conn.handle);
if (!canRead) throw new Error(`${lang}: 无读取权限`);
const file = await conn.handle.getFile();
const text = await file.text();
let json: unknown;
try {
json = JSON.parse(text);
} catch {
throw new Error(`${lang}: JSON 解析失败`);
}
const values = flattenValues(json);
await upsertLanguageTranslations(projectId, lang, values);
return lang;
})
);
const failed: string[] = [];
let success = 0;
for (const r of results) {
if (r.status === "fulfilled") success += 1;
else failed.push((r.reason as Error)?.message || "未知语言");
}
if (failed.length === 0) {
toast.success(`同步完成(${success}`);
} else if (success === 0) {
toast.error(`全部失败(${failed.length}${failed.join("")}`);
} else {
toast.warning(`部分成功(成功 ${success},失败 ${failed.length}${failed.join("")}`);
}
} finally {
setSyncing(false);
await onSynced?.();
}
}
return (
<Button
variant="outline"
onClick={handleSync}
disabled={syncing || disabled || !hasConnections}
title={!hasConnections ? "暂无已连接的语言" : "读取已连接文件并导入翻译"}
>
<RefreshCw />
{syncing ? "读取中..." : "一键读取"}
</Button>
);
}