The root .gitignore had `lib/` which matched frontend/src/lib/, causing Docker build to fail with "Module not found: Can't resolve '@/lib/utils'" and '@/lib/i18n'. Changed to `/lib/` so it only ignores the Python lib at repo root. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
162 lines
5.2 KiB
TypeScript
162 lines
5.2 KiB
TypeScript
import { API_BASE } from "./config";
|
|
|
|
export interface TranslatedText {
|
|
id: string;
|
|
translated_text: string;
|
|
}
|
|
|
|
export interface Language {
|
|
code: string;
|
|
name: string;
|
|
flag: string;
|
|
}
|
|
|
|
export interface Provider {
|
|
id: string;
|
|
name: string;
|
|
icon?: string;
|
|
description?: string;
|
|
mode?: "classic" | "llm";
|
|
}
|
|
|
|
export const languages: Language[] = [
|
|
{ code: "fr", name: "Français", flag: "🇫🇷" },
|
|
{ code: "en", name: "English", flag: "🇬🇧" },
|
|
{ code: "de", name: "Deutsch", flag: "🇩🇪" },
|
|
{ code: "es", name: "Español", flag: "🇪🇸" },
|
|
{ code: "it", name: "Italiano", flag: "🇮🇹" },
|
|
{ code: "pt", name: "Português", flag: "🇵🇹" },
|
|
{ code: "nl", name: "Nederlands", flag: "🇳🇱" },
|
|
{ code: "ru", name: "Русский", flag: "🇷🇺" },
|
|
{ code: "zh", name: "中文", flag: "🇨🇳" },
|
|
{ code: "ja", name: "日本語", flag: "🇯🇵" },
|
|
{ code: "ko", name: "한국어", flag: "🇰🇷" },
|
|
{ code: "ar", name: "العربية", flag: "🇸🇦" },
|
|
{ code: "fa", name: "فارسی", flag: "🇮🇷" },
|
|
{ code: "hi", name: "हिन्दी", flag: "🇮🇳" },
|
|
{ code: "tr", name: "Türkçe", flag: "🇹🇷" },
|
|
{ code: "pl", name: "Polski", flag: "🇵🇱" },
|
|
{ code: "sv", name: "Svenska", flag: "🇸🇪" },
|
|
{ code: "da", name: "Dansk", flag: "🇩🇰" },
|
|
{ code: "fi", name: "Suomi", flag: "🇫🇮" },
|
|
{ code: "no", name: "Norsk", flag: "🇳🇴" },
|
|
{ code: "cs", name: "Čeština", flag: "🇨🇿" },
|
|
{ code: "ro", name: "Română", flag: "🇷🇴" },
|
|
{ code: "hu", name: "Magyar", flag: "🇭🇺" },
|
|
{ code: "el", name: "Ελληνικά", flag: "🇬🇷" },
|
|
{ code: "he", name: "עברית", flag: "🇮🇱" },
|
|
{ code: "th", name: "ไทย", flag: "🇹🇭" },
|
|
{ code: "vi", name: "Tiếng Việt", flag: "🇻🇳" },
|
|
{ code: "id", name: "Bahasa Indonesia", flag: "🇮🇩" },
|
|
{ code: "uk", name: "Українська", flag: "🇺🇦" },
|
|
];
|
|
|
|
export const providers: Provider[] = [
|
|
{ id: "google", name: "Google Translate", description: "Fast translation, 130+ languages" },
|
|
{ id: "deepl", name: "DeepL", description: "High-quality neural translation" },
|
|
{ id: "openai", name: "OpenAI", description: "GPT-powered context-aware translation", mode: "llm" },
|
|
{ id: "ollama", name: "Ollama (Local)", description: "Self-hosted LLM translation", mode: "llm" },
|
|
{ id: "openrouter", name: "OpenRouter", description: "Multi-model AI translation", mode: "llm" },
|
|
];
|
|
|
|
export async function translateDocument(params: {
|
|
file: File;
|
|
targetLanguage: string;
|
|
provider: string;
|
|
ollamaModel?: string;
|
|
translateImages?: boolean;
|
|
systemPrompt?: string;
|
|
glossary?: string;
|
|
libreUrl?: string;
|
|
openaiApiKey?: string;
|
|
openaiModel?: string;
|
|
openrouterApiKey?: string;
|
|
openrouterModel?: string;
|
|
}): Promise<Blob> {
|
|
const formData = new FormData();
|
|
formData.append("file", params.file);
|
|
formData.append("target_language", params.targetLanguage);
|
|
formData.append("provider", params.provider);
|
|
if (params.ollamaModel) formData.append("ollama_model", params.ollamaModel);
|
|
if (params.translateImages !== undefined)
|
|
formData.append("translate_images", String(params.translateImages));
|
|
if (params.systemPrompt) formData.append("system_prompt", params.systemPrompt);
|
|
if (params.glossary) formData.append("glossary", params.glossary);
|
|
if (params.libreUrl) formData.append("libre_url", params.libreUrl);
|
|
|
|
const token =
|
|
typeof window !== "undefined" ? localStorage.getItem("token") : null;
|
|
const headers: Record<string, string> = {};
|
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
|
|
const res = await fetch(`${API_BASE}/api/v1/translate`, {
|
|
method: "POST",
|
|
headers,
|
|
body: formData,
|
|
});
|
|
|
|
if (!res.ok) {
|
|
let msg = `HTTP ${res.status}`;
|
|
try {
|
|
const j = await res.json();
|
|
msg = j.message || j.error || msg;
|
|
} catch {}
|
|
throw new Error(msg);
|
|
}
|
|
|
|
return res.blob();
|
|
}
|
|
|
|
export async function extractTextsFromDocument(
|
|
file: File,
|
|
): Promise<{ texts: Array<{ id: string; text: string }>; session_id: string }> {
|
|
const formData = new FormData();
|
|
formData.append("file", file);
|
|
|
|
const token =
|
|
typeof window !== "undefined" ? localStorage.getItem("token") : null;
|
|
const headers: Record<string, string> = {};
|
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
|
|
const res = await fetch(`${API_BASE}/api/v1/extract`, {
|
|
method: "POST",
|
|
headers,
|
|
body: formData,
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Extraction failed: HTTP ${res.status}`);
|
|
}
|
|
|
|
return res.json();
|
|
}
|
|
|
|
export async function reconstructDocument(
|
|
sessionId: string,
|
|
translations: TranslatedText[],
|
|
targetLanguage: string,
|
|
): Promise<Blob> {
|
|
const token =
|
|
typeof window !== "undefined" ? localStorage.getItem("token") : null;
|
|
const headers: Record<string, string> = {
|
|
"Content-Type": "application/json",
|
|
};
|
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
|
|
const res = await fetch(`${API_BASE}/api/v1/reconstruct`, {
|
|
method: "POST",
|
|
headers,
|
|
body: JSON.stringify({
|
|
session_id: sessionId,
|
|
translations,
|
|
target_language: targetLanguage,
|
|
}),
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Reconstruction failed: HTTP ${res.status}`);
|
|
}
|
|
|
|
return res.blob();
|
|
}
|